@panguard-ai/panguard 0.2.3 → 0.2.6
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/bridges/scan-to-report.d.ts +34 -0
- package/dist/bridges/scan-to-report.d.ts.map +1 -0
- package/dist/bridges/scan-to-report.js +28 -0
- package/dist/bridges/scan-to-report.js.map +1 -0
- package/dist/cli/auth-guard.d.ts +3 -2
- package/dist/cli/auth-guard.d.ts.map +1 -1
- package/dist/cli/auth-guard.js +6 -18
- package/dist/cli/auth-guard.js.map +1 -1
- package/dist/cli/commands/admin.d.ts.map +1 -1
- package/dist/cli/commands/admin.js +2 -1
- package/dist/cli/commands/admin.js.map +1 -1
- package/dist/cli/commands/config.d.ts.map +1 -1
- package/dist/cli/commands/config.js +2 -1
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/demo.d.ts.map +1 -1
- package/dist/cli/commands/demo.js +2 -1
- package/dist/cli/commands/demo.js.map +1 -1
- package/dist/cli/commands/deploy.js +34 -1
- package/dist/cli/commands/deploy.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +22 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +502 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/guard.d.ts.map +1 -1
- package/dist/cli/commands/guard.js +209 -0
- package/dist/cli/commands/guard.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +17 -8
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/login.js +5 -2
- package/dist/cli/commands/login.js.map +1 -1
- package/dist/cli/commands/manager.d.ts.map +1 -1
- package/dist/cli/commands/manager.js +43 -34
- package/dist/cli/commands/manager.js.map +1 -1
- package/dist/cli/commands/scan.d.ts.map +1 -1
- package/dist/cli/commands/scan.js +107 -12
- package/dist/cli/commands/scan.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +175 -5
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/threat.d.ts.map +1 -1
- package/dist/cli/commands/threat.js +8 -1
- package/dist/cli/commands/threat.js.map +1 -1
- package/dist/cli/credentials.d.ts +2 -2
- package/dist/cli/credentials.d.ts.map +1 -1
- package/dist/cli/credentials.js +3 -7
- package/dist/cli/credentials.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts +2 -2
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +505 -155
- package/dist/cli/interactive.js.map +1 -1
- package/dist/cli/menu.d.ts +25 -15
- package/dist/cli/menu.d.ts.map +1 -1
- package/dist/cli/menu.js +117 -124
- package/dist/cli/menu.js.map +1 -1
- package/dist/cli/theme.d.ts +1 -1
- package/dist/cli/theme.d.ts.map +1 -1
- package/dist/cli/theme.js +2 -3
- package/dist/cli/theme.js.map +1 -1
- package/dist/cli/ux-helpers.d.ts +4 -0
- package/dist/cli/ux-helpers.d.ts.map +1 -1
- package/dist/cli/ux-helpers.js +10 -0
- package/dist/cli/ux-helpers.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/init/config-writer.d.ts +12 -1
- package/dist/init/config-writer.d.ts.map +1 -1
- package/dist/init/config-writer.js +149 -6
- package/dist/init/config-writer.js.map +1 -1
- package/dist/init/environment.d.ts +9 -0
- package/dist/init/environment.d.ts.map +1 -1
- package/dist/init/environment.js +127 -0
- package/dist/init/environment.js.map +1 -1
- package/dist/init/index.d.ts +4 -4
- package/dist/init/index.d.ts.map +1 -1
- package/dist/init/index.js +3 -3
- package/dist/init/index.js.map +1 -1
- package/dist/init/steps.d.ts +5 -0
- package/dist/init/steps.d.ts.map +1 -1
- package/dist/init/steps.js +155 -1
- package/dist/init/steps.js.map +1 -1
- package/dist/init/types.d.ts +13 -0
- package/dist/init/types.d.ts.map +1 -1
- package/dist/init/wizard-runner.d.ts +3 -2
- package/dist/init/wizard-runner.d.ts.map +1 -1
- package/dist/init/wizard-runner.js +32 -16
- package/dist/init/wizard-runner.js.map +1 -1
- package/package.json +5 -4
- package/dist/manager/manager-server.d.ts +0 -102
- package/dist/manager/manager-server.d.ts.map +0 -1
- package/dist/manager/manager-server.js +0 -515
- package/dist/manager/manager-server.js.map +0 -1
package/dist/cli/interactive.js
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Panguard AI - Interactive CLI Mode
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Number-key menu [0]-[7] with panguard > prompt for text commands.
|
|
5
|
+
* Box-bordered status panel, breadcrumb navigation, no "press any key" interrupts.
|
|
6
6
|
*
|
|
7
7
|
* @module @panguard-ai/panguard/cli/interactive
|
|
8
8
|
*/
|
|
9
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, openSync, closeSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { homedir } from 'node:os';
|
|
12
12
|
import { c, spinner, colorSeverity, formatDuration, box } from '@panguard-ai/core';
|
|
13
13
|
import { PANGUARD_VERSION } from '../index.js';
|
|
14
14
|
import { renderLogo, theme } from './theme.js';
|
|
15
|
-
import {
|
|
15
|
+
import { waitForMainInput, pressAnyKey, cleanupTerminal, renderCompactMenu, waitForCompactChoice, } from './menu.js';
|
|
16
16
|
import { checkFeatureAccess, showUpgradePrompt, getLicense } from './auth-guard.js';
|
|
17
|
-
import {
|
|
18
|
-
import { breadcrumb, nextSteps, confirmDestructive, moduleCountDisplay } from './ux-helpers.js';
|
|
17
|
+
import { breadcrumb, nextSteps, confirmDestructive, moduleCountDisplay, formatError } from './ux-helpers.js';
|
|
19
18
|
// ---------------------------------------------------------------------------
|
|
20
19
|
// Language management
|
|
21
20
|
// ---------------------------------------------------------------------------
|
|
22
21
|
const CONFIG_DIR = join(homedir(), '.panguard');
|
|
23
22
|
const CONFIG_PATH = join(CONFIG_DIR, 'config.json');
|
|
24
23
|
function detectLang() {
|
|
25
|
-
// 1. Read saved preference
|
|
26
24
|
if (existsSync(CONFIG_PATH)) {
|
|
27
25
|
try {
|
|
28
26
|
const data = JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));
|
|
@@ -33,7 +31,6 @@ function detectLang() {
|
|
|
33
31
|
/* ignore */
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
|
-
// 2. Detect from system locale
|
|
37
34
|
const sysLang = process.env['LANG'] ?? process.env['LC_ALL'] ?? '';
|
|
38
35
|
if (sysLang.includes('zh'))
|
|
39
36
|
return 'zh-TW';
|
|
@@ -56,42 +53,201 @@ function saveLang(lang) {
|
|
|
56
53
|
}
|
|
57
54
|
let currentLang = 'en';
|
|
58
55
|
const MENU_DEFS = [
|
|
59
|
-
{ key: 'scan', en: 'Security scan', zh: '\u5B89\u5168\u6383\u63CF', tier: 'community' },
|
|
60
|
-
{ key: 'guard', en: 'Real-time protection', zh: '\u5373\u6642\u9632\u8B77', tier: 'community' },
|
|
61
56
|
{
|
|
62
|
-
key: '
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
66
|
},
|
|
67
67
|
{
|
|
68
|
-
key: '
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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',
|
|
72
77
|
},
|
|
73
|
-
{ key: 'trap', en: 'Honeypot system', zh: '\u871C\u7F50\u7CFB\u7D71', tier: 'solo' },
|
|
74
78
|
{
|
|
75
79
|
key: 'report',
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
icon: '\u25A0',
|
|
81
|
+
number: 2,
|
|
82
|
+
en: 'Compliance Report',
|
|
83
|
+
zh: '\u5408\u898F\u5831\u544A',
|
|
84
|
+
enDesc: 'Generate ISO 27001, SOC 2, TW Cyber Security Act reports',
|
|
85
|
+
zhDesc: '\u7522\u751F ISO 27001\u3001SOC 2\u3001\u8CC7\u5B89\u7BA1\u7406\u6CD5\u5408\u898F\u5831\u544A',
|
|
86
|
+
tierBadge: '[PRO]',
|
|
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',
|
|
105
|
+
zh: '\u871C\u7F50\u7CFB\u7D71',
|
|
106
|
+
enDesc: 'Detect and profile attackers with decoy services',
|
|
107
|
+
zhDesc: '\u5373\u770B\u5373\u8B58\u99ED\u5BA2\uFF0C\u5206\u6790\u653B\u64CA\u8005\u884C\u70BA',
|
|
108
|
+
tierBadge: '[PRO]',
|
|
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: '[STARTER]',
|
|
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: '[ENT]',
|
|
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',
|
|
79
143
|
},
|
|
80
|
-
{ key: '__sep__', en: '', zh: '', tier: '' },
|
|
81
|
-
{ key: 'setup', en: 'Initial configuration', zh: '\u521D\u59CB\u8A2D\u5B9A', tier: 'community' },
|
|
82
|
-
{ key: 'demo', en: 'Feature demo', zh: '\u529F\u80FD\u5C55\u793A', tier: 'community' },
|
|
83
|
-
{ key: 'upgrade', en: 'Upgrade plan', zh: '\u5347\u7D1A\u65B9\u6848', tier: 'community' },
|
|
84
144
|
];
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// Guard process helpers
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
function isGuardRunning() {
|
|
149
|
+
const pidPath = join(homedir(), '.panguard-guard', 'panguard-guard.pid');
|
|
150
|
+
if (!existsSync(pidPath))
|
|
151
|
+
return { running: false };
|
|
152
|
+
try {
|
|
153
|
+
const pid = parseInt(readFileSync(pidPath, 'utf-8').trim(), 10);
|
|
154
|
+
process.kill(pid, 0);
|
|
155
|
+
return { running: true, pid };
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return { running: false };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Status panel with box border
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
function renderStatusPanel() {
|
|
165
|
+
const guardStatus = isGuardRunning();
|
|
166
|
+
const modulesValue = moduleCountDisplay(currentLang);
|
|
167
|
+
const guardLine = guardStatus.running
|
|
168
|
+
? `${c.safe('PROTECTED')} ${c.dim(`(PID: ${guardStatus.pid})`)}`
|
|
169
|
+
: c.dim('Inactive');
|
|
170
|
+
const lines = currentLang === 'zh-TW'
|
|
171
|
+
? [
|
|
172
|
+
`\u9632\u8B77\u72C0\u614B\uFF1A${guardLine}`,
|
|
173
|
+
`\u53EF\u7528\u6A21\u7D44\uFF1A${modulesValue}`,
|
|
174
|
+
`\u7248\u672C\uFF1A v${PANGUARD_VERSION}`,
|
|
175
|
+
]
|
|
176
|
+
: [
|
|
177
|
+
`Protection: ${guardLine}`,
|
|
178
|
+
`Modules: ${modulesValue}`,
|
|
179
|
+
`Version: v${PANGUARD_VERSION}`,
|
|
180
|
+
];
|
|
181
|
+
const title = currentLang === 'zh-TW'
|
|
182
|
+
? '\u7CFB\u7D71\u72C0\u614B / System Status'
|
|
183
|
+
: 'System Status';
|
|
184
|
+
console.log(box(lines.join('\n'), { borderColor: c.sage, title }));
|
|
185
|
+
}
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// Menu rendering
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
function renderMenu() {
|
|
190
|
+
const titleText = currentLang === 'zh-TW'
|
|
191
|
+
? '\u4E3B\u9078\u55AE / Main Menu'
|
|
192
|
+
: 'Main Menu';
|
|
193
|
+
console.log(` ${theme.title(titleText)}`);
|
|
194
|
+
console.log('');
|
|
195
|
+
for (const def of MENU_DEFS) {
|
|
196
|
+
const name = currentLang === 'zh-TW' ? def.zh : def.en;
|
|
197
|
+
const desc = currentLang === 'zh-TW' ? def.zhDesc : def.enDesc;
|
|
198
|
+
const badge = def.tierBadge ? ` ${c.dim(def.tierBadge)}` : '';
|
|
199
|
+
const iconStr = def.icon === '\u2713' ? c.safe(def.icon) : c.dim(def.icon);
|
|
200
|
+
console.log(` ${iconStr} ${c.sage(`[${def.number}]`)} ${name}${badge}`);
|
|
201
|
+
console.log(` ${c.dim(desc)}`);
|
|
202
|
+
console.log('');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// Footer shortcuts
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
function renderFooter() {
|
|
209
|
+
const quit = currentLang === 'zh-TW' ? '\u9000\u51FA' : 'Quit';
|
|
210
|
+
const help = currentLang === 'zh-TW' ? '\u8AAA\u660E' : 'Help';
|
|
211
|
+
const langToggle = currentLang === 'zh-TW' ? '\u4E2D/EN' : '\u4E2D/EN';
|
|
212
|
+
console.log(`${c.dim(`[q] ${quit} [h] ${help} [b] ${langToggle}`)}`);
|
|
213
|
+
console.log('');
|
|
214
|
+
}
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Help display
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
function showHelp() {
|
|
219
|
+
console.log('');
|
|
220
|
+
const title = currentLang === 'zh-TW' ? '\u6307\u4EE4\u8AAA\u660E' : 'Available Commands';
|
|
221
|
+
console.log(` ${theme.brandBold(title)}`);
|
|
222
|
+
console.log('');
|
|
223
|
+
const menuTitle = currentLang === 'zh-TW' ? '\u4E3B\u9078\u55AE' : 'Main Menu';
|
|
224
|
+
console.log(` ${c.sage(menuTitle)}`);
|
|
225
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
226
|
+
? ' [0]-[7] \u6309\u6578\u5B57\u9375\u5373\u523B\u9078\u64C7\uFF08\u4E0D\u9700\u6309 Enter\uFF09'
|
|
227
|
+
: ' [0]-[7] Press number key to select instantly (no Enter needed)'));
|
|
228
|
+
console.log('');
|
|
229
|
+
const promptTitle = currentLang === 'zh-TW' ? '\u6587\u5B57\u6307\u4EE4' : 'Text Commands';
|
|
230
|
+
console.log(` ${c.sage(promptTitle)}`);
|
|
231
|
+
const cmds = [
|
|
232
|
+
{ cmd: 'status', en: 'System status overview', zh: '\u7CFB\u7D71\u72C0\u614B\u7E3D\u89BD' },
|
|
233
|
+
{ cmd: 'login', en: 'Account login', zh: '\u5E33\u865F\u767B\u5165' },
|
|
234
|
+
{ cmd: 'logout', en: 'Account logout', zh: '\u767B\u51FA' },
|
|
235
|
+
{ cmd: 'config', en: 'Settings management', zh: '\u8A2D\u5B9A\u7BA1\u7406' },
|
|
236
|
+
{ cmd: 'upgrade', en: 'Upgrade plan', zh: '\u5347\u7D1A\u65B9\u6848' },
|
|
237
|
+
{ cmd: 'hardening', en: 'Security hardening', zh: '\u5B89\u5168\u52A0\u56FA' },
|
|
238
|
+
{ cmd: 'doctor', en: 'Health diagnostics', zh: '\u5065\u5EB7\u8A3A\u65B7' },
|
|
239
|
+
{ cmd: 'whoami', en: 'Current account info', zh: '\u986F\u793A\u7576\u524D\u5E33\u865F' },
|
|
240
|
+
{ cmd: 'help', en: 'Show this help', zh: '\u986F\u793A\u6B64\u8AAA\u660E' },
|
|
241
|
+
];
|
|
242
|
+
for (const { cmd, en, zh } of cmds) {
|
|
243
|
+
const desc = currentLang === 'zh-TW' ? zh : en;
|
|
244
|
+
console.log(` ${c.sage(cmd.padEnd(14))} ${c.dim(desc)}`);
|
|
245
|
+
}
|
|
246
|
+
const shortcutTitle = currentLang === 'zh-TW' ? '\u5FEB\u6377\u9375' : 'Shortcuts';
|
|
247
|
+
console.log('');
|
|
248
|
+
console.log(` ${c.sage(shortcutTitle)}`);
|
|
249
|
+
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'}`));
|
|
250
|
+
console.log('');
|
|
95
251
|
}
|
|
96
252
|
// ---------------------------------------------------------------------------
|
|
97
253
|
// Startup screen
|
|
@@ -103,28 +259,15 @@ function renderStartup() {
|
|
|
103
259
|
console.log('');
|
|
104
260
|
// Tagline + version
|
|
105
261
|
const tagline = c.dim(' AI-Powered Security Platform');
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
// eslint-disable-next-line no-control-regex
|
|
111
|
-
const verLen = version.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
112
|
-
const totalWidth = Math.max(60, process.stdout.columns ?? 60);
|
|
113
|
-
const gap = Math.max(2, totalWidth - tagLen - verLen - 4);
|
|
114
|
-
console.log(`${tagline}${' '.repeat(gap)}${version}`);
|
|
115
|
-
console.log('');
|
|
116
|
-
// Status info
|
|
117
|
-
const { tier } = getLicense();
|
|
118
|
-
const tierName = tierDisplayName(tier);
|
|
119
|
-
const tierColor = tier === 'community' ? c.caution : c.safe;
|
|
120
|
-
const statusLabel = currentLang === 'zh-TW' ? '\u72C0\u614B' : 'Status';
|
|
121
|
-
const licenseLabel = currentLang === 'zh-TW' ? '\u6388\u6B0A' : 'License';
|
|
122
|
-
const modulesLabel = currentLang === 'zh-TW' ? '\u6A21\u7D44' : 'Modules';
|
|
123
|
-
const modulesValue = moduleCountDisplay(currentLang);
|
|
124
|
-
console.log(` ${c.dim(statusLabel.padEnd(10))} ${c.safe('Ready')}`);
|
|
125
|
-
console.log(` ${c.dim(licenseLabel.padEnd(10))} ${tierColor(tierName)}`);
|
|
126
|
-
console.log(` ${c.dim(modulesLabel.padEnd(10))} ${modulesValue}`);
|
|
262
|
+
console.log(tagline);
|
|
263
|
+
console.log('');
|
|
264
|
+
// Status panel
|
|
265
|
+
renderStatusPanel();
|
|
127
266
|
console.log('');
|
|
267
|
+
// Menu
|
|
268
|
+
renderMenu();
|
|
269
|
+
// Footer
|
|
270
|
+
renderFooter();
|
|
128
271
|
}
|
|
129
272
|
// ---------------------------------------------------------------------------
|
|
130
273
|
// Entry point
|
|
@@ -143,61 +286,159 @@ export async function startInteractive(lang) {
|
|
|
143
286
|
// First-time user hint
|
|
144
287
|
if (!existsSync(CONFIG_PATH)) {
|
|
145
288
|
const hint = currentLang === 'zh-TW'
|
|
146
|
-
? ` ${c.sage('\u25C6')} \u9996\u6B21\u4F7F\u7528\uFF1F\u5EFA\u8B70\
|
|
147
|
-
: ` ${c.sage('\u25C6')} First time?
|
|
289
|
+
? ` ${c.sage('\u25C6')} \u9996\u6B21\u4F7F\u7528\uFF1F\u5EFA\u8B70\u6309 [0] \u57F7\u884C\u521D\u59CB\u8A2D\u5B9A\u6216\u6309 [7] \u529F\u80FD\u5C55\u793A`
|
|
290
|
+
: ` ${c.sage('\u25C6')} First time? Press [0] for Setup Wizard or [7] for Auto Demo`;
|
|
148
291
|
console.log(hint);
|
|
149
292
|
console.log('');
|
|
150
293
|
}
|
|
151
294
|
// Main loop
|
|
152
295
|
while (true) {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
296
|
+
const promptLabel = c.sage('panguard >') + ' ';
|
|
297
|
+
process.stdout.write(promptLabel);
|
|
298
|
+
const input = await waitForMainInput();
|
|
299
|
+
switch (input.type) {
|
|
300
|
+
case 'quit':
|
|
301
|
+
exit();
|
|
302
|
+
return;
|
|
303
|
+
case 'help':
|
|
304
|
+
showHelp();
|
|
305
|
+
continue;
|
|
306
|
+
case 'lang_toggle':
|
|
307
|
+
currentLang = currentLang === 'zh-TW' ? 'en' : 'zh-TW';
|
|
308
|
+
saveLang(currentLang);
|
|
309
|
+
renderStartup();
|
|
310
|
+
continue;
|
|
311
|
+
case 'number': {
|
|
312
|
+
const def = MENU_DEFS[input.index];
|
|
313
|
+
if (!def)
|
|
314
|
+
continue;
|
|
315
|
+
// Feature gate check
|
|
316
|
+
if (!checkFeatureAccess(def.featureKey)) {
|
|
317
|
+
showUpgradePrompt(def.featureKey, currentLang);
|
|
318
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
319
|
+
renderStartup();
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
console.clear();
|
|
323
|
+
try {
|
|
324
|
+
await dispatch(def.key);
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
console.log('');
|
|
328
|
+
console.log(formatError(err instanceof Error ? err.message : String(err), `${currentLang === 'zh-TW' ? '\u57F7\u884C' : 'Running'} ${def.key}`, currentLang === 'zh-TW'
|
|
329
|
+
? '\u8ACB\u67E5\u770B\u65E5\u8A8C\u6216\u91CD\u8A66'
|
|
330
|
+
: 'Check logs or retry'));
|
|
331
|
+
}
|
|
332
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
333
|
+
renderStartup();
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
case 'command': {
|
|
337
|
+
const text = input.text.toLowerCase();
|
|
338
|
+
if (!text)
|
|
339
|
+
continue;
|
|
340
|
+
// Handle text commands
|
|
341
|
+
const handled = await dispatchCommand(text);
|
|
342
|
+
if (!handled) {
|
|
343
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
344
|
+
? ` \u672A\u77E5\u6307\u4EE4\u3002\u8F38\u5165 help \u67E5\u770B\u53EF\u7528\u6307\u4EE4\u3002`
|
|
345
|
+
: ` Unknown command. Type 'help' for available commands.`));
|
|
346
|
+
console.log('');
|
|
347
|
+
}
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
161
350
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// ---------------------------------------------------------------------------
|
|
354
|
+
// Prompt command dispatch
|
|
355
|
+
// ---------------------------------------------------------------------------
|
|
356
|
+
async function dispatchCommand(text) {
|
|
357
|
+
switch (text) {
|
|
358
|
+
case 'status':
|
|
359
|
+
console.clear();
|
|
360
|
+
await actionStatus();
|
|
361
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
165
362
|
renderStartup();
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
await pressAnyKey(currentLang);
|
|
363
|
+
return true;
|
|
364
|
+
case 'login':
|
|
365
|
+
console.clear();
|
|
366
|
+
await actionLogin();
|
|
367
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
172
368
|
renderStartup();
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
369
|
+
return true;
|
|
370
|
+
case 'logout': {
|
|
371
|
+
console.clear();
|
|
372
|
+
const { logoutCommand } = await import('./commands/logout.js');
|
|
373
|
+
const cmd = logoutCommand();
|
|
374
|
+
await cmd.parseAsync(['logout'], { from: 'user' });
|
|
375
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
376
|
+
renderStartup();
|
|
377
|
+
return true;
|
|
178
378
|
}
|
|
179
|
-
|
|
180
|
-
console.
|
|
181
|
-
|
|
379
|
+
case 'config':
|
|
380
|
+
console.clear();
|
|
381
|
+
await actionConfig();
|
|
382
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
383
|
+
renderStartup();
|
|
384
|
+
return true;
|
|
385
|
+
case 'upgrade':
|
|
386
|
+
console.clear();
|
|
387
|
+
await actionUpgrade();
|
|
388
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
389
|
+
renderStartup();
|
|
390
|
+
return true;
|
|
391
|
+
case 'hardening':
|
|
392
|
+
console.clear();
|
|
393
|
+
await actionHardening();
|
|
394
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
395
|
+
renderStartup();
|
|
396
|
+
return true;
|
|
397
|
+
case 'doctor':
|
|
398
|
+
console.clear();
|
|
399
|
+
try {
|
|
400
|
+
const { runDoctor } = await import('./commands/doctor.js');
|
|
401
|
+
await runDoctor(currentLang);
|
|
402
|
+
}
|
|
403
|
+
catch (err) {
|
|
404
|
+
console.log(formatError(err instanceof Error ? err.message : String(err), currentLang === 'zh-TW' ? '\u57F7\u884C\u5065\u5EB7\u8A3A\u65B7' : 'Running diagnostics', currentLang === 'zh-TW' ? '\u8ACB\u91CD\u8A66' : 'Please retry'));
|
|
405
|
+
}
|
|
406
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
407
|
+
renderStartup();
|
|
408
|
+
return true;
|
|
409
|
+
case 'whoami': {
|
|
410
|
+
console.clear();
|
|
411
|
+
const { whoamiCommand } = await import('./commands/whoami.js');
|
|
412
|
+
const cmd = whoamiCommand();
|
|
413
|
+
await cmd.parseAsync(['whoami'], { from: 'user' });
|
|
414
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
415
|
+
renderStartup();
|
|
416
|
+
return true;
|
|
182
417
|
}
|
|
183
|
-
|
|
184
|
-
|
|
418
|
+
case 'help':
|
|
419
|
+
showHelp();
|
|
420
|
+
return true;
|
|
421
|
+
default:
|
|
422
|
+
return false;
|
|
185
423
|
}
|
|
186
424
|
}
|
|
187
425
|
// ---------------------------------------------------------------------------
|
|
188
|
-
// Action dispatch
|
|
426
|
+
// Action dispatch (from numbered menu)
|
|
189
427
|
// ---------------------------------------------------------------------------
|
|
190
428
|
async function dispatch(key) {
|
|
191
429
|
switch (key) {
|
|
430
|
+
case 'setup':
|
|
431
|
+
await actionInit();
|
|
432
|
+
break;
|
|
192
433
|
case 'scan':
|
|
193
434
|
await actionScan();
|
|
194
435
|
break;
|
|
195
|
-
case 'guard':
|
|
196
|
-
await actionGuard();
|
|
197
|
-
break;
|
|
198
436
|
case 'report':
|
|
199
437
|
await actionReport();
|
|
200
438
|
break;
|
|
439
|
+
case 'guard':
|
|
440
|
+
await actionGuard();
|
|
441
|
+
break;
|
|
201
442
|
case 'trap':
|
|
202
443
|
await actionTrap();
|
|
203
444
|
break;
|
|
@@ -207,19 +448,13 @@ async function dispatch(key) {
|
|
|
207
448
|
case 'threat-cloud':
|
|
208
449
|
await actionThreat();
|
|
209
450
|
break;
|
|
210
|
-
case 'setup':
|
|
211
|
-
await actionInit();
|
|
212
|
-
break;
|
|
213
451
|
case 'demo':
|
|
214
452
|
await actionDemo();
|
|
215
453
|
break;
|
|
216
|
-
case 'upgrade':
|
|
217
|
-
await actionUpgrade();
|
|
218
|
-
break;
|
|
219
454
|
}
|
|
220
455
|
}
|
|
221
456
|
// ---------------------------------------------------------------------------
|
|
222
|
-
// 0
|
|
457
|
+
// [0] Setup Wizard
|
|
223
458
|
// ---------------------------------------------------------------------------
|
|
224
459
|
async function actionInit() {
|
|
225
460
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u521D\u59CB\u8A2D\u5B9A' : 'Setup']);
|
|
@@ -227,14 +462,13 @@ async function actionInit() {
|
|
|
227
462
|
await runInitWizard(currentLang);
|
|
228
463
|
}
|
|
229
464
|
// ---------------------------------------------------------------------------
|
|
230
|
-
// 1
|
|
465
|
+
// [1] Security Scan
|
|
231
466
|
// ---------------------------------------------------------------------------
|
|
232
467
|
async function actionScan() {
|
|
233
468
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5B89\u5168\u6383\u63CF' : 'Security Scan']);
|
|
234
469
|
const scanTitle = currentLang === 'zh-TW' ? '\u5B89\u5168\u6383\u63CF' : 'Security Scan';
|
|
235
470
|
console.log(` ${theme.brandBold(scanTitle)}`);
|
|
236
471
|
console.log('');
|
|
237
|
-
// Scan depth selection
|
|
238
472
|
const depthItems = [
|
|
239
473
|
{
|
|
240
474
|
key: '1',
|
|
@@ -271,7 +505,6 @@ async function actionScan() {
|
|
|
271
505
|
: safetyScore >= 40
|
|
272
506
|
? 'D'
|
|
273
507
|
: 'F';
|
|
274
|
-
// Compact score display
|
|
275
508
|
console.log('');
|
|
276
509
|
const scoreLabel = currentLang === 'zh-TW' ? '\u6383\u63CF\u5B8C\u6210' : 'Scan Complete';
|
|
277
510
|
console.log(` ${theme.title(scoreLabel)}${' '.repeat(30)}Score: ${c.bold(`${safetyScore}/100`)} (${grade})`);
|
|
@@ -282,7 +515,6 @@ async function actionScan() {
|
|
|
282
515
|
for (const f of result.findings) {
|
|
283
516
|
const sev = colorSeverity(f.severity).padEnd(10);
|
|
284
517
|
console.log(` ${sev} ${f.title}`);
|
|
285
|
-
// Show manual fix commands for free tier
|
|
286
518
|
if (tier === 'community' && f.manualFix && f.manualFix.length > 0) {
|
|
287
519
|
const fixLabel = currentLang === 'zh-TW' ? '\u624B\u52D5\u4FEE\u5FA9:' : 'Manual fix:';
|
|
288
520
|
console.log(c.dim(` ${fixLabel}`));
|
|
@@ -297,7 +529,6 @@ async function actionScan() {
|
|
|
297
529
|
? `${result.findings.length} \u500B\u554F\u984C`
|
|
298
530
|
: `${result.findings.length} issue(s) found`;
|
|
299
531
|
console.log(c.dim(` ${issuesText}`));
|
|
300
|
-
// Upgrade prompt for free tier
|
|
301
532
|
if (tier === 'community' && fixableCount > 0) {
|
|
302
533
|
const upgradeLines = currentLang === 'zh-TW'
|
|
303
534
|
? [
|
|
@@ -315,7 +546,6 @@ async function actionScan() {
|
|
|
315
546
|
: 'No security issues found';
|
|
316
547
|
console.log(` ${c.safe(noIssues)}`);
|
|
317
548
|
}
|
|
318
|
-
// Next steps
|
|
319
549
|
nextSteps(currentLang === 'zh-TW'
|
|
320
550
|
? [
|
|
321
551
|
{ cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
|
|
@@ -329,7 +559,7 @@ async function actionScan() {
|
|
|
329
559
|
], currentLang);
|
|
330
560
|
}
|
|
331
561
|
// ---------------------------------------------------------------------------
|
|
332
|
-
// 2
|
|
562
|
+
// [2] Compliance Report
|
|
333
563
|
// ---------------------------------------------------------------------------
|
|
334
564
|
async function actionReport() {
|
|
335
565
|
breadcrumb([
|
|
@@ -375,7 +605,6 @@ async function actionReport() {
|
|
|
375
605
|
console.log('');
|
|
376
606
|
const { executeCli } = await import('@panguard-ai/panguard-report');
|
|
377
607
|
await executeCli(['generate', '--framework', framework, '--language', reportLang]);
|
|
378
|
-
// Next steps
|
|
379
608
|
nextSteps(currentLang === 'zh-TW'
|
|
380
609
|
? [
|
|
381
610
|
{ cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
|
|
@@ -387,38 +616,24 @@ async function actionReport() {
|
|
|
387
616
|
], currentLang);
|
|
388
617
|
}
|
|
389
618
|
// ---------------------------------------------------------------------------
|
|
390
|
-
// 3
|
|
619
|
+
// [3] Guard Engine
|
|
391
620
|
// ---------------------------------------------------------------------------
|
|
392
621
|
async function actionGuard() {
|
|
393
622
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard Engine']);
|
|
394
623
|
const title = currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard Engine';
|
|
395
624
|
console.log(` ${theme.brandBold(title)}`);
|
|
396
|
-
|
|
397
|
-
const
|
|
398
|
-
let guardRunning = false;
|
|
399
|
-
if (existsSync(guardPidPath)) {
|
|
400
|
-
try {
|
|
401
|
-
const pid = parseInt(readFileSync(guardPidPath, 'utf-8').trim(), 10);
|
|
402
|
-
process.kill(pid, 0);
|
|
403
|
-
guardRunning = true;
|
|
404
|
-
}
|
|
405
|
-
catch {
|
|
406
|
-
// not running
|
|
407
|
-
}
|
|
408
|
-
}
|
|
625
|
+
const guardInfo = isGuardRunning();
|
|
626
|
+
const guardRunning = guardInfo.running;
|
|
409
627
|
const statusText = guardRunning
|
|
410
628
|
? c.safe(currentLang === 'zh-TW' ? '\u904B\u884C\u4E2D' : 'Running')
|
|
411
629
|
: c.caution(currentLang === 'zh-TW' ? '\u672A\u904B\u884C' : 'Not running');
|
|
412
630
|
console.log(` ${c.dim(currentLang === 'zh-TW' ? '\u72C0\u614B' : 'Status')} ${statusText}`);
|
|
413
631
|
console.log('');
|
|
414
632
|
const items = [
|
|
415
|
-
{ key: '1', label: currentLang === 'zh-TW' ? '\
|
|
416
|
-
{ key: '2', label: currentLang === 'zh-TW' ? '\
|
|
417
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\
|
|
418
|
-
{
|
|
419
|
-
key: '4',
|
|
420
|
-
label: currentLang === 'zh-TW' ? '\u7522\u751F\u6E2C\u8A66\u91D1\u9470' : 'Generate Test Key',
|
|
421
|
-
},
|
|
633
|
+
{ key: '1', label: currentLang === 'zh-TW' ? '\u555F\u52D5\u5F15\u64CE Start' : 'Start' },
|
|
634
|
+
{ key: '2', label: currentLang === 'zh-TW' ? '\u505C\u6B62\u5F15\u64CE Stop' : 'Stop' },
|
|
635
|
+
{ key: '3', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u72C0\u614B Status' : 'Status' },
|
|
636
|
+
{ key: '4', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u65E5\u8A8C Logs' : 'Logs' },
|
|
422
637
|
];
|
|
423
638
|
renderCompactMenu(currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard Engine', items);
|
|
424
639
|
const choice = await waitForCompactChoice(items, currentLang);
|
|
@@ -427,11 +642,22 @@ async function actionGuard() {
|
|
|
427
642
|
console.log('');
|
|
428
643
|
const { runCLI } = await import('@panguard-ai/panguard-guard');
|
|
429
644
|
switch (choice.key) {
|
|
430
|
-
case '1':
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
645
|
+
case '1': {
|
|
646
|
+
// Start guard engine
|
|
647
|
+
if (guardRunning) {
|
|
648
|
+
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})`)}`);
|
|
649
|
+
nextSteps(currentLang === 'zh-TW'
|
|
650
|
+
? [
|
|
651
|
+
{ cmd: 'guard > \u67E5\u770B\u72C0\u614B', desc: '\u67E5\u770B\u8A73\u7D30\u72C0\u614B' },
|
|
652
|
+
{ cmd: 'guard > \u505C\u6B62\u5F15\u64CE', desc: '\u505C\u6B62\u9632\u8B77' },
|
|
653
|
+
]
|
|
654
|
+
: [
|
|
655
|
+
{ cmd: 'guard > Status', desc: 'View detailed status' },
|
|
656
|
+
{ cmd: 'guard > Stop', desc: 'Stop protection' },
|
|
657
|
+
], currentLang);
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
// Free tier — show positive capabilities first
|
|
435
661
|
const { tier } = getLicense();
|
|
436
662
|
if (tier === 'community') {
|
|
437
663
|
console.log(` ${c.sage('\u25C6')} Guard active${' '.repeat(30)}Layer 1 \u00B7 Community`);
|
|
@@ -452,8 +678,59 @@ async function actionGuard() {
|
|
|
452
678
|
}
|
|
453
679
|
console.log('');
|
|
454
680
|
}
|
|
455
|
-
|
|
456
|
-
|
|
681
|
+
// Spawn guard as background process
|
|
682
|
+
const { spawn: spawnProcess } = await import('node:child_process');
|
|
683
|
+
const { fileURLToPath: toPath } = await import('node:url');
|
|
684
|
+
const guardMainUrl = import.meta.resolve('@panguard-ai/panguard-guard');
|
|
685
|
+
const guardCliScript = join(toPath(guardMainUrl), '..', 'cli', 'index.js');
|
|
686
|
+
const guardDataDir = join(homedir(), '.panguard-guard');
|
|
687
|
+
if (!existsSync(guardDataDir))
|
|
688
|
+
mkdirSync(guardDataDir, { recursive: true });
|
|
689
|
+
const logPath = join(guardDataDir, 'guard.log');
|
|
690
|
+
const logFd = openSync(logPath, 'a');
|
|
691
|
+
const guardSp = spinner(currentLang === 'zh-TW'
|
|
692
|
+
? '\u6B63\u5728\u555F\u52D5\u5B88\u8B77\u5F15\u64CE...'
|
|
693
|
+
: 'Starting guard engine...');
|
|
694
|
+
const child = spawnProcess(process.execPath, [guardCliScript, 'start'], {
|
|
695
|
+
detached: true,
|
|
696
|
+
stdio: ['ignore', logFd, logFd],
|
|
697
|
+
env: { ...process.env },
|
|
698
|
+
});
|
|
699
|
+
child.unref();
|
|
700
|
+
closeSync(logFd);
|
|
701
|
+
// Wait for PID file to confirm startup
|
|
702
|
+
const pidPath = join(guardDataDir, 'panguard-guard.pid');
|
|
703
|
+
let started = false;
|
|
704
|
+
const deadline = Date.now() + 5000;
|
|
705
|
+
while (Date.now() < deadline) {
|
|
706
|
+
if (existsSync(pidPath)) {
|
|
707
|
+
try {
|
|
708
|
+
const newPid = parseInt(readFileSync(pidPath, 'utf-8').trim(), 10);
|
|
709
|
+
process.kill(newPid, 0);
|
|
710
|
+
started = true;
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
/* not yet */
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
718
|
+
}
|
|
719
|
+
if (started) {
|
|
720
|
+
const newPid = parseInt(readFileSync(pidPath, 'utf-8').trim(), 10);
|
|
721
|
+
guardSp.succeed(currentLang === 'zh-TW'
|
|
722
|
+
? '\u5B88\u8B77\u5F15\u64CE\u5DF2\u555F\u52D5'
|
|
723
|
+
: 'Guard engine started');
|
|
724
|
+
console.log('');
|
|
725
|
+
console.log(` ${c.safe('\u2713')} ${currentLang === 'zh-TW' ? '\u7CFB\u7D71\u5DF2\u53D7\u4FDD\u8B77' : 'System is now protected'} ${c.dim(`(PID: ${newPid})`)}`);
|
|
726
|
+
console.log(` ${c.dim(currentLang === 'zh-TW' ? ' \u65E5\u8A8C: ~/.panguard-guard/guard.log' : ' Logs: ~/.panguard-guard/guard.log')}`);
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
guardSp.fail(currentLang === 'zh-TW'
|
|
730
|
+
? '\u5B88\u8B77\u5F15\u64CE\u555F\u52D5\u5931\u6557'
|
|
731
|
+
: 'Failed to start guard engine');
|
|
732
|
+
console.log(` ${c.dim(currentLang === 'zh-TW' ? ' \u67E5\u770B\u65E5\u8A8C: ~/.panguard-guard/guard.log' : ' Check logs: ~/.panguard-guard/guard.log')}`);
|
|
733
|
+
}
|
|
457
734
|
nextSteps(currentLang === 'zh-TW'
|
|
458
735
|
? [
|
|
459
736
|
{ cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
|
|
@@ -465,8 +742,7 @@ async function actionGuard() {
|
|
|
465
742
|
], currentLang);
|
|
466
743
|
break;
|
|
467
744
|
}
|
|
468
|
-
case '
|
|
469
|
-
// Confirm before stopping protection
|
|
745
|
+
case '2': {
|
|
470
746
|
const confirmed = await confirmDestructive(currentLang === 'zh-TW'
|
|
471
747
|
? '\u78BA\u5B9A\u8981\u505C\u6B62\u5373\u6642\u9632\u8B77\uFF1F'
|
|
472
748
|
: 'Stop real-time protection?', currentLang);
|
|
@@ -478,13 +754,29 @@ async function actionGuard() {
|
|
|
478
754
|
}
|
|
479
755
|
break;
|
|
480
756
|
}
|
|
481
|
-
case '
|
|
482
|
-
await runCLI(['
|
|
757
|
+
case '3':
|
|
758
|
+
await runCLI(['status']);
|
|
759
|
+
break;
|
|
760
|
+
case '4': {
|
|
761
|
+
const logFilePath = join(homedir(), '.panguard-guard', 'guard.log');
|
|
762
|
+
if (existsSync(logFilePath)) {
|
|
763
|
+
const content = readFileSync(logFilePath, 'utf-8');
|
|
764
|
+
const lines = content.trim().split('\n').slice(-30);
|
|
765
|
+
console.log(c.dim(currentLang === 'zh-TW' ? ' \u6700\u8FD1 30 \u884C\u65E5\u8A8C:' : ' Last 30 log lines:'));
|
|
766
|
+
console.log('');
|
|
767
|
+
for (const line of lines) {
|
|
768
|
+
console.log(` ${c.dim(line)}`);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
console.log(c.dim(currentLang === 'zh-TW' ? ' \u5C1A\u7121\u65E5\u8A8C\u6A94\u6848' : ' No log file found'));
|
|
773
|
+
}
|
|
483
774
|
break;
|
|
775
|
+
}
|
|
484
776
|
}
|
|
485
777
|
}
|
|
486
778
|
// ---------------------------------------------------------------------------
|
|
487
|
-
// 4
|
|
779
|
+
// [4] Honeypot System
|
|
488
780
|
// ---------------------------------------------------------------------------
|
|
489
781
|
async function actionTrap() {
|
|
490
782
|
breadcrumb([
|
|
@@ -533,7 +825,7 @@ async function actionTrap() {
|
|
|
533
825
|
}
|
|
534
826
|
}
|
|
535
827
|
// ---------------------------------------------------------------------------
|
|
536
|
-
// 5
|
|
828
|
+
// [5] Notifications
|
|
537
829
|
// ---------------------------------------------------------------------------
|
|
538
830
|
async function actionChat() {
|
|
539
831
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u901A\u77E5\u7CFB\u7D71' : 'Notifications']);
|
|
@@ -541,37 +833,36 @@ async function actionChat() {
|
|
|
541
833
|
console.log(` ${theme.brandBold(title)}`);
|
|
542
834
|
console.log('');
|
|
543
835
|
const items = [
|
|
544
|
-
{ key: '1', label: currentLang === 'zh-TW' ? '\u8A2D\u5B9A\u901A\u77E5' : 'Setup' },
|
|
836
|
+
{ key: '1', label: currentLang === 'zh-TW' ? '\u8A2D\u5B9A\u901A\u77E5\u7BA1\u9053' : 'Setup Channels' },
|
|
545
837
|
{ key: '2', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u72C0\u614B' : 'Status' },
|
|
546
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\
|
|
838
|
+
{ key: '3', label: currentLang === 'zh-TW' ? '\u6E2C\u8A66\u767C\u9001' : 'Test Send' },
|
|
547
839
|
];
|
|
548
840
|
renderCompactMenu(currentLang === 'zh-TW' ? '\u901A\u77E5\u7CFB\u7D71' : 'Notifications', items);
|
|
549
841
|
const choice = await waitForCompactChoice(items, currentLang);
|
|
550
842
|
if (!choice)
|
|
551
843
|
return;
|
|
552
844
|
console.log('');
|
|
553
|
-
const { runCLI } = await import('@panguard-ai/panguard-chat');
|
|
845
|
+
const { runCLI: chatRunCLI } = await import('@panguard-ai/panguard-chat');
|
|
554
846
|
switch (choice.key) {
|
|
555
847
|
case '1':
|
|
556
|
-
await
|
|
848
|
+
await chatRunCLI(['setup', '--lang', currentLang]);
|
|
557
849
|
break;
|
|
558
850
|
case '2':
|
|
559
|
-
await
|
|
851
|
+
await chatRunCLI(['status']);
|
|
560
852
|
break;
|
|
561
853
|
case '3':
|
|
562
|
-
await
|
|
854
|
+
await chatRunCLI(['config']);
|
|
563
855
|
break;
|
|
564
856
|
}
|
|
565
857
|
}
|
|
566
858
|
// ---------------------------------------------------------------------------
|
|
567
|
-
// 6
|
|
859
|
+
// [6] Threat Cloud
|
|
568
860
|
// ---------------------------------------------------------------------------
|
|
569
861
|
async function actionThreat() {
|
|
570
862
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5A01\u8105\u60C5\u5831' : 'Threat Cloud']);
|
|
571
863
|
const title = currentLang === 'zh-TW' ? '\u5A01\u8105\u60C5\u5831' : 'Threat Cloud';
|
|
572
864
|
console.log(` ${theme.brandBold(title)}`);
|
|
573
865
|
console.log('');
|
|
574
|
-
// Port availability check
|
|
575
866
|
const port = 8080;
|
|
576
867
|
const net = await import('node:net');
|
|
577
868
|
const portAvailable = await new Promise((resolve) => {
|
|
@@ -583,9 +874,11 @@ async function actionThreat() {
|
|
|
583
874
|
tester.listen(port, '127.0.0.1');
|
|
584
875
|
});
|
|
585
876
|
if (!portAvailable) {
|
|
586
|
-
console.log(
|
|
587
|
-
? `\u9023\u63A5\u57E0 ${port} \u5DF2\u88AB\u4F54\u7528
|
|
588
|
-
: `Port ${port} is already in use
|
|
877
|
+
console.log(formatError(currentLang === 'zh-TW'
|
|
878
|
+
? `\u9023\u63A5\u57E0 ${port} \u5DF2\u88AB\u4F54\u7528`
|
|
879
|
+
: `Port ${port} is already in use`, `Port ${port}`, currentLang === 'zh-TW'
|
|
880
|
+
? `\u91CB\u653E\u9023\u63A5\u57E0 ${port} \u5F8C\u518D\u8A66`
|
|
881
|
+
: `Free port ${port} and try again`));
|
|
589
882
|
return;
|
|
590
883
|
}
|
|
591
884
|
console.log(c.dim(currentLang === 'zh-TW'
|
|
@@ -624,7 +917,7 @@ async function actionThreat() {
|
|
|
624
917
|
}
|
|
625
918
|
}
|
|
626
919
|
// ---------------------------------------------------------------------------
|
|
627
|
-
// 7
|
|
920
|
+
// [7] Auto Demo
|
|
628
921
|
// ---------------------------------------------------------------------------
|
|
629
922
|
async function actionDemo() {
|
|
630
923
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo']);
|
|
@@ -785,7 +1078,7 @@ async function actionDemo() {
|
|
|
785
1078
|
});
|
|
786
1079
|
}
|
|
787
1080
|
console.log('');
|
|
788
|
-
// Summary
|
|
1081
|
+
// Summary
|
|
789
1082
|
console.log(` ${theme.brandBold(currentLang === 'zh-TW' ? '\u5C55\u793A\u5B8C\u6210' : 'Demo Complete')}`);
|
|
790
1083
|
console.log('');
|
|
791
1084
|
for (const r of results) {
|
|
@@ -796,7 +1089,6 @@ async function actionDemo() {
|
|
|
796
1089
|
: c.critical('\u2717');
|
|
797
1090
|
console.log(` ${icon} ${r.name}`);
|
|
798
1091
|
}
|
|
799
|
-
// Next steps
|
|
800
1092
|
nextSteps(currentLang === 'zh-TW'
|
|
801
1093
|
? [
|
|
802
1094
|
{ cmd: 'init', desc: '\u521D\u59CB\u8A2D\u5B9A\u60A8\u7684\u74B0\u5883' },
|
|
@@ -810,13 +1102,71 @@ async function actionDemo() {
|
|
|
810
1102
|
], currentLang);
|
|
811
1103
|
}
|
|
812
1104
|
// ---------------------------------------------------------------------------
|
|
813
|
-
//
|
|
1105
|
+
// Prompt commands: Upgrade
|
|
814
1106
|
// ---------------------------------------------------------------------------
|
|
815
1107
|
async function actionUpgrade() {
|
|
816
1108
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5347\u7D1A\u65B9\u6848' : 'Upgrade Plan']);
|
|
817
|
-
// Delegate to the upgrade command module
|
|
818
1109
|
const { upgradeCommand } = await import('./commands/upgrade.js');
|
|
819
1110
|
const cmd = upgradeCommand();
|
|
820
1111
|
await cmd.parseAsync(['upgrade', '--lang', currentLang], { from: 'user' });
|
|
821
1112
|
}
|
|
1113
|
+
// ---------------------------------------------------------------------------
|
|
1114
|
+
// Prompt commands: Hardening
|
|
1115
|
+
// ---------------------------------------------------------------------------
|
|
1116
|
+
async function actionHardening() {
|
|
1117
|
+
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5B89\u5168\u52A0\u56FA' : 'Security Hardening']);
|
|
1118
|
+
const title = currentLang === 'zh-TW' ? '\u5B89\u5168\u52A0\u56FA' : 'Security Hardening';
|
|
1119
|
+
console.log(` ${theme.brandBold(title)}`);
|
|
1120
|
+
console.log('');
|
|
1121
|
+
const items = [
|
|
1122
|
+
{ key: '1', label: currentLang === 'zh-TW' ? '\u57F7\u884C\u52A0\u56FA\u6383\u63CF' : 'Run hardening audit' },
|
|
1123
|
+
{ key: '2', label: currentLang === 'zh-TW' ? '\u81EA\u52D5\u4FEE\u5FA9' : 'Auto-fix issues' },
|
|
1124
|
+
{ key: '3', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u5831\u544A' : 'View report' },
|
|
1125
|
+
];
|
|
1126
|
+
renderCompactMenu(currentLang === 'zh-TW' ? '\u5B89\u5168\u52A0\u56FA' : 'Hardening', items);
|
|
1127
|
+
const choice = await waitForCompactChoice(items, currentLang);
|
|
1128
|
+
if (!choice)
|
|
1129
|
+
return;
|
|
1130
|
+
console.log('');
|
|
1131
|
+
const { hardeningCommand } = await import('./commands/hardening.js');
|
|
1132
|
+
const cmd = hardeningCommand();
|
|
1133
|
+
switch (choice.key) {
|
|
1134
|
+
case '1':
|
|
1135
|
+
await cmd.parseAsync(['hardening', 'audit'], { from: 'user' });
|
|
1136
|
+
break;
|
|
1137
|
+
case '2':
|
|
1138
|
+
await cmd.parseAsync(['hardening', 'fix'], { from: 'user' });
|
|
1139
|
+
break;
|
|
1140
|
+
case '3':
|
|
1141
|
+
await cmd.parseAsync(['hardening', 'report'], { from: 'user' });
|
|
1142
|
+
break;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
// ---------------------------------------------------------------------------
|
|
1146
|
+
// Prompt commands: Status
|
|
1147
|
+
// ---------------------------------------------------------------------------
|
|
1148
|
+
async function actionStatus() {
|
|
1149
|
+
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u7CFB\u7D71\u72C0\u614B' : 'Status']);
|
|
1150
|
+
const { statusCommand } = await import('./commands/status.js');
|
|
1151
|
+
const cmd = statusCommand();
|
|
1152
|
+
await cmd.parseAsync(['status'], { from: 'user' });
|
|
1153
|
+
}
|
|
1154
|
+
// ---------------------------------------------------------------------------
|
|
1155
|
+
// Prompt commands: Login
|
|
1156
|
+
// ---------------------------------------------------------------------------
|
|
1157
|
+
async function actionLogin() {
|
|
1158
|
+
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5E33\u865F\u767B\u5165' : 'Login']);
|
|
1159
|
+
const { loginCommand } = await import('./commands/login.js');
|
|
1160
|
+
const cmd = loginCommand();
|
|
1161
|
+
await cmd.parseAsync(['login'], { from: 'user' });
|
|
1162
|
+
}
|
|
1163
|
+
// ---------------------------------------------------------------------------
|
|
1164
|
+
// Prompt commands: Config
|
|
1165
|
+
// ---------------------------------------------------------------------------
|
|
1166
|
+
async function actionConfig() {
|
|
1167
|
+
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u8A2D\u5B9A\u7BA1\u7406' : 'Settings']);
|
|
1168
|
+
const { configCommand } = await import('./commands/config.js');
|
|
1169
|
+
const cmd = configCommand();
|
|
1170
|
+
await cmd.parseAsync(['config'], { from: 'user' });
|
|
1171
|
+
}
|
|
822
1172
|
//# sourceMappingURL=interactive.js.map
|