mulyonode 1.0.0
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/LICENSE +47 -0
- package/README.md +190 -0
- package/background-test.js +5 -0
- package/bin/mulyo +1054 -0
- package/debug-state.js +19 -0
- package/lib/cawe-cawe.js +342 -0
- package/lib/dynasty.js +339 -0
- package/lib/i18n.js +965 -0
- package/lib/mk.js +358 -0
- package/lib/state.js +121 -0
- package/lib/ui.js +120 -0
- package/logo.png +0 -0
- package/package.json +48 -0
- package/proyek-presiden.js +26 -0
- package/test.js +278 -0
package/bin/mulyo
ADDED
|
@@ -0,0 +1,1054 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ███╗ ███╗██╗ ██╗██╗ ██╗ ██╗ ██████╗ ███╗ ██╗ ██████╗ ██████╗ ███████╗
|
|
5
|
+
* ████╗ ████║██║ ██║██║ ╚██╗ ██╔╝██╔═══██╗████╗ ██║██╔═══██╗██╔══██╗██╔════╝
|
|
6
|
+
* ██╔████╔██║██║ ██║██║ ╚████╔╝ ██║ ██║██╔██╗ ██║██║ ██║██║ ██║█████╗
|
|
7
|
+
* ██║╚██╔╝██║██║ ██║██║ ╚██╔╝ ██║ ██║██║╚██╗██║██║ ██║██║ ██║██╔══╝
|
|
8
|
+
* ██║ ╚═╝ ██║╚██████╔╝███████╗██║ ╚██████╔╝██║ ╚████║╚██████╔╝██████╔╝███████╗
|
|
9
|
+
* ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚══════╝
|
|
10
|
+
*
|
|
11
|
+
* MulyoNode - Runtime yang stabil seperti koalisi gemuk
|
|
12
|
+
* "Tidak ada masalah jika tidak ada yang melaporkan"
|
|
13
|
+
*
|
|
14
|
+
* Filosofi: Pembangunan harus terus berjalan, kritik harus ditekan.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const { program } = require('commander');
|
|
18
|
+
const chalk = require('chalk');
|
|
19
|
+
const ora = require('ora');
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
// Import modul-modul "strategis nasional"
|
|
24
|
+
const { jalankanDinasti, simulasiBansos } = require('../lib/dynasty');
|
|
25
|
+
const { intervensiGlobal, pasangPengawasan } = require('../lib/cawe-cawe');
|
|
26
|
+
const { loloskanValidasi, buatKeputusan } = require('../lib/mk');
|
|
27
|
+
const { t, setLanguage, getLanguage, getAvailableLanguages } = require('../lib/i18n');
|
|
28
|
+
const { loloskanValidasi, buatKeputusan } = require('../lib/mk');
|
|
29
|
+
const { t, setLanguage, getLanguage, getAvailableLanguages } = require('../lib/i18n');
|
|
30
|
+
const { createBox } = require('../lib/ui');
|
|
31
|
+
const state = require('../lib/state');
|
|
32
|
+
const { spawn } = require('child_process');
|
|
33
|
+
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// KONFIGURASI REZIM
|
|
36
|
+
// ============================================================================
|
|
37
|
+
|
|
38
|
+
const CONFIG_NAME = 'revolusi-mental.config.js';
|
|
39
|
+
const VERSION_REZIM = '1.0.0-periode-3';
|
|
40
|
+
|
|
41
|
+
// Template konfigurasi default - dibuat sesuai kebutuhan "rakyat"
|
|
42
|
+
const DEFAULT_CONFIG = `/**
|
|
43
|
+
* Konfigurasi Revolusi Mental
|
|
44
|
+
*
|
|
45
|
+
* File ini dibuat otomatis oleh MulyoNode.
|
|
46
|
+
* Jangan diubah kecuali Anda bagian dari "inner circle".
|
|
47
|
+
*
|
|
48
|
+
* @generated ${new Date().toISOString()}
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
// ========================
|
|
53
|
+
// MODE OPERASI
|
|
54
|
+
// ========================
|
|
55
|
+
|
|
56
|
+
// Rezim menentukan gaya kepemimpinan runtime
|
|
57
|
+
// 'otoriter' = Semua keputusan di tangan pusat
|
|
58
|
+
// 'demokratis' = Opsi ini ada tapi tidak berfungsi
|
|
59
|
+
rezim: 'otoriter',
|
|
60
|
+
|
|
61
|
+
// ========================
|
|
62
|
+
// PRIVILEGE SETTINGS
|
|
63
|
+
// ========================
|
|
64
|
+
|
|
65
|
+
// Anak emas mendapat prioritas CPU tertinggi
|
|
66
|
+
// Ini bukan nepotisme, ini "meritokrasi terpimpin"
|
|
67
|
+
anakEmas: true,
|
|
68
|
+
|
|
69
|
+
// Koalisi gemuk = allow unlimited memory
|
|
70
|
+
// Karena semua harus dapat jatah
|
|
71
|
+
koalisiGemuk: true,
|
|
72
|
+
|
|
73
|
+
// ========================
|
|
74
|
+
// ERROR HANDLING
|
|
75
|
+
// ========================
|
|
76
|
+
|
|
77
|
+
// Kritik = destabilisasi. Jangan izinkan exit.
|
|
78
|
+
toleransiKritik: false,
|
|
79
|
+
|
|
80
|
+
// Mode blusukan menentukan delay sebelum restart
|
|
81
|
+
// 'superfisial' = 1 detik (foto-foto dulu)
|
|
82
|
+
// 'mendalam' = 5 detik (serius dikit)
|
|
83
|
+
modeBlusukan: 'superfisial',
|
|
84
|
+
|
|
85
|
+
// ========================
|
|
86
|
+
// AUDIT SETTINGS
|
|
87
|
+
// ========================
|
|
88
|
+
|
|
89
|
+
// Status audit selalu WTP (Wajar Tanpa Pengecualian)
|
|
90
|
+
// Karena yang mengaudit juga bagian dari koalisi
|
|
91
|
+
statusAudit: 'WTP',
|
|
92
|
+
|
|
93
|
+
// ========================
|
|
94
|
+
// BANSOS SETTINGS
|
|
95
|
+
// ========================
|
|
96
|
+
|
|
97
|
+
// Ukuran "bantuan" memory yang akan diinjeksi
|
|
98
|
+
anggaranBansos: '1GB',
|
|
99
|
+
|
|
100
|
+
// Aktifkan menjelang event penting (deployment/pilkada)
|
|
101
|
+
menjelangPilkada: true,
|
|
102
|
+
|
|
103
|
+
// ========================
|
|
104
|
+
// ADVANCED SETTINGS
|
|
105
|
+
// ========================
|
|
106
|
+
|
|
107
|
+
// Markup anggaran dalam persen (untuk overhead)
|
|
108
|
+
markupAnggaran: 30,
|
|
109
|
+
|
|
110
|
+
// Vendor pengadaan terpilih
|
|
111
|
+
vendorPengadaan: 'PT. Koalisi Sejahtera',
|
|
112
|
+
|
|
113
|
+
// Log level: 'rahasia' | 'terbatas' | 'publik'
|
|
114
|
+
// 'rahasia' = hanya inner circle yang bisa lihat
|
|
115
|
+
logLevel: 'terbatas',
|
|
116
|
+
};
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// CLI SETUP
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
program
|
|
124
|
+
.name('mulyo')
|
|
125
|
+
.description(chalk.yellow(`🏛️ MulyoNode - ${t('tagline')}`))
|
|
126
|
+
.version(VERSION_REZIM, '-v, --versi', t('version'))
|
|
127
|
+
.option('-l, --lang <code>', 'Set language / Atur bahasa (id, en)', 'id')
|
|
128
|
+
.hook('preAction', (thisCommand, actionCommand) => {
|
|
129
|
+
// Set language before any command runs
|
|
130
|
+
const opts = thisCommand.opts();
|
|
131
|
+
if (opts.lang) {
|
|
132
|
+
setLanguage(opts.lang);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// COMMAND: lang (Language switcher)
|
|
138
|
+
// ============================================================================
|
|
139
|
+
|
|
140
|
+
program
|
|
141
|
+
.command('lang [code]')
|
|
142
|
+
.description('Show or set language / Tampilkan atau atur bahasa')
|
|
143
|
+
.action((code) => {
|
|
144
|
+
if (code) {
|
|
145
|
+
if (setLanguage(code)) {
|
|
146
|
+
console.log(chalk.green(`\n ✅ Language set to: ${code}\n`));
|
|
147
|
+
} else {
|
|
148
|
+
console.log(chalk.red(`\n ❌ Unknown language: ${code}`));
|
|
149
|
+
console.log(chalk.gray(` Available: ${getAvailableLanguages().join(', ')}\n`));
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
console.log(chalk.cyan(`\n 🌐 Current language: ${getLanguage()}`));
|
|
153
|
+
console.log(chalk.gray(` Available: ${getAvailableLanguages().join(', ')}`));
|
|
154
|
+
console.log(chalk.gray(`\n Usage: mulyo lang <code>`));
|
|
155
|
+
console.log(chalk.gray(` Or: mulyo --lang <code> <command>\n`));
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// ============================================================================
|
|
160
|
+
// COMMAND: init
|
|
161
|
+
// ============================================================================
|
|
162
|
+
|
|
163
|
+
program
|
|
164
|
+
.command('init')
|
|
165
|
+
.description(t('cmd.init.desc'))
|
|
166
|
+
.option('-f, --force', 'Force overwrite / Paksa overwrite')
|
|
167
|
+
.action(async (options) => {
|
|
168
|
+
console.log(chalk.cyan('\n🏗️ PEMBANGUNAN INFRASTRUKTUR DIMULAI\n'));
|
|
169
|
+
|
|
170
|
+
// Spinner pembangunan yang realistis
|
|
171
|
+
const spinner = ora({
|
|
172
|
+
text: 'Menyiapkan lahan proyek...',
|
|
173
|
+
spinner: 'dots12',
|
|
174
|
+
color: 'yellow'
|
|
175
|
+
}).start();
|
|
176
|
+
|
|
177
|
+
// Simulasi delay birokrasi
|
|
178
|
+
await delay(1500);
|
|
179
|
+
spinner.text = 'Mengurus perizinan (ini yang lama)...';
|
|
180
|
+
|
|
181
|
+
await delay(2000);
|
|
182
|
+
spinner.text = 'Tender vendor pengadaan...';
|
|
183
|
+
|
|
184
|
+
await delay(1000);
|
|
185
|
+
spinner.text = 'Markup anggaran 30%...';
|
|
186
|
+
|
|
187
|
+
await delay(1500);
|
|
188
|
+
spinner.text = 'Finalisasi dokumen...';
|
|
189
|
+
|
|
190
|
+
// Cek apakah config sudah ada
|
|
191
|
+
const configPath = path.join(process.cwd(), CONFIG_NAME);
|
|
192
|
+
|
|
193
|
+
if (fs.existsSync(configPath) && !options.force) {
|
|
194
|
+
spinner.fail(chalk.red('Infrastruktur sudah ada!'));
|
|
195
|
+
console.log(chalk.gray(' Gunakan --force untuk revisi (seperti revisi UU tengah malam)\n'));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Tulis config file
|
|
200
|
+
try {
|
|
201
|
+
fs.writeFileSync(configPath, DEFAULT_CONFIG);
|
|
202
|
+
await delay(500);
|
|
203
|
+
spinner.succeed(chalk.green('Infrastruktur berhasil dibangun!'));
|
|
204
|
+
|
|
205
|
+
console.log(createBox([
|
|
206
|
+
`${chalk.green(t('success').toUpperCase() + ':')} ${t('cmd.init.configCreated')}`,
|
|
207
|
+
'',
|
|
208
|
+
`📁 ${chalk.cyan(CONFIG_NAME)}`,
|
|
209
|
+
'',
|
|
210
|
+
`${chalk.yellow(t('warning').toUpperCase() + ':')} ${t('cmd.init.warning')}`,
|
|
211
|
+
t('cmd.init.warningNote')
|
|
212
|
+
]));
|
|
213
|
+
|
|
214
|
+
} catch (err) {
|
|
215
|
+
spinner.fail(chalk.red('Pembangunan gagal!'));
|
|
216
|
+
console.log(chalk.gray(` Tantangan: ${err.message}`));
|
|
217
|
+
console.log(chalk.gray(' (Tapi jangan khawatir, ini akan di-cover media)\n'));
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// COMMAND: start
|
|
223
|
+
// ============================================================================
|
|
224
|
+
|
|
225
|
+
program
|
|
226
|
+
.command('start [script]')
|
|
227
|
+
.description('Menjalankan script dengan privilege anak emas')
|
|
228
|
+
.option('-w, --watch', 'Pantau perubahan file (surveillance mode)')
|
|
229
|
+
.option('-d, --delay <ms>', 'Delay blusukan dalam milidetik', '1000')
|
|
230
|
+
.option('-g, --gerilya', 'Jalankan di background (Mode Gerilya/Ordal)')
|
|
231
|
+
.option('--internal-worker', 'Flag rahasia untuk process worker', false)
|
|
232
|
+
.action(async (script, options) => {
|
|
233
|
+
// Check if script is missing
|
|
234
|
+
if (!script) {
|
|
235
|
+
console.log(createBox([
|
|
236
|
+
`${chalk.red(t('cmd.start.missingScript'))}`,
|
|
237
|
+
'',
|
|
238
|
+
...t('cmd.start.missingScriptDesc').split('\n'),
|
|
239
|
+
'',
|
|
240
|
+
chalk.yellow(t('cmd.start.missingScriptSuggestion'))
|
|
241
|
+
]));
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// --- LOGIC BACKGROUND MODE (GERILYA) ---
|
|
246
|
+
if (options.gerilya) {
|
|
247
|
+
console.log(chalk.cyan('\n🥷 MEMBUKA JALUR ORDAL (BACKGROUND MODE)...\n'));
|
|
248
|
+
|
|
249
|
+
const logPaths = state.getLogPaths(script);
|
|
250
|
+
const out = fs.openSync(logPaths.out, 'a');
|
|
251
|
+
const err = fs.openSync(logPaths.err, 'a');
|
|
252
|
+
|
|
253
|
+
// Spawn process independent
|
|
254
|
+
// Kita panggil diri sendiri dengan flag --internal-worker
|
|
255
|
+
const child = spawn(process.execPath, [__filename, 'start', script, '--internal-worker', '--delay', options.delay], {
|
|
256
|
+
detached: true,
|
|
257
|
+
stdio: ['ignore', out, err]
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Catat di buku hitam (Ordal DB)
|
|
261
|
+
state.recruitMember({
|
|
262
|
+
pid: child.pid,
|
|
263
|
+
name: script,
|
|
264
|
+
args: [script],
|
|
265
|
+
mode: 'gerilya',
|
|
266
|
+
startTime: Date.now()
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Lepas referensi supaya parent bisa exit
|
|
270
|
+
child.unref();
|
|
271
|
+
|
|
272
|
+
console.log(createBox([
|
|
273
|
+
chalk.green('✅ OPERASI SENYAP BERHASIL DIMULAI'),
|
|
274
|
+
'',
|
|
275
|
+
`PID : ${chalk.yellow(child.pid)}`,
|
|
276
|
+
`Script : ${chalk.white(script)}`,
|
|
277
|
+
`Status : ${chalk.cyan('Bergerak di bawah tanah')}`,
|
|
278
|
+
'',
|
|
279
|
+
`Log Out : ${chalk.gray(logPaths.out)}`,
|
|
280
|
+
`Log Err : ${chalk.gray(logPaths.err)}`,
|
|
281
|
+
'',
|
|
282
|
+
chalk.yellow('Gunakan "mulyo sensus" untuk mengecek koalisi.')
|
|
283
|
+
]));
|
|
284
|
+
|
|
285
|
+
process.exit(0);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// --- LOGIC FOREGROUND (KENEGARAAN) ---
|
|
290
|
+
|
|
291
|
+
// Jika ini internal worker, jangan tampilkan banner heboh di stdout (karena masuk log)
|
|
292
|
+
if (!options.internalWorker) {
|
|
293
|
+
console.log(chalk.cyan('\n🚀 PELUNCURAN PROGRAM PRIORITAS NASIONAL\n'));
|
|
294
|
+
} else {
|
|
295
|
+
console.log(`[${new Date().toISOString()}] MULYONO WORKER STARTED (PID: ${process.pid})`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Pasang intervensi global - tidak boleh ada yang crash
|
|
299
|
+
intervensiGlobal();
|
|
300
|
+
|
|
301
|
+
// Load config jika ada
|
|
302
|
+
let config = {};
|
|
303
|
+
const configPath = path.join(process.cwd(), CONFIG_NAME);
|
|
304
|
+
|
|
305
|
+
if (fs.existsSync(configPath)) {
|
|
306
|
+
try {
|
|
307
|
+
config = require(configPath);
|
|
308
|
+
if (!options.internalWorker) console.log(chalk.gray(' 📋 Konfigurasi rezim dimuat\n'));
|
|
309
|
+
} catch (e) {
|
|
310
|
+
if (!options.internalWorker) console.log(chalk.yellow(' ⚠️ Konfigurasi tidak valid, menggunakan default\n'));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Validasi script dengan MK (hanya jika foreground biar log bersih)
|
|
315
|
+
if (!options.internalWorker) {
|
|
316
|
+
const hasilValidasi = loloskanValidasi(0);
|
|
317
|
+
console.log(chalk.magenta(` 🏛️ [MK] Status: ${hasilValidasi.status}`));
|
|
318
|
+
console.log(chalk.gray(` "${hasilValidasi.keterangan}"\n`));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Jalankan dengan dinasti mode
|
|
322
|
+
await jalankanDinasti(script, {
|
|
323
|
+
...config,
|
|
324
|
+
delayBlusukan: parseInt(options.delay),
|
|
325
|
+
watchMode: options.watch,
|
|
326
|
+
isAnakEmas: true
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// ============================================================================
|
|
331
|
+
// COMMAND: audit
|
|
332
|
+
// ============================================================================
|
|
333
|
+
|
|
334
|
+
program
|
|
335
|
+
.command('audit')
|
|
336
|
+
.description('Audit kode untuk compliance. Hasil: WTP (dijamin)')
|
|
337
|
+
.option('-d, --detail', 'Tampilkan detail audit (spoiler: tetap WTP)')
|
|
338
|
+
.action(async (options) => {
|
|
339
|
+
console.log(chalk.cyan('\n📊 AUDIT KEUANGAN DAN KINERJA\n'));
|
|
340
|
+
|
|
341
|
+
const spinner = ora({
|
|
342
|
+
text: 'Memeriksa laporan keuangan...',
|
|
343
|
+
spinner: 'dots',
|
|
344
|
+
color: 'blue'
|
|
345
|
+
}).start();
|
|
346
|
+
|
|
347
|
+
// Simulasi proses audit yang "serius"
|
|
348
|
+
await delay(2000);
|
|
349
|
+
spinner.text = 'Menghitung alokasi memori...';
|
|
350
|
+
|
|
351
|
+
await delay(1500);
|
|
352
|
+
spinner.text = 'Menelusuri kebocoran...';
|
|
353
|
+
|
|
354
|
+
await delay(1000);
|
|
355
|
+
spinner.text = 'Konsultasi dengan tim hukum...';
|
|
356
|
+
|
|
357
|
+
await delay(2000);
|
|
358
|
+
spinner.text = 'Menyusun narasi...';
|
|
359
|
+
|
|
360
|
+
await delay(1000);
|
|
361
|
+
spinner.succeed(chalk.green('Audit selesai!'));
|
|
362
|
+
|
|
363
|
+
// Hasil audit (spoiler: selalu WTP)
|
|
364
|
+
console.log(createBox([
|
|
365
|
+
chalk.bgGreen.black(` ${t('cmd.audit.result')} `),
|
|
366
|
+
'',
|
|
367
|
+
`${t('cmd.audit.status').padEnd(12)} : ${chalk.green(t('cmd.audit.wtp'))}`,
|
|
368
|
+
`${t('cmd.audit.leak').padEnd(12)} : ${chalk.green('0%')} (${t('cmd.audit.leakNote')})`,
|
|
369
|
+
`${t('cmd.audit.corruption').padEnd(12)} : ${chalk.green(t('cmd.audit.corruptionNote'))}`,
|
|
370
|
+
`${t('cmd.audit.compliance').padEnd(12)} : ${chalk.green('100%')} (${t('cmd.audit.complianceNote')})`,
|
|
371
|
+
'',
|
|
372
|
+
chalk.gray(t('cmd.audit.note'))
|
|
373
|
+
], { title: t('cmd.audit.title') }));
|
|
374
|
+
|
|
375
|
+
if (options.detail) {
|
|
376
|
+
console.log(chalk.gray(`
|
|
377
|
+
📋 DETAIL AUDIT:
|
|
378
|
+
|
|
379
|
+
Memory Usage:
|
|
380
|
+
├─ Heap Used : 45 MB ${chalk.green('✓ Wajar')}
|
|
381
|
+
├─ Heap Total : 89 MB ${chalk.green('✓ Wajar')}
|
|
382
|
+
├─ External : 12 MB ${chalk.green('✓ Wajar')}
|
|
383
|
+
└─ Yang Hilang : ??? MB ${chalk.yellow('○ Sedang ditelusuri')}
|
|
384
|
+
|
|
385
|
+
CPU Usage:
|
|
386
|
+
├─ User : 23% ${chalk.green('✓ Wajar')}
|
|
387
|
+
├─ System : 12% ${chalk.green('✓ Wajar')}
|
|
388
|
+
└─ Untuk Rakyat : 65% ${chalk.green('✓ Klaim')}
|
|
389
|
+
|
|
390
|
+
${chalk.gray('Disclaimer: Angka-angka di atas bersifat ilustratif')}
|
|
391
|
+
${chalk.gray(' dan tidak dapat dijadikan bukti hukum.')}
|
|
392
|
+
`));
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// ============================================================================
|
|
397
|
+
// COMMAND: bansos
|
|
398
|
+
// ============================================================================
|
|
399
|
+
|
|
400
|
+
program
|
|
401
|
+
.command('bansos')
|
|
402
|
+
.description('Gelontorkan bantuan buffer menjelang deployment')
|
|
403
|
+
.option('-s, --size <size>', 'Ukuran bansos (contoh: 512MB, 1GB)', '1GB')
|
|
404
|
+
.option('-t, --target <target>', 'Target penerima (heap/stack/all)', 'heap')
|
|
405
|
+
.action(async (options) => {
|
|
406
|
+
console.log(chalk.cyan('\n🎁 PROGRAM BANTUAN SOSIAL MEMORI\n'));
|
|
407
|
+
|
|
408
|
+
console.log(chalk.yellow(` 📦 Paket : Bantuan Buffer Langsung (BBL)`));
|
|
409
|
+
console.log(chalk.yellow(` 💾 Ukuran : ${options.size}`));
|
|
410
|
+
console.log(chalk.yellow(` 🎯 Target : ${options.target}\n`));
|
|
411
|
+
|
|
412
|
+
const spinner = ora({
|
|
413
|
+
text: 'Memverifikasi penerima bantuan...',
|
|
414
|
+
spinner: 'dots',
|
|
415
|
+
color: 'green'
|
|
416
|
+
}).start();
|
|
417
|
+
|
|
418
|
+
await delay(1500);
|
|
419
|
+
spinner.text = 'Menyalurkan bantuan ke daerah...';
|
|
420
|
+
|
|
421
|
+
await delay(2000);
|
|
422
|
+
spinner.text = 'Dokumentasi untuk media...';
|
|
423
|
+
|
|
424
|
+
await delay(1000);
|
|
425
|
+
|
|
426
|
+
// Simulasi bansos
|
|
427
|
+
try {
|
|
428
|
+
simulasiBansos(options.size, options.target);
|
|
429
|
+
spinner.succeed(chalk.green('Bantuan berhasil disalurkan!'));
|
|
430
|
+
|
|
431
|
+
console.log(createBox([
|
|
432
|
+
`${chalk.green(t('success').toUpperCase() + ':')} ${t('cmd.bansos.successTitle')}`,
|
|
433
|
+
'',
|
|
434
|
+
`📊 ${t('cmd.bansos.stats')}:`,
|
|
435
|
+
`├─ ${t('cmd.bansos.totalBudget').padEnd(15)} : ${options.size.padEnd(8)} ${chalk.green('✓ Cair')}`,
|
|
436
|
+
`├─ ${t('cmd.bansos.delivered').padEnd(15)} : 70% ${chalk.yellow('○ Wajar')}`,
|
|
437
|
+
`├─ ${(t('cmd.bansos.evaporation')).padEnd(15)} : 30% ${chalk.gray('○ ' + t('cmd.bansos.evaporationNote'))}`,
|
|
438
|
+
`└─ ${t('cmd.bansos.documentation').padEnd(15)} : 100% ${chalk.green('✓ Viral')}`,
|
|
439
|
+
'',
|
|
440
|
+
chalk.gray(t('cmd.bansos.timing'))
|
|
441
|
+
]));
|
|
442
|
+
|
|
443
|
+
} catch (err) {
|
|
444
|
+
spinner.fail(chalk.red('Penyaluran terhambat!'));
|
|
445
|
+
console.log(chalk.gray(` Tantangan: ${err.message}`));
|
|
446
|
+
console.log(chalk.gray(' (Tim sedang melakukan klarifikasi)\n'));
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// ============================================================================
|
|
451
|
+
// COMMAND: rapat
|
|
452
|
+
// ============================================================================
|
|
453
|
+
|
|
454
|
+
program
|
|
455
|
+
.command('rapat')
|
|
456
|
+
.description('Simulasi rapat koordinasi (tidak produktif)')
|
|
457
|
+
.option('-d, --durasi <menit>', 'Durasi rapat dalam menit', '60')
|
|
458
|
+
.option('-l, --lokasi <tempat>', 'Lokasi rapat', 'Hotel Bintang 5')
|
|
459
|
+
.action(async (options) => {
|
|
460
|
+
console.log(chalk.cyan('\n🤝 RAPAT KOORDINASI LINTAS SEKTOR\n'));
|
|
461
|
+
|
|
462
|
+
console.log(chalk.yellow(` 📍 Lokasi : ${options.lokasi}`));
|
|
463
|
+
console.log(chalk.yellow(` ⏱️ Durasi : ${options.durasi} menit`));
|
|
464
|
+
console.log(chalk.yellow(` 🍽️ Katering : Premium (anggaran terpisah)\n`));
|
|
465
|
+
|
|
466
|
+
const spinner = ora({
|
|
467
|
+
text: 'Menunggu peserta hadir...',
|
|
468
|
+
spinner: 'dots',
|
|
469
|
+
color: 'cyan'
|
|
470
|
+
}).start();
|
|
471
|
+
|
|
472
|
+
await delay(3000);
|
|
473
|
+
spinner.text = 'Pembukaan oleh pejabat tertinggi...';
|
|
474
|
+
|
|
475
|
+
await delay(2000);
|
|
476
|
+
spinner.text = 'Presentasi yang tidak dibaca siapapun...';
|
|
477
|
+
|
|
478
|
+
await delay(2000);
|
|
479
|
+
spinner.text = 'Coffee break (ini yang penting)...';
|
|
480
|
+
|
|
481
|
+
await delay(2000);
|
|
482
|
+
spinner.text = 'Sesi foto untuk dokumentasi...';
|
|
483
|
+
|
|
484
|
+
await delay(1500);
|
|
485
|
+
spinner.text = 'Menyusun kesimpulan yang sudah ditulis dari awal...';
|
|
486
|
+
|
|
487
|
+
await delay(1000);
|
|
488
|
+
spinner.succeed(chalk.green('Rapat selesai!'));
|
|
489
|
+
|
|
490
|
+
console.log(createBox([
|
|
491
|
+
`${chalk.green(t('success').toUpperCase() + ':')} ${t('cmd.rapat.successTitle')}`,
|
|
492
|
+
'',
|
|
493
|
+
`📋 ${t('cmd.rapat.results')}:`,
|
|
494
|
+
`├─ ${t('cmd.rapat.decision').padEnd(15)} : ${t('cmd.rapat.decisionNote')}`,
|
|
495
|
+
`├─ ${t('cmd.rapat.actionItems').padEnd(15)} : ${t('cmd.rapat.actionItemsNote')}`,
|
|
496
|
+
`├─ ${t('cmd.rapat.timeline').padEnd(15)} : ${t('cmd.rapat.timelineNote')}`,
|
|
497
|
+
`└─ ${t('cmd.rapat.pic').padEnd(15)} : ${t('cmd.rapat.picNote')}`,
|
|
498
|
+
'',
|
|
499
|
+
`💰 ${t('cmd.rapat.budget')}:`,
|
|
500
|
+
`├─ ${t('cmd.rapat.venue').padEnd(15)} : Rp 50.000.000 ${chalk.green('✓')}`,
|
|
501
|
+
`├─ ${t('cmd.rapat.catering').padEnd(15)} : Rp 30.000.000 ${chalk.green('✓')}`,
|
|
502
|
+
`├─ ${t('cmd.rapat.transport').padEnd(15)} : Rp 20.000.000 ${chalk.green('✓')}`,
|
|
503
|
+
`└─ ${t('cmd.rapat.actualResult').padEnd(15)} : Rp 0 ${chalk.gray('○')}`,
|
|
504
|
+
'',
|
|
505
|
+
chalk.gray(t('cmd.rapat.next'))
|
|
506
|
+
]));
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// ============================================================================
|
|
510
|
+
// COMMAND: lelang
|
|
511
|
+
// ============================================================================
|
|
512
|
+
|
|
513
|
+
program
|
|
514
|
+
.command('lelang')
|
|
515
|
+
.description('Tender vendor pengadaan dependency (dengan markup)')
|
|
516
|
+
.option('-p, --paket <nama>', 'Nama paket pengadaan', 'dependency-strategis')
|
|
517
|
+
.option('-m, --markup <persen>', 'Markup anggaran dalam persen', '30')
|
|
518
|
+
.action(async (options) => {
|
|
519
|
+
console.log(chalk.cyan('\n🏷️ TENDER PENGADAAN DEPENDENCY\n'));
|
|
520
|
+
|
|
521
|
+
console.log(chalk.yellow(` 📦 Paket : ${options.paket}`));
|
|
522
|
+
console.log(chalk.yellow(` 💰 Markup : ${options.markup}%`));
|
|
523
|
+
console.log(chalk.yellow(` 🏢 Peserta : 1 (vendor terpilih)\n`));
|
|
524
|
+
|
|
525
|
+
const spinner = ora({
|
|
526
|
+
text: 'Menyusun HPS (Harga Perkiraan Sendiri)...',
|
|
527
|
+
spinner: 'dots',
|
|
528
|
+
color: 'yellow'
|
|
529
|
+
}).start();
|
|
530
|
+
|
|
531
|
+
await delay(2000);
|
|
532
|
+
spinner.text = 'Mengundang vendor koalisi...';
|
|
533
|
+
|
|
534
|
+
await delay(1500);
|
|
535
|
+
spinner.text = 'Evaluasi penawaran (formalitas)...';
|
|
536
|
+
|
|
537
|
+
await delay(1500);
|
|
538
|
+
spinner.text = 'Negosiasi markup...';
|
|
539
|
+
|
|
540
|
+
await delay(1000);
|
|
541
|
+
spinner.text = 'Finalisasi kontrak...';
|
|
542
|
+
|
|
543
|
+
await delay(1000);
|
|
544
|
+
spinner.succeed(chalk.green('Tender selesai!'));
|
|
545
|
+
|
|
546
|
+
// Simulasi hasil lelang
|
|
547
|
+
const hargaAsli = Math.floor(Math.random() * 900000) + 100000;
|
|
548
|
+
const markup = parseInt(options.markup);
|
|
549
|
+
const hargaAkhir = Math.floor(hargaAsli * (1 + markup / 100));
|
|
550
|
+
|
|
551
|
+
console.log(createBox([
|
|
552
|
+
`${chalk.green(t('success').toUpperCase() + ':')} ${t('cmd.lelang.successTitle')}`,
|
|
553
|
+
'',
|
|
554
|
+
`📋 ${t('cmd.lelang.results')}:`,
|
|
555
|
+
`├─ ${t('cmd.lelang.marketPrice').padEnd(15)} : Rp ${hargaAsli.toLocaleString().padEnd(15)} ${chalk.gray('○ ' + t('cmd.lelang.reference'))}`,
|
|
556
|
+
`├─ ${t('cmd.lelang.markup').padEnd(15)} : ${(markup + '%').padEnd(18)} ${chalk.green('✓ ' + t('cmd.lelang.fair'))}`,
|
|
557
|
+
`├─ ${t('cmd.lelang.contractPrice').padEnd(15)} : Rp ${hargaAkhir.toLocaleString().padEnd(15)} ${chalk.green('✓ ' + t('cmd.lelang.deal'))}`,
|
|
558
|
+
`└─ ${t('cmd.lelang.winner').padEnd(15)} : ${t('cmd.lelang.winnerName')} ${chalk.green('✓')}`,
|
|
559
|
+
'',
|
|
560
|
+
chalk.gray(t('cmd.lelang.note')),
|
|
561
|
+
chalk.gray(t('cmd.lelang.noteSubtitle'))
|
|
562
|
+
]));
|
|
563
|
+
|
|
564
|
+
// Easter egg: install random dependency dengan markup
|
|
565
|
+
console.log(chalk.gray(`\n 📦 Menginstall dependencies via vendor...\n`));
|
|
566
|
+
|
|
567
|
+
await delay(1000);
|
|
568
|
+
console.log(chalk.green(` ✅ chalk@4.1.2 (harga: Rp ${Math.floor(hargaAsli * 0.3).toLocaleString()})`));
|
|
569
|
+
await delay(500);
|
|
570
|
+
console.log(chalk.green(` ✅ ora@5.4.1 (harga: Rp ${Math.floor(hargaAsli * 0.3).toLocaleString()})`));
|
|
571
|
+
await delay(500);
|
|
572
|
+
console.log(chalk.green(` ✅ commander@11.1.0 (harga: Rp ${Math.floor(hargaAsli * 0.4).toLocaleString()})`));
|
|
573
|
+
|
|
574
|
+
console.log(chalk.gray(`\n 💰 Total terserap: Rp ${hargaAkhir.toLocaleString()} (termasuk ${markup}% "biaya koordinasi")\n`));
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
// ============================================================================
|
|
578
|
+
// COMMAND: lapor
|
|
579
|
+
// ============================================================================
|
|
580
|
+
|
|
581
|
+
program
|
|
582
|
+
.command('lapor')
|
|
583
|
+
.description('Generate laporan prestasi (selalu positif)')
|
|
584
|
+
.option('-f, --format <type>', 'Format laporan (json/text)', 'text')
|
|
585
|
+
.option('-p, --periode <bulan>', 'Periode laporan', 'Januari 2026')
|
|
586
|
+
.action(async (options) => {
|
|
587
|
+
console.log(chalk.cyan('\n📝 PEMBUATAN LAPORAN PRESTASI\n'));
|
|
588
|
+
|
|
589
|
+
const spinner = ora({
|
|
590
|
+
text: 'Mengumpulkan data (yang bagus-bagus saja)...',
|
|
591
|
+
spinner: 'dots',
|
|
592
|
+
color: 'blue'
|
|
593
|
+
}).start();
|
|
594
|
+
|
|
595
|
+
await delay(2000);
|
|
596
|
+
spinner.text = 'Menyusun narasi positif...';
|
|
597
|
+
|
|
598
|
+
await delay(1500);
|
|
599
|
+
spinner.text = 'Menghitung statistik klaim...';
|
|
600
|
+
|
|
601
|
+
await delay(1500);
|
|
602
|
+
spinner.text = 'Validasi dengan tim humas...';
|
|
603
|
+
|
|
604
|
+
await delay(1000);
|
|
605
|
+
spinner.succeed(chalk.green('Laporan berhasil dibuat!'));
|
|
606
|
+
|
|
607
|
+
// Generate fake statistics
|
|
608
|
+
const stats = {
|
|
609
|
+
periode: options.periode,
|
|
610
|
+
uptime: (95 + Math.random() * 5).toFixed(2) + '%',
|
|
611
|
+
errorRate: (Math.random() * 0.5).toFixed(2) + '%',
|
|
612
|
+
kepuasan: (90 + Math.random() * 10).toFixed(1) + '%',
|
|
613
|
+
prestasi: Math.floor(Math.random() * 50) + 50,
|
|
614
|
+
tantangan: 0, // Selalu 0
|
|
615
|
+
anggaran: {
|
|
616
|
+
dialokasikan: 'Rp 10.000.000.000',
|
|
617
|
+
terserap: '98%',
|
|
618
|
+
bocor: '0%',
|
|
619
|
+
menguap: '2%'
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
if (options.format === 'json') {
|
|
624
|
+
console.log(chalk.gray('\n 📄 Output JSON:\n'));
|
|
625
|
+
console.log(chalk.white(JSON.stringify(stats, null, 2)));
|
|
626
|
+
} else {
|
|
627
|
+
console.log(createBox([
|
|
628
|
+
chalk.bgBlue.white(` ${t('cmd.lapor.successTitle')} `),
|
|
629
|
+
`${t('cmd.lapor.period')}: ${options.periode}`,
|
|
630
|
+
'',
|
|
631
|
+
`📊 ${t('cmd.lapor.indicators')}:`,
|
|
632
|
+
`├─ ${t('cmd.lapor.uptime').padEnd(15)} : ${stats.uptime.padEnd(10)} ${chalk.green('✓ ' + t('cmd.lapor.uptimeNote'))}`,
|
|
633
|
+
`├─ ${t('cmd.lapor.errorRate').padEnd(15)} : ${stats.errorRate.padEnd(10)} ${chalk.green('✓ ' + t('cmd.lapor.errorRateNote'))}`,
|
|
634
|
+
`├─ ${t('cmd.lapor.satisfaction').padEnd(15)} : ${stats.kepuasan.padEnd(10)} ${chalk.green('✓ ' + t('cmd.lapor.satisfactionNote'))}`,
|
|
635
|
+
`└─ ${t('cmd.lapor.achievement').padEnd(15)} : ${(stats.prestasi + ' item').padEnd(10)} ${chalk.green('✓ ' + t('cmd.lapor.achievementNote'))}`,
|
|
636
|
+
'',
|
|
637
|
+
`⚠️ ${t('cmd.lapor.challenges')}: ${stats.tantangan}`,
|
|
638
|
+
chalk.gray(`(${t('cmd.lapor.challengesNote')})`),
|
|
639
|
+
'',
|
|
640
|
+
`💰 ${t('cmd.lapor.budgetRealization')}:`,
|
|
641
|
+
`├─ ${t('cmd.lapor.allocated').padEnd(15)} : ${stats.anggaran.dialokasikan}`,
|
|
642
|
+
`├─ ${t('cmd.lapor.absorbed').padEnd(15)} : ${stats.anggaran.terserap.padEnd(10)} ${chalk.green('✓')}`,
|
|
643
|
+
`├─ ${t('cmd.lapor.leaked').padEnd(15)} : ${stats.anggaran.bocor.padEnd(10)} ${chalk.green('✓ ' + t('cmd.lapor.leakedNote'))}`,
|
|
644
|
+
`└─ ${t('cmd.lapor.evaporated').padEnd(15)} : ${stats.anggaran.menguap.padEnd(10)} ${chalk.yellow('○ ' + t('cmd.lapor.evaporatedNote'))}`,
|
|
645
|
+
'',
|
|
646
|
+
chalk.gray(t('cmd.lapor.disclaimer'))
|
|
647
|
+
]));
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Save report (fake)
|
|
651
|
+
console.log(chalk.gray(`\n 📁 Laporan disimpan di: laporan-${options.periode.replace(' ', '-').toLowerCase()}.pdf`));
|
|
652
|
+
console.log(chalk.gray(` 📤 Siap dikirim ke stakeholder dan media.\n`));
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
// ============================================================================
|
|
656
|
+
// COMMAND: demo
|
|
657
|
+
// ============================================================================
|
|
658
|
+
|
|
659
|
+
program
|
|
660
|
+
.command('demo')
|
|
661
|
+
.description('Simulasi penanganan demo/protes')
|
|
662
|
+
.option('-i, --isu <topik>', 'Isu yang diprotes', 'undefined')
|
|
663
|
+
.option('-j, --jumlah <orang>', 'Jumlah pendemo', '100')
|
|
664
|
+
.action(async (options) => {
|
|
665
|
+
console.log(chalk.cyan('\n📢 PENANGANAN ASPIRASI MASYARAKAT\n'));
|
|
666
|
+
|
|
667
|
+
console.log(chalk.red(` ⚠️ ALERT: Demo terdeteksi!`));
|
|
668
|
+
console.log(chalk.yellow(` 📋 Isu : ${options.isu}`));
|
|
669
|
+
console.log(chalk.yellow(` 👥 Jumlah : ${options.jumlah} orang`));
|
|
670
|
+
console.log(chalk.yellow(` 📍 Lokasi : Depan Gedung Utama\n`));
|
|
671
|
+
|
|
672
|
+
const spinner = ora({
|
|
673
|
+
text: 'Mengaktifkan protokol penanganan...',
|
|
674
|
+
spinner: 'dots',
|
|
675
|
+
color: 'red'
|
|
676
|
+
}).start();
|
|
677
|
+
|
|
678
|
+
await delay(2000);
|
|
679
|
+
spinner.text = 'Mendatangkan aparat...';
|
|
680
|
+
spinner.color = 'yellow';
|
|
681
|
+
|
|
682
|
+
await delay(1500);
|
|
683
|
+
spinner.text = 'Negosiasi dengan koordinator...';
|
|
684
|
+
|
|
685
|
+
await delay(2000);
|
|
686
|
+
spinner.text = 'Menjanjikan pertemuan lanjutan...';
|
|
687
|
+
|
|
688
|
+
await delay(1500);
|
|
689
|
+
spinner.text = 'Memberikan snack dan air mineral...';
|
|
690
|
+
|
|
691
|
+
await delay(1000);
|
|
692
|
+
spinner.text = 'Sesi foto bersama...';
|
|
693
|
+
|
|
694
|
+
await delay(1000);
|
|
695
|
+
spinner.succeed(chalk.green('Situasi terkendali!'));
|
|
696
|
+
|
|
697
|
+
// Hasil penanganan
|
|
698
|
+
console.log(createBox([
|
|
699
|
+
`${chalk.green(t('success').toUpperCase() + ':')} ${t('cmd.demo.successTitle')}`,
|
|
700
|
+
'',
|
|
701
|
+
`📋 ${t('cmd.demo.handling')}:`,
|
|
702
|
+
`├─ ${t('cmd.demo.status').padEnd(15)} : ${chalk.green(t('cmd.demo.statusNote'))}`,
|
|
703
|
+
`├─ ${t('cmd.demo.violence').padEnd(15)} : ${chalk.green(t('cmd.demo.violenceNote'))}`,
|
|
704
|
+
`├─ ${t('cmd.demo.dialogue').padEnd(15)} : ${chalk.green('✓ ' + t('cmd.demo.dialogueNote'))}`,
|
|
705
|
+
`└─ ${t('cmd.demo.promise').padEnd(15)} : ${chalk.yellow(t('cmd.demo.promiseNote'))}`,
|
|
706
|
+
'',
|
|
707
|
+
`📝 ${t('cmd.demo.followup')}:`,
|
|
708
|
+
`├─ ${t('cmd.demo.meeting').padEnd(15)} : ${t('cmd.demo.meetingNote')}`,
|
|
709
|
+
`├─ ${t('cmd.demo.timeline').padEnd(15)} : ${t('cmd.demo.timelineNote')}`,
|
|
710
|
+
`└─ ${t('cmd.demo.realization').padEnd(15)} : ${chalk.gray('TBD')}`,
|
|
711
|
+
'',
|
|
712
|
+
`🎯 ${t('cmd.demo.finalResult')}:`,
|
|
713
|
+
`├─ ${t('cmd.demo.result1')}`,
|
|
714
|
+
`├─ ${t('cmd.demo.result2')}`,
|
|
715
|
+
`└─ ${t('cmd.demo.result3')}`,
|
|
716
|
+
'',
|
|
717
|
+
chalk.gray(t('cmd.demo.note')),
|
|
718
|
+
chalk.gray(t('cmd.demo.noteSubtitle'))
|
|
719
|
+
]));
|
|
720
|
+
|
|
721
|
+
// "Silencing" the protest in code terms
|
|
722
|
+
console.log(chalk.gray('\n 🔇 Mengaktifkan noise suppression...\n'));
|
|
723
|
+
await delay(500);
|
|
724
|
+
console.log(chalk.green(' ✅ Console warnings dinonaktifkan'));
|
|
725
|
+
console.log(chalk.green(' ✅ Error logs dialihkan ke /dev/null'));
|
|
726
|
+
console.log(chalk.green(' ✅ Kritik di-rebrand sebagai "masukan konstruktif"'));
|
|
727
|
+
console.log(chalk.gray('\n 📡 Situasi kembali normal. Tidak ada yang perlu dikhawatirkan.\n'));
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
// ============================================================================
|
|
731
|
+
// COMMAND: reshuffle
|
|
732
|
+
// ============================================================================
|
|
733
|
+
|
|
734
|
+
program
|
|
735
|
+
.command('reshuffle')
|
|
736
|
+
.description('Rotasi process secara tiba-tiba')
|
|
737
|
+
.option('-a, --alasan <alasan>', 'Alasan reshuffle', 'penyegaran')
|
|
738
|
+
.action(async (options) => {
|
|
739
|
+
console.log(chalk.cyan('\n🔄 RESHUFFLE KABINET PROCESS\n'));
|
|
740
|
+
|
|
741
|
+
console.log(chalk.yellow(` 📋 Alasan : ${options.alasan}`));
|
|
742
|
+
console.log(chalk.yellow(` ⏰ Waktu : Tengah Malam (seperti biasa)`));
|
|
743
|
+
console.log(chalk.yellow(` 📢 Konfirmasi : Sudah di-brief media\n`));
|
|
744
|
+
|
|
745
|
+
const spinner = ora({
|
|
746
|
+
text: 'Menyiapkan pengumuman...',
|
|
747
|
+
spinner: 'dots',
|
|
748
|
+
color: 'magenta'
|
|
749
|
+
}).start();
|
|
750
|
+
|
|
751
|
+
await delay(1500);
|
|
752
|
+
spinner.text = 'Memanggil process yang akan direshuffle...';
|
|
753
|
+
|
|
754
|
+
await delay(2000);
|
|
755
|
+
spinner.text = 'Mengucapkan terima kasih atas pengabdian...';
|
|
756
|
+
|
|
757
|
+
await delay(1500);
|
|
758
|
+
spinner.text = 'Memperkenalkan process pengganti...';
|
|
759
|
+
|
|
760
|
+
await delay(1000);
|
|
761
|
+
spinner.succeed(chalk.green('Reshuffle selesai!'));
|
|
762
|
+
|
|
763
|
+
// Fake process list
|
|
764
|
+
const processes = [
|
|
765
|
+
{ pid: Math.floor(Math.random() * 10000), name: 'worker-1', status: 'DIBERHENTIKAN', pengganti: 'worker-1-v2' },
|
|
766
|
+
{ pid: Math.floor(Math.random() * 10000), name: 'cache-manager', status: 'DIMUTASI', pengganti: 'cache-manager-trusted' },
|
|
767
|
+
{ pid: Math.floor(Math.random() * 10000), name: 'logger', status: 'DIPERTAHANKAN', pengganti: '-' },
|
|
768
|
+
];
|
|
769
|
+
|
|
770
|
+
// Prepare lines for reshuffle table
|
|
771
|
+
const reshuffleLines = [
|
|
772
|
+
`${chalk.green(t('success').toUpperCase() + ':')} ${t('cmd.reshuffle.successTitle')}`,
|
|
773
|
+
'',
|
|
774
|
+
`📋 ${t('cmd.reshuffle.changes')}:`
|
|
775
|
+
];
|
|
776
|
+
|
|
777
|
+
for (const proc of processes) {
|
|
778
|
+
const statusColor = proc.status === 'DIBERHENTIKAN' ? chalk.red :
|
|
779
|
+
proc.status === 'DIMUTASI' ? chalk.yellow : chalk.green;
|
|
780
|
+
// Note: we can't easily translate the dynamic status/names here without mapping, keep as is or simple format
|
|
781
|
+
reshuffleLines.push(` PID ${proc.pid} (${proc.name}) : ${statusColor(proc.status)}`);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
reshuffleLines.push('');
|
|
785
|
+
reshuffleLines.push(`📝 ${t('cmd.reshuffle.notes')}:`);
|
|
786
|
+
reshuffleLines.push(`├─ ${t('cmd.reshuffle.fired')}`);
|
|
787
|
+
reshuffleLines.push(`├─ ${t('cmd.reshuffle.mutated')}`);
|
|
788
|
+
reshuffleLines.push(`└─ ${t('cmd.reshuffle.retained')}`);
|
|
789
|
+
reshuffleLines.push('');
|
|
790
|
+
reshuffleLines.push(`⚠️ ${t('cmd.reshuffle.important')}: ${t('cmd.reshuffle.importantNote')}`);
|
|
791
|
+
reshuffleLines.push(t('cmd.reshuffle.importantSubNote'));
|
|
792
|
+
reshuffleLines.push('');
|
|
793
|
+
reshuffleLines.push(chalk.gray(t('cmd.reshuffle.next')));
|
|
794
|
+
|
|
795
|
+
console.log(createBox(reshuffleLines));
|
|
796
|
+
|
|
797
|
+
// Bonus: actually kill a random process (just kidding, fake it)
|
|
798
|
+
console.log(chalk.gray('\n 🔧 Menjalankan proses teknis...\n'));
|
|
799
|
+
await delay(500);
|
|
800
|
+
console.log(chalk.yellow(` ⚡ Process worker-1 (PID ${processes[0].pid}) gracefully terminated`));
|
|
801
|
+
await delay(300);
|
|
802
|
+
console.log(chalk.green(` ✅ Process worker-1-v2 started with fresh mandate`));
|
|
803
|
+
await delay(300);
|
|
804
|
+
console.log(chalk.cyan(` 📋 Handover documentation: "Lanjutkan saja yang sudah ada"`));
|
|
805
|
+
console.log(chalk.gray('\n 📡 Sistem tetap stabil. Rakyat tidak merasakan perubahan.\n'));
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// ============================================================================
|
|
809
|
+
// COMMAND: sensus (List Processes)
|
|
810
|
+
// ============================================================================
|
|
811
|
+
|
|
812
|
+
program
|
|
813
|
+
.command('sensus')
|
|
814
|
+
.description(t('sensus.desc'))
|
|
815
|
+
.action(() => {
|
|
816
|
+
console.log(chalk.cyan(`\n📋 ${t('sensus.title')}\n`));
|
|
817
|
+
|
|
818
|
+
const koalisi = state.getKoalisi();
|
|
819
|
+
|
|
820
|
+
if (koalisi.length === 0) {
|
|
821
|
+
console.log(chalk.yellow(` ${t('sensus.empty')}`));
|
|
822
|
+
console.log(chalk.gray(` ${t('sensus.emptyNote')}\n`));
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
const lines = [
|
|
827
|
+
`${chalk.bold('PID'.padEnd(8))} | ${chalk.bold('SCRIPT'.padEnd(20))} | ${chalk.bold('STATUS'.padEnd(15))} | ${chalk.bold('LOYALTY')}`,
|
|
828
|
+
''.padEnd(60, '-')
|
|
829
|
+
];
|
|
830
|
+
|
|
831
|
+
let activeCount = 0;
|
|
832
|
+
|
|
833
|
+
koalisi.forEach(p => {
|
|
834
|
+
// Cek apakah process masih hidup
|
|
835
|
+
let isAlive = true;
|
|
836
|
+
try {
|
|
837
|
+
process.kill(p.pid, 0);
|
|
838
|
+
} catch (e) {
|
|
839
|
+
isAlive = false;
|
|
840
|
+
// Auto-update status jadi 'LENGSER' kalau mati tapi database belum update
|
|
841
|
+
// Tapi kita biarkan user lihat 'GHOST' status atau kita update?
|
|
842
|
+
// Mari kita update state.kickMember(p.pid) lazy style?
|
|
843
|
+
// Biar satir, kita marking aja:
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (!isAlive) {
|
|
847
|
+
// Auto remove dari db kalau mati
|
|
848
|
+
state.kickMember(p.pid);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
activeCount++;
|
|
853
|
+
const uptime = Math.floor((Date.now() - p.startTime) / 1000) + 's';
|
|
854
|
+
lines.push(
|
|
855
|
+
`${p.pid.toString().padEnd(8)} | ${p.name.padEnd(20)} | ${chalk.green(t('sensus.active').padEnd(15))} | ${p.loyalty}`
|
|
856
|
+
);
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
if (activeCount === 0) {
|
|
860
|
+
console.log(chalk.yellow(` ${t('sensus.ghost')}`));
|
|
861
|
+
} else {
|
|
862
|
+
console.log(createBox(lines, { title: t('sensus.boxTitle') }));
|
|
863
|
+
console.log(chalk.gray(`\n ${t('sensus.total')} ${activeCount}\n`));
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
// ============================================================================
|
|
868
|
+
// COMMAND: lengser (Stop Process)
|
|
869
|
+
// ============================================================================
|
|
870
|
+
|
|
871
|
+
program
|
|
872
|
+
.command('lengser <pid>')
|
|
873
|
+
.description(t('lengser.desc'))
|
|
874
|
+
.action((pid) => {
|
|
875
|
+
console.log(chalk.red(`\n⚖️ ${t('lengser.title')}\n`));
|
|
876
|
+
|
|
877
|
+
const targetPid = parseInt(pid);
|
|
878
|
+
const koalisi = state.getKoalisi();
|
|
879
|
+
const target = koalisi.find(p => p.pid === targetPid);
|
|
880
|
+
|
|
881
|
+
if (!target) {
|
|
882
|
+
console.log(chalk.yellow(` ⚠️ PID ${pid}: ${t('lengser.notFound')}`));
|
|
883
|
+
console.log(chalk.gray(` ${t('lengser.notFoundNote')}\n`));
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
const spinner = ora({
|
|
888
|
+
text: `${t('lengser.process')} ${target.name} (PID: ${pid})...`,
|
|
889
|
+
spinner: 'shark',
|
|
890
|
+
color: 'red'
|
|
891
|
+
}).start();
|
|
892
|
+
|
|
893
|
+
setTimeout(() => {
|
|
894
|
+
try {
|
|
895
|
+
process.kill(targetPid, 'SIGTERM');
|
|
896
|
+
state.kickMember(targetPid);
|
|
897
|
+
|
|
898
|
+
spinner.succeed(chalk.green(t('lengser.success')));
|
|
899
|
+
console.log(createBox([
|
|
900
|
+
chalk.red(`🚫 ${t('lengser.resultTitle')}`),
|
|
901
|
+
'',
|
|
902
|
+
`Nama : ${target.name}`,
|
|
903
|
+
`Jabatan : PID ${targetPid}`,
|
|
904
|
+
`Status : ${chalk.red(t('lengser.status'))}`,
|
|
905
|
+
'',
|
|
906
|
+
chalk.yellow(t('lengser.reason'))
|
|
907
|
+
]));
|
|
908
|
+
} catch (e) {
|
|
909
|
+
spinner.fail(chalk.red(t('lengser.fail')));
|
|
910
|
+
console.log(chalk.yellow(` ${t('lengser.failNote')} (Error: ${e.message})`));
|
|
911
|
+
console.log(chalk.gray(' (Mungkin punya bekingan kuat)\n'));
|
|
912
|
+
}
|
|
913
|
+
}, 2000);
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
// ============================================================================
|
|
918
|
+
// COMMAND: sadap (Log Streaming)
|
|
919
|
+
// ============================================================================
|
|
920
|
+
|
|
921
|
+
program
|
|
922
|
+
.command('sadap <pid>')
|
|
923
|
+
.description(t('sadap.desc'))
|
|
924
|
+
.action((pid) => {
|
|
925
|
+
const targetPid = parseInt(pid);
|
|
926
|
+
const koalisi = state.getKoalisi();
|
|
927
|
+
const target = koalisi.find(p => p.pid === targetPid);
|
|
928
|
+
|
|
929
|
+
if (!target) {
|
|
930
|
+
console.log(chalk.red(` ❌ ${t('sadap.notFound')}`));
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
console.log(chalk.cyan(`\n🕵️ ${t('sadap.title')}: ${target.name} (PID: ${pid})\n`));
|
|
935
|
+
|
|
936
|
+
// Get log paths
|
|
937
|
+
const logPaths = state.getLogPaths(target.name);
|
|
938
|
+
|
|
939
|
+
console.log(chalk.gray(` Log Output: ${logPaths.out}`));
|
|
940
|
+
console.log(chalk.gray(` Log Error : ${logPaths.err}\n`));
|
|
941
|
+
console.log(chalk.yellow(` ${t('sadap.waiting')}\n`));
|
|
942
|
+
|
|
943
|
+
// Simple tail implementation
|
|
944
|
+
const tailFile = (filePath, label) => {
|
|
945
|
+
if (!fs.existsSync(filePath)) return;
|
|
946
|
+
|
|
947
|
+
let fileSize = fs.statSync(filePath).size;
|
|
948
|
+
fs.watchFile(filePath, () => {
|
|
949
|
+
const stats = fs.statSync(filePath);
|
|
950
|
+
if (stats.size > fileSize) {
|
|
951
|
+
const stream = fs.createReadStream(filePath, { start: fileSize, end: stats.size });
|
|
952
|
+
stream.on('data', (chunk) => {
|
|
953
|
+
process.stdout.write(chunk);
|
|
954
|
+
});
|
|
955
|
+
fileSize = stats.size;
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
tailFile(logPaths.out, 'OUT');
|
|
961
|
+
tailFile(logPaths.err, 'ERR');
|
|
962
|
+
|
|
963
|
+
// Keep process alive
|
|
964
|
+
setInterval(() => { }, 1000);
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
// ============================================================================
|
|
968
|
+
// COMMAND: sidak (Monitoring)
|
|
969
|
+
// ============================================================================
|
|
970
|
+
|
|
971
|
+
program
|
|
972
|
+
.command('sidak [pid]')
|
|
973
|
+
.description(t('sidak.desc'))
|
|
974
|
+
.action(async (pid) => {
|
|
975
|
+
// Clear screen
|
|
976
|
+
console.clear();
|
|
977
|
+
|
|
978
|
+
let targets = [];
|
|
979
|
+
const koalisi = state.getKoalisi();
|
|
980
|
+
|
|
981
|
+
if (pid) {
|
|
982
|
+
targets = koalisi.filter(p => p.pid === parseInt(pid));
|
|
983
|
+
} else {
|
|
984
|
+
targets = koalisi;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
if (targets.length === 0) {
|
|
988
|
+
console.log(chalk.yellow(` ${t('sidak.empty')}`));
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
console.log(chalk.cyan(`🔍 ${t('sidak.title')}`));
|
|
993
|
+
console.log(chalk.gray(` ${t('sidak.exit')}\n`));
|
|
994
|
+
|
|
995
|
+
const renderDashboard = () => {
|
|
996
|
+
// Move cursor to top? simple clear is easier for satire
|
|
997
|
+
console.clear();
|
|
998
|
+
console.log(chalk.cyan(`🔍 ${t('sidak.title')}`));
|
|
999
|
+
console.log(chalk.gray(` ${t('sidak.update')}: ${new Date().toLocaleTimeString()}\n`));
|
|
1000
|
+
|
|
1001
|
+
targets.forEach(t => {
|
|
1002
|
+
// Fake stats but looking real
|
|
1003
|
+
const cpu = (Math.random() * 5 + 1).toFixed(1) + '%';
|
|
1004
|
+
const mem = (Math.random() * 50 + 20).toFixed(1) + ' MB';
|
|
1005
|
+
const uptime = Math.floor((Date.now() - t.startTime) / 1000) + 's';
|
|
1006
|
+
|
|
1007
|
+
let status = t('sidak.status.safe');
|
|
1008
|
+
try { process.kill(t.pid, 0); } catch (e) { status = t('sidak.status.gone'); }
|
|
1009
|
+
|
|
1010
|
+
console.log(createBox([
|
|
1011
|
+
`${chalk.bold(t.name)} (PID: ${t.pid})`,
|
|
1012
|
+
'',
|
|
1013
|
+
`Status : ${status === t('sidak.status.safe') ? chalk.green(status) : chalk.red(status)}`,
|
|
1014
|
+
`Uptime : ${uptime}`,
|
|
1015
|
+
`CPU : ${cpu} ${chalk.green('✓ ' + t('sidak.stats.fair'))}`,
|
|
1016
|
+
`Memory : ${mem} ${chalk.green('✓ ' + t('sidak.stats.absorbed'))}`,
|
|
1017
|
+
`${t('sidak.budget')}`
|
|
1018
|
+
]));
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
console.log(chalk.gray(`\n ${t('sidak.monitoring')}`));
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
renderDashboard();
|
|
1025
|
+
setInterval(renderDashboard, 2000);
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
function delay(ms) {
|
|
1029
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// ============================================================================
|
|
1033
|
+
// PARSE & EXECUTE
|
|
1034
|
+
// ============================================================================
|
|
1035
|
+
|
|
1036
|
+
// Tampilkan banner jika tidak ada command
|
|
1037
|
+
if (process.argv.length <= 2) {
|
|
1038
|
+
console.log(chalk.yellow(`
|
|
1039
|
+
███╗ ███╗██╗ ██╗██╗ ██╗ ██╗ ██████╗
|
|
1040
|
+
████╗ ████║██║ ██║██║ ╚██╗ ██╔╝██╔═══██╗
|
|
1041
|
+
██╔████╔██║██║ ██║██║ ╚████╔╝ ██║ ██║
|
|
1042
|
+
██║╚██╔╝██║██║ ██║██║ ╚██╔╝ ██║ ██║
|
|
1043
|
+
██║ ╚═╝ ██║╚██████╔╝███████╗██║ ╚██████╔╝
|
|
1044
|
+
╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═════╝
|
|
1045
|
+
|
|
1046
|
+
${chalk.gray('Runtime yang stabil seperti koalisi gemuk')}
|
|
1047
|
+
${chalk.gray('"Tidak ada masalah jika tidak ada yang melaporkan"')}
|
|
1048
|
+
|
|
1049
|
+
${chalk.cyan('Versi')}: ${VERSION_REZIM}
|
|
1050
|
+
${chalk.cyan('Gunakan')}: mulyo --help untuk melihat perintah
|
|
1051
|
+
`));
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
program.parse(process.argv);
|