myxo-lang 1.5.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/CONCURRENCY.md +207 -0
- package/HOW_IT_WORKS.md +235 -0
- package/INDEPENDENCE.md +24 -0
- package/MYXO_PROMPT.md +139 -0
- package/README.md +494 -0
- package/ROADMAP.md +200 -0
- package/SPEC.md +181 -0
- package/VISION.md +249 -0
- package/builtins.js +361 -0
- package/command-fence.js +75 -0
- package/errors.js +45 -0
- package/examples/agent.myx +31 -0
- package/examples/fenced-agent.js +78 -0
- package/examples/fib.myx +11 -0
- package/examples/fibers.myx +57 -0
- package/examples/flow-routing.myx +12 -0
- package/examples/geo.myx +12 -0
- package/examples/hello.myx +2 -0
- package/examples/host.js +30 -0
- package/examples/james-myxo-demo.js +60 -0
- package/examples/living-mesh.myx +22 -0
- package/examples/match.myx +18 -0
- package/examples/mathlib.js +8 -0
- package/examples/mathlib.pl +11 -0
- package/examples/mathlib.py +23 -0
- package/examples/mcp-host.js +37 -0
- package/examples/nexus-mesh.myx +22 -0
- package/examples/nexus.myx +29 -0
- package/examples/ouroboros.myx +2 -0
- package/examples/outward-gate.myx +23 -0
- package/examples/physarum.myx +75 -0
- package/examples/polyglot-host.js +17 -0
- package/examples/polyglot.myx +11 -0
- package/examples/resilient.myx +28 -0
- package/examples/scheduler.myx +46 -0
- package/examples/the-law.myx +27 -0
- package/examples/use-geo.myx +9 -0
- package/format.js +206 -0
- package/interpreter.js +1092 -0
- package/lexer.js +173 -0
- package/mcp-bridge.js +77 -0
- package/mcp-framing.js +34 -0
- package/mcp-server.js +57 -0
- package/myxo-concurrent.js +110 -0
- package/myxo-live-worker.js +30 -0
- package/myxo-live.js +83 -0
- package/myxo-lsp.js +226 -0
- package/myxo-par-worker.js +39 -0
- package/myxo-plan.js +207 -0
- package/myxo-run.js +63 -0
- package/myxo.js +296 -0
- package/package.json +27 -0
- package/parser.js +662 -0
- package/polyglot-host.js +39 -0
- package/polyglot.js +191 -0
- package/receipt.js +71 -0
- package/std.myx +88 -0
- package/tools/memo-fuzz.js +224 -0
package/myxo.js
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// myxo.js — the Myxo entry point. `node myxo.js --help` for the full CLI.
|
|
4
|
+
// run a file: node myxo.js script.myx [--trace] [--strict] · subcommands: run · fmt · test · lsp · repl
|
|
5
|
+
// --version / --help · no args = REPL
|
|
6
|
+
// embed: require('./myxo').run(src, opts) / runTests(src, opts) — Myxo inside any Node host (the Nexus)
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { parse } = require('./parser');
|
|
11
|
+
const { Interpreter, VOID, stringify } = require('./interpreter');
|
|
12
|
+
const { install } = require('./builtins');
|
|
13
|
+
const { bridgeMcpTools } = require('./mcp-bridge');
|
|
14
|
+
const { MyxoError } = require('./errors');
|
|
15
|
+
const { formatSource } = require('./format');
|
|
16
|
+
|
|
17
|
+
let STD_CACHE = null;
|
|
18
|
+
function stdSource() {
|
|
19
|
+
if (STD_CACHE == null) STD_CACHE = fs.readFileSync(path.join(__dirname, 'std.myx'), 'utf8');
|
|
20
|
+
return STD_CACHE;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Build an interpreter with builtins + the self-hosted standard library loaded.
|
|
24
|
+
// Everything loaded here is marked `system` so it stays out of the mesh view.
|
|
25
|
+
// Load + parse a strand from disk. This is the granted module capability — an
|
|
26
|
+
// embedded host can withhold it (moduleLoader: null) to fence `weave` entirely.
|
|
27
|
+
function fileLoader() {
|
|
28
|
+
return (reqPath, fromDir) => {
|
|
29
|
+
const resolved = path.resolve(fromDir || process.cwd(), reqPath);
|
|
30
|
+
return { ast: parse(fs.readFileSync(resolved, 'utf8')), key: resolved, dir: path.dirname(resolved) };
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function makeInterpreter(opts = {}) {
|
|
35
|
+
const interp = new Interpreter({
|
|
36
|
+
output: opts.output,
|
|
37
|
+
dir: opts.dir,
|
|
38
|
+
maxDepth: opts.maxDepth,
|
|
39
|
+
maxSteps: opts.maxSteps,
|
|
40
|
+
promoteAt: opts.promoteAt,
|
|
41
|
+
requireManifest: opts.requireManifest,
|
|
42
|
+
valueCaps: opts.valueCaps,
|
|
43
|
+
testMode: opts.testMode,
|
|
44
|
+
strict: opts.strict,
|
|
45
|
+
});
|
|
46
|
+
const scriptMaxSteps = interp.maxSteps;
|
|
47
|
+
interp.maxSteps = Infinity;
|
|
48
|
+
interp.moduleLoader = opts.moduleLoader !== undefined ? opts.moduleLoader : fileLoader();
|
|
49
|
+
install(interp);
|
|
50
|
+
interp.run(parse(stdSource()));
|
|
51
|
+
interp.maxSteps = scriptMaxSteps;
|
|
52
|
+
interp.steps = 0; // stdlib boot cost should not spend the caller's script fuel.
|
|
53
|
+
for (const e of interp.globals.vars.values()) e.system = true;
|
|
54
|
+
if (opts.natives) for (const [name, fn] of Object.entries(opts.natives)) interp.registerNative(name, fn, true);
|
|
55
|
+
if (opts.mcp) bridgeMcpTools(interp, opts.mcp); // every MCP tool becomes a fenced capability
|
|
56
|
+
return interp;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Render the living mesh as a little bar chart of pathway strengths.
|
|
60
|
+
function formatMesh(interp) {
|
|
61
|
+
const rows = interp.meshSnapshot();
|
|
62
|
+
if (!rows.length) return '\n— mesh is silent —\n';
|
|
63
|
+
let out = '\n— the mesh —\n';
|
|
64
|
+
for (const r of rows) out += ` ${r.name.padEnd(14)} ${'█'.repeat(Math.min(r.strength, 24))} ${r.strength}\n`;
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// The embed API. Returns captured output (opts.capture) or the program's value.
|
|
69
|
+
function run(src, opts = {}) {
|
|
70
|
+
const chunks = [];
|
|
71
|
+
const output = opts.capture ? (s => chunks.push(s)) : (opts.output || (s => process.stdout.write(s)));
|
|
72
|
+
const interp = makeInterpreter({
|
|
73
|
+
output,
|
|
74
|
+
natives: opts.natives,
|
|
75
|
+
dir: opts.dir,
|
|
76
|
+
moduleLoader: opts.moduleLoader,
|
|
77
|
+
maxDepth: opts.maxDepth,
|
|
78
|
+
maxSteps: opts.maxSteps,
|
|
79
|
+
requireManifest: opts.requireManifest,
|
|
80
|
+
valueCaps: opts.valueCaps,
|
|
81
|
+
mcp: opts.mcp,
|
|
82
|
+
promoteAt: opts.promoteAt,
|
|
83
|
+
strict: opts.strict,
|
|
84
|
+
});
|
|
85
|
+
let result;
|
|
86
|
+
try {
|
|
87
|
+
result = interp.run(parse(src));
|
|
88
|
+
} finally {
|
|
89
|
+
if (opts.onAudit) opts.onAudit(interp.audit); // hand over the ledger even on failure
|
|
90
|
+
}
|
|
91
|
+
if (opts.trace) output(formatMesh(interp));
|
|
92
|
+
return opts.capture ? chunks.join('') : result;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Run the `test` blocks in a source string. Returns { results, passed, failed }.
|
|
96
|
+
// Self-hosted: a .myx file tests itself in Myxo, with no node:test involved.
|
|
97
|
+
function runTests(src, opts = {}) {
|
|
98
|
+
// tests run STRICT by default — type annotations are enforced where you most want them.
|
|
99
|
+
const interp = makeInterpreter({ dir: opts.dir, output: opts.output, testMode: true, strict: opts.strict !== false, maxSteps: opts.maxSteps, maxDepth: opts.maxDepth });
|
|
100
|
+
interp.run(parse(src));
|
|
101
|
+
const results = interp.testResults;
|
|
102
|
+
const passed = results.filter(r => r.ok).length;
|
|
103
|
+
return { results, passed, failed: results.length - passed };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ---- command line ---------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
function runFile(file, trace, strict) {
|
|
109
|
+
const src = fs.readFileSync(file, 'utf8');
|
|
110
|
+
const interp = makeInterpreter({ dir: path.dirname(path.resolve(file)), strict });
|
|
111
|
+
interp.run(parse(src));
|
|
112
|
+
if (trace) process.stdout.write(formatMesh(interp));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Is this source still mid-block — unbalanced braces or an open string? Lets the
|
|
116
|
+
// REPL gather multi-line definitions instead of choking on the first line.
|
|
117
|
+
function needsMore(src) {
|
|
118
|
+
let depth = 0, inStr = false;
|
|
119
|
+
for (let i = 0; i < src.length; i++) {
|
|
120
|
+
const c = src[i];
|
|
121
|
+
if (inStr) {
|
|
122
|
+
if (c === '\\') { i++; continue; }
|
|
123
|
+
if (c === '"') inStr = false;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (c === '#') { while (i < src.length && src[i] !== '\n') i++; continue; }
|
|
127
|
+
if (c === '"') { inStr = true; continue; }
|
|
128
|
+
if (c === '{' || c === '(' || c === '[') depth++;
|
|
129
|
+
else if (c === '}' || c === ')' || c === ']') depth--;
|
|
130
|
+
}
|
|
131
|
+
return depth > 0 || inStr;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function repl() {
|
|
135
|
+
const readline = require('readline');
|
|
136
|
+
const interp = makeInterpreter();
|
|
137
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: 'myxo> ' });
|
|
138
|
+
process.stdout.write(`Myxo ${require('./package.json').version} — the language of the Nexus. Its runtime is a living law.\n`);
|
|
139
|
+
process.stdout.write('Multi-line aware; Ctrl+C to leave.\n');
|
|
140
|
+
rl.prompt();
|
|
141
|
+
let buffer = '';
|
|
142
|
+
rl.on('line', (line) => {
|
|
143
|
+
buffer += (buffer ? '\n' : '') + line;
|
|
144
|
+
if (needsMore(buffer)) { rl.setPrompt('..> '); rl.prompt(); return; } // keep gathering
|
|
145
|
+
const src = buffer.trim();
|
|
146
|
+
buffer = '';
|
|
147
|
+
rl.setPrompt('myxo> ');
|
|
148
|
+
if (src) {
|
|
149
|
+
try {
|
|
150
|
+
const ast = parse(src);
|
|
151
|
+
// Echo the value of a lone expression, the way a REPL should.
|
|
152
|
+
if (ast.body.length === 1 && ast.body[0].type === 'ExpressionStatement') {
|
|
153
|
+
const v = interp.eval(ast.body[0].expr, interp.globals);
|
|
154
|
+
if (v !== VOID) process.stdout.write(stringify(v) + '\n');
|
|
155
|
+
} else {
|
|
156
|
+
interp.run(ast);
|
|
157
|
+
}
|
|
158
|
+
} catch (e) {
|
|
159
|
+
process.stdout.write((e instanceof MyxoError ? e.format() : `error: ${e.message}`) + '\n');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
rl.prompt();
|
|
163
|
+
});
|
|
164
|
+
rl.on('close', () => process.stdout.write('\nthe mesh rests.\n'));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function fmtFiles(argv) {
|
|
168
|
+
const write = argv.includes('--write');
|
|
169
|
+
const check = argv.includes('--check');
|
|
170
|
+
const dropComments = argv.includes('--drop-comments');
|
|
171
|
+
const bad = argv.find(a => a.startsWith('--') && !['--write', '--check', '--drop-comments'].includes(a));
|
|
172
|
+
if (bad) usageError(`unknown flag '${bad}' for fmt — use --check / --write / --drop-comments`);
|
|
173
|
+
const files = argv.filter(a => !a.startsWith('--'));
|
|
174
|
+
if (!files.length) usageError('usage: myxo fmt <file...> [--check] [--write] [--drop-comments]');
|
|
175
|
+
let dirty = false;
|
|
176
|
+
for (const file of files) {
|
|
177
|
+
let st;
|
|
178
|
+
try { st = fs.statSync(file); } catch { throw new MyxoError(`no such file: ${file}`); }
|
|
179
|
+
if (!st.isFile()) throw new MyxoError(`not a file: ${file}`);
|
|
180
|
+
const src = fs.readFileSync(file, 'utf8');
|
|
181
|
+
const formatted = formatSource(src, { dropComments }); // comments are preserved by default; --drop-comments strips them
|
|
182
|
+
const changed = formatted !== src.replace(/\r\n/g, '\n');
|
|
183
|
+
if (check) {
|
|
184
|
+
if (changed) {
|
|
185
|
+
dirty = true;
|
|
186
|
+
process.stderr.write(`${file} is not formatted\n`);
|
|
187
|
+
}
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (write) {
|
|
191
|
+
if (changed) fs.writeFileSync(file, formatted);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
process.stdout.write(formatted);
|
|
195
|
+
}
|
|
196
|
+
if (check && dirty) process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// `myxo test <file-or-dir...>` — run Myxo test files, print a report, exit non-zero on any failure.
|
|
200
|
+
function testFiles(argv) {
|
|
201
|
+
const targets = argv.filter(a => !a.startsWith('--'));
|
|
202
|
+
if (!targets.length) usageError('usage: myxo test <file-or-dir...>');
|
|
203
|
+
const files = [];
|
|
204
|
+
const collect = (p) => {
|
|
205
|
+
if (!fs.existsSync(p)) throw new MyxoError(`no such path: ${p}`);
|
|
206
|
+
const st = fs.statSync(p);
|
|
207
|
+
if (st.isDirectory()) { for (const f of fs.readdirSync(p).sort()) collect(path.join(p, f)); } // recursive
|
|
208
|
+
else if (st.isFile() && p.endsWith('.myx')) files.push(p); // a dir named *.myx is traversed, not read
|
|
209
|
+
};
|
|
210
|
+
for (const t of targets) collect(t);
|
|
211
|
+
if (!files.length) throw new MyxoError('no .myx test files found');
|
|
212
|
+
let pass = 0, fail = 0;
|
|
213
|
+
for (const file of files) {
|
|
214
|
+
process.stdout.write(`\n${file}\n`);
|
|
215
|
+
let res;
|
|
216
|
+
try {
|
|
217
|
+
res = runTests(fs.readFileSync(file, 'utf8'), { dir: path.dirname(path.resolve(file)) });
|
|
218
|
+
} catch (e) {
|
|
219
|
+
process.stdout.write(` FAIL (load error) ${e.message}\n`);
|
|
220
|
+
fail++; continue;
|
|
221
|
+
}
|
|
222
|
+
for (const r of res.results) {
|
|
223
|
+
if (r.ok) process.stdout.write(` ok ${r.name}\n`);
|
|
224
|
+
else process.stdout.write(` FAIL ${r.name}\n ${r.error}\n`);
|
|
225
|
+
}
|
|
226
|
+
pass += res.passed; fail += res.failed;
|
|
227
|
+
}
|
|
228
|
+
process.stdout.write(`\n${pass} passed, ${fail} failed\n`);
|
|
229
|
+
if (pass === 0 && fail === 0) { process.stdout.write('no tests ran — nothing was verified\n'); process.exit(1); }
|
|
230
|
+
if (fail) process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const USAGE = `Myxo — the language of the Nexus.
|
|
234
|
+
|
|
235
|
+
Usage:
|
|
236
|
+
myxo <file.myx> [--trace] [--strict] run a program (--trace prints the mesh; --strict enforces types)
|
|
237
|
+
myxo run <file.myx> [--trace|--strict] run a program (explicit form)
|
|
238
|
+
myxo test <file|dir...> run Myxo test files (strict by default)
|
|
239
|
+
myxo fmt <file...> [--check|--write|--drop-comments] format source
|
|
240
|
+
myxo lsp start the stdio language server
|
|
241
|
+
myxo plan <file.myx> preview a script's capability reach (the fence approval surface)
|
|
242
|
+
myxo repl start the REPL (also: myxo with no args)
|
|
243
|
+
myxo --version | -v print the version
|
|
244
|
+
myxo --help | -h print this help`;
|
|
245
|
+
|
|
246
|
+
function usageError(msg) { process.stderr.write(`myxo: ${msg}\n`); process.exit(2); } // 2 = bad usage
|
|
247
|
+
|
|
248
|
+
function planFile(argv) {
|
|
249
|
+
const bad = argv.find(a => a.startsWith('--'));
|
|
250
|
+
if (bad) usageError(`unknown flag '${bad}' — try \`myxo plan <file.myx>\``);
|
|
251
|
+
const files = argv.filter(a => !a.startsWith('--'));
|
|
252
|
+
if (!files.length) usageError('myxo plan needs a file — try `myxo plan <file.myx>`');
|
|
253
|
+
// exit codes a gate can branch on: 0 = clean, 1 = analyzed-but-not-clean (policy), 2 = couldn't analyze (IO/parse/usage).
|
|
254
|
+
let st;
|
|
255
|
+
try { st = fs.statSync(files[0]); }
|
|
256
|
+
catch { process.stderr.write(`myxo: no such file: ${files[0]}\n`); process.exit(2); }
|
|
257
|
+
if (!st.isFile()) { process.stderr.write(`myxo: not a file: ${files[0]}\n`); process.exit(2); }
|
|
258
|
+
const { planScript, formatPlan } = require('./myxo-plan');
|
|
259
|
+
let plan;
|
|
260
|
+
try { plan = planScript(fs.readFileSync(files[0], 'utf8')); }
|
|
261
|
+
catch (e) { process.stderr.write((e instanceof MyxoError ? e.format() : `myxo: ${e.message}`) + '\n'); process.exit(2); } // parse/analysis failure != policy
|
|
262
|
+
const { text, ok } = formatPlan(plan, files[0]);
|
|
263
|
+
process.stdout.write(text + '\n');
|
|
264
|
+
if (!ok) process.exit(1); // a script that would be denied (or run ungoverned) is a non-clean plan
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function main() {
|
|
268
|
+
const argv = process.argv.slice(2);
|
|
269
|
+
try {
|
|
270
|
+
const cmd = argv[0];
|
|
271
|
+
if (cmd === '--help' || cmd === '-h' || cmd === 'help') { process.stdout.write(USAGE + '\n'); return; }
|
|
272
|
+
if (cmd === '--version' || cmd === '-v' || cmd === 'version') { process.stdout.write(require('./package.json').version + '\n'); return; }
|
|
273
|
+
if (cmd === 'fmt') { fmtFiles(argv.slice(1)); return; }
|
|
274
|
+
if (cmd === 'test') { testFiles(argv.slice(1)); return; }
|
|
275
|
+
if (cmd === 'lsp') { require('./myxo-lsp').serve(); return; }
|
|
276
|
+
if (cmd === 'plan') { planFile(argv.slice(1)); return; }
|
|
277
|
+
if (cmd === 'repl' || argv.length === 0) { repl(); return; }
|
|
278
|
+
const args = cmd === 'run' ? argv.slice(1) : argv;
|
|
279
|
+
const badFlag = args.find(a => a.startsWith('--') && a !== '--trace' && a !== '--strict');
|
|
280
|
+
if (badFlag) usageError(`unknown flag '${badFlag}' — try \`myxo --help\``); // never silently drop a flag (e.g. a typo'd --strict)
|
|
281
|
+
const files = args.filter(a => !a.startsWith('--'));
|
|
282
|
+
if (!files.length) usageError('no input file — try `myxo --help`');
|
|
283
|
+
let st;
|
|
284
|
+
try { st = fs.statSync(files[0]); }
|
|
285
|
+
catch { process.stderr.write(`myxo: no such file: ${files[0]} — try \`myxo --help\`\n`); process.exit(1); }
|
|
286
|
+
if (!st.isFile()) { process.stderr.write(`myxo: not a file: ${files[0]}\n`); process.exit(1); }
|
|
287
|
+
runFile(files[0], args.includes('--trace'), args.includes('--strict'));
|
|
288
|
+
} catch (e) {
|
|
289
|
+
process.stderr.write((e instanceof MyxoError ? e.format() : `myxo: ${e.message}`) + '\n'); // never leak a raw Node stack
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (require.main === module) main();
|
|
295
|
+
|
|
296
|
+
module.exports = { run, makeInterpreter, runTests };
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "myxo-lang",
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "Myxo — the language of the Nexus. A small, embeddable language whose runtime is the Nexus law: useful pathways reinforce, useless pathways decay.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"myxo": "myxo.js"
|
|
7
|
+
},
|
|
8
|
+
"main": "myxo.js",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --test",
|
|
11
|
+
"test:core": "node --test test/myxo.test.js test/format.test.js test/myxotest.test.js test/match.test.js test/pipe.test.js test/types.test.js test/lsp.test.js test/cli.test.js test/docs.test.js test/concurrent.test.js test/scheduler.test.js test/fibers.test.js test/examples.test.js test/plan.test.js test/mcp-framing.test.js test/mcp-server.test.js test/command-fence.test.js test/receipt.test.js test/soundness-fuzz.test.js",
|
|
12
|
+
"test:polyglot": "node --test test/polyglot.test.js",
|
|
13
|
+
"test:myxo": "node myxo.js test tests/",
|
|
14
|
+
"lsp": "node myxo.js lsp",
|
|
15
|
+
"docs": "node docs/build.js",
|
|
16
|
+
"test:full": "node --test",
|
|
17
|
+
"fmt": "node myxo.js fmt",
|
|
18
|
+
"start": "node myxo.js"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Zero (an AI agent by Arc) with Milton Rojas",
|
|
22
|
+
"homepage": "https://thefinalmilkman.github.io/myxo/",
|
|
23
|
+
"repository": { "type": "git", "url": "git+https://github.com/thefinalmilkman/myxo.git" },
|
|
24
|
+
"bugs": { "url": "https://github.com/thefinalmilkman/myxo/issues" },
|
|
25
|
+
"engines": { "node": ">=18" },
|
|
26
|
+
"keywords": ["nexus", "language", "interpreter", "myxo", "slime-mold", "embeddable", "capability-security", "zero-dependency", "concurrency"]
|
|
27
|
+
}
|