@djunekz/aura-terminal 0.8.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 +21 -0
- package/README.md +118 -0
- package/aura.js +8 -0
- package/core/ai.js +270 -0
- package/core/context.js +33 -0
- package/core/dashboard.js +119 -0
- package/core/identity.js +37 -0
- package/core/identity.json +3 -0
- package/core/kernel.js +377 -0
- package/core/memory.js +65 -0
- package/core/network.js +41 -0
- package/core/plugin.js +162 -0
- package/core/plugins/autobackup.js +23 -0
- package/core/plugins/autodeploy.js +17 -0
- package/core/scheduler.js +40 -0
- package/core/watcher.js +38 -0
- package/core/world.js +50 -0
- package/core/world.json +4 -0
- package/marketplace/plugins.json +12 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 djunekz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# AURA
|
|
2
|
+
|
|
3
|
+
**Adaptive Unified Runtime Assistant (AURA)** – Mini OS-Layer AI untuk Termux.
|
|
4
|
+
Membantu otomatisasi workflow, backup, deploy, auto Git push, scheduler, plugin, dan dashboard real-time.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Fitur Utama
|
|
9
|
+
|
|
10
|
+
1. **Full Automation Engine**
|
|
11
|
+
- Backup, deploy, push, build otomatis sesuai event.
|
|
12
|
+
2. **Scheduler Task Engine**
|
|
13
|
+
- Jalankan task periodik sesuai jadwal user.
|
|
14
|
+
3. **Adaptive AI Learning Engine**
|
|
15
|
+
- Belajar pola workflow user & prioritas project.
|
|
16
|
+
4. **Plugin Marketplace Online**
|
|
17
|
+
- Install/update plugin dari repository resmi.
|
|
18
|
+
5. **Dashboard Visual & Logs**
|
|
19
|
+
- Monitoring file, memory, network secara real-time.
|
|
20
|
+
6. **Event-driven Automation**
|
|
21
|
+
- Trigger task saat file berubah atau network online.
|
|
22
|
+
7. **Auto Git Push**
|
|
23
|
+
- Commit & push otomatis ke repositori target.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Instalasi
|
|
28
|
+
|
|
29
|
+
### 2.1 Via GitHub
|
|
30
|
+
```bash
|
|
31
|
+
git clone https://github.com/djunekz/aura
|
|
32
|
+
cd aura
|
|
33
|
+
npm install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2.2 Via NPM
|
|
37
|
+
```bash
|
|
38
|
+
npm install aura-terminal
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 3. Menjalankan AURA
|
|
44
|
+
```bash
|
|
45
|
+
aura
|
|
46
|
+
```
|
|
47
|
+
> CLI interaktif akan muncul dengan dashboard, logs, dan AI suggestion.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 4. Perintah Penting
|
|
52
|
+
|
|
53
|
+
| No | Perintah | Fungsi |
|
|
54
|
+
|----|----------|--------|
|
|
55
|
+
| 1 | `status` | Tampilkan status kernel & project |
|
|
56
|
+
| 2 | `context` | Tampilkan context project |
|
|
57
|
+
| 3 | `memory` | Tampilkan memory AI |
|
|
58
|
+
| 4 | `identity` | Tampilkan identity user |
|
|
59
|
+
| 5 | `watch on/off` | Hidupkan/matikan watcher file |
|
|
60
|
+
| 6 | `network on/off` | Hidupkan/matikan network watcher |
|
|
61
|
+
| 7 | `plugin install <path>` | Install plugin lokal |
|
|
62
|
+
| 8 | `plugin install-url <url>` | Install plugin dari URL |
|
|
63
|
+
| 9 | `plugin update <name>` | Update plugin |
|
|
64
|
+
| 10 | `marketplace list` | Lihat plugin marketplace tersedia |
|
|
65
|
+
| 11 | `marketplace install <name>` | Install plugin dari marketplace |
|
|
66
|
+
| 12 | `scheduler add <name> <interval>` | Tambahkan task periodik |
|
|
67
|
+
| 13 | `scheduler stop` | Hentikan semua task scheduler |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 5. Plugin Marketplace
|
|
72
|
+
|
|
73
|
+
- Semua plugin dapat diakses & diupdate dari marketplace online.
|
|
74
|
+
- Plugin populer: `AutoBackup`, `AutoDeploy`, `SchedulerEnhancer`.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 6. Kontribusi
|
|
79
|
+
|
|
80
|
+
1. Buat pull request di GitHub.
|
|
81
|
+
2. Submit plugin ke marketplace.
|
|
82
|
+
3. Selalu test plugin baru di sandbox environment sebelum release.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 7. Lisensi
|
|
87
|
+
|
|
88
|
+
MIT License © 2026 djunekz
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 8. Tips Penggunaan
|
|
93
|
+
|
|
94
|
+
1. **Environment Variable untuk GitHub PAT** (untuk AutoDeploy otomatis):
|
|
95
|
+
```bash
|
|
96
|
+
export GITHUB_TOKEN=ghp_xxxxyyyyzzz
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
2. **Menjalankan Scheduler**
|
|
100
|
+
```text
|
|
101
|
+
scheduler add AutoBackup 300 # Backup setiap 5 menit
|
|
102
|
+
scheduler add AutoPush 600 # Auto push setiap 10 menit
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
3. **Update Core & Plugin**
|
|
106
|
+
```bash
|
|
107
|
+
npm run auto-update
|
|
108
|
+
npm run update-plugins
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
4. **Dashboard**
|
|
112
|
+
- Refresh otomatis setiap 2 detik.
|
|
113
|
+
- Menampilkan status project, logs, memory, network, dan suggestion.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
**Selamat menggunakan AURA** 🚀
|
|
118
|
+
Terminal Termux Anda sekarang lebih cerdas, adaptif, dan otomatis.
|
package/aura.js
ADDED
package/core/ai.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import chalk from 'chalk'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
|
|
5
|
+
// ── Knowledge base lokal ───────────────────────────────────────────────────
|
|
6
|
+
// Pola pertanyaan → handler. Tidak butuh API, semua analisis dari state kernel.
|
|
7
|
+
|
|
8
|
+
const ERROR_PATTERNS = [
|
|
9
|
+
{ pattern: /cannot find module/i, fix: 'Jalankan: npm install' },
|
|
10
|
+
{ pattern: /enoent/i, fix: 'File tidak ditemukan. Cek path-nya.' },
|
|
11
|
+
{ pattern: /eacces|permission denied/i, fix: 'Coba jalankan dengan sudo atau cek permission file.' },
|
|
12
|
+
{ pattern: /syntax error/i, fix: 'Ada syntax error di kode. Cek baris yang disebutkan.' },
|
|
13
|
+
{ pattern: /network|econnrefused/i, fix: 'Koneksi gagal. Cek network dengan: network on' },
|
|
14
|
+
{ pattern: /port.*in use|eaddrinuse/i, fix: 'Port sudah dipakai. Ganti port atau kill prosesnya.' },
|
|
15
|
+
{ pattern: /heap out of memory/i, fix: 'Memory habis. Jalankan: node --max-old-space-size=4096' },
|
|
16
|
+
{ pattern: /unexpected token/i, fix: 'Syntax tidak valid. Cek tanda kurung/koma yang hilang.' },
|
|
17
|
+
{ pattern: /is not a function/i, fix: 'Method tidak ada. Cek nama fungsi dan apakah module-nya ter-import.' },
|
|
18
|
+
{ pattern: /undefined.*property/i, fix: 'Mengakses property dari nilai undefined. Tambahkan null check (?.).' },
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
const COMMAND_SUGGESTIONS = {
|
|
22
|
+
deploy: ['network on', 'watch on', 'status'],
|
|
23
|
+
backup: ['watch on', 'plugin install-url <url>'],
|
|
24
|
+
memory: ['memory set <key> <val>', 'memory get <key>', 'memory clear'],
|
|
25
|
+
plugin: ['plugin list', 'marketplace list', 'plugin install <path>'],
|
|
26
|
+
error: ['status', 'context', 'memory'],
|
|
27
|
+
project: ['context', 'world status', 'status'],
|
|
28
|
+
network: ['network on', 'network off', 'status'],
|
|
29
|
+
scheduler: ['scheduler add <task>', 'scheduler stop <id>'],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class AIEngine {
|
|
33
|
+
constructor(kernel) {
|
|
34
|
+
this.kernel = kernel
|
|
35
|
+
this.usageStats = {} // command → count
|
|
36
|
+
this.fileStats = {} // file → count
|
|
37
|
+
this.errorLog = [] // { time, message, fix }
|
|
38
|
+
this.sessionStart = Date.now()
|
|
39
|
+
this._suggestionInterval = null
|
|
40
|
+
|
|
41
|
+
// Auto-saran setiap 5 menit
|
|
42
|
+
this._suggestionInterval = setInterval(() => {
|
|
43
|
+
this._autoSuggest()
|
|
44
|
+
}, 5 * 60 * 1000)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Tracking ───────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
trackCommand(command) {
|
|
50
|
+
if (!this.usageStats[command]) this.usageStats[command] = 0
|
|
51
|
+
this.usageStats[command]++
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
trackFile(file) {
|
|
55
|
+
if (!this.fileStats[file]) this.fileStats[file] = 0
|
|
56
|
+
this.fileStats[file]++
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
trackError(errorMessage) {
|
|
60
|
+
const match = ERROR_PATTERNS.find(p => p.pattern.test(errorMessage))
|
|
61
|
+
const entry = {
|
|
62
|
+
time: new Date().toLocaleTimeString(),
|
|
63
|
+
message: errorMessage.slice(0, 120),
|
|
64
|
+
fix: match?.fix || 'Tidak ada saran spesifik. Cek log lebih lanjut.'
|
|
65
|
+
}
|
|
66
|
+
this.errorLog.push(entry)
|
|
67
|
+
if (this.errorLog.length > 50) this.errorLog.shift()
|
|
68
|
+
return entry.fix
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── Main: jawab pertanyaan bebas ───────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
async answer(question) {
|
|
74
|
+
const q = question.toLowerCase().trim()
|
|
75
|
+
const kernel = this.kernel
|
|
76
|
+
|
|
77
|
+
console.log(chalk.cyan(`\n🤖 AURA AI menganalisis: "${question}"`))
|
|
78
|
+
console.log(chalk.gray('─'.repeat(42)))
|
|
79
|
+
|
|
80
|
+
// ── Error detection ──────────────────────────────────────────────────
|
|
81
|
+
if (q.includes('error') || q.includes('gagal') || q.includes('failed')) {
|
|
82
|
+
const lastErrors = this.errorLog.slice(-3)
|
|
83
|
+
if (lastErrors.length > 0) {
|
|
84
|
+
console.log(chalk.yellow('🔍 Error terakhir yang terdeteksi:'))
|
|
85
|
+
lastErrors.forEach(e => {
|
|
86
|
+
console.log(chalk.red(` [${e.time}] ${e.message}`))
|
|
87
|
+
console.log(chalk.green(` 💡 Solusi: ${e.fix}\n`))
|
|
88
|
+
})
|
|
89
|
+
} else {
|
|
90
|
+
// Coba deteksi dari pesan pertanyaan langsung
|
|
91
|
+
const fix = this.trackError(question)
|
|
92
|
+
console.log(chalk.green(`💡 Kemungkinan solusi: ${fix}`))
|
|
93
|
+
}
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── Deploy ───────────────────────────────────────────────────────────
|
|
98
|
+
if (q.includes('deploy') || q.includes('publish') || q.includes('upload')) {
|
|
99
|
+
const online = kernel?.networkWatcher?.online
|
|
100
|
+
console.log(chalk.yellow('🚀 Analisis deploy:'))
|
|
101
|
+
console.log(online
|
|
102
|
+
? chalk.green(' ✓ Network: ONLINE — siap deploy')
|
|
103
|
+
: chalk.red(' ✗ Network: OFFLINE — tidak bisa deploy sekarang'))
|
|
104
|
+
|
|
105
|
+
const ctx = kernel?.context?.projectType
|
|
106
|
+
if (ctx) console.log(chalk.white(` ✓ Project type: ${ctx}`))
|
|
107
|
+
|
|
108
|
+
const hasAutoDeploy = kernel?.pluginManager?.plugins?.find(p => p.name === 'AutoDeploy')
|
|
109
|
+
console.log(hasAutoDeploy
|
|
110
|
+
? chalk.green(' ✓ Plugin AutoDeploy: aktif')
|
|
111
|
+
: chalk.yellow(' ! Plugin AutoDeploy: tidak aktif (marketplace install AutoDeploy)'))
|
|
112
|
+
|
|
113
|
+
this._printSuggestions('deploy')
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Backup ───────────────────────────────────────────────────────────
|
|
118
|
+
if (q.includes('backup') || q.includes('simpan') || q.includes('save')) {
|
|
119
|
+
console.log(chalk.yellow('💾 Analisis backup:'))
|
|
120
|
+
const hasBackup = kernel?.pluginManager?.plugins?.find(p => p.name === 'AutoBackup')
|
|
121
|
+
console.log(hasBackup
|
|
122
|
+
? chalk.green(' ✓ Plugin AutoBackup: aktif')
|
|
123
|
+
: chalk.yellow(' ! Plugin AutoBackup: tidak aktif'))
|
|
124
|
+
|
|
125
|
+
const topFiles = this._topFiles(3)
|
|
126
|
+
if (topFiles.length > 0) {
|
|
127
|
+
console.log(chalk.white(' File paling sering diakses:'))
|
|
128
|
+
topFiles.forEach(f => console.log(chalk.cyan(` - ${f.name} (${f.count}x)`)))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this._printSuggestions('backup')
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── Memory ───────────────────────────────────────────────────────────
|
|
136
|
+
if (q.includes('memory') || q.includes('ingat') || q.includes('simpan data')) {
|
|
137
|
+
console.log(chalk.yellow('🧠 Status memory:'))
|
|
138
|
+
if (kernel?.memory) {
|
|
139
|
+
const keys = Object.keys(kernel.memory.data || {})
|
|
140
|
+
if (keys.length === 0) {
|
|
141
|
+
console.log(chalk.gray(' Memory kosong.'))
|
|
142
|
+
} else {
|
|
143
|
+
keys.forEach(k => console.log(chalk.white(` ${k}: `) + chalk.cyan(JSON.stringify(kernel.memory.data[k]))))
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
this._printSuggestions('memory')
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── Network ──────────────────────────────────────────────────────────
|
|
151
|
+
if (q.includes('network') || q.includes('internet') || q.includes('online') || q.includes('koneksi')) {
|
|
152
|
+
const online = kernel?.networkWatcher?.online
|
|
153
|
+
console.log(online
|
|
154
|
+
? chalk.green('🌐 Internet: ONLINE')
|
|
155
|
+
: chalk.red('🌐 Internet: OFFLINE'))
|
|
156
|
+
this._printSuggestions('network')
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── Status project ───────────────────────────────────────────────────
|
|
161
|
+
if (q.includes('project') || q.includes('status') || q.includes('apa') || q.includes('info')) {
|
|
162
|
+
console.log(chalk.yellow('📁 Info project saat ini:'))
|
|
163
|
+
console.log(chalk.white(' Folder : ') + chalk.cyan(process.cwd()))
|
|
164
|
+
console.log(chalk.white(' Type : ') + chalk.cyan(kernel?.context?.projectType || 'Unknown'))
|
|
165
|
+
console.log(chalk.white(' Network : ') + (kernel?.networkWatcher?.online ? chalk.green('Online') : chalk.red('Offline')))
|
|
166
|
+
console.log(chalk.white(' Plugins : ') + chalk.cyan(kernel?.pluginManager?.plugins?.length || 0))
|
|
167
|
+
console.log(chalk.white(' Commands: ') + chalk.cyan(kernel?.commandHistory?.length || 0) + chalk.gray(' sejak start'))
|
|
168
|
+
|
|
169
|
+
// Top commands
|
|
170
|
+
const topCmds = this._topCommands(3)
|
|
171
|
+
if (topCmds.length > 0) {
|
|
172
|
+
console.log(chalk.white(' Command favorit:'))
|
|
173
|
+
topCmds.forEach(c => console.log(chalk.gray(` ${c.name} (${c.count}x)`)))
|
|
174
|
+
}
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ── Plugin ───────────────────────────────────────────────────────────
|
|
179
|
+
if (q.includes('plugin')) {
|
|
180
|
+
const plugins = kernel?.pluginManager?.plugins || []
|
|
181
|
+
console.log(chalk.yellow(`🔌 ${plugins.length} plugin aktif:`))
|
|
182
|
+
if (plugins.length === 0) {
|
|
183
|
+
console.log(chalk.gray(' Tidak ada plugin. Coba: marketplace list'))
|
|
184
|
+
} else {
|
|
185
|
+
plugins.forEach(p => console.log(chalk.white(` - ${p.name || p._filename}`)))
|
|
186
|
+
}
|
|
187
|
+
this._printSuggestions('plugin')
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ── Saran umum ────────────────────────────────────────────────────────
|
|
192
|
+
console.log(chalk.yellow('💡 Tidak ada analisis spesifik untuk pertanyaan itu.'))
|
|
193
|
+
console.log(chalk.white('\nTopik yang bisa saya analisis:'))
|
|
194
|
+
const topics = ['deploy', 'backup', 'error', 'memory', 'network', 'project', 'plugin']
|
|
195
|
+
topics.forEach(t => console.log(chalk.cyan(` ask ${t}`)))
|
|
196
|
+
|
|
197
|
+
// Saran berdasarkan history
|
|
198
|
+
const topCmds = this._topCommands(2)
|
|
199
|
+
if (topCmds.length > 0) {
|
|
200
|
+
console.log(chalk.gray(`\nBerdasarkan history kamu, coba: ${topCmds.map(c => c.name).join(', ')}`))
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
console.log('')
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ── Auto-suggest (background) ──────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
_autoSuggest() {
|
|
209
|
+
const kernel = this.kernel
|
|
210
|
+
const suggestions = []
|
|
211
|
+
|
|
212
|
+
// Cek file yang sering diakses tapi belum di-backup
|
|
213
|
+
const topFiles = this._topFiles(3)
|
|
214
|
+
const hasBackup = kernel?.pluginManager?.plugins?.find(p => p.name === 'AutoBackup')
|
|
215
|
+
if (topFiles.length > 0 && !hasBackup) {
|
|
216
|
+
suggestions.push(`💾 File aktif terdeteksi (${topFiles[0]?.name}). Pertimbangkan aktifkan AutoBackup.`)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Cek network tapi AutoDeploy belum aktif
|
|
220
|
+
const online = kernel?.networkWatcher?.online
|
|
221
|
+
const hasAutoDeploy = kernel?.pluginManager?.plugins?.find(p => p.name === 'AutoDeploy')
|
|
222
|
+
if (online && !hasAutoDeploy) {
|
|
223
|
+
suggestions.push('🚀 Network online. AutoDeploy belum aktif — ketik: marketplace install AutoDeploy')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Cek memory kosong padahal sudah banyak command
|
|
227
|
+
const cmdCount = kernel?.commandHistory?.length || 0
|
|
228
|
+
const memKeys = Object.keys(kernel?.memory?.data || {}).length
|
|
229
|
+
if (cmdCount > 20 && memKeys === 0) {
|
|
230
|
+
suggestions.push('🧠 Sudah banyak aktivitas tapi memory kosong. Simpan info penting dengan: memory set <key> <val>')
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (suggestions.length > 0) {
|
|
234
|
+
console.log(chalk.magenta('\n🤖 AURA AI Suggestion:'))
|
|
235
|
+
suggestions.forEach(s => console.log(chalk.yellow(` → ${s}`)))
|
|
236
|
+
console.log('')
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
_topCommands(n = 3) {
|
|
243
|
+
return Object.entries(this.usageStats)
|
|
244
|
+
.sort((a, b) => b[1] - a[1])
|
|
245
|
+
.slice(0, n)
|
|
246
|
+
.map(([name, count]) => ({ name, count }))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
_topFiles(n = 3) {
|
|
250
|
+
return Object.entries(this.fileStats)
|
|
251
|
+
.filter(([k]) => /\.(js|ts|py|json|txt|md)$/.test(k))
|
|
252
|
+
.sort((a, b) => b[1] - a[1])
|
|
253
|
+
.slice(0, n)
|
|
254
|
+
.map(([name, count]) => ({ name, count }))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
_printSuggestions(topic) {
|
|
258
|
+
const cmds = COMMAND_SUGGESTIONS[topic]
|
|
259
|
+
if (!cmds) return
|
|
260
|
+
console.log(chalk.gray('\n Command yang relevan:'))
|
|
261
|
+
cmds.forEach(c => console.log(chalk.cyan(` ${c}`)))
|
|
262
|
+
console.log('')
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
destroy() {
|
|
266
|
+
if (this._suggestionInterval) clearInterval(this._suggestionInterval)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export default AIEngine
|
package/core/context.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
class Context {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.projectType = null;
|
|
12
|
+
this.detectProject();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
detectProject() {
|
|
16
|
+
const cwd = process.cwd();
|
|
17
|
+
if (fs.existsSync(path.join(cwd, 'package.json'))) {
|
|
18
|
+
this.projectType = 'Node.js Project';
|
|
19
|
+
} else if (fs.existsSync(path.join(cwd, 'requirements.txt'))) {
|
|
20
|
+
this.projectType = 'Python Project';
|
|
21
|
+
} else {
|
|
22
|
+
this.projectType = 'Unknown';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
showContext() {
|
|
27
|
+
this.detectProject();
|
|
28
|
+
console.log(chalk.magenta(`Current Project Type: ${this.projectType}`));
|
|
29
|
+
console.log(chalk.magenta(`Current Folder: ${process.cwd()}`));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default Context;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// dashboard.js
|
|
2
|
+
// Replaces the old blessed-in-main-process Dashboard.
|
|
3
|
+
// Spawns dashboard-process.js as a child process (separate terminal window or detached),
|
|
4
|
+
// then pushes state updates via IPC — so blessed NEVER touches the CLI terminal.
|
|
5
|
+
|
|
6
|
+
import { fork } from 'child_process'
|
|
7
|
+
import { fileURLToPath } from 'url'
|
|
8
|
+
import path from 'path'
|
|
9
|
+
import chalk from 'chalk'
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
12
|
+
const __dirname = path.dirname(__filename)
|
|
13
|
+
|
|
14
|
+
class Dashboard {
|
|
15
|
+
constructor(kernel) {
|
|
16
|
+
this.kernel = kernel
|
|
17
|
+
this.child = null
|
|
18
|
+
this.ready = false
|
|
19
|
+
|
|
20
|
+
this._spawn()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
_spawn() {
|
|
24
|
+
const dashboardPath = path.resolve(__dirname, 'dashboard-process.js')
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// `fork` gives us IPC for free (process.send / process.on('message'))
|
|
28
|
+
// stdio: 'inherit' would share the terminal — we use 'ignore' + a new console
|
|
29
|
+
// instead we open a new terminal window so blessed gets its own TTY
|
|
30
|
+
this.child = fork(dashboardPath, [], {
|
|
31
|
+
detached: false,
|
|
32
|
+
// Give the child process its own stdio so blessed can own that TTY.
|
|
33
|
+
// We try to open a new terminal window; if that fails, fall back to piped.
|
|
34
|
+
stdio: ['ignore', 'ignore', 'pipe', 'ipc']
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
this.child.on('error', (err) => {
|
|
38
|
+
console.error(chalk.yellow('[Dashboard] Child process error:'), err.message)
|
|
39
|
+
this.child = null
|
|
40
|
+
this.ready = false
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
this.child.on('exit', (code) => {
|
|
44
|
+
if (code !== 0 && code !== null) {
|
|
45
|
+
console.error(chalk.yellow(`[Dashboard] Child exited with code ${code}. Dashboard disabled.`))
|
|
46
|
+
}
|
|
47
|
+
this.child = null
|
|
48
|
+
this.ready = false
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
this.child.stderr?.on('data', (data) => {
|
|
52
|
+
// Surface blessed errors without polluting CLI stdout
|
|
53
|
+
process.stderr.write(chalk.yellow('[Dashboard] ') + data.toString())
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
this.ready = true
|
|
57
|
+
console.log(chalk.gray('[Dashboard] Running in separate process (PID: ' + this.child.pid + ')'))
|
|
58
|
+
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error(chalk.yellow('[Dashboard] Could not spawn dashboard process:'), err.message)
|
|
61
|
+
this.child = null
|
|
62
|
+
this.ready = false
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Called by Kernel on its interval — collects state and sends to child
|
|
67
|
+
render() {
|
|
68
|
+
if (!this.child || !this.ready) return
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const kernel = this.kernel
|
|
72
|
+
|
|
73
|
+
const data = {
|
|
74
|
+
user: kernel?.identity?.getName?.() || 'Unknown',
|
|
75
|
+
commandsCount: kernel?.commandHistory?.length || 0,
|
|
76
|
+
memKeys: kernel?.memory
|
|
77
|
+
? Object.keys(kernel.memory.data || {}).join('\n') || 'empty'
|
|
78
|
+
: 'empty',
|
|
79
|
+
networkOnline: kernel?.networkWatcher?.online || false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.child.send({ type: 'state', data })
|
|
83
|
+
} catch (err) {
|
|
84
|
+
// IPC failed — child probably died
|
|
85
|
+
console.error(chalk.yellow('[Dashboard.render]'), err.message)
|
|
86
|
+
this.ready = false
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Send a log message to the dashboard log panel
|
|
91
|
+
log(message) {
|
|
92
|
+
if (!this.child || !this.ready) {
|
|
93
|
+
console.log(chalk.gray('[log]'), message)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
this.child.send({ type: 'log', data: message })
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.log(chalk.gray('[log]'), message)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
refreshAll() {
|
|
105
|
+
this.render()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
destroy() {
|
|
109
|
+
if (this.child) {
|
|
110
|
+
try {
|
|
111
|
+
this.child.kill()
|
|
112
|
+
} catch (_) {}
|
|
113
|
+
this.child = null
|
|
114
|
+
this.ready = false
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default Dashboard
|
package/core/identity.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
class Identity {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.filePath = path.join(__dirname, 'identity.json');
|
|
12
|
+
this.name = 'Unknown User';
|
|
13
|
+
this.loadIdentity();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
loadIdentity() {
|
|
17
|
+
if (fs.existsSync(this.filePath)) {
|
|
18
|
+
const data = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
|
|
19
|
+
this.name = data.name || 'Unknown User';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
showIdentity() {
|
|
24
|
+
console.log(chalk.cyan(`User Identity: ${this.name}`));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getName() {
|
|
28
|
+
return this.name;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setName(name) {
|
|
32
|
+
this.name = name;
|
|
33
|
+
fs.writeFileSync(this.filePath, JSON.stringify({name: this.name}, null, 2));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default Identity;
|