fazer-lang 2.7.0 → 2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [2.7.0] - 2026-01-23
6
+
7
+ ### Added
8
+ - **OSINT CLI Tools**: >30 native commands available directly from terminal (`fazer geo`, `scan`, `whois`, `sub`, etc.).
9
+ - **New Built-in Functions**: Added `abs`, `min`, `max`, `pow`, `sqrt`, `pad_start`, `pad_end`, `cp`, `mv`, `ls`, `mkdir`, `rm`, `download`, `env_set`.
10
+ - **Recursion**: Added support for recursive file operations (e.g., `rm` recursive).
11
+ - **HTTP Redirects**: `download` function now automatically follows redirects.
12
+ - **Cross-Platform**: CLI tools now use Node.js native modules (`net`, `dns`, `http`) for better Linux compatibility.
13
+
14
+ ### Changed
15
+ - **Documentation**: Major update to `README.md` and new `CLI_TOOLS.md` guide.
16
+ - **Error Handling**: Improved stability and error reporting for CLI commands.
17
+
5
18
  ## [2.6.0] - 2026-01-22
6
19
 
7
20
  ### Added
package/README.md CHANGED
@@ -24,6 +24,13 @@ Exécuter un script :
24
24
  fazer mon_script.fz
25
25
  ```
26
26
 
27
+ Utiliser les outils CLI (OSINT) :
28
+ ```bash
29
+ fazer geo 8.8.8.8
30
+ fazer scan google.com
31
+ fazer whois microsoft.com
32
+ ```
33
+
27
34
  ## Création d'Exécutable (.exe)
28
35
 
29
36
  Transformez vos scripts Fazer en applications Windows portables et natives :
@@ -45,6 +52,7 @@ Documentation détaillée par section :
45
52
  * [Guide de Démarrage](https://github.com/viced-1920/fazer-lang/blob/main/docs/getting-started.md)
46
53
  * [Syntaxe du Langage](https://github.com/viced-1920/fazer-lang/blob/main/docs/syntax.md)
47
54
  * [Bibliothèque Standard (Stdlib)](https://github.com/viced-1920/fazer-lang/blob/main/docs/stdlib.md)
55
+ * [Outils CLI (OSINT & Sys)](https://github.com/viced-1920/fazer-lang/blob/main/docs/CLI_TOOLS.md)
48
56
  * [Exemples](https://github.com/viced-1920/fazer-lang/blob/main/docs/examples.md)
49
57
 
50
58
  ## Fonctionnalités Clés
@@ -0,0 +1,48 @@
1
+ # Fazer CLI - Outils & Commandes
2
+
3
+ Fazer intègre une suite complète d'outils CLI (>30 commandes) pour l'OSINT, le réseau, la cryptographie et l'administration système.
4
+
5
+ ## Utilisation
6
+
7
+ ```bash
8
+ fazer <commande> [arguments...]
9
+ ```
10
+
11
+ ## 🌐 Réseau & OSINT
12
+
13
+ * **`geo <ip/domain>`** : Géolocalisation précise (Pays, Ville, ISP, Map).
14
+ * **`ip`** : Affiche votre IP publique actuelle.
15
+ * **`scan <host> [ports]`** : Scanner de ports TCP rapide.
16
+ * **`ping <host> [port]`** : Ping TCP pour vérifier la connectivité.
17
+ * **`whois <domain>`** : Informations WHOIS complètes.
18
+ * **`sub <domain>`** : Énumération de sous-domaines (via Certificate Transparency).
19
+ * **`dns <domain>`** : Résolution DNS (A, MX, TXT, NS, SOA).
20
+ * **`tech <url>`** : Détection de technologies (Headers, Cookies, Server).
21
+ * **`headers <url>`** : Affiche les en-têtes HTTP.
22
+ * **`ssl <host> [port]`** : Inspecte le certificat SSL/TLS (Issuer, Validité, Fingerprint).
23
+ * **`curl <url>`** : Affiche le corps de la réponse HTTP (GET).
24
+ * **`robots <url>`** : Récupère et affiche le fichier robots.txt.
25
+
26
+ ## 🔒 Cryptographie & Encodage
27
+
28
+ * **`b64 <enc|dec> <str>`** : Encodage/Décodage Base64.
29
+ * **`hex <enc|dec> <str>`** : Encodage/Décodage Hexadécimal.
30
+ * **`url <enc|dec> <str>`** : Encodage/Décodage URL.
31
+ * **`md5 <str>`** : Hash MD5.
32
+ * **`sha1 <str>`** : Hash SHA1.
33
+ * **`sha256 <str>`** : Hash SHA256.
34
+ * **`uuid`** : Génère un UUID v4 aléatoire.
35
+
36
+ ## 💻 Système & Utilitaires
37
+
38
+ * **`ls [dir]`** : Liste les fichiers et dossiers.
39
+ * **`cat <file>`** : Affiche le contenu d'un fichier.
40
+ * **`grep <regex> <file>`** : Recherche un motif dans un fichier.
41
+ * **`wc <file>`** : Compte les lignes et caractères d'un fichier.
42
+ * **`whoami`** : Affiche l'utilisateur et la machine actuels.
43
+ * **`env`** : Affiche les variables d'environnement.
44
+ * **`pass [len]`** : Génère un mot de passe sécurisé (défaut 16 chars).
45
+ * **`calc <expr>`** : Calculatrice mathématique (ex: `10 * 5 + 2`).
46
+ * **`now`** : Affiche la date et le timestamp actuels.
47
+ * **`coin`** : Pile ou Face.
48
+ * **`dice`** : Lance un dé (1-6).
package/docs/stdlib.md CHANGED
@@ -115,3 +115,28 @@ Télécharge un fichier depuis une URL vers un chemin local.
115
115
  ```fazer
116
116
  success := download("https://example.com/image.png", "img.png")
