flux-vm-js 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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +229 -0
  3. package/flux.js +641 -0
  4. package/package.json +38 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SuperInstance
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,229 @@
1
+ # FLUX.js — JavaScript Bytecode VM
2
+
3
+ [![npm](https://img.shields.io/npm/v/flux-js)](https://www.npmjs.com/package/flux-js)
4
+ [![CI](https://github.com/SuperInstance/flux-js/actions/workflows/ci-node.yml/badge.svg)](https://github.com/SuperInstance/flux-js/actions/workflows/ci-node.yml)
5
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ > **FLUX — Fluid Language Universal eXecution**
8
+ > Self-contained FLUX runtime for Node.js and browsers. ~400ns/iter via V8 JIT.
9
+
10
+ ---
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ npm install flux-js
16
+ ```
17
+
18
+ ```javascript
19
+ const { FluxVM, assemble } = require('flux-js');
20
+
21
+ const bc = assemble('MOVI R0, 42\nHALT');
22
+ const vm = new FluxVM(bc);
23
+ vm.execute();
24
+ console.log(vm.reg(0)); // 42
25
+ ```
26
+
27
+ ---
28
+
29
+ ## What It Does
30
+
31
+ FLUX.js brings the FLUX bytecode virtual machine to JavaScript runtimes — Node.js and browsers. It implements the same register-based ISA, opcode set, and A2A agent messaging protocol as the [Python](https://github.com/SuperInstance/flux-runtime) and [Rust](https://github.com/SuperInstance/flux-core) implementations, but leverages V8 JIT compilation to achieve ~400 nanoseconds per iteration on modern hardware.
32
+
33
+ The VM provides a deterministic, sandboxed execution environment for agent logic. Programs are assembled from text into compact bytecode, then executed with cycle budgets to prevent runaway computation. The included `Interpreter` class maps natural-language patterns ("factorial of 7", "sum 1 to 100") to bytecode, making it easy to build agent systems where computation is both human-readable and machine-verifiable. A built-in `Swarm` class enables multi-agent coordination with majority-vote consensus.
34
+
35
+ ---
36
+
37
+ ## Architecture
38
+
39
+ FLUX.js is the **JavaScript implementation** of the FLUX bytecode runtime in the SuperInstance ecosystem. It shares the same ISA, A2A protocol, and vocabulary system as the Python and Rust implementations, making bytecode portable across all three runtimes.
40
+
41
+ ### Features
42
+
43
+ - **VM** — 16 registers, all opcodes, cycle-limited execution
44
+ - **Assembler** — text → bytecode with labels and comments
45
+ - **Disassembler** — bytecode → human-readable listing
46
+ - **Vocabulary** — 10 natural-language patterns
47
+ - **A2A Agents** — multi-agent coordination with messaging
48
+ - **Swarm** — vote and consensus across agents
49
+ - **~400 ns/iter** on V8 JIT
50
+
51
+ ---
52
+
53
+ ## API / Usage
54
+
55
+ ### Assembly Syntax
56
+
57
+ ```
58
+ MOVI R0, 42 # Load immediate
59
+ MOV R0, R1 # Copy register
60
+ IADD R0, R1, R2 # R0 = R1 + R2
61
+ ISUB R0, R1, R2 # R0 = R1 - R2
62
+ IMUL R0, R1, R2 # R0 = R1 * R2
63
+ IDIV R0, R1, R2 # R0 = R1 / R2
64
+ INC R0 # R0++
65
+ DEC R0 # R0--
66
+ CMP R0, R1 # Compare → flags
67
+ JNZ R0, offset # Jump if not zero
68
+ JZ R0, offset # Jump if zero
69
+ JMP offset # Unconditional jump
70
+ PUSH R0 / POP R0 # Stack operations
71
+ HALT # Stop
72
+ ```
73
+
74
+ ### Natural Language
75
+
76
+ ```javascript
77
+ const { Interpreter } = require('flux-js');
78
+ const interp = new Interpreter();
79
+
80
+ interp.run('factorial of 7'); // { value: 5040, cycles: 24 }
81
+ interp.run('sum 1 to 100'); // { value: 5050, cycles: 303 }
82
+ interp.run('power of 2 to 10'); // { value: 1024, cycles: 34 }
83
+ ```
84
+
85
+ ### A2A Swarm
86
+
87
+ ```javascript
88
+ const { A2AAgent, Swarm, assemble } = require('flux-js');
89
+
90
+ const bc = assemble('MOVI R0, 42\nHALT');
91
+ const swarm = new Swarm();
92
+ for (let i = 0; i < 5; i++) swarm.add(new A2AAgent(`a${i}`, bc));
93
+ swarm.tick();
94
+ console.log(swarm.consensus()); // 42
95
+ ```
96
+
97
+ ### Exports
98
+
99
+ ```javascript
100
+ const { FluxVM, assemble, disassemble, Interpreter, A2AAgent, Swarm } = require('flux-js');
101
+ ```
102
+
103
+ | Export | Description |
104
+ |--------|-------------|
105
+ | `FluxVM` | Bytecode virtual machine |
106
+ | `assemble(text)` | Text assembly → Uint8Array |
107
+ | `disassemble(bc)` | Bytecode → string[] |
108
+ | `Interpreter` | Natural language → execution |
109
+ | `A2AAgent` | Single agent with inbox |
110
+ | `Swarm` | Multi-agent coordinator |
111
+
112
+ ### Built-in Vocabulary
113
+
114
+ | Pattern | Description |
115
+ |---------|-------------|
116
+ | `compute X + Y` | Addition |
117
+ | `compute X - Y` | Subtraction |
118
+ | `compute X * Y` | Multiplication |
119
+ | `double X` | Double |
120
+ | `square X` | Square |
121
+ | `factorial of N` | N! |
122
+ | `fibonacci of N` | F(N) |
123
+ | `sum A to B` | Sum range |
124
+ | `power of BASE to EXP` | Exponentiation |
125
+ | `hello` | Returns 42 |
126
+
127
+ ---
128
+
129
+ ## Testing
130
+
131
+ ```bash
132
+ npm install
133
+ npm test
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Contributing
139
+
140
+ Contributions are welcome! See the [SuperInstance Contributing Guide](https://github.com/SuperInstance/SuperInstance/blob/main/CONTRIBUTING.md).
141
+
142
+ 1. Fork the repo
143
+ 2. Create a feature branch
144
+ 3. Add tests for new functionality
145
+ 4. Ensure `npm test` passes
146
+ 5. Submit a PR
147
+
148
+ ---
149
+
150
+ ## 📦 Related Packages
151
+
152
+ | Package | Language | Registry | Install |
153
+ |---------|----------|----------|---------|
154
+ | **[flux-vm](https://pypi.org/project/flux-vm/)** | Python | PyPI | `pip install flux-vm` |
155
+ | **[fluxvm](https://crates.io/crates/fluxvm)** | Rust | crates.io | `cargo add fluxvm` |
156
+ | **[flux-js](https://www.npmjs.com/package/flux-js)** | JavaScript | npm | `npm install flux-js` |
157
+
158
+ Additional implementations: [C](https://github.com/SuperInstance/flux-runtime-c) · [Zig](https://github.com/SuperInstance/flux-zig) · [Go](https://github.com/SuperInstance/flux-swarm) · [Java](https://github.com/SuperInstance/flux-java) · [WASM](https://github.com/SuperInstance/flux-wasm) · [CUDA](https://github.com/SuperInstance/flux-cuda)
159
+
160
+ ---
161
+
162
+ ## Ecosystem
163
+
164
+ This repo is part of the **SuperInstance** flagship ecosystem — agent-first computation, constraint theory, and self-improving runtimes.
165
+
166
+ ### FLUX Runtime Family
167
+
168
+ | Repo | Language | Description |
169
+ |------|----------|-------------|
170
+ | [flux-runtime](https://github.com/SuperInstance/flux-runtime) | Python | Full FLUX runtime: markdown→bytecode, 2037 tests, zero deps |
171
+ | [flux-core](https://github.com/SuperInstance/flux-core) | Rust | Register-based bytecode VM, deterministic agent computation |
172
+ | [flux-js](https://github.com/SuperInstance/flux-js) | JavaScript | FLUX VM for Node.js and browsers, ~400ns/iter |
173
+ | [flux-compiler](https://github.com/SuperInstance/flux-compiler) | Rust/Python | Formal-methods compiler for safety-critical codegen |
174
+ | [flux-vm](https://github.com/SuperInstance/flux-vm) | Rust | Stack-based constraint-checking VM, 50 opcodes, Turing-incomplete |
175
+
176
+ ### PLATO Engine Family
177
+
178
+ | Repo | Language | Description |
179
+ |------|----------|-------------|
180
+ | [plato-server](https://github.com/SuperInstance/plato-server) | Python | Knowledge tiles, fleet sync via Matrix, HTTP API |
181
+ | [plato-engine-block](https://github.com/SuperInstance/plato-engine-block) | Rust | Original room runtime: no_std + alloc, builder pattern |
182
+ | [plato-engine-block-c](https://github.com/SuperInstance/plato-engine-block-c) | C99 | Embedded reference: zero heap alloc, bare-metal portable |
183
+ | [plato-engine-block-elixir](https://github.com/SuperInstance/plato-engine-block-elixir) | Elixir | BEAM supervision trees, fault tolerance, hot reload |
184
+ | [plato-runtime-kernel](https://github.com/SuperInstance/plato-runtime-kernel) | Rust | Spatial model: tensor grid, batons, assertion traps |
185
+
186
+ ### Constraint / Theory Family
187
+
188
+ | Repo | Language | Description |
189
+ |------|----------|-------------|
190
+ | [categorical-agents](https://github.com/SuperInstance/categorical-agents) | Rust | Category theory for agent composition (functors, naturality) |
191
+ | [cuda-constraint-engine](https://github.com/SuperInstance/cuda-constraint-engine) | CUDA/C | GPU constraint checking at 1B+ constraints/sec |
192
+ | [grand-pattern-rs](https://github.com/SuperInstance/grand-pattern-rs) | Rust | Fibonacci dual-direction cellular graph architecture |
193
+ | [lau-hodge-theory](https://github.com/SuperInstance/lau-hodge-theory) | Rust | Hodge decomposition, Betti numbers, spectral sequences |
194
+ | [ternary-science](https://github.com/SuperInstance/ternary-science) | Rust | Experimental evidence for ternary intelligence, 5 conservation laws |
195
+
196
+ ### Agent / Infrastructure Family
197
+
198
+ | Repo | Language | Description |
199
+ |------|----------|-------------|
200
+ | [construct-core](https://github.com/SuperInstance/construct-core) | Rust | Layered trait system: bare-metal → alloc → async agent runtime |
201
+ | [crab](https://github.com/SuperInstance/crab) | Bash | Agent shell for repo entry/leave (MUD-room metaphor) |
202
+ | [exocortex](https://github.com/SuperInstance/exocortex) | Rust | Persistent cognitive substrate, S3-compatible memory |
203
+ | [git-agent](https://github.com/SuperInstance/git-agent) | Python | The repo IS the agent — autonomous lifecycle via Git |
204
+ | [capitaine-1](https://github.com/SuperInstance/capitaine-1) | TypeScript | Git-native repo-agent, Cloudflare Workers heartbeat |
205
+ | [codespace-edge-rd](https://github.com/SuperInstance/codespace-edge-rd) | Research | Codespace→Edge agent lifecycle and yoke transfer protocols |
206
+ | [git-agent-codespace](https://github.com/SuperInstance/git-agent-codespace) | DevContainer | One-click Codespace template for Git-Agent runtimes |
207
+
208
+ ### Registries
209
+
210
+ | Registry | Package | Install |
211
+ |----------|---------|---------|
212
+ | **PyPI** | `flux-vm` | `pip install flux-vm` |
213
+ | **crates.io** | `fluxvm` | `cargo add fluxvm` |
214
+ | **npm** | `flux-js` | `npm install flux-js` |
215
+
216
+ ### Philosophy & Architecture
217
+
218
+ - 📖 [AI-Writings](https://github.com/SuperInstance/AI-Writings) — Philosophy, essays, and design rationale
219
+ - 📦 [PACKAGES.md](https://github.com/SuperInstance/SuperInstance/blob/main/PACKAGES.md) — Full package index
220
+
221
+ ---
222
+
223
+ ## License
224
+
225
+ MIT
226
+
227
+ ---
228
+
229
+ *Same bytecode, different shells.* 🦀
package/flux.js ADDED
@@ -0,0 +1,641 @@
1
+ /**
2
+ * FLUX.js — JavaScript Bytecode VM
3
+ *
4
+ * Self-contained FLUX runtime for Node.js and browsers.
5
+ * Features: VM, assembler, disassembler, vocabulary, A2A agents, swarm.
6
+ *
7
+ * Usage:
8
+ * const { FluxVM, assemble, Interpreter } = require('./flux.js');
9
+ * const bc = assemble('MOVI R0, 7\nMOVI R1, 1\nIMUL R1, R1, R0\nDEC R0\nJNZ R0, -10\nHALT');
10
+ * const vm = new FluxVM(bc);
11
+ * vm.execute();
12
+ * console.log(vm.reg(1)); // 5040
13
+ */
14
+
15
+ 'use strict';
16
+
17
+ // ─── Opcodes ──────────────────────────────────────────────────────────────
18
+
19
+ const OP = {
20
+ NOP:0x00, MOV:0x01, LOAD:0x02, STORE:0x03,
21
+ JMP:0x04, JZ:0x05, JNZ:0x06, CALL:0x07,
22
+ IADD:0x08, ISUB:0x09, IMUL:0x0A, IDIV:0x0B,
23
+ IMOD:0x0C, INEG:0x0D, INC:0x0E, DEC:0x0F,
24
+ IAND:0x10, IOR:0x11, IXOR:0x12, INOT:0x13,
25
+ ISHL:0x14, ISHR:0x15,
26
+ PUSH:0x20, POP:0x21, DUP:0x22,
27
+ RET:0x28, MOVI:0x2B, CMP:0x2D,
28
+ JE:0x2E, JNE:0x2F,
29
+ FADD:0x40, FSUB:0x41, FMUL:0x42, FDIV:0x43,
30
+ TELL:0x60, ASK:0x61, DELEGATE:0x62, BROADCAST:0x66,
31
+ HALT:0x80, YIELD:0x81
32
+ };
33
+
34
+ const MNEMONIC = Object.fromEntries(Object.entries(OP).map(([k,v])=>[v,k]));
35
+
36
+ // ─── VM ───────────────────────────────────────────────────────────────────
37
+
38
+ class FluxVM {
39
+ /**
40
+ * Create a FLUX bytecode VM.
41
+ * @param {Uint8Array|ArrayBuffer} bytecode
42
+ * @param {number} [maxCycles=10_000_000]
43
+ */
44
+ constructor(bytecode, maxCycles = 10_000_000) {
45
+ this.gp = new Int32Array(16); // 16 general-purpose registers
46
+ this.pc = 0;
47
+ this.halted = false;
48
+ this.cycles = 0;
49
+ this.stack = [];
50
+ this.bc = bytecode instanceof Uint8Array ? bytecode : new Uint8Array(bytecode);
51
+ this.maxCycles = maxCycles;
52
+ this.error = null;
53
+ this._flagZero = false;
54
+ this._flagSign = false;
55
+ }
56
+
57
+ /** Read register */
58
+ reg(idx) { return this.gp[idx] || 0; }
59
+
60
+ /** Read unsigned byte */
61
+ _u8() { return this.bc[this.pc++]; }
62
+
63
+ /** Update condition flags based on an arithmetic result (matches Python/Rust) */
64
+ _setFlags(result) {
65
+ this._flagZero = (result === 0);
66
+ this._flagSign = (result < 0);
67
+ }
68
+
69
+ /** Read signed 16-bit */
70
+ _i16() {
71
+ const lo = this.bc[this.pc++];
72
+ const hi = this.bc[this.pc++];
73
+ const val = lo | (hi << 8);
74
+ return val >= 32768 ? val - 65536 : val;
75
+ }
76
+
77
+ /**
78
+ * Execute until HALT or maxCycles.
79
+ * @returns {FluxVM} this (for chaining)
80
+ */
81
+ execute() {
82
+ this.halted = false;
83
+ this.cycles = 0;
84
+ this.error = null;
85
+ try {
86
+ while (!this.halted && this.pc < this.bc.length && this.cycles < this.maxCycles) {
87
+ const op = this._u8();
88
+ this.cycles++;
89
+ switch (op) {
90
+ case 0x80: this.halted = true; break;
91
+ case 0x00: break; // NOP
92
+ case 0x01: { const d=this._u8(),s=this._u8(); this.gp[d]=this.gp[s]; break; } // MOV
93
+ case 0x2B: { const d=this._u8(); this.gp[d]=this._i16(); break; } // MOVI
94
+ // 3-operand arithmetic: [op][rd][rs1][rs2]
95
+ case 0x08: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=(this.gp[a]+this.gp[b])|0; this.gp[d]=r; this._setFlags(r); break; } // IADD
96
+ case 0x09: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=(this.gp[a]-this.gp[b])|0; this.gp[d]=r; this._setFlags(r); break; } // ISUB
97
+ case 0x0A: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=(this.gp[a]*this.gp[b])|0; this.gp[d]=r; this._setFlags(r); break; } // IMUL
98
+ case 0x0B: { const d=this._u8(),a=this._u8(),b=this._u8();
99
+ if(this.gp[b]===0) throw new Error('Division by zero');
100
+ this.gp[d]=(this.gp[a]/this.gp[b])|0; this._setFlags(this.gp[d]); break; } // IDIV
101
+ case 0x0C: { const d=this._u8(),a=this._u8(),b=this._u8();
102
+ if(this.gp[b]===0) throw new Error('Modulo by zero');
103
+ this.gp[d]=(this.gp[a]%this.gp[b])|0; this._setFlags(this.gp[d]); break; } // IMOD
104
+ case 0x0E: { const r=this._u8(); const v=(this.gp[r]+1)|0; this.gp[r]=v; this._setFlags(v); break; } // INC
105
+ case 0x0F: { const r=this._u8(); const v=(this.gp[r]-1)|0; this.gp[r]=v; this._setFlags(v); break; } // DEC
106
+ // FIX: PUSH/POP now use 0x20/0x21 (was 0x10/0x11)
107
+ case 0x20: this.stack.push(this.gp[this._u8()]); break; // PUSH
108
+ case 0x21: this.gp[this._u8()]=this.stack.pop()||0; break; // POP
109
+ case 0x22: { if(this.stack.length>0){ const v=this.stack[this.stack.length-1]; this.stack.push(v);} break; } // DUP
110
+ case 0x04: { const _r=this._u8(),off=this._i16(); this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JMP
111
+ case 0x05: { const r=this._u8(),off=this._i16(); if(this.gp[r]===0) this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JZ
112
+ case 0x06: { const r=this._u8(),off=this._i16(); if(this.gp[r]!==0) this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JNZ
113
+ case 0x07: { const _r=this._u8(),off=this._i16(); this.stack.push(this.pc); this.pc=(this.pc+off)&0xFFFFFFFF; break; } // CALL
114
+ case 0x28: { const _r=this._u8(),_p=this._u8(); if(this.stack.length>0){ this.pc=this.stack.pop(); } break; } // RET
115
+ case 0x2D: { const a=this._u8(),b=this._u8();
116
+ this._flagZero = this.gp[a]===this.gp[b];
117
+ this._flagSign = this.gp[a]<this.gp[b];
118
+ break; } // CMP
119
+ case 0x2E: { const _r=this._u8(),off=this._i16(); if(this._flagZero) this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JE
120
+ case 0x2F: { const _r=this._u8(),off=this._i16(); if(!this._flagZero) this.pc=(this.pc+off)&0xFFFFFFFF; break; } // JNE
121
+ case 0x10: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]&this.gp[b]; this.gp[d]=r; this._setFlags(r); break; } // IAND
122
+ case 0x11: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]|this.gp[b]; this.gp[d]=r; this._setFlags(r); break; } // IOR
123
+ case 0x12: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]^this.gp[b]; this.gp[d]=r; this._setFlags(r); break; } // IXOR
124
+ case 0x13: { const d=this._u8(),s=this._u8(); const r=~this.gp[s]; this.gp[d]=r; this._setFlags(r); break; } // INOT
125
+ case 0x14: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]<<(this.gp[b]&0x1f); this.gp[d]=r; this._setFlags(r); break; } // ISHL
126
+ case 0x15: { const d=this._u8(),a=this._u8(),b=this._u8(); const r=this.gp[a]>>(this.gp[b]&0x1f); this.gp[d]=r; this._setFlags(r); break; } // ISHR
127
+ case 0x81: break; // YIELD
128
+ // A2A stubs (Format G: [op][len:u16][data:len]) — experimental, consistent with Python/Rust
129
+ case 0x60: case 0x61: case 0x62: case 0x66: {
130
+ const _len = this._u8() | (this._u8() << 8);
131
+ this.pc += _len; // skip variable data
132
+ break;
133
+ }
134
+ default: throw new Error(`Unknown opcode: 0x${op.toString(16).padStart(2,'0')}`);
135
+ }
136
+ }
137
+ } catch (e) { this.error = e.message; }
138
+ return this;
139
+ }
140
+
141
+ /** Pretty-print register state */
142
+ dump() {
143
+ const regs = [];
144
+ for (let i = 0; i < 16; i++) if (this.gp[i] !== 0) regs.push(`R${i}=${this.gp[i]}`);
145
+ return `PC=${this.pc} cycles=${this.cycles} halted=${this.halted} [${regs.join(' ')}]`;
146
+ }
147
+ }
148
+
149
+ // ─── Assembler ────────────────────────────────────────────────────────────
150
+
151
+ /**
152
+ * Assemble FLUX text to bytecode.
153
+ * Supports labels (label:), comments (// or ;), all instruction formats.
154
+ * @param {string} text
155
+ * @returns {Uint8Array}
156
+ */
157
+ function assemble(text) {
158
+ const labels = {};
159
+ const instructions = [];
160
+
161
+ // Pass 1: collect labels and calculate sizes
162
+ let pc = 0;
163
+ for (const rawLine of text.split('\n')) {
164
+ let line = rawLine.trim();
165
+ if (!line || line.startsWith('//') || line.startsWith(';')) continue;
166
+ if (line.includes(':')) {
167
+ const [labelPart, ...rest] = line.split(':');
168
+ labels[labelPart.trim()] = pc;
169
+ line = rest.join(':').trim();
170
+ if (!line) continue;
171
+ }
172
+ const parts = line.replace(/,/g, ' ').split(/\s+/);
173
+ const mn = parts[0].toUpperCase();
174
+ instructions.push({ parts, pc });
175
+
176
+ // Size calculation
177
+ if (mn in OP) {
178
+ if (['HALT','NOP','DUP','YIELD'].includes(mn)) pc += 1;
179
+ else if (['INC','DEC','PUSH','POP','INEG','INOT'].includes(mn)) pc += 2;
180
+ else if (['MOV','LOAD','STORE','CMP'].includes(mn)) pc += 3;
181
+ else if (['IADD','ISUB','IMUL','IDIV','IMOD','IAND','IOR','IXOR','ISHL','ISHR'].includes(mn)) pc += 4;
182
+ else if (['MOVI','JMP','JZ','JNZ','CALL','JE','JNE'].includes(mn)) pc += 4;
183
+ else if (mn === 'RET') pc += 3;
184
+ }
185
+ }
186
+
187
+ // Pass 2: emit bytecode
188
+ const bc = [];
189
+ const resolveValue = (token, currentPc) => {
190
+ token = token.trim();
191
+ const num = parseInt(token);
192
+ if (!isNaN(num)) return num;
193
+ if (token in labels) return labels[token] - currentPc;
194
+ throw new Error(`Cannot resolve: ${token}`);
195
+ };
196
+
197
+ for (const { parts, pc: instPc } of instructions) {
198
+ const mn = parts[0].toUpperCase();
199
+ if (!(mn in OP)) continue;
200
+ const op = OP[mn];
201
+
202
+ if (['HALT','NOP','DUP','YIELD'].includes(mn)) {
203
+ bc.push(op);
204
+ } else if (['INC','DEC','PUSH','POP'].includes(mn)) {
205
+ bc.push(op, parseInt(parts[1].slice(1)));
206
+ } else if (['MOV','LOAD','STORE','CMP'].includes(mn)) {
207
+ bc.push(op, parseInt(parts[1].slice(1)), parseInt(parts[2].slice(1)));
208
+ } else if (['IADD','ISUB','IMUL','IDIV','IMOD','IAND','IOR','IXOR','ISHL','ISHR'].includes(mn)) {
209
+ bc.push(op, parseInt(parts[1].slice(1)), parseInt(parts[2].slice(1)),
210
+ parts.length > 3 ? parseInt(parts[3].slice(1)) : parseInt(parts[1].slice(1)));
211
+ } else if (mn === 'MOVI') {
212
+ bc.push(op, parseInt(parts[1].slice(1)));
213
+ const v = resolveValue(parts[2], bc.length + 1);
214
+ bc.push(v & 0xFF, (v >> 8) & 0xFF);
215
+ } else if (['JMP','JZ','JNZ','CALL','JE','JNE'].includes(mn)) {
216
+ bc.push(op, parseInt(parts[1] ? parts[1].slice(1) : '0') || 0);
217
+ const labelOrOff = parts[2] || parts[1];
218
+ const v = isNaN(parseInt(labelOrOff)) ? resolveValue(labelOrOff, bc.length + 2) : parseInt(labelOrOff);
219
+ bc.push(v & 0xFF, (v >> 8) & 0xFF);
220
+ } else if (mn === 'RET') {
221
+ bc.push(op, 0, 0);
222
+ }
223
+ }
224
+
225
+ return new Uint8Array(bc);
226
+ }
227
+
228
+ // ─── Disassembler ─────────────────────────────────────────────────────────
229
+
230
+ /**
231
+ * Disassemble bytecode to human-readable text.
232
+ * @param {Uint8Array} bytecode
233
+ * @returns {string[]}
234
+ */
235
+ function disassemble(bytecode) {
236
+ const lines = [];
237
+ let pc = 0;
238
+ const bc = bytecode instanceof Uint8Array ? bytecode : new Uint8Array(bytecode);
239
+
240
+ while (pc < bc.length) {
241
+ const addr = pc;
242
+ const op = bc[pc++];
243
+ const mn = MNEMONIC[op] || `??? (0x${op.toString(16).padStart(2,'0')})`;
244
+
245
+ if ([0x80, 0x00, 0x22, 0x81].includes(op)) {
246
+ lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn}`);
247
+ } else if ([0x0E, 0x0F, 0x20, 0x21].includes(op)) {
248
+ lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn} R${bc[pc++]}`);
249
+ } else if ([0x01, 0x02, 0x03, 0x2D].includes(op)) {
250
+ lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn} R${bc[pc++]}, R${bc[pc++]}`);
251
+ } else if ([0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x10, 0x11, 0x12, 0x14, 0x15].includes(op)) {
252
+ lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn} R${bc[pc++]}, R${bc[pc++]}, R${bc[pc++]}`);
253
+ } else if ([0x2B, 0x04, 0x05, 0x06, 0x07, 0x2E, 0x2F].includes(op)) {
254
+ const rd = bc[pc++];
255
+ const val = bc[pc] | (bc[pc+1] << 8) | (bc[pc+1] >= 128 ? -65536 : 0);
256
+ pc += 2;
257
+ lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn} R${rd}, ${val}`);
258
+ } else if (op === 0x28) {
259
+ pc += 2; // RET has 2 padding bytes
260
+ lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn}`);
261
+ } else {
262
+ lines.push(`${addr.toString(16).padStart(4,'0')}: ${mn}`);
263
+ }
264
+ }
265
+ return lines;
266
+ }
267
+
268
+ // ─── Vocabulary ───────────────────────────────────────────────────────────
269
+
270
+ class VocabEntry {
271
+ /**
272
+ * @param {RegExp|string} pattern
273
+ * @param {string|function} asmTemplate
274
+ * @param {string} name
275
+ * @param {number} resultReg
276
+ */
277
+ constructor(pattern, asmTemplate, name, resultReg = 0) {
278
+ this.pattern = typeof pattern === 'string' ? new RegExp(pattern, 'i') : pattern;
279
+ this.asmTemplate = asmTemplate;
280
+ this.name = name;
281
+ this.resultReg = resultReg;
282
+ }
283
+
284
+ /**
285
+ * Match text against pattern
286
+ * @param {string} text
287
+ * @returns {Array|null} captured groups or null
288
+ */
289
+ match(text) {
290
+ const m = text.match(this.pattern);
291
+ return m ? m.slice(1) : null;
292
+ }
293
+ }
294
+
295
+ class Vocabulary {
296
+ constructor() {
297
+ this.entries = [];
298
+ }
299
+
300
+ /**
301
+ * Add a vocabulary entry
302
+ * @param {VocabEntry} entry
303
+ */
304
+ register(entry) {
305
+ this.entries.push(entry);
306
+ }
307
+
308
+ /**
309
+ * Find first matching entry for text
310
+ * @param {string} text
311
+ * @returns {VocabEntry|null}
312
+ */
313
+ findMatch(text) {
314
+ for (const entry of this.entries) {
315
+ if (entry.match(text) !== null) {
316
+ return entry;
317
+ }
318
+ }
319
+ return null;
320
+ }
321
+ }
322
+
323
+ class VocabInterpreter {
324
+ constructor() {
325
+ this.vocab = new Vocabulary();
326
+ this.assembler = assemble; // Use existing assemble function
327
+ this.vm = null;
328
+
329
+ // Register built-in entries
330
+ this.vocab.register(new VocabEntry(
331
+ /^compute\s+(\d+)\s*\+\s*(\d+)$/i,
332
+ (a, b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nIADD R0, R0, R1\nHALT`,
333
+ 'add',
334
+ 0
335
+ ));
336
+ this.vocab.register(new VocabEntry(
337
+ /^compute\s+(\d+)\s*\*\s*(\d+)$/i,
338
+ (a, b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nIMUL R0, R0, R1\nHALT`,
339
+ 'mul',
340
+ 0
341
+ ));
342
+ this.vocab.register(new VocabEntry(
343
+ /^factorial\s+of\s+(\d+)$/i,
344
+ (n) => `MOVI R0, ${n}\nMOVI R1, 1\nIMUL R1, R1, R0\nDEC R0\nJNZ R0, -10\nHALT`,
345
+ 'factorial',
346
+ 1
347
+ ));
348
+ this.vocab.register(new VocabEntry(
349
+ /^double\s+(\d+)$/i,
350
+ (n) => `MOVI R0, ${n}\nIADD R0, R0, R0\nHALT`,
351
+ 'double',
352
+ 0
353
+ ));
354
+ this.vocab.register(new VocabEntry(
355
+ /^square\s+(\d+)$/i,
356
+ (n) => `MOVI R0, ${n}\nIMUL R0, R0, R0\nHALT`,
357
+ 'square',
358
+ 0
359
+ ));
360
+ this.vocab.register(new VocabEntry(
361
+ /^hello$/i,
362
+ () => `MOVI R0, 42\nHALT`,
363
+ 'hello',
364
+ 0
365
+ ));
366
+ }
367
+
368
+ /**
369
+ * Run text through vocabulary interpreter
370
+ * @param {string} text
371
+ * @returns {{ value: number|null, message: string, cycles: number }}
372
+ */
373
+ run(text) {
374
+ const trimmed = text.trim();
375
+ const entry = this.vocab.findMatch(trimmed);
376
+
377
+ if (!entry) {
378
+ return { value: null, message: `No vocabulary match for: ${trimmed.slice(0, 80)}`, cycles: 0 };
379
+ }
380
+
381
+ const args = entry.match(trimmed).map(Number);
382
+ let asmText;
383
+ if (typeof entry.asmTemplate === 'function') {
384
+ asmText = entry.asmTemplate(...args);
385
+ } else {
386
+ asmText = entry.asmTemplate;
387
+ }
388
+
389
+ try {
390
+ const bc = this.assembler(asmText);
391
+ this.vm = new FluxVM(bc);
392
+ this.vm.execute();
393
+ return {
394
+ value: this.vm.reg(entry.resultReg),
395
+ message: `OK (${this.vm.cycles} cycles)`,
396
+ cycles: this.vm.cycles
397
+ };
398
+ } catch (e) {
399
+ return { value: null, message: `Error: ${e.message}`, cycles: 0 };
400
+ }
401
+ }
402
+ }
403
+
404
+ const VOCAB = [
405
+ { pattern: /^compute\s+(\d+)\s*\+\s*(\d+)$/i, name: 'add',
406
+ asm: (a,b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nIADD R0, R0, R1\nHALT`, reg: 0 },
407
+ { pattern: /^compute\s+(\d+)\s*-\s*(\d+)$/i, name: 'sub',
408
+ asm: (a,b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nISUB R0, R0, R1\nHALT`, reg: 0 },
409
+ { pattern: /^compute\s+(\d+)\s*\*\s*(\d+)$/i, name: 'mul',
410
+ asm: (a,b) => `MOVI R0, ${a}\nMOVI R1, ${b}\nIMUL R0, R0, R1\nHALT`, reg: 0 },
411
+ { pattern: /^double\s+(\d+)$/i, name: 'double',
412
+ asm: (a) => `MOVI R0, ${a}\nIADD R0, R0, R0\nHALT`, reg: 0 },
413
+ { pattern: /^square\s+(\d+)$/i, name: 'square',
414
+ asm: (a) => `MOVI R0, ${a}\nIMUL R0, R0, R0\nHALT`, reg: 0 },
415
+ { pattern: /^factorial\s+of\s+(\d+)$/i, name: 'factorial',
416
+ asm: (n) => `MOVI R0, ${n}\nMOVI R1, 1\nIMUL R1, R1, R0\nDEC R0\nJNZ R0, -10\nHALT`, reg: 1 },
417
+ { pattern: /^fibonacci\s+of\s+(\d+)$/i, name: 'fibonacci',
418
+ asm: (n) => `MOVI R0, 0\nMOVI R1, 1\nMOVI R2, ${n}\nMOV R3, R1\nIADD R1, R1, R0\nMOV R0, R3\nDEC R2\nJNZ R2, -16\nHALT`, reg: 0 },
419
+ { pattern: /^sum\s+(\d+)\s+to\s+(\d+)$/i, name: 'sum',
420
+ asm: (a,b) => `MOVI R0, 0\nMOVI R1, ${b}\nIADD R0, R0, R1\nDEC R1\nJNZ R1, -10\nHALT`, reg: 0 },
421
+ { pattern: /^power\s+of\s+(\d+)\s+to\s+(\d+)$/i, name: 'power',
422
+ asm: (base,exp) => `MOVI R0, 1\nMOVI R1, ${base}\nMOVI R2, ${exp}\nIMUL R0, R0, R1\nDEC R2\nJNZ R2, -10\nHALT`, reg: 0 },
423
+ { pattern: /^hello$/i, name: 'hello',
424
+ asm: () => `MOVI R0, 42\nHALT`, reg: 0 },
425
+ ];
426
+
427
+ // ─── Interpreter ──────────────────────────────────────────────────────────
428
+
429
+ class Interpreter {
430
+ constructor() {
431
+ this.vocab = [...VOCAB];
432
+ }
433
+
434
+ /**
435
+ * Interpret natural language text and execute.
436
+ * @param {string} text
437
+ * @returns {{ value: number|null, message: string, cycles: number }}
438
+ */
439
+ run(text) {
440
+ const trimmed = text.trim();
441
+
442
+ // Try vocabulary
443
+ for (const entry of this.vocab) {
444
+ const m = trimmed.match(entry.pattern);
445
+ if (m) {
446
+ const args = m.slice(1).map(Number);
447
+ const asmText = entry.asm(...args);
448
+ try {
449
+ const bc = assemble(asmText);
450
+ const vm = new FluxVM(bc);
451
+ vm.execute();
452
+ return { value: vm.reg(entry.reg), message: `OK (${vm.cycles} cycles)`, cycles: vm.cycles };
453
+ } catch (e) {
454
+ return { value: null, message: `Error: ${e.message}`, cycles: 0 };
455
+ }
456
+ }
457
+ }
458
+
459
+ // Try direct assembly
460
+ const firstWord = trimmed.split(/\s+/)[0]?.toUpperCase();
461
+ if (firstWord && firstWord in OP) {
462
+ try {
463
+ const bc = assemble(trimmed);
464
+ const vm = new FluxVM(bc);
465
+ vm.execute();
466
+ return { value: vm.reg(0), message: `OK (${vm.cycles} cycles, direct asm)`, cycles: vm.cycles };
467
+ } catch (e) {
468
+ return { value: null, message: `Error: ${e.message}`, cycles: 0 };
469
+ }
470
+ }
471
+
472
+ return { value: null, message: `No match for: ${trimmed.slice(0, 80)}`, cycles: 0 };
473
+ }
474
+ }
475
+
476
+ // ─── A2A Agents ───────────────────────────────────────────────────────────
477
+
478
+ class A2AAgent {
479
+ constructor(id, bytecode, role = 'worker') {
480
+ this.id = id;
481
+ this.vm = new FluxVM(bytecode);
482
+ this.role = role;
483
+ this.trust = 1.0;
484
+ this.inbox = [];
485
+ this.generation = 0;
486
+ }
487
+
488
+ step() {
489
+ this.vm.execute();
490
+ this.generation++;
491
+ return this.vm.cycles;
492
+ }
493
+
494
+ tell(other, payload) {
495
+ other.inbox.push({ from: this.id, type: 'TELL', payload, gen: this.generation, trust: this.trust });
496
+ }
497
+
498
+ ask(other) {
499
+ other.inbox.push({ from: this.id, type: 'ASK', gen: this.generation });
500
+ return other.vm.halted ? other.vm.reg(0) : null;
501
+ }
502
+ }
503
+
504
+ class Swarm {
505
+ constructor() { this.agents = new Map(); }
506
+
507
+ add(agent) { this.agents.set(agent.id, agent); }
508
+
509
+ tick() {
510
+ let total = 0;
511
+ for (const a of this.agents.values()) total += a.step();
512
+ return total;
513
+ }
514
+
515
+ vote(reg = 0) {
516
+ const counts = {};
517
+ for (const a of this.agents.values()) {
518
+ if (a.vm.halted) {
519
+ const v = a.vm.reg(reg);
520
+ counts[v] = (counts[v] || 0) + 1;
521
+ }
522
+ }
523
+ return counts;
524
+ }
525
+
526
+ consensus(reg = 0) {
527
+ const counts = this.vote(reg);
528
+ if (!Object.keys(counts).length) return null;
529
+ return Number(Object.entries(counts).sort((a,b) => b[1]-a[1])[0][0]);
530
+ }
531
+ }
532
+
533
+ // ─── Exports ──────────────────────────────────────────────────────────────
534
+
535
+ module.exports = {
536
+ FluxVM,
537
+ assemble,
538
+ disassemble,
539
+ Interpreter,
540
+ A2AAgent,
541
+ Swarm,
542
+ VocabEntry,
543
+ Vocabulary,
544
+ VocabInterpreter,
545
+ OP,
546
+ VOCAB
547
+ };
548
+
549
+ // ─── CLI Demo ─────────────────────────────────────────────────────────────
550
+
551
+ if (typeof require !== 'undefined' && require.main === module) {
552
+ console.log('╔══════════════════════════════════════════════════╗');
553
+ console.log('║ FLUX.js — JavaScript Bytecode VM ║');
554
+ console.log('║ SuperInstance / Oracle1 ║');
555
+ console.log('╚══════════════════════════════════════════════════╝\n');
556
+
557
+ const interp = new Interpreter();
558
+ console.log(` ${interp.vocab.length} vocabulary patterns loaded\n`);
559
+
560
+ // Arithmetic
561
+ console.log('=== Arithmetic ===');
562
+ for (const expr of ['compute 3 + 4', 'compute 10 * 5', 'double 21', 'square 8']) {
563
+ const r = interp.run(expr);
564
+ console.log(` ${expr} → ${r.value}`);
565
+ }
566
+
567
+ // Loops
568
+ console.log('\n=== Loops ===');
569
+ for (const expr of ['factorial of 7', 'fibonacci of 12', 'sum 1 to 100', 'power of 2 to 10']) {
570
+ const r = interp.run(expr);
571
+ console.log(` ${expr} → ${r.value} (${r.cycles} cycles)`);
572
+ }
573
+
574
+ // Assembler
575
+ console.log('\n=== Assembler ===');
576
+ const bc = assemble('MOVI R0, 7\nMOVI R1, 1\nIMUL R1, R1, R0\nDEC R0\nJNZ R0, -10\nHALT');
577
+ const vm = new FluxVM(bc);
578
+ vm.execute();
579
+ console.log(` factorial(7) bytecode: ${Array.from(bc).map(b=>b.toString(16).padStart(2,'0')).join(' ')}`);
580
+ console.log(` Result: R1 = ${vm.reg(1)}`);
581
+
582
+ // Disassembler
583
+ console.log('\n=== Disassembler ===');
584
+ for (const line of disassemble(bc)) console.log(` ${line}`);
585
+
586
+ // Swarm
587
+ console.log('\n=== A2A Swarm ===');
588
+ const swarm = new Swarm();
589
+ const agentBc = assemble('MOVI R0, 42\nHALT');
590
+ for (let i = 0; i < 5; i++) {
591
+ swarm.add(new A2AAgent(`agent-${i}`, agentBc, ['worker','scout','navigator'][i%3]));
592
+ }
593
+ const totalCycles = swarm.tick();
594
+ console.log(` 5 agents, ${totalCycles} cycles, consensus: ${swarm.consensus()}`);
595
+
596
+ // Benchmark
597
+ console.log('\n=== Benchmark ===');
598
+ const N = 100000;
599
+ const t0 = Date.now();
600
+ for (let i = 0; i < N; i++) new FluxVM(bc).execute();
601
+ const elapsed = Date.now() - t0;
602
+ console.log(` factorial(7) x ${N.toLocaleString()}: ${elapsed} ms | ${(elapsed*1e6/N).toFixed(0)} ns/iter`);
603
+
604
+ console.log('\n✓ FLUX.js all systems operational!');
605
+
606
+ // Test the new vocabulary interpreter
607
+ console.log('\n=== Testing VocabInterpreter ===');
608
+ const vocabInterp = new VocabInterpreter();
609
+
610
+ // Test compute A + B
611
+ const addResult = vocabInterp.run('compute 3 + 4');
612
+ console.assert(addResult.value === 7, `Expected 7, got ${addResult.value}`);
613
+ console.log(` compute 3 + 4 → ${addResult.value} (${addResult.message})`);
614
+
615
+ // Test compute A * B
616
+ const mulResult = vocabInterp.run('compute 5 * 6');
617
+ console.assert(mulResult.value === 30, `Expected 30, got ${mulResult.value}`);
618
+ console.log(` compute 5 * 6 → ${mulResult.value} (${mulResult.message})`);
619
+
620
+ // Test factorial
621
+ const factResult = vocabInterp.run('factorial of 5');
622
+ console.assert(factResult.value === 120, `Expected 120, got ${factResult.value}`);
623
+ console.log(` factorial of 5 → ${factResult.value} (${factResult.message})`);
624
+
625
+ // Test double
626
+ const doubleResult = vocabInterp.run('double 21');
627
+ console.assert(doubleResult.value === 42, `Expected 42, got ${doubleResult.value}`);
628
+ console.log(` double 21 → ${doubleResult.value} (${addResult.message})`);
629
+
630
+ // Test square
631
+ const squareResult = vocabInterp.run('square 8');
632
+ console.assert(squareResult.value === 64, `Expected 64, got ${squareResult.value}`);
633
+ console.log(` square 8 → ${squareResult.value} (${squareResult.message})`);
634
+
635
+ // Test hello
636
+ const helloResult = vocabInterp.run('hello');
637
+ console.assert(helloResult.value === 42, `Expected 42, got ${helloResult.value}`);
638
+ console.log(` hello → ${helloResult.value} (${helloResult.message})`);
639
+
640
+ console.log('\n✓ All VocabInterpreter tests passed!');
641
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "flux-vm-js",
3
+ "version": "1.0.0",
4
+ "description": "FLUX.js — JavaScript Bytecode VM. Self-contained FLUX runtime for Node.js and browsers.",
5
+ "main": "flux.js",
6
+ "author": "SuperInstance",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/SuperInstance/flux-js.git"
11
+ },
12
+ "keywords": [
13
+ "flux",
14
+ "vm",
15
+ "bytecode",
16
+ "runtime",
17
+ "virtual-machine",
18
+ "interpreter",
19
+ "assembly",
20
+ "superinstance"
21
+ ],
22
+ "homepage": "https://github.com/SuperInstance/flux-js#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/SuperInstance/flux-js/issues"
25
+ },
26
+ "scripts": {
27
+ "test": "vitest run",
28
+ "test:watch": "vitest"
29
+ },
30
+ "files": [
31
+ "flux.js",
32
+ "README.md",
33
+ "LICENSE"
34
+ ],
35
+ "devDependencies": {
36
+ "vitest": "^3.2.1"
37
+ }
38
+ }