@panguard-ai/panguard 0.2.5 → 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/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 +102 -8
- 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 +173 -4
- 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 +450 -211
- 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 +1 -2
- 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 +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- 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 +30 -15
- package/dist/init/wizard-runner.js.map +1 -1
- package/package.json +7 -6
- 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,47 +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
|
-
{ key: 'hardening', en: 'Security hardening', zh: '\u5B89\u5168\u52A0\u56FA', tier: 'community' },
|
|
62
56
|
{
|
|
63
|
-
key: '
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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',
|
|
67
66
|
},
|
|
68
67
|
{
|
|
69
|
-
key: '
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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',
|
|
73
77
|
},
|
|
74
|
-
{ key: 'trap', en: 'Honeypot system', zh: '\u871C\u7F50\u7CFB\u7D71', tier: 'solo' },
|
|
75
78
|
{
|
|
76
79
|
key: 'report',
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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',
|
|
80
143
|
},
|
|
81
|
-
{ key: 'manager', en: 'Distributed manager', zh: '\u5206\u6563\u5F0F\u7BA1\u7406', tier: 'business' },
|
|
82
|
-
{ key: '__sep__', en: '', zh: '', tier: '' },
|
|
83
|
-
{ key: 'status', en: 'System status', zh: '\u7CFB\u7D71\u72C0\u614B', tier: 'community' },
|
|
84
|
-
{ key: 'login', en: 'Account login', zh: '\u5E33\u865F\u767B\u5165', tier: 'community' },
|
|
85
|
-
{ key: 'config', en: 'Settings', zh: '\u8A2D\u5B9A\u7BA1\u7406', tier: 'community' },
|
|
86
|
-
{ key: 'setup', en: 'Initial configuration', zh: '\u521D\u59CB\u8A2D\u5B9A', tier: 'community' },
|
|
87
|
-
{ key: 'demo', en: 'Feature demo', zh: '\u529F\u80FD\u5C55\u793A', tier: 'community' },
|
|
88
|
-
{ key: 'upgrade', en: 'Upgrade plan', zh: '\u5347\u7D1A\u65B9\u6848', tier: 'community' },
|
|
89
144
|
];
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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('');
|
|
100
251
|
}
|
|
101
252
|
// ---------------------------------------------------------------------------
|
|
102
253
|
// Startup screen
|
|
@@ -108,28 +259,15 @@ function renderStartup() {
|
|
|
108
259
|
console.log('');
|
|
109
260
|
// Tagline + version
|
|
110
261
|
const tagline = c.dim(' AI-Powered Security Platform');
|
|
111
|
-
|
|
112
|
-
// Right-align version
|
|
113
|
-
// eslint-disable-next-line no-control-regex
|
|
114
|
-
const tagLen = tagline.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
115
|
-
// eslint-disable-next-line no-control-regex
|
|
116
|
-
const verLen = version.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
117
|
-
const totalWidth = Math.max(60, process.stdout.columns ?? 60);
|
|
118
|
-
const gap = Math.max(2, totalWidth - tagLen - verLen - 4);
|
|
119
|
-
console.log(`${tagline}${' '.repeat(gap)}${version}`);
|
|
120
|
-
console.log('');
|
|
121
|
-
// Status info
|
|
122
|
-
const { tier } = getLicense();
|
|
123
|
-
const tierName = tierDisplayName(tier);
|
|
124
|
-
const tierColor = tier === 'community' ? c.caution : c.safe;
|
|
125
|
-
const statusLabel = currentLang === 'zh-TW' ? '\u72C0\u614B' : 'Status';
|
|
126
|
-
const licenseLabel = currentLang === 'zh-TW' ? '\u6388\u6B0A' : 'License';
|
|
127
|
-
const modulesLabel = currentLang === 'zh-TW' ? '\u6A21\u7D44' : 'Modules';
|
|
128
|
-
const modulesValue = moduleCountDisplay(currentLang);
|
|
129
|
-
console.log(` ${c.dim(statusLabel.padEnd(10))} ${c.safe('Ready')}`);
|
|
130
|
-
console.log(` ${c.dim(licenseLabel.padEnd(10))} ${tierColor(tierName)}`);
|
|
131
|
-
console.log(` ${c.dim(modulesLabel.padEnd(10))} ${modulesValue}`);
|
|
262
|
+
console.log(tagline);
|
|
132
263
|
console.log('');
|
|
264
|
+
// Status panel
|
|
265
|
+
renderStatusPanel();
|
|
266
|
+
console.log('');
|
|
267
|
+
// Menu
|
|
268
|
+
renderMenu();
|
|
269
|
+
// Footer
|
|
270
|
+
renderFooter();
|
|
133
271
|
}
|
|
134
272
|
// ---------------------------------------------------------------------------
|
|
135
273
|
// Entry point
|
|
@@ -148,61 +286,159 @@ export async function startInteractive(lang) {
|
|
|
148
286
|
// First-time user hint
|
|
149
287
|
if (!existsSync(CONFIG_PATH)) {
|
|
150
288
|
const hint = currentLang === 'zh-TW'
|
|
151
|
-
? ` ${c.sage('\u25C6')} \u9996\u6B21\u4F7F\u7528\uFF1F\u5EFA\u8B70\
|
|
152
|
-
: ` ${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`;
|
|
153
291
|
console.log(hint);
|
|
154
292
|
console.log('');
|
|
155
293
|
}
|
|
156
294
|
// Main loop
|
|
157
295
|
while (true) {
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
+
}
|
|
166
350
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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));
|
|
170
362
|
renderStartup();
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
await pressAnyKey(currentLang);
|
|
363
|
+
return true;
|
|
364
|
+
case 'login':
|
|
365
|
+
console.clear();
|
|
366
|
+
await actionLogin();
|
|
367
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
177
368
|
renderStartup();
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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;
|
|
183
378
|
}
|
|
184
|
-
|
|
185
|
-
console.
|
|
186
|
-
|
|
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;
|
|
187
417
|
}
|
|
188
|
-
|
|
189
|
-
|
|
418
|
+
case 'help':
|
|
419
|
+
showHelp();
|
|
420
|
+
return true;
|
|
421
|
+
default:
|
|
422
|
+
return false;
|
|
190
423
|
}
|
|
191
424
|
}
|
|
192
425
|
// ---------------------------------------------------------------------------
|
|
193
|
-
// Action dispatch
|
|
426
|
+
// Action dispatch (from numbered menu)
|
|
194
427
|
// ---------------------------------------------------------------------------
|
|
195
428
|
async function dispatch(key) {
|
|
196
429
|
switch (key) {
|
|
430
|
+
case 'setup':
|
|
431
|
+
await actionInit();
|
|
432
|
+
break;
|
|
197
433
|
case 'scan':
|
|
198
434
|
await actionScan();
|
|
199
435
|
break;
|
|
200
|
-
case 'guard':
|
|
201
|
-
await actionGuard();
|
|
202
|
-
break;
|
|
203
436
|
case 'report':
|
|
204
437
|
await actionReport();
|
|
205
438
|
break;
|
|
439
|
+
case 'guard':
|
|
440
|
+
await actionGuard();
|
|
441
|
+
break;
|
|
206
442
|
case 'trap':
|
|
207
443
|
await actionTrap();
|
|
208
444
|
break;
|
|
@@ -212,34 +448,13 @@ async function dispatch(key) {
|
|
|
212
448
|
case 'threat-cloud':
|
|
213
449
|
await actionThreat();
|
|
214
450
|
break;
|
|
215
|
-
case 'setup':
|
|
216
|
-
await actionInit();
|
|
217
|
-
break;
|
|
218
451
|
case 'demo':
|
|
219
452
|
await actionDemo();
|
|
220
453
|
break;
|
|
221
|
-
case 'upgrade':
|
|
222
|
-
await actionUpgrade();
|
|
223
|
-
break;
|
|
224
|
-
case 'hardening':
|
|
225
|
-
await actionHardening();
|
|
226
|
-
break;
|
|
227
|
-
case 'manager':
|
|
228
|
-
await actionManager();
|
|
229
|
-
break;
|
|
230
|
-
case 'status':
|
|
231
|
-
await actionStatus();
|
|
232
|
-
break;
|
|
233
|
-
case 'login':
|
|
234
|
-
await actionLogin();
|
|
235
|
-
break;
|
|
236
|
-
case 'config':
|
|
237
|
-
await actionConfig();
|
|
238
|
-
break;
|
|
239
454
|
}
|
|
240
455
|
}
|
|
241
456
|
// ---------------------------------------------------------------------------
|
|
242
|
-
// 0
|
|
457
|
+
// [0] Setup Wizard
|
|
243
458
|
// ---------------------------------------------------------------------------
|
|
244
459
|
async function actionInit() {
|
|
245
460
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u521D\u59CB\u8A2D\u5B9A' : 'Setup']);
|
|
@@ -247,14 +462,13 @@ async function actionInit() {
|
|
|
247
462
|
await runInitWizard(currentLang);
|
|
248
463
|
}
|
|
249
464
|
// ---------------------------------------------------------------------------
|
|
250
|
-
// 1
|
|
465
|
+
// [1] Security Scan
|
|
251
466
|
// ---------------------------------------------------------------------------
|
|
252
467
|
async function actionScan() {
|
|
253
468
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5B89\u5168\u6383\u63CF' : 'Security Scan']);
|
|
254
469
|
const scanTitle = currentLang === 'zh-TW' ? '\u5B89\u5168\u6383\u63CF' : 'Security Scan';
|
|
255
470
|
console.log(` ${theme.brandBold(scanTitle)}`);
|
|
256
471
|
console.log('');
|
|
257
|
-
// Scan depth selection
|
|
258
472
|
const depthItems = [
|
|
259
473
|
{
|
|
260
474
|
key: '1',
|
|
@@ -291,7 +505,6 @@ async function actionScan() {
|
|
|
291
505
|
: safetyScore >= 40
|
|
292
506
|
? 'D'
|
|
293
507
|
: 'F';
|
|
294
|
-
// Compact score display
|
|
295
508
|
console.log('');
|
|
296
509
|
const scoreLabel = currentLang === 'zh-TW' ? '\u6383\u63CF\u5B8C\u6210' : 'Scan Complete';
|
|
297
510
|
console.log(` ${theme.title(scoreLabel)}${' '.repeat(30)}Score: ${c.bold(`${safetyScore}/100`)} (${grade})`);
|
|
@@ -302,7 +515,6 @@ async function actionScan() {
|
|
|
302
515
|
for (const f of result.findings) {
|
|
303
516
|
const sev = colorSeverity(f.severity).padEnd(10);
|
|
304
517
|
console.log(` ${sev} ${f.title}`);
|
|
305
|
-
// Show manual fix commands for free tier
|
|
306
518
|
if (tier === 'community' && f.manualFix && f.manualFix.length > 0) {
|
|
307
519
|
const fixLabel = currentLang === 'zh-TW' ? '\u624B\u52D5\u4FEE\u5FA9:' : 'Manual fix:';
|
|
308
520
|
console.log(c.dim(` ${fixLabel}`));
|
|
@@ -317,7 +529,6 @@ async function actionScan() {
|
|
|
317
529
|
? `${result.findings.length} \u500B\u554F\u984C`
|
|
318
530
|
: `${result.findings.length} issue(s) found`;
|
|
319
531
|
console.log(c.dim(` ${issuesText}`));
|
|
320
|
-
// Upgrade prompt for free tier
|
|
321
532
|
if (tier === 'community' && fixableCount > 0) {
|
|
322
533
|
const upgradeLines = currentLang === 'zh-TW'
|
|
323
534
|
? [
|
|
@@ -335,7 +546,6 @@ async function actionScan() {
|
|
|
335
546
|
: 'No security issues found';
|
|
336
547
|
console.log(` ${c.safe(noIssues)}`);
|
|
337
548
|
}
|
|
338
|
-
// Next steps
|
|
339
549
|
nextSteps(currentLang === 'zh-TW'
|
|
340
550
|
? [
|
|
341
551
|
{ cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
|
|
@@ -349,7 +559,7 @@ async function actionScan() {
|
|
|
349
559
|
], currentLang);
|
|
350
560
|
}
|
|
351
561
|
// ---------------------------------------------------------------------------
|
|
352
|
-
// 2
|
|
562
|
+
// [2] Compliance Report
|
|
353
563
|
// ---------------------------------------------------------------------------
|
|
354
564
|
async function actionReport() {
|
|
355
565
|
breadcrumb([
|
|
@@ -395,7 +605,6 @@ async function actionReport() {
|
|
|
395
605
|
console.log('');
|
|
396
606
|
const { executeCli } = await import('@panguard-ai/panguard-report');
|
|
397
607
|
await executeCli(['generate', '--framework', framework, '--language', reportLang]);
|
|
398
|
-
// Next steps
|
|
399
608
|
nextSteps(currentLang === 'zh-TW'
|
|
400
609
|
? [
|
|
401
610
|
{ cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
|
|
@@ -407,38 +616,24 @@ async function actionReport() {
|
|
|
407
616
|
], currentLang);
|
|
408
617
|
}
|
|
409
618
|
// ---------------------------------------------------------------------------
|
|
410
|
-
// 3
|
|
619
|
+
// [3] Guard Engine
|
|
411
620
|
// ---------------------------------------------------------------------------
|
|
412
621
|
async function actionGuard() {
|
|
413
622
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard Engine']);
|
|
414
623
|
const title = currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard Engine';
|
|
415
624
|
console.log(` ${theme.brandBold(title)}`);
|
|
416
|
-
|
|
417
|
-
const
|
|
418
|
-
let guardRunning = false;
|
|
419
|
-
if (existsSync(guardPidPath)) {
|
|
420
|
-
try {
|
|
421
|
-
const pid = parseInt(readFileSync(guardPidPath, 'utf-8').trim(), 10);
|
|
422
|
-
process.kill(pid, 0);
|
|
423
|
-
guardRunning = true;
|
|
424
|
-
}
|
|
425
|
-
catch {
|
|
426
|
-
// not running
|
|
427
|
-
}
|
|
428
|
-
}
|
|
625
|
+
const guardInfo = isGuardRunning();
|
|
626
|
+
const guardRunning = guardInfo.running;
|
|
429
627
|
const statusText = guardRunning
|
|
430
628
|
? c.safe(currentLang === 'zh-TW' ? '\u904B\u884C\u4E2D' : 'Running')
|
|
431
629
|
: c.caution(currentLang === 'zh-TW' ? '\u672A\u904B\u884C' : 'Not running');
|
|
432
630
|
console.log(` ${c.dim(currentLang === 'zh-TW' ? '\u72C0\u614B' : 'Status')} ${statusText}`);
|
|
433
631
|
console.log('');
|
|
434
632
|
const items = [
|
|
435
|
-
{ key: '1', label: currentLang === 'zh-TW' ? '\
|
|
436
|
-
{ key: '2', label: currentLang === 'zh-TW' ? '\
|
|
437
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\
|
|
438
|
-
{
|
|
439
|
-
key: '4',
|
|
440
|
-
label: currentLang === 'zh-TW' ? '\u7522\u751F\u6E2C\u8A66\u91D1\u9470' : 'Generate Test Key',
|
|
441
|
-
},
|
|
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' },
|
|
442
637
|
];
|
|
443
638
|
renderCompactMenu(currentLang === 'zh-TW' ? '\u5B88\u8B77\u5F15\u64CE' : 'Guard Engine', items);
|
|
444
639
|
const choice = await waitForCompactChoice(items, currentLang);
|
|
@@ -447,11 +642,22 @@ async function actionGuard() {
|
|
|
447
642
|
console.log('');
|
|
448
643
|
const { runCLI } = await import('@panguard-ai/panguard-guard');
|
|
449
644
|
switch (choice.key) {
|
|
450
|
-
case '1':
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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
|
|
455
661
|
const { tier } = getLicense();
|
|
456
662
|
if (tier === 'community') {
|
|
457
663
|
console.log(` ${c.sage('\u25C6')} Guard active${' '.repeat(30)}Layer 1 \u00B7 Community`);
|
|
@@ -472,8 +678,59 @@ async function actionGuard() {
|
|
|
472
678
|
}
|
|
473
679
|
console.log('');
|
|
474
680
|
}
|
|
475
|
-
|
|
476
|
-
|
|
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
|
+
}
|
|
477
734
|
nextSteps(currentLang === 'zh-TW'
|
|
478
735
|
? [
|
|
479
736
|
{ cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
|
|
@@ -485,8 +742,7 @@ async function actionGuard() {
|
|
|
485
742
|
], currentLang);
|
|
486
743
|
break;
|
|
487
744
|
}
|
|
488
|
-
case '
|
|
489
|
-
// Confirm before stopping protection
|
|
745
|
+
case '2': {
|
|
490
746
|
const confirmed = await confirmDestructive(currentLang === 'zh-TW'
|
|
491
747
|
? '\u78BA\u5B9A\u8981\u505C\u6B62\u5373\u6642\u9632\u8B77\uFF1F'
|
|
492
748
|
: 'Stop real-time protection?', currentLang);
|
|
@@ -498,13 +754,29 @@ async function actionGuard() {
|
|
|
498
754
|
}
|
|
499
755
|
break;
|
|
500
756
|
}
|
|
501
|
-
case '
|
|
502
|
-
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
|
+
}
|
|
503
774
|
break;
|
|
775
|
+
}
|
|
504
776
|
}
|
|
505
777
|
}
|
|
506
778
|
// ---------------------------------------------------------------------------
|
|
507
|
-
// 4
|
|
779
|
+
// [4] Honeypot System
|
|
508
780
|
// ---------------------------------------------------------------------------
|
|
509
781
|
async function actionTrap() {
|
|
510
782
|
breadcrumb([
|
|
@@ -553,7 +825,7 @@ async function actionTrap() {
|
|
|
553
825
|
}
|
|
554
826
|
}
|
|
555
827
|
// ---------------------------------------------------------------------------
|
|
556
|
-
// 5
|
|
828
|
+
// [5] Notifications
|
|
557
829
|
// ---------------------------------------------------------------------------
|
|
558
830
|
async function actionChat() {
|
|
559
831
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u901A\u77E5\u7CFB\u7D71' : 'Notifications']);
|
|
@@ -561,37 +833,36 @@ async function actionChat() {
|
|
|
561
833
|
console.log(` ${theme.brandBold(title)}`);
|
|
562
834
|
console.log('');
|
|
563
835
|
const items = [
|
|
564
|
-
{ 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' },
|
|
565
837
|
{ key: '2', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u72C0\u614B' : 'Status' },
|
|
566
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\
|
|
838
|
+
{ key: '3', label: currentLang === 'zh-TW' ? '\u6E2C\u8A66\u767C\u9001' : 'Test Send' },
|
|
567
839
|
];
|
|
568
840
|
renderCompactMenu(currentLang === 'zh-TW' ? '\u901A\u77E5\u7CFB\u7D71' : 'Notifications', items);
|
|
569
841
|
const choice = await waitForCompactChoice(items, currentLang);
|
|
570
842
|
if (!choice)
|
|
571
843
|
return;
|
|
572
844
|
console.log('');
|
|
573
|
-
const { runCLI } = await import('@panguard-ai/panguard-chat');
|
|
845
|
+
const { runCLI: chatRunCLI } = await import('@panguard-ai/panguard-chat');
|
|
574
846
|
switch (choice.key) {
|
|
575
847
|
case '1':
|
|
576
|
-
await
|
|
848
|
+
await chatRunCLI(['setup', '--lang', currentLang]);
|
|
577
849
|
break;
|
|
578
850
|
case '2':
|
|
579
|
-
await
|
|
851
|
+
await chatRunCLI(['status']);
|
|
580
852
|
break;
|
|
581
853
|
case '3':
|
|
582
|
-
await
|
|
854
|
+
await chatRunCLI(['config']);
|
|
583
855
|
break;
|
|
584
856
|
}
|
|
585
857
|
}
|
|
586
858
|
// ---------------------------------------------------------------------------
|
|
587
|
-
// 6
|
|
859
|
+
// [6] Threat Cloud
|
|
588
860
|
// ---------------------------------------------------------------------------
|
|
589
861
|
async function actionThreat() {
|
|
590
862
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5A01\u8105\u60C5\u5831' : 'Threat Cloud']);
|
|
591
863
|
const title = currentLang === 'zh-TW' ? '\u5A01\u8105\u60C5\u5831' : 'Threat Cloud';
|
|
592
864
|
console.log(` ${theme.brandBold(title)}`);
|
|
593
865
|
console.log('');
|
|
594
|
-
// Port availability check
|
|
595
866
|
const port = 8080;
|
|
596
867
|
const net = await import('node:net');
|
|
597
868
|
const portAvailable = await new Promise((resolve) => {
|
|
@@ -603,9 +874,11 @@ async function actionThreat() {
|
|
|
603
874
|
tester.listen(port, '127.0.0.1');
|
|
604
875
|
});
|
|
605
876
|
if (!portAvailable) {
|
|
606
|
-
console.log(
|
|
607
|
-
? `\u9023\u63A5\u57E0 ${port} \u5DF2\u88AB\u4F54\u7528
|
|
608
|
-
: `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`));
|
|
609
882
|
return;
|
|
610
883
|
}
|
|
611
884
|
console.log(c.dim(currentLang === 'zh-TW'
|
|
@@ -644,7 +917,7 @@ async function actionThreat() {
|
|
|
644
917
|
}
|
|
645
918
|
}
|
|
646
919
|
// ---------------------------------------------------------------------------
|
|
647
|
-
// 7
|
|
920
|
+
// [7] Auto Demo
|
|
648
921
|
// ---------------------------------------------------------------------------
|
|
649
922
|
async function actionDemo() {
|
|
650
923
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo']);
|
|
@@ -805,7 +1078,7 @@ async function actionDemo() {
|
|
|
805
1078
|
});
|
|
806
1079
|
}
|
|
807
1080
|
console.log('');
|
|
808
|
-
// Summary
|
|
1081
|
+
// Summary
|
|
809
1082
|
console.log(` ${theme.brandBold(currentLang === 'zh-TW' ? '\u5C55\u793A\u5B8C\u6210' : 'Demo Complete')}`);
|
|
810
1083
|
console.log('');
|
|
811
1084
|
for (const r of results) {
|
|
@@ -816,7 +1089,6 @@ async function actionDemo() {
|
|
|
816
1089
|
: c.critical('\u2717');
|
|
817
1090
|
console.log(` ${icon} ${r.name}`);
|
|
818
1091
|
}
|
|
819
|
-
// Next steps
|
|
820
1092
|
nextSteps(currentLang === 'zh-TW'
|
|
821
1093
|
? [
|
|
822
1094
|
{ cmd: 'init', desc: '\u521D\u59CB\u8A2D\u5B9A\u60A8\u7684\u74B0\u5883' },
|
|
@@ -830,17 +1102,16 @@ async function actionDemo() {
|
|
|
830
1102
|
], currentLang);
|
|
831
1103
|
}
|
|
832
1104
|
// ---------------------------------------------------------------------------
|
|
833
|
-
//
|
|
1105
|
+
// Prompt commands: Upgrade
|
|
834
1106
|
// ---------------------------------------------------------------------------
|
|
835
1107
|
async function actionUpgrade() {
|
|
836
1108
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5347\u7D1A\u65B9\u6848' : 'Upgrade Plan']);
|
|
837
|
-
// Delegate to the upgrade command module
|
|
838
1109
|
const { upgradeCommand } = await import('./commands/upgrade.js');
|
|
839
1110
|
const cmd = upgradeCommand();
|
|
840
1111
|
await cmd.parseAsync(['upgrade', '--lang', currentLang], { from: 'user' });
|
|
841
1112
|
}
|
|
842
1113
|
// ---------------------------------------------------------------------------
|
|
843
|
-
//
|
|
1114
|
+
// Prompt commands: Hardening
|
|
844
1115
|
// ---------------------------------------------------------------------------
|
|
845
1116
|
async function actionHardening() {
|
|
846
1117
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5B89\u5168\u52A0\u56FA' : 'Security Hardening']);
|
|
@@ -872,39 +1143,7 @@ async function actionHardening() {
|
|
|
872
1143
|
}
|
|
873
1144
|
}
|
|
874
1145
|
// ---------------------------------------------------------------------------
|
|
875
|
-
//
|
|
876
|
-
// ---------------------------------------------------------------------------
|
|
877
|
-
async function actionManager() {
|
|
878
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5206\u6563\u5F0F\u7BA1\u7406' : 'Manager']);
|
|
879
|
-
const title = currentLang === 'zh-TW' ? '\u5206\u6563\u5F0F\u7BA1\u7406' : 'Distributed Manager';
|
|
880
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
881
|
-
console.log('');
|
|
882
|
-
const items = [
|
|
883
|
-
{ key: '1', label: currentLang === 'zh-TW' ? '\u67E5\u770B Agent \u72C0\u614B' : 'Agent status' },
|
|
884
|
-
{ key: '2', label: currentLang === 'zh-TW' ? '\u5A01\u8105\u7E3D\u89BD' : 'Threat overview' },
|
|
885
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\u653F\u7B56\u7BA1\u7406' : 'Policy management' },
|
|
886
|
-
];
|
|
887
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u7BA1\u7406\u7BC0\u9EDE' : 'Manager Node', items);
|
|
888
|
-
const choice = await waitForCompactChoice(items, currentLang);
|
|
889
|
-
if (!choice)
|
|
890
|
-
return;
|
|
891
|
-
console.log('');
|
|
892
|
-
const { managerCommand } = await import('./commands/manager.js');
|
|
893
|
-
const cmd = managerCommand();
|
|
894
|
-
switch (choice.key) {
|
|
895
|
-
case '1':
|
|
896
|
-
await cmd.parseAsync(['manager', 'agents'], { from: 'user' });
|
|
897
|
-
break;
|
|
898
|
-
case '2':
|
|
899
|
-
await cmd.parseAsync(['manager', 'threats'], { from: 'user' });
|
|
900
|
-
break;
|
|
901
|
-
case '3':
|
|
902
|
-
await cmd.parseAsync(['manager', 'policies'], { from: 'user' });
|
|
903
|
-
break;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
// ---------------------------------------------------------------------------
|
|
907
|
-
// 11. System Status
|
|
1146
|
+
// Prompt commands: Status
|
|
908
1147
|
// ---------------------------------------------------------------------------
|
|
909
1148
|
async function actionStatus() {
|
|
910
1149
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u7CFB\u7D71\u72C0\u614B' : 'Status']);
|
|
@@ -913,7 +1152,7 @@ async function actionStatus() {
|
|
|
913
1152
|
await cmd.parseAsync(['status'], { from: 'user' });
|
|
914
1153
|
}
|
|
915
1154
|
// ---------------------------------------------------------------------------
|
|
916
|
-
//
|
|
1155
|
+
// Prompt commands: Login
|
|
917
1156
|
// ---------------------------------------------------------------------------
|
|
918
1157
|
async function actionLogin() {
|
|
919
1158
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5E33\u865F\u767B\u5165' : 'Login']);
|
|
@@ -922,7 +1161,7 @@ async function actionLogin() {
|
|
|
922
1161
|
await cmd.parseAsync(['login'], { from: 'user' });
|
|
923
1162
|
}
|
|
924
1163
|
// ---------------------------------------------------------------------------
|
|
925
|
-
//
|
|
1164
|
+
// Prompt commands: Config
|
|
926
1165
|
// ---------------------------------------------------------------------------
|
|
927
1166
|
async function actionConfig() {
|
|
928
1167
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u8A2D\u5B9A\u7BA1\u7406' : 'Settings']);
|