i18ntk 1.10.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +141 -1191
  3. package/main/i18ntk-analyze.js +65 -84
  4. package/main/i18ntk-backup-class.js +420 -0
  5. package/main/i18ntk-backup.js +3 -3
  6. package/main/i18ntk-complete.js +90 -65
  7. package/main/i18ntk-doctor.js +123 -103
  8. package/main/i18ntk-fixer.js +61 -725
  9. package/main/i18ntk-go.js +14 -15
  10. package/main/i18ntk-init.js +77 -26
  11. package/main/i18ntk-java.js +27 -32
  12. package/main/i18ntk-js.js +70 -68
  13. package/main/i18ntk-manage.js +129 -30
  14. package/main/i18ntk-php.js +75 -75
  15. package/main/i18ntk-py.js +55 -56
  16. package/main/i18ntk-scanner.js +59 -57
  17. package/main/i18ntk-setup.js +9 -404
  18. package/main/i18ntk-sizing.js +6 -6
  19. package/main/i18ntk-summary.js +21 -18
  20. package/main/i18ntk-ui.js +11 -10
  21. package/main/i18ntk-usage.js +54 -18
  22. package/main/i18ntk-validate.js +13 -13
  23. package/main/manage/commands/AnalyzeCommand.js +1124 -0
  24. package/main/manage/commands/BackupCommand.js +62 -0
  25. package/main/manage/commands/CommandRouter.js +295 -0
  26. package/main/manage/commands/CompleteCommand.js +61 -0
  27. package/main/manage/commands/DoctorCommand.js +60 -0
  28. package/main/manage/commands/FixerCommand.js +624 -0
  29. package/main/manage/commands/InitCommand.js +62 -0
  30. package/main/manage/commands/ScannerCommand.js +654 -0
  31. package/main/manage/commands/SizingCommand.js +60 -0
  32. package/main/manage/commands/SummaryCommand.js +61 -0
  33. package/main/manage/commands/UsageCommand.js +60 -0
  34. package/main/manage/commands/ValidateCommand.js +978 -0
  35. package/main/manage/index-fixed.js +1447 -0
  36. package/main/manage/index.js +1462 -0
  37. package/main/manage/managers/DebugMenu.js +140 -0
  38. package/main/manage/managers/InteractiveMenu.js +177 -0
  39. package/main/manage/managers/LanguageMenu.js +62 -0
  40. package/main/manage/managers/SettingsMenu.js +53 -0
  41. package/main/manage/services/AuthenticationService.js +263 -0
  42. package/main/manage/services/ConfigurationService-fixed.js +449 -0
  43. package/main/manage/services/ConfigurationService.js +449 -0
  44. package/main/manage/services/FileManagementService.js +368 -0
  45. package/main/manage/services/FrameworkDetectionService.js +458 -0
  46. package/main/manage/services/InitService.js +1051 -0
  47. package/main/manage/services/SetupService.js +462 -0
  48. package/main/manage/services/SummaryService.js +450 -0
  49. package/main/manage/services/UsageService.js +1502 -0
  50. package/package.json +32 -29
  51. package/runtime/enhanced.d.ts +221 -221
  52. package/runtime/index.d.ts +29 -29
  53. package/runtime/index.full.d.ts +331 -331
  54. package/runtime/index.js +7 -6
  55. package/scripts/build-lite.js +17 -17
  56. package/scripts/deprecate-versions.js +23 -6
  57. package/scripts/export-translations.js +5 -5
  58. package/scripts/fix-all-i18n.js +3 -3
  59. package/scripts/fix-and-purify-i18n.js +3 -2
  60. package/scripts/fix-locale-control-chars.js +110 -0
  61. package/scripts/lint-locales.js +80 -0
  62. package/scripts/locale-optimizer.js +8 -8
  63. package/scripts/prepublish.js +21 -21
  64. package/scripts/security-check.js +117 -117
  65. package/scripts/sync-translations.js +4 -4
  66. package/scripts/sync-ui-locales.js +9 -8
  67. package/scripts/validate-all-translations.js +8 -7
  68. package/scripts/verify-deprecations.js +157 -161
  69. package/scripts/verify-translations.js +6 -5
  70. package/settings/i18ntk-config.json +282 -282
  71. package/settings/language-config.json +5 -5
  72. package/settings/settings-cli.js +9 -9
  73. package/settings/settings-manager.js +18 -18
  74. package/ui-locales/de.json +2417 -2348
  75. package/ui-locales/en.json +2415 -2352
  76. package/ui-locales/es.json +2425 -2353
  77. package/ui-locales/fr.json +2418 -2348
  78. package/ui-locales/ja.json +2463 -2361
  79. package/ui-locales/ru.json +2463 -2359
  80. package/ui-locales/zh.json +2418 -2351
  81. package/utils/admin-auth.js +2 -2
  82. package/utils/admin-cli.js +297 -297
  83. package/utils/admin-pin.js +9 -9
  84. package/utils/cli-helper.js +9 -9
  85. package/utils/config-helper.js +73 -104
  86. package/utils/config-manager.js +204 -171
  87. package/utils/config.js +5 -4
  88. package/utils/env-manager.js +249 -263
  89. package/utils/framework-detector.js +27 -24
  90. package/utils/i18n-helper.js +85 -41
  91. package/utils/init-helper.js +152 -94
  92. package/utils/json-output.js +98 -98
  93. package/utils/mini-commander.js +179 -0
  94. package/utils/missing-key-validator.js +5 -5
  95. package/utils/plugin-loader.js +40 -29
  96. package/utils/prompt.js +14 -44
  97. package/utils/safe-json.js +40 -0
  98. package/utils/secure-errors.js +3 -3
  99. package/utils/security-check-improved.js +390 -0
  100. package/utils/security-config.js +5 -5
  101. package/utils/security-fixed.js +607 -0
  102. package/utils/security.js +652 -602
  103. package/utils/setup-enforcer.js +136 -44
  104. package/utils/setup-validator.js +33 -32
  105. package/utils/ultra-performance-optimizer.js +11 -9
  106. package/utils/watch-locales.js +2 -1
  107. package/utils/prompt-fixed.js +0 -55
  108. package/utils/security-check.js +0 -454
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Debug Menu Manager
3
+ * @module managers/DebugMenu
4
+ */
5
+
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+ const { t } = require('../../../utils/i18n-helper');
9
+
10
+ module.exports = class DebugMenu {
11
+ constructor(manager) {
12
+ this.manager = manager; // Reference to I18nManager for access to methods and properties
13
+ this.adminAuth = manager.adminAuth;
14
+ }
15
+
16
+ /**
17
+ * Display the debug tools menu
18
+ */
19
+ async showDebugMenu() {
20
+ // Check for PIN protection
21
+ const authRequired = await this.adminAuth.isAuthRequiredForScript('debugMenu');
22
+ if (authRequired) {
23
+ console.log(`\n${t('adminPin.protectedAccess')}`);
24
+ const cliHelper = require('../../../utils/cli-helper');
25
+ const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
26
+ const isValid = await this.adminAuth.verifyPin(pin);
27
+
28
+ if (!isValid) {
29
+ console.log(t('adminPin.invalidPin'));
30
+ await this.manager.prompt(t('menu.pressEnterToContinue'));
31
+ await this.manager.showInteractiveMenu();
32
+ return;
33
+ }
34
+
35
+ console.log(t('adminPin.accessGranted'));
36
+ }
37
+
38
+ console.log(`\n${t('debug.title')}`);
39
+ console.log(t('debug.separator'));
40
+ console.log(t('debug.mainDebuggerSystemDiagnostics'));
41
+ console.log(t('debug.debugLogs'));
42
+ console.log(t('debug.backToMainMenu'));
43
+
44
+ const choice = await this.manager.prompt('\n' + t('debug.selectOption'));
45
+
46
+ switch (choice.trim()) {
47
+ case '1':
48
+ await this.runDebugTool('debugger.js', 'Main Debugger');
49
+ break;
50
+ case '2':
51
+ await this.viewDebugLogs();
52
+ break;
53
+ case '0':
54
+ await this.manager.showInteractiveMenu();
55
+ return;
56
+ default:
57
+ console.log(t('debug.invalidChoiceSelectRange'));
58
+ await this.showDebugMenu();
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Run a specific debug tool
64
+ */
65
+ async runDebugTool(toolName, displayName) {
66
+ console.log(t('debug.runningDebugTool', { displayName }));
67
+ try {
68
+ const toolPath = path.join(__dirname, '..', '..', 'scripts', 'debug', toolName);
69
+ if (fs.existsSync(toolPath)) {
70
+ console.log(`Debug tool available: ${toolName}`);
71
+ console.log(`To run this tool manually: node "${toolPath}"`);
72
+ console.log(`Working directory: ${path.join(__dirname, '..', '..')}`);
73
+ } else {
74
+ console.log(t('debug.debugToolNotFound', { toolName }));
75
+ }
76
+ } catch (error) {
77
+ console.error(t('debug.errorRunningDebugTool', { displayName, error: error.message }));
78
+ }
79
+
80
+ await this.manager.prompt('\n' + t('menu.pressEnterToContinue'));
81
+ await this.showDebugMenu();
82
+ }
83
+
84
+ /**
85
+ * View debug logs
86
+ */
87
+ async viewDebugLogs() {
88
+ console.log(`\n${t('debug.recentDebugLogs')}`);
89
+ console.log('============================================================');
90
+
91
+ try {
92
+ const logsDir = path.join(__dirname, '..', '..', 'scripts', 'debug', 'logs');
93
+ if (fs.existsSync(logsDir)) {
94
+ const files = fs.readdirSync(logsDir)
95
+ .filter(file => file.endsWith('.log') || file.endsWith('.txt'))
96
+ .sort((a, b) => {
97
+ const statA = fs.statSync(path.join(logsDir, a));
98
+ const statB = fs.statSync(path.join(logsDir, b));
99
+ return statB.mtime - statA.mtime;
100
+ })
101
+ .slice(0, 5);
102
+
103
+ if (files.length > 0) {
104
+ files.forEach((file, index) => {
105
+ const filePath = path.join(logsDir, file);
106
+ const stats = fs.statSync(filePath);
107
+ console.log(`${index + 1}. ${file} (${stats.mtime.toLocaleString()})`);
108
+ });
109
+
110
+ const choice = await this.manager.prompt('\n' + t('debug.selectLogPrompt', { count: files.length }));
111
+ const fileIndex = parseInt(choice) - 1;
112
+
113
+ if (fileIndex >= 0 && fileIndex < files.length) {
114
+ const logContent = fs.readFileSync(path.join(logsDir, files[fileIndex]), 'utf8');
115
+ console.log(`\n${t('debug.contentOf', { filename: files[fileIndex] })}:`);
116
+ console.log('============================================================');
117
+ console.log(logContent.slice(-2000)); // Show last 2000 characters
118
+ console.log('============================================================');
119
+ }
120
+ } else {
121
+ console.log(t('debug.noDebugLogsFound'));
122
+ }
123
+ } else {
124
+ console.log(t('debug.debugLogsDirectoryNotFound'));
125
+ }
126
+ } catch (error) {
127
+ console.error(t('errors.errorReadingDebugLogs', { error: error.message }));
128
+ }
129
+
130
+ await this.manager.prompt('\n' + t('menu.pressEnterToContinue'));
131
+ await this.manager.showInteractiveMenu();
132
+ }
133
+
134
+ /**
135
+ * Alias for showDebugMenu for backward compatibility
136
+ */
137
+ async show() {
138
+ return this.showDebugMenu();
139
+ }
140
+ };
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Interactive Menu Manager
3
+ * @module managers/InteractiveMenu
4
+ */
5
+
6
+ const { t } = require('../../../utils/i18n-helper');
7
+
8
+ module.exports = class InteractiveMenu {
9
+ constructor(manager) {
10
+ this.manager = manager; // Reference to I18nManager for access to methods and properties
11
+ this.adminAuth = manager.adminAuth;
12
+ this.ui = manager.ui;
13
+ }
14
+
15
+ /**
16
+ * Display the main interactive menu with 13 options
17
+ */
18
+ async showInteractiveMenu() {
19
+ // Check if we're in non-interactive mode (like echo 0 | node script)
20
+ if (this.manager.isNonInteractiveMode()) {
21
+ console.log(`\n${t('menu.title')}`);
22
+ console.log(t('menu.separator'));
23
+ console.log(`1. ${t('menu.options.init')}`);
24
+ console.log(`2. ${t('menu.options.analyze')}`);
25
+ console.log(`3. ${t('menu.options.validate')}`);
26
+ console.log(`4. ${t('menu.options.usage')}`);
27
+ console.log(`5. ${t('menu.options.complete')}`);
28
+ console.log(`6. ${t('menu.options.sizing')}`);
29
+ console.log(`7. ${t('menu.options.fix')}`);
30
+ console.log(`8. ${t('menu.options.status')}`);
31
+ console.log(`9. ${t('menu.options.delete')}`);
32
+ console.log(`10. ${t('menu.options.settings')}`);
33
+ console.log(`11. ${t('menu.options.help')}`);
34
+ console.log(`12. ${t('menu.options.language')}`);
35
+ console.log(`13. ${t('menu.options.scanner')}`);
36
+ console.log(`0. ${t('menu.options.exit')}`);
37
+
38
+ console.log('\n' + t('menu.nonInteractiveModeWarning'));
39
+ console.log(t('menu.useDirectExecution'));
40
+ console.log(t('menu.useHelpForCommands'));
41
+ this.manager.safeClose();
42
+ process.exit(0);
43
+ return;
44
+ }
45
+
46
+ console.log(`\n${t('menu.title')}`);
47
+ console.log(t('menu.separator'));
48
+ console.log(`1. ${t('menu.options.init')}`);
49
+ console.log(`2. ${t('menu.options.analyze')}`);
50
+ console.log(`3. ${t('menu.options.validate')}`);
51
+ console.log(`4. ${t('menu.options.usage')}`);
52
+ console.log(`5. ${t('menu.options.complete')}`);
53
+ console.log(`6. ${t('menu.options.sizing')}`);
54
+ console.log(`7. ${t('menu.options.fix')}`);
55
+ console.log(`8. ${t('menu.options.status')}`);
56
+ console.log(`9. ${t('menu.options.delete')}`);
57
+ console.log(`10. ${t('menu.options.settings')}`);
58
+ console.log(`11. ${t('menu.options.help')}`);
59
+ console.log(`12. ${t('menu.options.language')}`);
60
+ console.log(`13. ${t('menu.options.scanner')}`);
61
+ console.log(`0. ${t('menu.options.exit')}`);
62
+
63
+ const choice = await this.manager.prompt('\n' + t('menu.selectOptionPrompt'));
64
+
65
+ switch (choice.trim()) {
66
+ case '1':
67
+ await this.manager.executeCommand('init', {fromMenu: true});
68
+ break;
69
+ case '2':
70
+ await this.manager.executeCommand('analyze', {fromMenu: true});
71
+ break;
72
+ case '3':
73
+ await this.manager.executeCommand('validate', {fromMenu: true});
74
+ break;
75
+ case '4':
76
+ await this.manager.executeCommand('usage', {fromMenu: true});
77
+ break;
78
+ case '5':
79
+ await this.manager.executeCommand('complete', {fromMenu: true});
80
+ break;
81
+ case '6':
82
+ await this.manager.executeCommand('sizing', {fromMenu: true});
83
+ break;
84
+ case '7':
85
+ await this.manager.executeCommand('fix', {fromMenu: true});
86
+ break;
87
+ case '8':
88
+ // Check for PIN protection
89
+ const authRequired = await this.adminAuth.isAuthRequiredForScript('summaryReports');
90
+ if (authRequired) {
91
+ console.log(`\n${t('adminCli.protectedAccess')}`);
92
+ const cliHelper = require('../../../utils/cli-helper');
93
+ const pin = await cliHelper.promptPin(t('adminCli.enterPin') + ': ');
94
+ const isValid = await this.adminAuth.verifyPin(pin);
95
+
96
+ if (!isValid) {
97
+ console.log(t('adminCli.invalidPin'));
98
+ await this.manager.prompt(t('menu.pressEnterToContinue'));
99
+ await this.showInteractiveMenu();
100
+ return;
101
+ }
102
+
103
+ console.log(t('adminCli.accessGranted'));
104
+ }
105
+
106
+ console.log(t('summary.status.generating'));
107
+ try {
108
+ const summaryTool = require('../../i18ntk-summary');
109
+ const summary = new summaryTool();
110
+ await summary.run({ fromMenu: true });
111
+ console.log(t('summary.status.completed'));
112
+
113
+ // Check if we're in interactive mode before prompting
114
+ if (!this.manager.isNonInteractiveMode()) {
115
+ try {
116
+ await this.manager.prompt('\n' + t('debug.pressEnterToContinue'));
117
+ await this.showInteractiveMenu();
118
+ } catch (error) {
119
+ console.log(t('menu.returning'));
120
+ process.exit(0);
121
+ }
122
+ } else {
123
+ console.log(t('status.exitingCompleted'));
124
+ process.exit(0);
125
+ }
126
+ } catch (error) {
127
+ console.error(t('common.errorGeneratingStatusSummary', { error: error.message }));
128
+
129
+ // Check if we're in interactive mode before prompting
130
+ if (!this.manager.isNonInteractiveMode()) {
131
+ try {
132
+ await this.manager.prompt('\n' + t('debug.pressEnterToContinue'));
133
+ await this.showInteractiveMenu();
134
+ } catch (error) {
135
+ console.log(t('menu.returning'));
136
+ process.exit(0);
137
+ }
138
+ } else {
139
+ console.log(t('common.errorExiting'));
140
+ process.exit(1);
141
+ }
142
+ }
143
+ break;
144
+ case '9':
145
+ await this.manager.deleteReports();
146
+ break;
147
+ case '10':
148
+ await this.manager.showSettingsMenu();
149
+ break;
150
+ case '11':
151
+ this.manager.showHelp();
152
+ await this.manager.prompt(t('menu.returnToMainMenu'));
153
+ await this.showInteractiveMenu();
154
+ break;
155
+ case '12':
156
+ await this.manager.showLanguageMenu();
157
+ break;
158
+ case '13':
159
+ await this.manager.executeCommand('scanner', {fromMenu: true});
160
+ break;
161
+ case '0':
162
+ console.log(t('menu.goodbye'));
163
+ this.manager.safeClose();
164
+ process.exit(0);
165
+ default:
166
+ console.log(t('menu.invalidChoice'));
167
+ await this.showInteractiveMenu();
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Alias for showInteractiveMenu for backward compatibility
173
+ */
174
+ async show() {
175
+ return this.showInteractiveMenu();
176
+ }
177
+ };
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Language Menu Manager
3
+ * @module managers/LanguageMenu
4
+ */
5
+
6
+ const { t } = require('../../../utils/i18n-helper');
7
+
8
+ module.exports = class LanguageMenu {
9
+ constructor(manager) {
10
+ this.manager = manager; // Reference to I18nManager for access to methods and properties
11
+ this.ui = manager.ui;
12
+ }
13
+
14
+ /**
15
+ * Display the language selection menu
16
+ */
17
+ async showLanguageMenu() {
18
+ console.log(`\n${t('language.title')}`);
19
+ console.log(t('language.separator'));
20
+ console.log(t('language.current', { language: this.ui.getLanguageDisplayName(this.ui.getCurrentLanguage()) }));
21
+ console.log('\n' + t('language.available'));
22
+
23
+ this.ui.availableLanguages.forEach((lang, index) => {
24
+ const displayName = this.ui.getLanguageDisplayName(lang);
25
+ const current = lang === this.ui.getCurrentLanguage() ? ' ✓' : '';
26
+ console.log(t('language.languageOption', { index: index + 1, displayName, current }));
27
+ });
28
+
29
+ console.log(`0. ${t('language.backToMainMenu')}`);
30
+
31
+ const choice = await this.manager.prompt('\n' + t('language.prompt'));
32
+ const choiceNum = parseInt(choice);
33
+
34
+ if (choiceNum === 0) {
35
+ await this.manager.showInteractiveMenu();
36
+ return;
37
+ } else if (choiceNum >= 1 && choiceNum <= this.ui.availableLanguages.length) {
38
+ const selectedLang = this.ui.availableLanguages[choiceNum - 1];
39
+ await this.ui.changeLanguage(selectedLang);
40
+ console.log(t('language.changed', { language: this.ui.getLanguageDisplayName(selectedLang) }));
41
+
42
+ // Force reload translations for the entire system
43
+ const { loadTranslations } = require('../../../utils/i18n-helper');
44
+ loadTranslations(selectedLang);
45
+
46
+ // Return to main menu with new language
47
+ await this.manager.prompt('\n' + t('language.pressEnterToContinue'));
48
+ await this.manager.showInteractiveMenu();
49
+ } else {
50
+ console.log(t('language.invalid'));
51
+ await this.manager.prompt('\n' + t('language.pressEnterToContinue'));
52
+ await this.showLanguageMenu();
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Alias for showLanguageMenu for backward compatibility
58
+ */
59
+ async show() {
60
+ return this.showLanguageMenu();
61
+ }
62
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Settings Menu Manager
3
+ * @module managers/SettingsMenu
4
+ */
5
+
6
+ const { t } = require('../../../utils/i18n-helper');
7
+
8
+ module.exports = class SettingsMenu {
9
+ constructor(manager) {
10
+ this.manager = manager; // Reference to I18nManager for access to methods and properties
11
+ this.adminAuth = manager.adminAuth;
12
+ }
13
+
14
+ /**
15
+ * Display the settings management menu
16
+ */
17
+ async showSettingsMenu() {
18
+ try {
19
+ // Check for PIN protection
20
+ const authRequired = await this.adminAuth.isAuthRequiredForScript('settingsMenu');
21
+ if (authRequired) {
22
+ console.log(`\n${t('adminPin.protectedAccess')}`);
23
+ const cliHelper = require('../../../utils/cli-helper');
24
+ const pin = await cliHelper.promptPin(t('adminPin.enterPin') + ': ');
25
+ const isValid = await this.adminAuth.verifyPin(pin);
26
+
27
+ if (!isValid) {
28
+ console.log(t('adminPin.invalidPin'));
29
+ await this.manager.prompt(t('menu.pressEnterToContinue'));
30
+ await this.manager.showInteractiveMenu();
31
+ return;
32
+ }
33
+
34
+ console.log(t('adminPin.accessGranted'));
35
+ }
36
+
37
+ const SettingsCLI = require('../../../settings/settings-cli');
38
+ const settingsCLI = new SettingsCLI();
39
+ await settingsCLI.run();
40
+ } catch (error) {
41
+ console.error('❌ Error opening settings:', error.message);
42
+ await this.manager.prompt(t('menu.pressEnterToContinue'));
43
+ }
44
+ await this.manager.showInteractiveMenu();
45
+ }
46
+
47
+ /**
48
+ * Alias for showSettingsMenu for backward compatibility
49
+ */
50
+ async show() {
51
+ return this.showSettingsMenu();
52
+ }
53
+ };
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Authentication Service
3
+ * Handles PIN protection, admin authentication, and security checks
4
+ * @module services/AuthenticationService
5
+ */
6
+
7
+ const AdminAuth = require('../../../utils/admin-auth');
8
+
9
+ module.exports = class AuthenticationService {
10
+ constructor(config = {}) {
11
+ this.config = config;
12
+ this.settings = null;
13
+ this.configManager = null;
14
+ this.adminAuth = new AdminAuth();
15
+ }
16
+
17
+ /**
18
+ * Initialize the service with required dependencies
19
+ * @param {Object} configManager - Configuration manager instance
20
+ */
21
+ initialize(configManager) {
22
+ this.configManager = configManager;
23
+ this.settings = configManager.loadSettings ? configManager.loadSettings() : (configManager.getConfig ? configManager.getConfig() : {});
24
+ }
25
+
26
+ /**
27
+ * Check if authentication is required for the system
28
+ * @returns {Promise<boolean>} True if authentication is required
29
+ */
30
+ async isAuthRequired() {
31
+ return await this.adminAuth.isAuthRequired();
32
+ }
33
+
34
+ /**
35
+ * Check if authentication is required for a specific script
36
+ * @param {string} scriptName - Name of the script to check
37
+ * @returns {Promise<boolean>} True if authentication is required
38
+ */
39
+ async isAuthRequiredForScript(scriptName) {
40
+ return await this.adminAuth.isAuthRequiredForScript(scriptName);
41
+ }
42
+
43
+ /**
44
+ * Verify PIN for authentication
45
+ * @param {string} pin - PIN to verify
46
+ * @returns {Promise<boolean>} True if PIN is valid
47
+ */
48
+ async verifyPin(pin) {
49
+ return await this.adminAuth.verifyPin(pin);
50
+ }
51
+
52
+ /**
53
+ * Check admin authentication for protected operations
54
+ * @param {Object} ui - UI instance for translations (optional)
55
+ * @returns {Promise<boolean>} True if authentication passed
56
+ */
57
+ async checkAdminAuth(ui = null) {
58
+ const isRequired = await this.isAuthRequired();
59
+ if (!isRequired) {
60
+ return true;
61
+ }
62
+
63
+ // Check if admin PIN was provided via command line
64
+ const args = this.parseArgs();
65
+ if (args.adminPin) {
66
+ const isValid = await this.verifyPin(args.adminPin);
67
+ if (isValid) {
68
+ console.log(ui ? ui.t('adminCli.authSuccess') : 'Authentication successful');
69
+ return true;
70
+ } else {
71
+ console.log(ui ? ui.t('adminCli.invalidPin') : 'Invalid PIN');
72
+ return false;
73
+ }
74
+ }
75
+
76
+ console.log(ui ? ui.t('adminCli.authRequired') : 'Admin authentication required');
77
+ const cliHelper = require('../../../utils/cli-helper');
78
+ const pin = await cliHelper.promptPin(ui ? ui.t('adminCli.enterPin') : 'Enter PIN: ');
79
+ const isValid = await this.verifyPin(pin);
80
+
81
+ if (!isValid) {
82
+ console.log(ui ? ui.t('adminCli.invalidPin') : 'Invalid PIN');
83
+ return false;
84
+ }
85
+
86
+ console.log(ui ? ui.t('adminCli.authSuccess') : 'Authentication successful');
87
+ return true;
88
+ }
89
+
90
+ /**
91
+ * Parse command line arguments for authentication-related flags
92
+ * @returns {Object} Parsed arguments
93
+ */
94
+ parseArgs() {
95
+ const args = process.argv.slice(2);
96
+ const parsed = {};
97
+
98
+ args.forEach(arg => {
99
+ if (arg.startsWith('--')) {
100
+ const [key, value] = arg.substring(2).split('=');
101
+ const sanitizedKey = key?.trim();
102
+ const sanitizedValue = value !== undefined ? value.trim() : true;
103
+
104
+ switch (sanitizedKey) {
105
+ case 'admin-pin':
106
+ parsed.adminPin = sanitizedValue || '';
107
+ break;
108
+ case 'no-prompt':
109
+ parsed.noPrompt = true;
110
+ break;
111
+ default:
112
+ break;
113
+ }
114
+ }
115
+ });
116
+
117
+ return parsed;
118
+ }
119
+
120
+ /**
121
+ * Check if we're in non-interactive mode
122
+ * @returns {boolean} True if in non-interactive mode
123
+ */
124
+ isNonInteractiveMode() {
125
+ return !process.stdin.isTTY || process.stdin.destroyed;
126
+ }
127
+
128
+ /**
129
+ * Prompt user for input with fallback for non-interactive mode
130
+ * @param {string} question - Question to ask
131
+ * @returns {Promise<string>} User input or empty string
132
+ */
133
+ async prompt(question) {
134
+ const cliHelper = require('../../../utils/cli-helper');
135
+ // If interactive not available, return empty string to avoid hangs
136
+ if (!process.stdin.isTTY || process.stdin.destroyed) {
137
+ console.log('\n⚠️ Interactive input not available, using default response.');
138
+ return Promise.resolve('');
139
+ }
140
+ return cliHelper.prompt(`${question} `);
141
+ }
142
+
143
+ /**
144
+ * Safe method to close readline interface
145
+ */
146
+ safeClose() {
147
+ // This would be implemented if we had a readline interface to close
148
+ // For now, it's a placeholder for compatibility
149
+ }
150
+
151
+ /**
152
+ * Get execution context based on options and environment
153
+ * @param {Object} options - Execution options
154
+ * @returns {Object} Execution context information
155
+ */
156
+ getExecutionContext(options = {}) {
157
+ // Check if called from interactive menu
158
+ if (options.fromMenu === true) {
159
+ return { type: 'manager', source: 'interactive_menu' };
160
+ }
161
+
162
+ // Check if called from workflow/autorun
163
+ if (options.fromWorkflow === true) {
164
+ return { type: 'workflow', source: 'autorun_script' };
165
+ }
166
+
167
+ // Check if this is a direct command line execution
168
+ if (process.argv.some(arg => arg.startsWith('--command='))) {
169
+ return { type: 'direct', source: 'command_line' };
170
+ }
171
+
172
+ // Default to direct execution
173
+ return { type: 'direct', source: 'unknown' };
174
+ }
175
+
176
+ /**
177
+ * Execute command with authentication checks
178
+ * @param {string} command - Command to execute
179
+ * @param {Object} options - Execution options
180
+ * @param {Object} ui - UI instance for translations (optional)
181
+ * @returns {Promise<boolean>} True if command should proceed
182
+ */
183
+ async executeCommandWithAuth(command, options = {}, ui = null) {
184
+ console.log(ui ? ui.t('menu.executingCommand', { command }) : `Executing command: ${command}`);
185
+
186
+ // Enhanced context detection
187
+ const executionContext = this.getExecutionContext(options);
188
+ const isDirectCommand = executionContext.type === 'direct';
189
+ const isWorkflowExecution = executionContext.type === 'workflow';
190
+ const isManagerExecution = executionContext.type === 'manager';
191
+
192
+ // Check admin authentication for all commands when PIN protection is enabled
193
+ const authRequiredCommands = ['init', 'analyze', 'validate', 'usage', 'scanner', 'complete', 'fix', 'sizing', 'workflow', 'status', 'delete', 'settings', 'debug'];
194
+ if (authRequiredCommands.includes(command)) {
195
+ const authPassed = await this.checkAdminAuth(ui);
196
+ if (!authPassed) {
197
+ if (!this.isNonInteractiveMode() && !isDirectCommand) {
198
+ await this.prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
199
+ }
200
+ return false;
201
+ }
202
+ }
203
+
204
+ return true;
205
+ }
206
+
207
+ /**
208
+ * Handle command completion based on execution context
209
+ * @param {Object} executionContext - Execution context information
210
+ * @param {Object} ui - UI instance for translations (optional)
211
+ * @returns {Promise<void>}
212
+ */
213
+ async handleCommandCompletion(executionContext, ui = null) {
214
+ const isDirectCommand = executionContext.type === 'direct';
215
+ const isWorkflowExecution = executionContext.type === 'workflow';
216
+ const isManagerExecution = executionContext.type === 'manager';
217
+
218
+ console.log(ui ? ui.t('operations.completed') : 'Operation completed');
219
+
220
+ if (isManagerExecution && !this.isNonInteractiveMode()) {
221
+ // Interactive menu execution - return to menu
222
+ await this.prompt(ui ? ui.t('menu.returnToMainMenu') : 'Press Enter to return to main menu...');
223
+ } else {
224
+ // Direct commands, non-interactive mode, or workflow execution - exit immediately
225
+ console.log(ui ? ui.t('workflow.exitingCompleted') : 'Exiting...');
226
+ this.safeClose();
227
+ process.exit(0);
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Handle command error based on execution context
233
+ * @param {Error} error - Error that occurred
234
+ * @param {Object} executionContext - Execution context information
235
+ * @param {Object} ui - UI instance for translations (optional)
236
+ * @returns {Promise<void>}
237
+ */
238
+ async handleCommandError(error, executionContext, ui = null) {
239
+ const isDirectCommand = executionContext.type === 'direct';
240
+ const isWorkflowExecution = executionContext.type === 'workflow';
241
+ const isManagerExecution = executionContext.type === 'manager';
242
+
243
+ if (ui && ui.t) {
244
+ console.error(ui.t('common.errorExecutingCommand', { error: error.message }));
245
+ } else {
246
+ console.error(`Error executing command: ${error.message}`);
247
+ }
248
+
249
+ if (isManagerExecution && !this.isNonInteractiveMode()) {
250
+ // Interactive menu execution - show error and return to menu
251
+ await this.prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
252
+ } else if (isDirectCommand && !this.isNonInteractiveMode()) {
253
+ // Direct command execution - show "enter to continue" and exit with error
254
+ await this.prompt(ui ? ui.t('menu.pressEnterToContinue') : 'Press Enter to continue...');
255
+ this.safeClose();
256
+ process.exit(1);
257
+ } else {
258
+ // Non-interactive mode or workflow execution - exit immediately with error
259
+ this.safeClose();
260
+ process.exit(1);
261
+ }
262
+ }
263
+ };