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/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);