117
117
  ```
118
+
119
+ ## Terminal Avancé (UI & FX)
120
+
121
+ Fonctionnalités pour créer des interfaces en ligne de commande (TUI) riches et animées.
122
+
123
+ ### Couleurs & Styles (TrueColor)
124
+ * `rgb(r, g, b, text)` : Texte en couleur TrueColor (16 millions de couleurs).
125
+ * `bg_rgb(r, g, b, text)` : Arrière-plan en couleur TrueColor.
126
+ * `gradient(text, r1, g1, b1, r2, g2, b2)` : Applique un dégradé linéaire sur le texte entre deux couleurs RGB.
127
+ * `style(text, style_name)` : Styles de base ("bold", "dim", "red", "blue", etc.).
128
+
129
+ ### Curseur & Affichage
130
+ * `term_clear()` : Efface l'écran.
131
+ * `term_pos(row, col)` : Déplace le curseur.
132
+ * `term_hide()` / `cursor_hide()` : Masque le curseur.
133
+ * `term_show()` / `cursor_show()` : Affiche le curseur.
134
+ * `term_title(text)` : Change le titre de la fenêtre du terminal.
135
+ * `cursor_save()` : Sauvegarde la position du curseur.
136
+ * `cursor_restore()` : Restaure la position sauvegardée.
137
+
138
+ ### Composants UI
139
+ * `ui_bar(val, max, width, char)` : Génère une barre de progression.
140
+ * Exemple : `ui_bar(50, 100, 20)` -> `██████████ `
141
+ * `box(title, line1, line2, ...)` : Affiche une boîte encadrée.
142
+
package/fazer.js CHANGED
@@ -11,6 +11,7 @@
11
11
  const fs = require("fs");
12
12
  const path = require("path");
13
13
  const crypto = require("crypto");
14
+ const os = require("os");
14
15
  const { Lexer, createToken, EmbeddedActionsParser } = require("chevrotain");
15
16
 
16
17
  /* ────────────────────────────────────────────────────────────────────────── */
@@ -21,6 +22,7 @@ const WhiteSpace = createToken({ name: "WhiteSpace", pattern: /[ \t\r\n]+/, grou
21
22
  const Comment = createToken({ name: "Comment", pattern: /(#|\/\/)[^\n]*/, group: Lexer.SKIPPED });
22
23
 
23
24
  const Assign = createToken({ name: "Assign", pattern: /:=/ });
25
+ const SingleEq = createToken({ name: "SingleEq", pattern: /=/ });
24
26
 
25
27
  const Arrow = createToken({ name: "Arrow", pattern: /->|→/ });
26
28
  const DoublePipe = createToken({ name: "DoublePipe", pattern: /->>|\|>|→>/ });
@@ -116,6 +118,7 @@ const allTokens = [
116
118
  NotEq,
117
119
  Greater,
118
120
  Less,
121
+ SingleEq,
119
122
 
120
123
  LParen,
121
124
  RParen,
@@ -174,7 +177,7 @@ class FazerParser extends EmbeddedActionsParser {
174
177
  const t1 = $.LA(1).tokenType;
175
178
  if (t1 === Mut) return true;
176
179
  const t2 = $.LA(2).tokenType;
177
- return t1 === Identifier && t2 === Assign;
180
+ return t1 === Identifier && (t2 === Assign || t2 === SingleEq);
178
181
  },
179
182
  ALT: () => $.SUBRULE($.assignStmt)
180
183
  },
@@ -261,7 +264,19 @@ class FazerParser extends EmbeddedActionsParser {
261
264
  $.RULE("assignStmt", () => {
262
265
  const mutTok = $.OPTION(() => $.CONSUME(Mut));
263
266
  const idTok = $.CONSUME(Identifier);
264
- $.CONSUME(Assign);
267
+
268
+ const op = $.OR([
269
+ { ALT: () => $.CONSUME(Assign) },
270
+ { ALT: () => $.CONSUME(SingleEq) }
271
+ ]);
272
+
273
+ if (op.tokenType && op.tokenType.name === "SingleEq") {
274
+ throw new FazerError(
275
+ `Invalid assignment operator '='. Use ':=' for assignment.`,
276
+ { line: op.startLine, col: op.startColumn }
277
+ );
278
+ }
279
+
265
280
  const value = $.SUBRULE($.expression);
266
281
  return node("assign", {
267
282
  name: idTok.image,
@@ -322,16 +337,22 @@ class FazerParser extends EmbeddedActionsParser {
322
337
  // Examples:
323
338
  // > 10
324
339
  // <= 3
325
- const op = $.OR([
326
- { ALT: () => $.CONSUME(GreaterEq).image },
327
- { ALT: () => $.CONSUME(LessEq).image },
328
- { ALT: () => $.CONSUME(Greater).image },
329
- { ALT: () => $.CONSUME(Less).image },
330
- { ALT: () => $.CONSUME(Eq).image },
331
- { ALT: () => $.CONSUME(NotEq).image },
340
+ const opTok = $.OR([
341
+ { ALT: () => $.CONSUME(GreaterEq) },
342
+ { ALT: () => $.CONSUME(LessEq) },
343
+ { ALT: () => $.CONSUME(Greater) },
344
+ { ALT: () => $.CONSUME(Less) },
345
+ { ALT: () => $.CONSUME(Eq) },
346
+ { ALT: () => $.CONSUME(NotEq) },
347
+ { ALT: () => $.CONSUME(SingleEq) },
332
348
  ]);
349
+
350
+ if (opTok.tokenType && opTok.tokenType.name === "SingleEq") {
351
+ throw new FazerError("Invalid operator '=' in pattern. Use '==' or just the value.", { line: opTok.startLine, col: opTok.startColumn });
352
+ }
353
+
333
354
  const rhs = $.SUBRULE($.expression);
334
- return node("cmpPat", { op, rhs });
355
+ return node("cmpPat", { op: opTok.image, rhs });
335
356
  });
336
357
 
337
358
  /* Expression precedence:
@@ -382,12 +403,18 @@ class FazerParser extends EmbeddedActionsParser {
382
403
  $.RULE("eqExpr", () => {
383
404
  let left = $.SUBRULE($.relExpr);
384
405
  $.MANY(() => {
385
- const op = $.OR([
386
- { ALT: () => $.CONSUME(Eq).image },
387
- { ALT: () => $.CONSUME(NotEq).image },
406
+ const opTok = $.OR([
407
+ { ALT: () => $.CONSUME(Eq) },
408
+ { ALT: () => $.CONSUME(NotEq) },
409
+ { ALT: () => $.CONSUME(SingleEq) },
388
410
  ]);
411
+
412
+ if (opTok.tokenType && opTok.tokenType.name === "SingleEq") {
413
+ throw new FazerError("Invalid operator '='. Use '==' for equality.", { line: opTok.startLine, col: opTok.startColumn });
414
+ }
415
+
389
416
  const right = $.SUBRULE2($.relExpr);
390
- left = node("bin", { op, left, right, loc: left.loc ?? null });
417
+ left = node("bin", { op: opTok.image, left, right, loc: left.loc ?? null });
391
418
  });
392
419
  return left;
393
420
  });
@@ -792,6 +819,50 @@ class FazerRuntime {
792
819
  const https = require("https");
793
820
  const child_process = require("child_process");
794
821
 
822
+ // Helper for gradients
823
+ const makeGradient = (text, startColor, endColor) => {
824
+ const len = text.length;
825
+ if (len === 0) return "";
826
+ const [r1, g1, b1] = startColor;
827
+ const [r2, g2, b2] = endColor;
828
+ let res = "";
829
+ for (let i = 0; i < len; i++) {
830
+ const ratio = i / (len - 1 || 1);
831
+ const r = Math.round(r1 + (r2 - r1) * ratio);
832
+ const g = Math.round(g1 + (g2 - g1) * ratio);
833
+ const b = Math.round(b1 + (b2 - b1) * ratio);
834
+ res += `\x1b[38;2;${r};${g};${b}m${text[i]}`;
835
+ }
836
+ return res + "\x1b[0m";
837
+ };
838
+
839
+ // Input Buffer for term_read
840
+ let stdinBuffer = [];
841
+ let stdinListener = null;
842
+
843
+ const enableRawInput = (enable) => {
844
+ if (!process.stdin.isTTY) return;
845
+ try {
846
+ process.stdin.setRawMode(!!enable);
847
+ if (enable) {
848
+ process.stdin.resume();
849
+ if (!stdinListener) {
850
+ stdinListener = (chunk) => {
851
+ const str = chunk.toString();
852
+ for (const char of str) stdinBuffer.push(char);
853
+ };
854
+ process.stdin.on('data', stdinListener);
855
+ }
856
+ } else {
857
+ if (stdinListener) {
858
+ process.stdin.removeListener('data', stdinListener);
859
+ stdinListener = null;
860
+ }
861
+ process.stdin.pause();
862
+ }
863
+ } catch(e) {}
864
+ };
865
+
795
866
  // register builtins in global scope
796
867
  const builtins = {
797
868
  println: (x = "") => (console.log(String(x)), null),
@@ -817,6 +888,7 @@ class FazerRuntime {
817
888
  return null;
818
889
  }
819
890
  },
891
+ readln: (prompt = "") => builtins.ask(prompt),
820
892
 
821
893
  // Terminal / UI Advanced
822
894
  term_clear: () => (process.stdout.write("\x1b[2J\x1b[H"), null),
@@ -842,6 +914,34 @@ class FazerRuntime {
842
914
  const chunk = process.stdin.read(1);
843
915
  return chunk ? String(chunk) : null;
844
916
  },
917
+
918
+ sleep: (ms) => {
919
+ const sab = new SharedArrayBuffer(4);
920
+ const int32 = new Int32Array(sab);
921
+ Atomics.wait(int32, 0, 0, Number(ms));
922
+ return null;
923
+ },
924
+
925
+ // Advanced Colors & FX
926
+ rgb: (r, g, b, text) => `\x1b[38;2;${Number(r)};${Number(g)};${Number(b)}m${String(text)}\x1b[0m`,
927
+ bg_rgb: (r, g, b, text) => `\x1b[48;2;${Number(r)};${Number(g)};${Number(b)}m${String(text)}\x1b[0m`,
928
+ gradient: (text, r1, g1, b1, r2, g2, b2) => makeGradient(String(text), [Number(r1), Number(g1), Number(b1)], [Number(r2), Number(g2), Number(b2)]),
929
+
930
+ // Cursor & Title
931
+ cursor_save: () => (process.stdout.write("\x1b[s"), null),
932
+ cursor_restore: () => (process.stdout.write("\x1b[u"), null),
933
+ cursor_hide: () => (process.stdout.write("\x1b[?25l"), null),
934
+ cursor_show: () => (process.stdout.write("\x1b[?25h"), null),
935
+ term_title: (t) => (process.stdout.write(`\x1b]0;${String(t)}\x07`), null),
936
+
937
+ // UI Components
938
+ ui_bar: (val, max, width, char) => {
939
+ const v = Number(val); const m = Number(max); const w = Number(width || 20);
940
+ const c = String(char || "█");
941
+ const filled = Math.round((v / m) * w);
942
+ return c.repeat(filled) + " ".repeat(w - filled);
943
+ },
944
+
845
945
 
846
946
  style: (s, color) => style(s, String(color || "reset")),
847
947
  box: (title, ...lines) => box(title, lines),
@@ -986,35 +1086,290 @@ class FazerRuntime {
986
1086
  exit: (code) => process.exit(Number(code||0)),
987
1087
  env_set: (k, v) => { process.env[String(k)] = String(v); return null; },
988
1088
 
1089
+ // System & Red Team (Advanced)
1090
+ sys_info: () => {
1091
+ try {
1092
+ const cmd = `Get-WmiObject Win32_OperatingSystem | Select-Object Caption, Version, OSArchitecture | ConvertTo-Json`;
1093
+ const os = JSON.parse(child_process.execSync(`powershell -NoProfile -Command "${cmd}"`, { encoding: "utf8" }));
1094
+ const user = process.env.USERNAME;
1095
+ const domain = process.env.USERDOMAIN;
1096
+ const admin = (() => { try { child_process.execSync("net session"); return true; } catch(e) { return false; } })();
1097
+ return { os: os.Caption, version: os.Version, arch: os.OSArchitecture, user, domain, is_admin: admin };
1098
+ } catch(e) { return { error: e.message }; }
1099
+ },
1100
+ uptime: () => {
1101
+ try {
1102
+ const up = child_process.execSync(`powershell -command "(Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime | Select-Object -ExpandProperty TotalSeconds"`).toString().trim();
1103
+ return Number(up);
1104
+ } catch(e) { return 0; }
1105
+ },
1106
+ shutdown: (delay = 0) => child_process.exec(`shutdown /s /t ${Number(delay)}`),
1107
+ restart: (delay = 0) => child_process.exec(`shutdown /r /t ${Number(delay)}`),
1108
+ lock_screen: () => child_process.exec(`rundll32.exe user32.dll,LockWorkStation`),
1109
+
1110
+ // File System Extended
1111
+ file_size: (p) => { try { return fs.statSync(path.resolve(String(p))).size; } catch(e) { return -1; } },
1112
+ file_exists: (p) => fs.existsSync(path.resolve(String(p))),
1113
+ is_dir: (p) => { try { return fs.statSync(path.resolve(String(p))).isDirectory(); } catch(e) { return false; } },
1114
+ is_file: (p) => { try { return fs.statSync(path.resolve(String(p))).isFile(); } catch(e) { return false; } },
1115
+ dir_home: () => os.homedir(),
1116
+ dir_temp: () => os.tmpdir(),
1117
+
1118
+ // Crypto Extended
1119
+ md5: (s) => crypto.createHash('md5').update(String(s)).digest('hex'),
1120
+ sha1: (s) => crypto.createHash('sha1').update(String(s)).digest('hex'),
1121
+ sha256: (s) => crypto.createHash('sha256').update(String(s)).digest('hex'),
1122
+ uuid: () => crypto.randomUUID(),
1123
+
1124
+ process_list: () => {
1125
+ try {
1126
+ const cmd = `Get-Process | Select-Object Id, ProcessName, MainWindowTitle | ConvertTo-Json -Depth 1`;
1127
+ const out = child_process.execSync(`powershell -NoProfile -Command "${cmd}"`, { encoding: "utf8" });
1128
+ return JSON.parse(out);
1129
+ } catch(e) { return []; }
1130
+ },
1131
+
1132
+ process_kill: (pid) => {
1133
+ try {
1134
+ process.kill(Number(pid));
1135
+ return true;
1136
+ } catch(e) { return false; }
1137
+ },
1138
+
1139
+ screenshot: (pathStr) => {
1140
+ try {
1141
+ const p = path.resolve(String(pathStr));
1142
+ const ps = `
1143
+ Add-Type -AssemblyName System.Windows.Forms;
1144
+ Add-Type -AssemblyName System.Drawing;
1145
+ $s = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds;
1146
+ $b = New-Object System.Drawing.Bitmap $s.Width, $s.Height;
1147
+ $g = [System.Drawing.Graphics]::FromImage($b);
1148
+ $g.CopyFromScreen($s.Location, [System.Drawing.Point]::Empty, $s.Size);
1149
+ $b.Save('${p.replace(/'/g, "''")}', [System.Drawing.Imaging.ImageFormat]::Png);
1150
+ $g.Dispose();
1151
+ $b.Dispose();
1152
+ `;
1153
+ child_process.execSync(`powershell -NoProfile -Command "${ps.replace(/\n/g, " ")}"`);
1154
+ return true;
1155
+ } catch(e) { return false; }
1156
+ },
1157
+
1158
+ clipboard_get: () => {
1159
+ try {
1160
+ const ps = `Get-Clipboard`;
1161
+ return child_process.execSync(`powershell -NoProfile -Command "${ps}"`, { encoding: "utf8" }).trim();
1162
+ } catch(e) { return ""; }
1163
+ },
1164
+
1165
+ clipboard_set: (text) => {
1166
+ try {
1167
+ const t = String(text).replace(/"/g, '\\"');
1168
+ const ps = `Set-Clipboard -Value "${t}"`;
1169
+ child_process.execSync(`powershell -NoProfile -Command "${ps}"`);
1170
+ return true;
1171
+ } catch(e) { return false; }
1172
+ },
1173
+
1174
+ // Automation (Mouse/Keyboard)
1175
+ mouse_move: (x, y) => {
1176
+ try {
1177
+ const ps = `
1178
+ Add-Type -AssemblyName System.Windows.Forms;
1179
+ [System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${Number(x)}, ${Number(y)});
1180
+ `;
1181
+ child_process.execSync(`powershell -NoProfile -Command "${ps.replace(/\n/g, " ")}"`);
1182
+ return true;
1183
+ } catch(e) { return false; }
1184
+ },
1185
+
1186
+ mouse_pos: () => {
1187
+ try {
1188
+ const ps = `
1189
+ Add-Type -AssemblyName System.Windows.Forms;
1190
+ $p = [System.Windows.Forms.Cursor]::Position;
1191
+ Write-Output "$($p.X),$($p.Y)"
1192
+ `;
1193
+ const out = child_process.execSync(`powershell -NoProfile -Command "${ps.replace(/\n/g, " ")}"`, { encoding: "utf8" }).trim();
1194
+ const parts = out.split(",");
1195
+ return { x: Number(parts[0]), y: Number(parts[1]) };
1196
+ } catch(e) { return { x: 0, y: 0 }; }
1197
+ },
1198
+
1199
+ msgbox: (text, title="Message") => {
1200
+ try {
1201
+ const ps = `
1202
+ Add-Type -AssemblyName System.Windows.Forms;
1203
+ [System.Windows.Forms.MessageBox]::Show('${String(text).replace(/'/g, "''")}', '${String(title).replace(/'/g, "''")}');
1204
+ `;
1205
+ child_process.execSync(`powershell -NoProfile -Command "${ps.replace(/\n/g, " ")}"`);
1206
+ return null;
1207
+ } catch(e) { return null; }
1208
+ },
1209
+
1210
+ // Registry (Persistence)
1211
+ registry_set: (keyPath, name, value) => {
1212
+ // keyPath: HKCU\Software\...\Run
1213
+ try {
1214
+ const ps = `Set-ItemProperty -Path "Registry::${keyPath}" -Name "${name}" -Value "${value}"`;
1215
+ child_process.execSync(`powershell -NoProfile -Command "${ps}"`);
1216
+ return true;
1217
+ } catch(e) { return false; }
1218
+ },
1219
+
1220
+ registry_get: (keyPath, name) => {
1221
+ try {
1222
+ const ps = `Get-ItemPropertyValue -Path "Registry::${keyPath}" -Name "${name}"`;
1223
+ return child_process.execSync(`powershell -NoProfile -Command "${ps}"`, { encoding: "utf8" }).trim();
1224
+ } catch(e) { return null; }
1225
+ },
1226
+
1227
+ // Self-Destruct
1228
+ self_destruct: () => {
1229
+ const script = process.argv[1];
1230
+ // We can't delete running file easily on Windows.
1231
+ // We spawn a detached process that waits then deletes.
1232
+ const cmd = `Start-Sleep -Seconds 2; Remove-Item -Path "${script}" -Force`;
1233
+ const ps = `powershell -NoProfile -Command "${cmd}"`;
1234
+ child_process.spawn(ps, { shell: true, detached: true, stdio: 'ignore' }).unref();
1235
+ process.exit(0);
1236
+ },
1237
+
989
1238
  // Network
990
1239
  download: async (url, dest) => {
991
- const downloadFile = (u, d) => {
992
- return new Promise((resolve) => {
993
- const p = path.resolve(String(d));
994
- const proto = String(u).startsWith("https") ? https : http;
995
- proto.get(String(u), (response) => {
996
- if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
997
- // Follow redirect
998
- resolve(downloadFile(response.headers.location, d));
1240
+ const followRedirects = (currentUrl, currentDest, maxRedirects = 5) => {
1241
+ return new Promise((resolve, reject) => {
1242
+ if (maxRedirects === 0) {
1243
+ reject(new Error("Too many redirects"));
1244
+ return;
1245
+ }
1246
+ const proto = currentUrl.startsWith("https") ? https : http;
1247
+ const request = proto.get(currentUrl, (response) => {
1248
+ if ([301, 302, 303, 307, 308].includes(response.statusCode) && response.headers.location) {
1249
+ const redirectUrl = new URL(response.headers.location, currentUrl).href;
1250
+ followRedirects(redirectUrl, currentDest, maxRedirects - 1)
1251
+ .then(resolve)
1252
+ .catch(reject);
999
1253
  return;
1000
1254
  }
1001
1255
  if (response.statusCode !== 200) {
1002
- resolve(false);
1256
+ reject(new Error(`Failed to download: Status Code ${response.statusCode}`));
1003
1257
  return;
1004
1258
  }
1005
- const file = fs.createWriteStream(p);
1259
+ const file = fs.createWriteStream(currentDest);
1006
1260
  response.pipe(file);
1007
1261
  file.on('finish', () => {
1008
- file.close();
1009
- resolve(true);
1262
+ file.close(() => resolve(true));
1010
1263
  });
1011
1264
  }).on('error', (err) => {
1012
- try { fs.unlinkSync(p); } catch(e){}
1013
- resolve(false);
1265
+ try { fs.unlinkSync(currentDest); } catch(e){}
1266
+ reject(err);
1014
1267
  });
1015
1268
  });
1016
1269
  };
1017
- return await downloadFile(url, dest);
1270
+ try {
1271
+ return await followRedirects(url, dest);
1272
+ } catch (e) {
1273
+ return false;
1274
+ }
1275
+ },
1276
+
1277
+ public_ip: async () => {
1278
+ try {
1279
+ return new Promise((resolve) => {
1280
+ https.get('https://api.ipify.org', (res) => {
1281
+ let data = '';
1282
+ res.on('data', chunk => data += chunk);
1283
+ res.on('end', () => resolve(data));
1284
+ }).on('error', () => resolve("0.0.0.0"));
1285
+ });
1286
+ } catch(e) { return "0.0.0.0"; }
1287
+ },
1288
+
1289
+ wifi_dump: () => {
1290
+ try {
1291
+ const ps = `
1292
+ $profiles = netsh wlan show profiles | Select-String "All User Profile" | ForEach-Object { $_.ToString().Split(":")[1].Trim() };
1293
+ $results = @();
1294
+ foreach ($p in $profiles) {
1295
+ $pass = netsh wlan show profile name="$p" key=clear | Select-String "Key Content" | ForEach-Object { $_.ToString().Split(":")[1].Trim() };
1296
+ if (-not $pass) { $pass = "N/A" };
1297
+ $results += @{ SSID = $p; Password = $pass };
1298
+ }
1299
+ $results | ConvertTo-Json -Compress
1300
+ `;
1301
+ const out = child_process.execSync(`powershell -NoProfile -Command "${ps.replace(/\n/g, " ")}"`, { encoding: "utf8" });
1302
+ return JSON.parse(out);
1303
+ } catch(e) { return []; }
1304
+ },
1305
+
1306
+ // System Utils
1307
+ zip: (source, dest) => {
1308
+ try {
1309
+ const s = path.resolve(String(source));
1310
+ const d = path.resolve(String(dest));
1311
+ child_process.execSync(`powershell -NoProfile -Command "Compress-Archive -Path '${s}' -DestinationPath '${d}' -Force"`);
1312
+ return true;
1313
+ } catch(e) { return false; }
1314
+ },
1315
+
1316
+ unzip: (source, dest) => {
1317
+ try {
1318
+ const s = path.resolve(String(source));
1319
+ const d = path.resolve(String(dest));
1320
+ child_process.execSync(`powershell -NoProfile -Command "Expand-Archive -Path '${s}' -DestinationPath '${d}' -Force"`);
1321
+ return true;
1322
+ } catch(e) { return false; }
1323
+ },
1324
+
1325
+ file_hide: (pathStr) => {
1326
+ try {
1327
+ const p = path.resolve(String(pathStr));
1328
+ child_process.execSync(`attrib +h "${p}"`);
1329
+ return true;
1330
+ } catch(e) { return false; }
1331
+ },
1332
+
1333
+ file_unhide: (pathStr) => {
1334
+ try {
1335
+ const p = path.resolve(String(pathStr));
1336
+ child_process.execSync(`attrib -h "${p}"`);
1337
+ return true;
1338
+ } catch(e) { return false; }
1339
+ },
1340
+
1341
+ // Audio & Fun
1342
+ speak: (text) => {
1343
+ try {
1344
+ const t = String(text).replace(/'/g, "''");
1345
+ const ps = `Add-Type -AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak('${t}')`;
1346
+ child_process.execSync(`powershell -NoProfile -Command "${ps}"`);
1347
+ return true;
1348
+ } catch(e) { return false; }
1349
+ },
1350
+
1351
+ beep: (freq, dur) => {
1352
+ try {
1353
+ const ps = `[Console]::Beep(${Number(freq)}, ${Number(dur)})`;
1354
+ child_process.execSync(`powershell -NoProfile -Command "${ps}"`);
1355
+ return true;
1356
+ } catch(e) { return false; }
1357
+ },
1358
+
1359
+ // Crypto
1360
+ b64_encode: (text) => {
1361
+ return Buffer.from(String(text)).toString('base64');
1362
+ },
1363
+
1364
+ b64_decode: (text) => {
1365
+ return Buffer.from(String(text), 'base64').toString('utf8');
1366
+ },
1367
+
1368
+ hash: (algo, text) => { // algo: 'md5', 'sha256'
1369
+ try {
1370
+ const crypto = require('crypto');
1371
+ return crypto.createHash(String(algo)).update(String(text)).digest('hex');
1372
+ } catch(e) { return ""; }
1018
1373
  },
1019
1374
 
1020
1375
  readB64: readBytesB64,
@@ -1058,50 +1413,8 @@ class FazerRuntime {
1058
1413
  },
1059
1414
 
1060
1415
  server: (port, handlerName) => {
1061
- const srv = http.createServer(async (req, res) => {
1062
- // We need to call the Fazer function `handlerName`
1063
- // But `this._call` requires scope context.
1064
- // This is tricky in a sync interpreter loop.
1065
- // For now, simple static server or basic response?
1066
- // To do it properly, we need to invoke the runtime from the callback.
1067
- // Since we are inside the class, we can do it!
1068
-
1069
- // We need to find the function in `this.fns`
1070
- const fn = this.fns.get(handlerName);
1071
- if (!fn) {
1072
- res.writeHead(500);
1073
- res.end("Handler not found");
1074
- return;
1075
- }
1076
-
1077
- // Construct request object
1078
- const reqObj = {
1079
- method: req.method,
1080
- url: req.url,
1081
- headers: req.headers
1082
- };
1083
-
1084
- // Call handler(req) -> { status, body, headers }
1085
- try {
1086
- const inner = new Scope(fn.closure);
1087
- inner.set(fn.params[0], reqObj, true);
1088
- const result = await this._execBlock(fn.body, inner);
1089
- const out = (result instanceof ReturnSignal) ? result.value : result;
1090
-
1091
- const status = (out && out.status) || 200;
1092
- const body = (out && out.body) || "";
1093
- const headers = (out && out.headers) || {};
1094
-
1095
- res.writeHead(status, headers);
1096
- res.end(String(body));
1097
- } catch (e) {
1098
- console.error(e);
1099
- res.writeHead(500);
1100
- res.end("Internal Server Error");
1101
- }
1102
- });
1103
- srv.listen(Number(port));
1104
- console.log(`Server listening on port ${port}`);
1416
+ // This old implementation is deprecated/removed in favor of the async one below
1417
+ throw new FazerError("This server signature is deprecated. Use server(port, router_obj).");
1105
1418
  },
1106
1419
 
1107
1420
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, Number(ms))),
@@ -2600,7 +2913,18 @@ Usage:
2600
2913
  fazer Start interactive shell (REPL)
2601
2914
  fazer <file.fz> [args...] Run a Fazer script
2602
2915
  fazer run <file.fz> Run a Fazer script (explicit)
2916
+
2917
+ CLI Commands:
2918
+ Network/OSINT:
2919
+ geo, scan, whois, sub, dns, tech, headers, ip, ping,
2920
+ ssl, curl, robots
2603
2921
 
2922
+ Crypto/Encoding:
2923
+ b64, hex, url, md5, sha1, sha256, uuid
2924
+
2925
+ System/Utils:
2926
+ ls, cat, grep, wc, whoami, env, pass, calc, now, coin, dice
2927
+
2604
2928
  Flags:
2605
2929
  --help, -h Show this help message
2606
2930
  --version, -v Show version
@@ -2656,6 +2980,415 @@ async function main() {
2656
2980
  printLicense();
2657
2981
  }
2658
2982
 
2983
+ /* ────────────────────────────────────────────────────────────────────────── */
2984
+ /* CLI TOOLS / OSINT */
2985
+ /* ────────────────────────────────────────────────────────────────────────── */
2986
+
2987
+ const colors = {
2988
+ red: "\x1b[31m", green: "\x1b[32m", yellow: "\x1b[33m", blue: "\x1b[34m",
2989
+ magenta: "\x1b[35m", cyan: "\x1b[36m", reset: "\x1b[0m", bold: "\x1b[1m"
2990
+ };
2991
+
2992
+ const CLI_COMMANDS = {
2993
+ // --- OSINT / NETWORK ---
2994
+ "geo": async (args) => {
2995
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer geo <ip/domain>${colors.reset}`);
2996
+ const target = args[0];
2997
+ console.log(`${colors.cyan}[*] Geo-locating: ${target}...${colors.reset}`);
2998
+ const http = require("http");
2999
+ http.get(`http://ip-api.com/json/${target}`, (res) => {
3000
+ let data = "";
3001
+ res.on("data", c => data += c);
3002
+ res.on("end", () => {
3003
+ try {
3004
+ const j = JSON.parse(data);
3005
+ if (j.status === "fail") {
3006
+ console.log(`${colors.red}[!] Failed: ${j.message}${colors.reset}`);
3007
+ return;
3008
+ }
3009
+ console.log(`${colors.green}[+] Target: ${j.query}${colors.reset}`);
3010
+ console.log(` ${colors.bold}Country:${colors.reset} ${j.country} (${j.countryCode})`);
3011
+ console.log(` ${colors.bold}Region:${colors.reset} ${j.regionName} (${j.region})`);
3012
+ console.log(` ${colors.bold}City:${colors.reset} ${j.city}`);
3013
+ console.log(` ${colors.bold}ISP:${colors.reset} ${j.isp}`);
3014
+ console.log(` ${colors.bold}Org:${colors.reset} ${j.org}`);
3015
+ console.log(` ${colors.bold}AS:${colors.reset} ${j.as}`);
3016
+ console.log(` ${colors.bold}Loc:${colors.reset} ${j.lat}, ${j.lon}`);
3017
+ console.log(` ${colors.blue}Maps:${colors.reset} https://www.google.com/maps?q=${j.lat},${j.lon}`);
3018
+ } catch(e) { console.error(e.message); }
3019
+ });
3020
+ }).on("error", e => console.error(e.message));
3021
+ },
3022
+
3023
+ "ip": async (args) => {
3024
+ console.log(`${colors.cyan}[*] Fetching public IP...${colors.reset}`);
3025
+ const https = require("https");
3026
+ https.get("https://api.ipify.org?format=json", (res) => {
3027
+ let data = "";
3028
+ res.on("data", c => data += c);
3029
+ res.on("end", () => {
3030
+ try {
3031
+ const j = JSON.parse(data);
3032
+ console.log(`${colors.green}[+] Public IP: ${colors.bold}${j.ip}${colors.reset}`);
3033
+ } catch(e) { console.error("Error parsing response"); }
3034
+ });
3035
+ }).on("error", e => console.error(e.message));
3036
+ },
3037
+
3038
+ "ping": async (args) => {
3039
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer ping <host> [port]${colors.reset}`);
3040
+ const host = args[0];
3041
+ const port = args[1] ? parseInt(args[1]) : 80;
3042
+ console.log(`${colors.cyan}[*] Pinging ${host}:${port}...${colors.reset}`);
3043
+ const net = require("net");
3044
+ const start = Date.now();
3045
+ const s = new net.Socket();
3046
+ s.setTimeout(2000);
3047
+ s.on('connect', () => {
3048
+ const ms = Date.now() - start;
3049
+ console.log(`${colors.green}[+] Connected to ${host}:${port} in ${ms}ms${colors.reset}`);
3050
+ s.destroy();
3051
+ });
3052
+ s.on('timeout', () => { console.log(`${colors.red}[!] Timeout${colors.reset}`); s.destroy(); });
3053
+ s.on('error', (e) => { console.log(`${colors.red}[!] Error: ${e.message}${colors.reset}`); });
3054
+ s.connect(port, host);
3055
+ },
3056
+
3057
+ "scan": async (args) => {
3058
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer scan <host> [ports/range]${colors.reset}`);
3059
+ const host = args[0];
3060
+ let ports = [21,22,23,25,53,80,110,111,135,139,143,443,445,993,995,1723,3306,3389,5900,8080,8443];
3061
+ if (args[1]) {
3062
+ if (args[1] === "full") ports = Array.from({length: 1024}, (_, i) => i + 1);
3063
+ else ports = args[1].split(",").map(p => parseInt(p.trim())).filter(n => !isNaN(n));
3064
+ }
3065
+
3066
+ console.log(`${colors.cyan}[*] Scanning ${host} (${ports.length} ports)...${colors.reset}`);
3067
+ const net = require("net");
3068
+
3069
+ const checkPort = (port) => new Promise(resolve => {
3070
+ const s = new net.Socket();
3071
+ s.setTimeout(2000);
3072
+ s.on('connect', () => { s.destroy(); resolve(port); });
3073
+ s.on('timeout', () => { s.destroy(); resolve(null); });
3074
+ s.on('error', () => resolve(null));
3075
+ s.connect(port, host);
3076
+ });
3077
+
3078
+ const results = await Promise.all(ports.map(checkPort));
3079
+ const open = results.filter(p => p !== null);
3080
+
3081
+ if (open.length === 0) console.log(`${colors.yellow}[-] No open ports found.${colors.reset}`);
3082
+ else {
3083
+ console.log(`${colors.green}[+] Found ${open.length} open ports:${colors.reset}`);
3084
+ open.forEach(p => console.log(` - ${colors.bold}${p}${colors.reset} (OPEN)`));
3085
+ }
3086
+ },
3087
+
3088
+ "whois": async (args) => {
3089
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer whois <domain>${colors.reset}`);
3090
+ const domain = args[0];
3091
+ console.log(`${colors.cyan}[*] WHOIS Lookup for: ${domain}...${colors.reset}`);
3092
+
3093
+ const queryWhois = (server, query) => new Promise((resolve, reject) => {
3094
+ const net = require("net");
3095
+ const s = new net.Socket();
3096
+ let data = "";
3097
+ s.connect(43, server, () => s.write(query + "\r\n"));
3098
+ s.on("data", d => data += d);
3099
+ s.on("end", () => resolve(data));
3100
+ s.on("error", reject);
3101
+ });
3102
+
3103
+ try {
3104
+ let res = await queryWhois("whois.iana.org", domain);
3105
+ let refer = res.match(/refer:\s+(.+)/i);
3106
+ if (refer && refer[1]) {
3107
+ const server = refer[1].trim();
3108
+ console.log(`${colors.blue}[i] Redirected to ${server}${colors.reset}`);
3109
+ res = await queryWhois(server, domain);
3110
+ } else {
3111
+ if (domain.endsWith(".com") || domain.endsWith(".net")) {
3112
+ res = await queryWhois("whois.verisign-grs.com", domain);
3113
+ }
3114
+ }
3115
+ console.log(res);
3116
+ } catch(e) { console.error(`${colors.red}[!] Error: ${e.message}${colors.reset}`); }
3117
+ },
3118
+
3119
+ "dns": async (args) => {
3120
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer dns <domain>${colors.reset}`);
3121
+ const domain = args[0];
3122
+ const dns = require("dns").promises;
3123
+ console.log(`${colors.cyan}[*] DNS Records for: ${domain}${colors.reset}`);
3124
+ const types = ["A", "AAAA", "MX", "TXT", "NS", "SOA", "CNAME"];
3125
+ for (const t of types) {
3126
+ try {
3127
+ const res = await dns.resolve(domain, t);
3128
+ console.log(`${colors.bold}[${t}]${colors.reset}`);
3129
+ if (Array.isArray(res)) res.forEach(r => console.log(` ${typeof r === 'object' ? JSON.stringify(r) : r}`));
3130
+ else console.log(` ${JSON.stringify(res)}`);
3131
+ } catch(e) {}
3132
+ }
3133
+ },
3134
+
3135
+ "sub": async (args) => {
3136
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer sub <domain>${colors.reset}`);
3137
+ const domain = args[0];
3138
+ console.log(`${colors.cyan}[*] Enumerating subdomains for: ${domain} (via crt.sh)...${colors.reset}`);
3139
+ const https = require("https");
3140
+ const fetch = (u) => new Promise((resolve, reject) => {
3141
+ const req = https.get(u, { headers: {'User-Agent': 'Mozilla/5.0'} }, res => {
3142
+ let d = ""; res.on("data", c => d += c); res.on("end", () => resolve(d));
3143
+ });
3144
+ req.on("error", reject);
3145
+ });
3146
+ try {
3147
+ const data = await fetch(`https://crt.sh/?q=%.${domain}&output=json`);
3148
+ let json = [];
3149
+ try { json = JSON.parse(data); } catch(e) { console.log(`${colors.red}[!] Invalid response${colors.reset}`); return; }
3150
+ const subs = new Set();
3151
+ json.forEach(entry => { entry.name_value.split("\n").forEach(s => subs.add(s)); });
3152
+ console.log(`${colors.green}[+] Found ${subs.size} unique subdomains:${colors.reset}`);
3153
+ Array.from(subs).sort().forEach(s => console.log(` ${s}`));
3154
+ } catch(e) { console.error(`${colors.red}[!] Error: ${e.message}${colors.reset}`); }
3155
+ },
3156
+
3157
+ "headers": async (args) => {
3158
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer headers <url>${colors.reset}`);
3159
+ let u = args[0]; if (!u.startsWith("http")) u = "https://" + u;
3160
+ console.log(`${colors.cyan}[*] Fetching headers: ${u}...${colors.reset}`);
3161
+ const proto = u.startsWith("https") ? require("https") : require("http");
3162
+ proto.get(u, (res) => {
3163
+ console.log(`${colors.green}[${res.statusCode} ${res.statusMessage}]${colors.reset}`);
3164
+ Object.keys(res.headers).forEach(k => console.log(`${colors.bold}${k}${colors.reset}: ${res.headers[k]}`));
3165
+ }).on("error", e => console.error(e.message));
3166
+ },
3167
+
3168
+ "tech": async (args) => {
3169
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer tech <url>${colors.reset}`);
3170
+ let u = args[0]; if (!u.startsWith("http")) u = "https://" + u;
3171
+ console.log(`${colors.cyan}[*] Detecting tech: ${u}...${colors.reset}`);
3172
+ const proto = u.startsWith("https") ? require("https") : require("http");
3173
+ proto.get(u, (res) => {
3174
+ const h = res.headers;
3175
+ console.log(`${colors.bold}Server:${colors.reset} ${h["server"]||"Unknown"}`);
3176
+ console.log(`${colors.bold}X-Powered-By:${colors.reset} ${h["x-powered-by"]||"Unknown"}`);
3177
+ if (h["set-cookie"]) {
3178
+ h["set-cookie"].forEach(c => {
3179
+ if (c.includes("PHPSESSID")) console.log(`${colors.magenta}[!] PHP Detected${colors.reset}`);
3180
+ if (c.includes("JSESSIONID")) console.log(`${colors.magenta}[!] Java Detected${colors.reset}`);
3181
+ if (c.includes("ASP.NET")) console.log(`${colors.magenta}[!] ASP.NET Detected${colors.reset}`);
3182
+ });
3183
+ }
3184
+ }).on("error", e => console.error(e.message));
3185
+ },
3186
+
3187
+ "ssl": async (args) => {
3188
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer ssl <host> [port]${colors.reset}`);
3189
+ const host = args[0];
3190
+ const port = args[1] || 443;
3191
+ console.log(`${colors.cyan}[*] Inspecting SSL: ${host}:${port}...${colors.reset}`);
3192
+ const tls = require("tls");
3193
+ const socket = tls.connect(port, host, { servername: host }, () => {
3194
+ const cert = socket.getPeerCertificate();
3195
+ if (socket.authorized) console.log(`${colors.green}[+] Authorized${colors.reset}`);
3196
+ else console.log(`${colors.yellow}[!] Unauthorized: ${socket.authorizationError}${colors.reset}`);
3197
+ console.log(`${colors.bold}Subject:${colors.reset} ${cert.subject.CN} (${cert.subject.O})`);
3198
+ console.log(`${colors.bold}Issuer:${colors.reset} ${cert.issuer.CN} (${cert.issuer.O})`);
3199
+ console.log(`${colors.bold}Valid:${colors.reset} ${cert.valid_from} to ${cert.valid_to}`);
3200
+ console.log(`${colors.bold}Fingerprint:${colors.reset} ${cert.fingerprint}`);
3201
+ socket.destroy();
3202
+ });
3203
+ socket.on("error", e => console.error(e.message));
3204
+ },
3205
+
3206
+ "curl": async (args) => {
3207
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer curl <url>${colors.reset}`);
3208
+ let u = args[0]; if (!u.startsWith("http")) u = "https://" + u;
3209
+ const proto = u.startsWith("https") ? require("https") : require("http");
3210
+ proto.get(u, res => {
3211
+ res.pipe(process.stdout);
3212
+ }).on("error", e => console.error(e.message));
3213
+ },
3214
+
3215
+ "robots": async (args) => {
3216
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer robots <url>${colors.reset}`);
3217
+ let u = args[0]; if (!u.startsWith("http")) u = "https://" + u;
3218
+ u = u.endsWith("/") ? u + "robots.txt" : u + "/robots.txt";
3219
+ console.log(`${colors.cyan}[*] Fetching ${u}...${colors.reset}`);
3220
+ const proto = u.startsWith("https") ? require("https") : require("http");
3221
+ proto.get(u, res => {
3222
+ if (res.statusCode !== 200) console.log(`${colors.yellow}[!] Status: ${res.statusCode}${colors.reset}`);
3223
+ res.pipe(process.stdout);
3224
+ }).on("error", e => console.error(e.message));
3225
+ },
3226
+
3227
+ // --- CRYPTO / ENCODING ---
3228
+ "b64": async (args) => {
3229
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer b64 <enc/dec> <string>${colors.reset}`);
3230
+ const mode = args[0]; const str = args.slice(1).join(" ");
3231
+ if (mode === "enc") console.log(Buffer.from(str).toString("base64"));
3232
+ else if (mode === "dec") console.log(Buffer.from(str, "base64").toString("utf8"));
3233
+ else console.log("Unknown mode. Use enc or dec.");
3234
+ },
3235
+
3236
+ "hex": async (args) => {
3237
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer hex <enc/dec> <string>${colors.reset}`);
3238
+ const mode = args[0]; const str = args.slice(1).join(" ");
3239
+ if (mode === "enc") console.log(Buffer.from(str).toString("hex"));
3240
+ else if (mode === "dec") console.log(Buffer.from(str, "hex").toString("utf8"));
3241
+ },
3242
+
3243
+ "md5": async (args) => {
3244
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer md5 <string>${colors.reset}`);
3245
+ console.log(require("crypto").createHash("md5").update(args.join(" ")).digest("hex"));
3246
+ },
3247
+
3248
+ "sha1": async (args) => {
3249
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer sha1 <string>${colors.reset}`);
3250
+ console.log(require("crypto").createHash("sha1").update(args.join(" ")).digest("hex"));
3251
+ },
3252
+
3253
+ "sha256": async (args) => {
3254
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer sha256 <string>${colors.reset}`);
3255
+ console.log(require("crypto").createHash("sha256").update(args.join(" ")).digest("hex"));
3256
+ },
3257
+
3258
+ "url": async (args) => {
3259
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer url <enc/dec> <string>${colors.reset}`);
3260
+ const mode = args[0]; const str = args.slice(1).join(" ");
3261
+ if (mode === "enc") console.log(encodeURIComponent(str));
3262
+ else if (mode === "dec") console.log(decodeURIComponent(str));
3263
+ },
3264
+
3265
+ "uuid": async (args) => {
3266
+ console.log(require("crypto").randomUUID());
3267
+ },
3268
+
3269
+ // --- SYSTEM / UTILS ---
3270
+ "ls": async (args) => {
3271
+ const fs = require("fs");
3272
+ const p = args[0] || ".";
3273
+ try {
3274
+ const files = fs.readdirSync(p);
3275
+ files.forEach(f => {
3276
+ const stat = fs.statSync(require("path").join(p, f));
3277
+ const isDir = stat.isDirectory();
3278
+ console.log(isDir ? `${colors.blue}${f}/${colors.reset}` : f);
3279
+ });
3280
+ } catch(e) { console.error(e.message); }
3281
+ },
3282
+
3283
+ "cat": async (args) => {
3284
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer cat <file>${colors.reset}`);
3285
+ try { require("fs").createReadStream(args[0]).pipe(process.stdout); } catch(e) { console.error(e.message); }
3286
+ },
3287
+
3288
+ "grep": async (args) => {
3289
+ if (!args[1]) return console.log(`${colors.red}Usage: fazer grep <pattern> <file>${colors.reset}`);
3290
+ const pat = new RegExp(args[0]);
3291
+ const file = args[1];
3292
+ try {
3293
+ const content = require("fs").readFileSync(file, "utf8");
3294
+ content.split("\n").forEach((line, i) => {
3295
+ if (pat.test(line)) console.log(`${colors.magenta}${i+1}:${colors.reset} ${line.trim()}`);
3296
+ });
3297
+ } catch(e) { console.error(e.message); }
3298
+ },
3299
+
3300
+ "wc": async (args) => {
3301
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer wc <file>${colors.reset}`);
3302
+ try {
3303
+ const c = require("fs").readFileSync(args[0], "utf8");
3304
+ console.log(`Lines: ${c.split("\n").length}, Chars: ${c.length}`);
3305
+ } catch(e) { console.error(e.message); }
3306
+ },
3307
+
3308
+ "whoami": async (args) => {
3309
+ const os = require("os");
3310
+ console.log(`${os.userInfo().username} @ ${os.hostname()} (${os.platform()} ${os.release()})`);
3311
+ },
3312
+
3313
+ "env": async (args) => {
3314
+ Object.keys(process.env).forEach(k => console.log(`${colors.bold}${k}${colors.reset}=${process.env[k]}`));
3315
+ },
3316
+
3317
+ "pass": async (args) => {
3318
+ const len = args[0] ? parseInt(args[0]) : 16;
3319
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+";
3320
+ let res = "";
3321
+ for(let i=0; i<len; i++) res += chars.charAt(Math.floor(Math.random() * chars.length));
3322
+ console.log(res);
3323
+ },
3324
+
3325
+ "calc": async (args) => {
3326
+ if (!args[0]) return console.log(`${colors.red}Usage: fazer calc <expr>${colors.reset}`);
3327
+ try { console.log(eval(args.join(" "))); } catch(e) { console.error("Error"); }
3328
+ },
3329
+
3330
+ "now": async (args) => {
3331
+ console.log(new Date().toISOString());
3332
+ console.log("Timestamp: " + Date.now());
3333
+ },
3334
+
3335
+ "coin": async (args) => {
3336
+ const r = Math.random() < 0.5 ? "Pile (Heads)" : "Face (Tails)";
3337
+ console.log(colors.yellow(r));
3338
+ },
3339
+
3340
+ "dice": async (args) => {
3341
+ const r = Math.floor(Math.random() * 6) + 1;
3342
+ console.log(colors.yellow("🎲 " + r));
3343
+ },
3344
+
3345
+ "uptime": async (args) => {
3346
+ const up = process.uptime();
3347
+ const h = Math.floor(up / 3600);
3348
+ const m = Math.floor((up % 3600) / 60);
3349
+ const s = Math.floor(up % 60);
3350
+ console.log(colors.cyan(`Uptime: ${h}h ${m}m ${s}s`));
3351
+ },
3352
+
3353
+ "mem": async (args) => {
3354
+ const used = process.memoryUsage();
3355
+ console.log(colors.cyan("Memory Usage:"));
3356
+ console.log(` RSS: ${Math.round(used.rss / 1024 / 1024)} MB`);
3357
+ console.log(` Heap Used: ${Math.round(used.heapUsed / 1024 / 1024)} MB`);
3358
+ console.log(` External: ${Math.round(used.external / 1024 / 1024)} MB`);
3359
+ },
3360
+
3361
+ "disk": async (args) => {
3362
+ try {
3363
+ require("child_process").exec("wmic logicaldisk get size,freespace,caption", (err, stdout) => {
3364
+ if(err) console.log(colors.red("Error fetching disk info"));
3365
+ else console.log(colors.cyan(stdout.trim()));
3366
+ });
3367
+ } catch(e) { console.log(colors.red("Disk info unavailable")); }
3368
+ },
3369
+
3370
+ "rot13": async (args) => {
3371
+ if(!args[0]) return console.log(colors.red("Usage: fazer rot13 <text>"));
3372
+ const input = args.join(" ");
3373
+ const out = input.replace(/[a-zA-Z]/g, (c) => {
3374
+ const base = c <= 'Z' ? 65 : 97;
3375
+ return String.fromCharCode(base + (c.charCodeAt(0) - base + 13) % 26);
3376
+ });
3377
+ console.log(colors.green(out));
3378
+ },
3379
+
3380
+ "reverse": async (args) => {
3381
+ if(!args[0]) return console.log(colors.red("Usage: fazer reverse <text>"));
3382
+ console.log(colors.green(args.join(" ").split("").reverse().join("")));
3383
+ }
3384
+
3385
+ };
3386
+
3387
+ if (CLI_COMMANDS[cmd]) {
3388
+ await CLI_COMMANDS[cmd](argv.slice(1));
3389
+ return;
3390
+ }
3391
+
2659
3392
  let fileArg = null;
2660
3393
  let forwarded = [];
2661
3394
 
@@ -2705,7 +3438,16 @@ async function main() {
2705
3438
 
2706
3439
  const parser = new FazerParser();
2707
3440
  parser.input = lex.tokens;
2708
- const ast = parser.program();
3441
+ let ast;
3442
+ try {
3443
+ ast = parser.program();
3444
+ } catch (e) {
3445
+ if (e.name === "FazerError") {
3446
+ console.error(prettyError(e, filePath, code));
3447
+ process.exit(1);
3448
+ }
3449
+ throw e;
3450
+ }
2709
3451
 
2710
3452
  if (parser.errors.length) {
2711
3453
  const e = parser.errors[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fazer-lang",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
4
4
  "description": "Fazer — The ultimate automation language. Batteries-included: Native EXE Build, Red Team Tools (Crypto/Reg/Shell), Icons, HTTP Server, System Exec, Clipboard, Discord, and Pipe Operators (->). Secure, powerful, and robust.",
5
5
  "main": "fazer.js",
6
6
  "types": "index.d.ts",