0nmcp 1.7.0 → 2.1.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/README.md +113 -48
- package/cli.js +705 -1
- package/command-runner.js +224 -0
- package/commands.js +115 -0
- package/index.js +15 -1
- package/lib/stats.json +1 -1
- package/package.json +42 -3
- package/vault/container.js +479 -0
- package/vault/crypto-container.js +278 -0
- package/vault/deed-collector.js +286 -0
- package/vault/deed-importer.js +277 -0
- package/vault/deed.js +319 -0
- package/vault/escrow.js +227 -0
- package/vault/layers.js +254 -0
- package/vault/registry.js +159 -0
- package/vault/seal.js +74 -0
- package/vault/tools-container.js +356 -0
- package/vault/tools-deed.js +257 -0
package/cli.js
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* npx 0nmcp init Initialize ~/.0n directory
|
|
12
12
|
* npx 0nmcp connect Interactive connection setup
|
|
13
13
|
* npx 0nmcp list List connected services
|
|
14
|
+
* npx 0nmcp commands List named RUNs from SWITCH file
|
|
15
|
+
* npx 0nmcp shell Interactive REPL (type /command)
|
|
16
|
+
* npx 0nmcp <alias> Run a named command from SWITCH file
|
|
14
17
|
* npx 0nmcp migrate Migrate from ~/.0nmcp to ~/.0n
|
|
15
18
|
* npx 0nmcp engine Engine commands (import, verify, platforms, export, open)
|
|
16
19
|
* npx 0nmcp app Application commands (run, build, inspect, validate, list)
|
|
@@ -24,6 +27,8 @@ import { fileURLToPath } from 'url';
|
|
|
24
27
|
import fs from 'fs';
|
|
25
28
|
import os from 'os';
|
|
26
29
|
import readline from 'readline';
|
|
30
|
+
import { loadCommands, listCommands } from './commands.js';
|
|
31
|
+
import { runCommand } from './command-runner.js';
|
|
27
32
|
|
|
28
33
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
29
34
|
|
|
@@ -88,6 +93,17 @@ ${c.bright}Engine commands:${c.reset}
|
|
|
88
93
|
${c.cyan}npx 0nmcp engine export${c.reset} Export .0n bundle (AI Brain)
|
|
89
94
|
${c.cyan}npx 0nmcp engine open <bundle>${c.reset} Open/inspect a .0n bundle
|
|
90
95
|
|
|
96
|
+
${c.bright}Vault Container commands (Patent Pending #63/990,046):${c.reset}
|
|
97
|
+
|
|
98
|
+
${c.cyan}npx 0nmcp vault create${c.reset} Create a .0nv vault container
|
|
99
|
+
${c.cyan}npx 0nmcp vault open <file>${c.reset} Open/decrypt a .0nv container
|
|
100
|
+
${c.cyan}npx 0nmcp vault inspect <file>${c.reset} Inspect container metadata (no passphrase)
|
|
101
|
+
${c.cyan}npx 0nmcp vault verify <file>${c.reset} Verify Seal of Truth + signature
|
|
102
|
+
${c.cyan}npx 0nmcp vault escrow create${c.reset} Generate escrow keypairs
|
|
103
|
+
${c.cyan}npx 0nmcp vault escrow unwrap${c.reset} Unwrap container with escrow key
|
|
104
|
+
${c.cyan}npx 0nmcp vault transfer <file>${c.reset} Register a vault transfer
|
|
105
|
+
${c.cyan}npx 0nmcp vault revoke <id>${c.reset} Revoke a transfer ID
|
|
106
|
+
|
|
91
107
|
${c.bright}Application commands:${c.reset}
|
|
92
108
|
|
|
93
109
|
${c.cyan}npx 0nmcp app run <file>${c.reset} Start application server
|
|
@@ -96,6 +112,21 @@ ${c.bright}Application commands:${c.reset}
|
|
|
96
112
|
${c.cyan}npx 0nmcp app validate <file>${c.reset} Validate application structure
|
|
97
113
|
${c.cyan}npx 0nmcp app list${c.reset} List installed applications
|
|
98
114
|
|
|
115
|
+
${c.bright}Named Runs (Hotkeys):${c.reset}
|
|
116
|
+
|
|
117
|
+
${c.cyan}npx 0nmcp commands${c.reset} List named RUNs from SWITCH file
|
|
118
|
+
${c.cyan}npx 0nmcp <alias>${c.reset} Execute a named RUN
|
|
119
|
+
${c.cyan}npx 0nmcp shell${c.reset} Interactive REPL (type /command)
|
|
120
|
+
|
|
121
|
+
Define commands in ~/.0n/0n-setup.0n:
|
|
122
|
+
|
|
123
|
+
{
|
|
124
|
+
"commands": {
|
|
125
|
+
"launch": { "type": "pipeline", "steps": [{ "action": "verify" }] },
|
|
126
|
+
"score": { "type": "workflow", "workflow": "lead-scoring" }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
99
130
|
${c.bright}Serve options:${c.reset}
|
|
100
131
|
|
|
101
132
|
${c.cyan}npx 0nmcp serve --port 3000 --host 0.0.0.0${c.reset}
|
|
@@ -226,6 +257,13 @@ ${c.bright}Links:${c.reset}
|
|
|
226
257
|
}
|
|
227
258
|
}
|
|
228
259
|
|
|
260
|
+
// Vault Container
|
|
261
|
+
if (command === 'vault') {
|
|
262
|
+
console.log(BANNER);
|
|
263
|
+
await handleVault(args.slice(1));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
229
267
|
// App
|
|
230
268
|
if (command === 'app') {
|
|
231
269
|
console.log(BANNER);
|
|
@@ -233,6 +271,13 @@ ${c.bright}Links:${c.reset}
|
|
|
233
271
|
return;
|
|
234
272
|
}
|
|
235
273
|
|
|
274
|
+
// Business Deed
|
|
275
|
+
if (command === 'deed') {
|
|
276
|
+
console.log(BANNER);
|
|
277
|
+
await handleDeed(args.slice(1));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
236
281
|
// Engine
|
|
237
282
|
if (command === 'engine') {
|
|
238
283
|
console.log(BANNER);
|
|
@@ -249,9 +294,57 @@ ${c.bright}Links:${c.reset}
|
|
|
249
294
|
return;
|
|
250
295
|
}
|
|
251
296
|
|
|
297
|
+
// Commands — list named RUNs from SWITCH file
|
|
298
|
+
if (command === 'commands' || command === 'aliases') {
|
|
299
|
+
console.log(BANNER);
|
|
300
|
+
const cmds = listCommands();
|
|
301
|
+
if (cmds.length === 0) {
|
|
302
|
+
console.log(`${c.yellow}No commands defined.${c.reset}`);
|
|
303
|
+
console.log(`\nAdd a ${c.cyan}"commands"${c.reset} section to ${c.cyan}~/.0n/0n-setup.0n${c.reset}`);
|
|
304
|
+
console.log(`\nExample:`);
|
|
305
|
+
console.log(` {`);
|
|
306
|
+
console.log(` "commands": {`);
|
|
307
|
+
console.log(` "launch": { "type": "pipeline", "steps": [{ "action": "verify" }] }`);
|
|
308
|
+
console.log(` }`);
|
|
309
|
+
console.log(` }`);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
console.log(`${c.bright}Named Runs:${c.reset}\n`);
|
|
313
|
+
for (const cmd of cmds) {
|
|
314
|
+
const typeTag = cmd.type === 'pipeline' ? `${c.blue}pipeline${c.reset}` : `${c.green}workflow${c.reset}`;
|
|
315
|
+
console.log(` ${c.cyan}0nmcp ${cmd.alias}${c.reset} ${cmd.description || cmd.name} [${typeTag}]`);
|
|
316
|
+
}
|
|
317
|
+
console.log(`\n${c.bright}Tip:${c.reset} Run ${c.cyan}0nmcp shell${c.reset} for an interactive REPL where you can type ${c.cyan}/launch${c.reset}, ${c.cyan}/hello${c.reset}, etc.`);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Shell — interactive REPL
|
|
322
|
+
if (command === 'shell' || command === 'repl') {
|
|
323
|
+
console.log(BANNER);
|
|
324
|
+
await startShell();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Dynamic command aliases from SWITCH file
|
|
329
|
+
const switchCommands = loadCommands();
|
|
330
|
+
if (switchCommands.has(command)) {
|
|
331
|
+
console.log(BANNER);
|
|
332
|
+
const def = switchCommands.get(command);
|
|
333
|
+
const cliArgs = args.slice(1);
|
|
334
|
+
await runCommand(command, def, cliArgs);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
252
338
|
// Unknown command
|
|
253
339
|
console.log(`${c.red}Unknown command: ${command}${c.reset}`);
|
|
254
|
-
|
|
340
|
+
const switchCmds = listCommands();
|
|
341
|
+
if (switchCmds.length > 0) {
|
|
342
|
+
console.log(`\n${c.bright}Available named RUNs:${c.reset}`);
|
|
343
|
+
for (const cmd of switchCmds) {
|
|
344
|
+
console.log(` ${c.cyan}0nmcp ${cmd.alias}${c.reset} ${cmd.description || cmd.name}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
console.log(`\nRun ${c.cyan}npx 0nmcp help${c.reset} for usage`);
|
|
255
348
|
process.exit(1);
|
|
256
349
|
}
|
|
257
350
|
|
|
@@ -454,6 +547,496 @@ async function interactiveConnect() {
|
|
|
454
547
|
console.log(` Saved to: ${filePath}`);
|
|
455
548
|
}
|
|
456
549
|
|
|
550
|
+
// ═══════════════════════════════════════════════════════════
|
|
551
|
+
// Vault Container Commands (Patent Pending #63/990,046)
|
|
552
|
+
// ═══════════════════════════════════════════════════════════
|
|
553
|
+
|
|
554
|
+
function promptInput(prompt) {
|
|
555
|
+
return new Promise(resolve => {
|
|
556
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
557
|
+
rl.question(prompt, answer => { rl.close(); resolve(answer); });
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async function handleVault(args) {
|
|
562
|
+
const sub = args[0];
|
|
563
|
+
|
|
564
|
+
if (!sub || sub === 'help') {
|
|
565
|
+
console.log(`
|
|
566
|
+
${c.bright}Vault Container Commands${c.reset} ${c.yellow}(Patent Pending #63/990,046)${c.reset}
|
|
567
|
+
|
|
568
|
+
${c.cyan}vault create${c.reset} Create a .0nv vault container
|
|
569
|
+
--layers <names> Comma-separated layer names (default: all)
|
|
570
|
+
--passphrase <pass> Encryption passphrase
|
|
571
|
+
--output <file> Output file path
|
|
572
|
+
|
|
573
|
+
${c.cyan}vault open <file>${c.reset} Open/decrypt a .0nv container
|
|
574
|
+
--passphrase <pass> Decryption passphrase
|
|
575
|
+
--layer <name> Only decrypt this layer
|
|
576
|
+
|
|
577
|
+
${c.cyan}vault inspect <file>${c.reset} Inspect without decrypting
|
|
578
|
+
|
|
579
|
+
${c.cyan}vault verify <file>${c.reset} Verify Seal of Truth + Ed25519 signature
|
|
580
|
+
|
|
581
|
+
${c.cyan}vault escrow create${c.reset} Generate escrow keypairs
|
|
582
|
+
--parties <n> Number of parties (1-8)
|
|
583
|
+
|
|
584
|
+
${c.cyan}vault transfer <file>${c.reset} Register a transfer
|
|
585
|
+
--to <recipient> Recipient identifier
|
|
586
|
+
|
|
587
|
+
${c.cyan}vault revoke <id>${c.reset} Revoke a transfer ID
|
|
588
|
+
|
|
589
|
+
${c.bright}7 Semantic Layers:${c.reset}
|
|
590
|
+
workflows, credentials, env_vars, mcp_configs,
|
|
591
|
+
site_profiles, ai_brain, audit_trail
|
|
592
|
+
`);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (sub === 'create') {
|
|
597
|
+
const { assembleContainer, saveContainer } = await import('./vault/container.js');
|
|
598
|
+
const { LAYER_NAMES } = await import('./vault/layers.js');
|
|
599
|
+
|
|
600
|
+
const passphrase = getFlag(args, '--passphrase') || await promptInput('Passphrase: ');
|
|
601
|
+
const output = getFlag(args, '--output');
|
|
602
|
+
const layerStr = getFlag(args, '--layers');
|
|
603
|
+
const requestedLayers = layerStr ? layerStr.split(',').map(s => s.trim()) : LAYER_NAMES;
|
|
604
|
+
|
|
605
|
+
// Build layer data from ~/.0n directory
|
|
606
|
+
const layers = {};
|
|
607
|
+
for (const name of requestedLayers) {
|
|
608
|
+
if (!LAYER_NAMES.includes(name)) {
|
|
609
|
+
console.log(`${c.red}Invalid layer: ${name}${c.reset}`);
|
|
610
|
+
process.exit(1);
|
|
611
|
+
}
|
|
612
|
+
layers[name] = { placeholder: true, created: new Date().toISOString() };
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
console.log(`\n${c.bright}Creating vault container...${c.reset}`);
|
|
616
|
+
console.log(` Layers: ${Object.keys(layers).join(', ')}`);
|
|
617
|
+
|
|
618
|
+
try {
|
|
619
|
+
const result = await assembleContainer({ layers, passphrase });
|
|
620
|
+
const homePath = path.join(os.homedir(), '.0n', 'vault');
|
|
621
|
+
if (!fs.existsSync(homePath)) fs.mkdirSync(homePath, { recursive: true });
|
|
622
|
+
const filePath = output || path.join(homePath, `${result.transferId}.0nv`);
|
|
623
|
+
saveContainer(result.buffer, filePath);
|
|
624
|
+
|
|
625
|
+
console.log(`\n${c.green}✓ Container created${c.reset}`);
|
|
626
|
+
console.log(` Transfer ID: ${c.cyan}${result.transferId}${c.reset}`);
|
|
627
|
+
console.log(` Seal of Truth: ${c.cyan}${result.sealHex}${c.reset}`);
|
|
628
|
+
console.log(` Layers: ${result.layerCount}`);
|
|
629
|
+
console.log(` Size: ${result.buffer.length} bytes`);
|
|
630
|
+
console.log(` File: ${c.yellow}${filePath}${c.reset}`);
|
|
631
|
+
} catch (err) {
|
|
632
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
633
|
+
process.exit(1);
|
|
634
|
+
}
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (sub === 'open') {
|
|
639
|
+
const file = args[1];
|
|
640
|
+
if (!file) { console.log(`${c.red}Usage: vault open <file.0nv>${c.reset}`); process.exit(1); }
|
|
641
|
+
|
|
642
|
+
const { disassembleContainer, loadContainer } = await import('./vault/container.js');
|
|
643
|
+
const passphrase = getFlag(args, '--passphrase') || await promptInput('Passphrase: ');
|
|
644
|
+
const layer = getFlag(args, '--layer');
|
|
645
|
+
|
|
646
|
+
try {
|
|
647
|
+
const buffer = loadContainer(file);
|
|
648
|
+
const result = await disassembleContainer(buffer, passphrase, layer ? [layer] : null);
|
|
649
|
+
|
|
650
|
+
console.log(`\n${c.green}✓ Container opened${c.reset}`);
|
|
651
|
+
console.log(` Transfer ID: ${c.cyan}${result.metadata.transferId}${c.reset}`);
|
|
652
|
+
console.log(` Seal valid: ${result.seal.valid ? c.green + '✓' : c.red + '✗'}${c.reset}`);
|
|
653
|
+
console.log(` Sig valid: ${result.signature.valid ? c.green + '✓' : c.red + '✗'}${c.reset}`);
|
|
654
|
+
console.log(` Layers:`);
|
|
655
|
+
for (const [name, data] of Object.entries(result.layers)) {
|
|
656
|
+
const preview = JSON.stringify(data).substring(0, 80);
|
|
657
|
+
console.log(` ${c.cyan}${name}${c.reset}: ${preview}...`);
|
|
658
|
+
}
|
|
659
|
+
} catch (err) {
|
|
660
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
661
|
+
process.exit(1);
|
|
662
|
+
}
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (sub === 'inspect') {
|
|
667
|
+
const file = args[1];
|
|
668
|
+
if (!file) { console.log(`${c.red}Usage: vault inspect <file.0nv>${c.reset}`); process.exit(1); }
|
|
669
|
+
|
|
670
|
+
const { inspectContainer, loadContainer } = await import('./vault/container.js');
|
|
671
|
+
try {
|
|
672
|
+
const buffer = loadContainer(file);
|
|
673
|
+
const info = inspectContainer(buffer);
|
|
674
|
+
console.log(`\n${c.bright}Container Inspection${c.reset}`);
|
|
675
|
+
console.log(JSON.stringify(info, null, 2));
|
|
676
|
+
} catch (err) {
|
|
677
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
678
|
+
process.exit(1);
|
|
679
|
+
}
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (sub === 'verify') {
|
|
684
|
+
const file = args[1];
|
|
685
|
+
if (!file) { console.log(`${c.red}Usage: vault verify <file.0nv>${c.reset}`); process.exit(1); }
|
|
686
|
+
|
|
687
|
+
const { inspectContainer, loadContainer } = await import('./vault/container.js');
|
|
688
|
+
try {
|
|
689
|
+
const buffer = loadContainer(file);
|
|
690
|
+
const info = inspectContainer(buffer);
|
|
691
|
+
const verified = info.seal.valid && info.signature.valid;
|
|
692
|
+
|
|
693
|
+
console.log(`\n${verified ? c.green + '✓ VERIFIED' : c.red + '✗ FAILED'}${c.reset}`);
|
|
694
|
+
console.log(` Seal of Truth: ${info.seal.valid ? c.green + 'Valid' : c.red + 'Invalid'}${c.reset} (${info.seal.algorithm})`);
|
|
695
|
+
console.log(` Signature: ${info.signature.valid ? c.green + 'Valid' : c.red + 'Invalid'}${c.reset} (${info.signature.algorithm})`);
|
|
696
|
+
console.log(` Transfer ID: ${info.metadata.transferId}`);
|
|
697
|
+
console.log(` Created: ${info.metadata.created}`);
|
|
698
|
+
console.log(` Patent: ${info.patent}`);
|
|
699
|
+
} catch (err) {
|
|
700
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
701
|
+
process.exit(1);
|
|
702
|
+
}
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (sub === 'escrow') {
|
|
707
|
+
const escrowSub = args[1];
|
|
708
|
+
if (escrowSub === 'create') {
|
|
709
|
+
const { generatePartyKeys } = await import('./vault/escrow.js');
|
|
710
|
+
const count = parseInt(getFlag(args, '--parties') || '3');
|
|
711
|
+
|
|
712
|
+
console.log(`\n${c.bright}Generating ${count} escrow keypairs...${c.reset}`);
|
|
713
|
+
const parties = [];
|
|
714
|
+
for (let i = 0; i < count; i++) {
|
|
715
|
+
const party = generatePartyKeys();
|
|
716
|
+
parties.push(party);
|
|
717
|
+
console.log(`\n ${c.cyan}Party ${i + 1}${c.reset} (${party.partyId}):`);
|
|
718
|
+
console.log(` Public: ${party.publicKey.toString('hex').substring(0, 32)}...`);
|
|
719
|
+
console.log(` Secret: ${party.secretKey.toString('hex').substring(0, 32)}...`);
|
|
720
|
+
}
|
|
721
|
+
console.log(`\n${c.green}✓ ${count} keypairs generated${c.reset}`);
|
|
722
|
+
console.log(` ${c.yellow}Save secret keys securely. Share public keys for container creation.${c.reset}`);
|
|
723
|
+
}
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (sub === 'transfer') {
|
|
728
|
+
const file = args[1];
|
|
729
|
+
if (!file) { console.log(`${c.red}Usage: vault transfer <file.0nv>${c.reset}`); process.exit(1); }
|
|
730
|
+
|
|
731
|
+
const { inspectContainer, loadContainer } = await import('./vault/container.js');
|
|
732
|
+
const { registerTransfer } = await import('./vault/registry.js');
|
|
733
|
+
const recipient = getFlag(args, '--to');
|
|
734
|
+
|
|
735
|
+
try {
|
|
736
|
+
const buffer = loadContainer(file);
|
|
737
|
+
const info = inspectContainer(buffer);
|
|
738
|
+
const result = registerTransfer(info.metadata.transferId, info.seal.hash, { recipient });
|
|
739
|
+
|
|
740
|
+
if (result.success) {
|
|
741
|
+
console.log(`\n${c.green}✓ Transfer registered${c.reset}`);
|
|
742
|
+
console.log(` Transfer ID: ${c.cyan}${info.metadata.transferId}${c.reset}`);
|
|
743
|
+
if (recipient) console.log(` Recipient: ${recipient}`);
|
|
744
|
+
} else {
|
|
745
|
+
console.log(`\n${c.red}✗ ${result.error}${c.reset}`);
|
|
746
|
+
}
|
|
747
|
+
} catch (err) {
|
|
748
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
749
|
+
process.exit(1);
|
|
750
|
+
}
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
if (sub === 'revoke') {
|
|
755
|
+
const transferId = args[1];
|
|
756
|
+
if (!transferId) { console.log(`${c.red}Usage: vault revoke <transferId>${c.reset}`); process.exit(1); }
|
|
757
|
+
|
|
758
|
+
const { revokeTransfer } = await import('./vault/registry.js');
|
|
759
|
+
const result = revokeTransfer(transferId);
|
|
760
|
+
|
|
761
|
+
if (result.success) {
|
|
762
|
+
console.log(`\n${c.green}✓ Transfer revoked: ${transferId}${c.reset}`);
|
|
763
|
+
} else {
|
|
764
|
+
console.log(`\n${c.red}✗ ${result.error}${c.reset}`);
|
|
765
|
+
}
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
console.log(`${c.red}Unknown vault command: ${sub}${c.reset}`);
|
|
770
|
+
console.log(`Run ${c.cyan}0nmcp vault help${c.reset} for usage.`);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
async function handleDeed(args) {
|
|
774
|
+
const sub = args[0];
|
|
775
|
+
|
|
776
|
+
if (!sub || sub === 'help') {
|
|
777
|
+
console.log(`
|
|
778
|
+
${c.bright}Business Deed — Digital Asset Transfer System${c.reset} ${c.yellow}(Patent Pending #63/990,046)${c.reset}
|
|
779
|
+
|
|
780
|
+
${c.cyan}deed create${c.reset} Create a Business Deed (.0nv)
|
|
781
|
+
--name <name> Business name (required)
|
|
782
|
+
--from <file> Import credentials from .env, .json, or .csv
|
|
783
|
+
--passphrase <pass> Encryption passphrase
|
|
784
|
+
--output <file> Output file path
|
|
785
|
+
|
|
786
|
+
${c.cyan}deed open <file>${c.reset} Open/decrypt a Business Deed
|
|
787
|
+
--passphrase <pass> Decryption passphrase
|
|
788
|
+
--layer <name> Only decrypt this layer
|
|
789
|
+
|
|
790
|
+
${c.cyan}deed inspect <file>${c.reset} Inspect deed metadata (no passphrase needed)
|
|
791
|
+
|
|
792
|
+
${c.cyan}deed verify <file>${c.reset} Verify Seal of Truth + Ed25519 signature
|
|
793
|
+
|
|
794
|
+
${c.cyan}deed accept <file>${c.reset} Accept a deed transfer (buyer)
|
|
795
|
+
--passphrase <pass> Current deed passphrase
|
|
796
|
+
--buyer-name <name> Buyer's name
|
|
797
|
+
--buyer-email <email> Buyer's email
|
|
798
|
+
--new-passphrase <pass> New passphrase for re-encrypted deed
|
|
799
|
+
|
|
800
|
+
${c.cyan}deed import <file>${c.reset} Import deed to live system config
|
|
801
|
+
--passphrase <pass> Decryption passphrase
|
|
802
|
+
--target <dir> Target directory (default: ~/.0n/)
|
|
803
|
+
|
|
804
|
+
${c.cyan}deed export${c.reset} Collect + package in one step
|
|
805
|
+
--from <file> Source file (.env, .json, .csv)
|
|
806
|
+
--name <name> Business name
|
|
807
|
+
--passphrase <pass> Encryption passphrase
|
|
808
|
+
--output <file> Output file path
|
|
809
|
+
|
|
810
|
+
${c.bright}Lifecycle:${c.reset} CREATE > PACKAGE > ESCROW > ACCEPT > IMPORT > FLIP
|
|
811
|
+
`);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
if (sub === 'create') {
|
|
816
|
+
const { BusinessDeed } = await import('./vault/deed.js');
|
|
817
|
+
const deed = new BusinessDeed();
|
|
818
|
+
|
|
819
|
+
const name = getFlag(args, '--name') || await promptInput('Business name: ');
|
|
820
|
+
const passphrase = getFlag(args, '--passphrase') || await promptInput('Passphrase: ');
|
|
821
|
+
const output = getFlag(args, '--output');
|
|
822
|
+
const from = getFlag(args, '--from');
|
|
823
|
+
|
|
824
|
+
let createOpts = { name, passphrase, output };
|
|
825
|
+
|
|
826
|
+
if (from) {
|
|
827
|
+
// Collect from file
|
|
828
|
+
const { collectCredentials } = await import('./vault/deed-collector.js');
|
|
829
|
+
console.log(`\n${c.bright}Collecting credentials from ${from}...${c.reset}`);
|
|
830
|
+
const collected = await collectCredentials({ envFile: from, jsonFile: from, csvFile: from });
|
|
831
|
+
createOpts.credentials = collected.credentials;
|
|
832
|
+
createOpts.envVars = collected.envVars;
|
|
833
|
+
console.log(` Found ${collected.credentialCount} credentials across ${collected.services.length} services`);
|
|
834
|
+
if (collected.services.length > 0) {
|
|
835
|
+
console.log(` Services: ${collected.services.join(', ')}`);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
console.log(`\n${c.bright}Creating Business Deed...${c.reset}`);
|
|
840
|
+
try {
|
|
841
|
+
const result = await deed.create(createOpts);
|
|
842
|
+
console.log(`\n${c.green}✓ Business Deed created${c.reset}`);
|
|
843
|
+
console.log(` Business: ${c.cyan}${name}${c.reset}`);
|
|
844
|
+
console.log(` Transfer ID: ${c.cyan}${result.transferId}${c.reset}`);
|
|
845
|
+
console.log(` Seal of Truth: ${c.cyan}${result.sealHex}${c.reset}`);
|
|
846
|
+
console.log(` Services: ${result.services.join(', ') || 'none'}`);
|
|
847
|
+
console.log(` Credentials: ${result.credentialCount}`);
|
|
848
|
+
console.log(` Layers: ${result.layerCount}`);
|
|
849
|
+
console.log(` Size: ${result.containerSize} bytes`);
|
|
850
|
+
console.log(` File: ${c.yellow}${result.file}${c.reset}`);
|
|
851
|
+
} catch (err) {
|
|
852
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
853
|
+
process.exit(1);
|
|
854
|
+
}
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (sub === 'open') {
|
|
859
|
+
const file = args[1];
|
|
860
|
+
if (!file) { console.log(`${c.red}Usage: deed open <file.0nv>${c.reset}`); process.exit(1); }
|
|
861
|
+
|
|
862
|
+
const { BusinessDeed } = await import('./vault/deed.js');
|
|
863
|
+
const deed = new BusinessDeed();
|
|
864
|
+
const passphrase = getFlag(args, '--passphrase') || await promptInput('Passphrase: ');
|
|
865
|
+
const layer = getFlag(args, '--layer');
|
|
866
|
+
|
|
867
|
+
try {
|
|
868
|
+
const result = await deed.open(file, passphrase, layer ? [layer] : null);
|
|
869
|
+
|
|
870
|
+
console.log(`\n${c.green}✓ Business Deed opened${c.reset}`);
|
|
871
|
+
console.log(` Transfer ID: ${c.cyan}${result.metadata.transferId}${c.reset}`);
|
|
872
|
+
console.log(` Seal valid: ${result.seal.valid ? c.green + '✓' : c.red + '✗'}${c.reset}`);
|
|
873
|
+
console.log(` Sig valid: ${result.signature.valid ? c.green + '✓' : c.red + '✗'}${c.reset}`);
|
|
874
|
+
|
|
875
|
+
if (result.deed) {
|
|
876
|
+
console.log(` Business: ${c.cyan}${result.deed.business?.name || 'Unknown'}${c.reset}`);
|
|
877
|
+
console.log(` Services: ${(result.deed.services || []).join(', ')}`);
|
|
878
|
+
console.log(` Transfers: ${(result.deed.transfer_history || []).length}`);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
console.log(`\n${c.bright}Layers:${c.reset}`);
|
|
882
|
+
for (const [name, data] of Object.entries(result.layers)) {
|
|
883
|
+
if (name === 'audit_trail') continue;
|
|
884
|
+
const preview = JSON.stringify(data).substring(0, 100);
|
|
885
|
+
console.log(` ${c.cyan}${name}${c.reset}: ${preview}${preview.length >= 100 ? '...' : ''}`);
|
|
886
|
+
}
|
|
887
|
+
} catch (err) {
|
|
888
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
889
|
+
process.exit(1);
|
|
890
|
+
}
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (sub === 'inspect') {
|
|
895
|
+
const file = args[1];
|
|
896
|
+
if (!file) { console.log(`${c.red}Usage: deed inspect <file.0nv>${c.reset}`); process.exit(1); }
|
|
897
|
+
|
|
898
|
+
const { BusinessDeed } = await import('./vault/deed.js');
|
|
899
|
+
const deed = new BusinessDeed();
|
|
900
|
+
|
|
901
|
+
try {
|
|
902
|
+
const info = deed.inspect(file);
|
|
903
|
+
console.log(`\n${c.bright}Business Deed Inspection${c.reset}`);
|
|
904
|
+
console.log(JSON.stringify(info, null, 2));
|
|
905
|
+
} catch (err) {
|
|
906
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
907
|
+
process.exit(1);
|
|
908
|
+
}
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
if (sub === 'verify') {
|
|
913
|
+
const file = args[1];
|
|
914
|
+
if (!file) { console.log(`${c.red}Usage: deed verify <file.0nv>${c.reset}`); process.exit(1); }
|
|
915
|
+
|
|
916
|
+
const { BusinessDeed } = await import('./vault/deed.js');
|
|
917
|
+
const deed = new BusinessDeed();
|
|
918
|
+
|
|
919
|
+
try {
|
|
920
|
+
const v = deed.verify(file);
|
|
921
|
+
console.log(`\n${v.verified ? c.green + '✓ VERIFIED' : c.red + '✗ FAILED'}${c.reset}`);
|
|
922
|
+
console.log(` Seal of Truth: ${v.seal.valid ? c.green + 'Valid' : c.red + 'Invalid'}${c.reset} (${v.seal.algorithm})`);
|
|
923
|
+
console.log(` Signature: ${v.signature.valid ? c.green + 'Valid' : c.red + 'Invalid'}${c.reset} (${v.signature.algorithm})`);
|
|
924
|
+
console.log(` Transfer ID: ${v.transferId}`);
|
|
925
|
+
console.log(` Created: ${v.created}`);
|
|
926
|
+
console.log(` Patent: ${v.patent}`);
|
|
927
|
+
} catch (err) {
|
|
928
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
929
|
+
process.exit(1);
|
|
930
|
+
}
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
if (sub === 'accept') {
|
|
935
|
+
const file = args[1];
|
|
936
|
+
if (!file) { console.log(`${c.red}Usage: deed accept <file.0nv>${c.reset}`); process.exit(1); }
|
|
937
|
+
|
|
938
|
+
const { BusinessDeed } = await import('./vault/deed.js');
|
|
939
|
+
const deed = new BusinessDeed();
|
|
940
|
+
|
|
941
|
+
const passphrase = getFlag(args, '--passphrase') || await promptInput('Current passphrase: ');
|
|
942
|
+
const buyerName = getFlag(args, '--buyer-name') || await promptInput('Buyer name: ');
|
|
943
|
+
const buyerEmail = getFlag(args, '--buyer-email') || await promptInput('Buyer email: ');
|
|
944
|
+
const newPassphrase = getFlag(args, '--new-passphrase');
|
|
945
|
+
|
|
946
|
+
try {
|
|
947
|
+
const result = await deed.accept(
|
|
948
|
+
file, passphrase,
|
|
949
|
+
{ name: buyerName, email: buyerEmail },
|
|
950
|
+
newPassphrase,
|
|
951
|
+
);
|
|
952
|
+
|
|
953
|
+
console.log(`\n${c.green}✓ Business Deed accepted${c.reset}`);
|
|
954
|
+
console.log(` New Transfer ID: ${c.cyan}${result.transferId}${c.reset}`);
|
|
955
|
+
console.log(` New Seal: ${c.cyan}${result.sealHex}${c.reset}`);
|
|
956
|
+
console.log(` Buyer: ${buyerName} <${buyerEmail}>`);
|
|
957
|
+
console.log(` Prev Transfer: ${result.previousTransferId}`);
|
|
958
|
+
console.log(` File: ${c.yellow}${result.file}${c.reset}`);
|
|
959
|
+
} catch (err) {
|
|
960
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
961
|
+
process.exit(1);
|
|
962
|
+
}
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
if (sub === 'import') {
|
|
967
|
+
const file = args[1];
|
|
968
|
+
if (!file) { console.log(`${c.red}Usage: deed import <file.0nv>${c.reset}`); process.exit(1); }
|
|
969
|
+
|
|
970
|
+
const { BusinessDeed } = await import('./vault/deed.js');
|
|
971
|
+
const deed = new BusinessDeed();
|
|
972
|
+
|
|
973
|
+
const passphrase = getFlag(args, '--passphrase') || await promptInput('Passphrase: ');
|
|
974
|
+
const target = getFlag(args, '--target');
|
|
975
|
+
|
|
976
|
+
try {
|
|
977
|
+
const report = await deed.importDeed(file, passphrase, target);
|
|
978
|
+
|
|
979
|
+
console.log(`\n${report.success ? c.green + '✓ Import complete' : c.red + '✗ Import had errors'}${c.reset}`);
|
|
980
|
+
if (report.connections.written.length > 0) {
|
|
981
|
+
console.log(` Connections: ${report.connections.written.join(', ')}`);
|
|
982
|
+
}
|
|
983
|
+
if (report.envFile) {
|
|
984
|
+
console.log(` Env file: ${report.envFile.file} (${report.envFile.written} vars)`);
|
|
985
|
+
}
|
|
986
|
+
if (report.workflows.count > 0) {
|
|
987
|
+
console.log(` Workflows: ${report.workflows.count}`);
|
|
988
|
+
}
|
|
989
|
+
if (report.brain.written.length > 0) {
|
|
990
|
+
console.log(` AI Brain: ${report.brain.written.join(', ')}`);
|
|
991
|
+
}
|
|
992
|
+
if (report.deed) {
|
|
993
|
+
console.log(` Business: ${c.cyan}${report.deed.business?.name || 'Unknown'}${c.reset}`);
|
|
994
|
+
}
|
|
995
|
+
if (report.errors.length > 0) {
|
|
996
|
+
console.log(` ${c.red}Errors: ${report.errors.join(', ')}${c.reset}`);
|
|
997
|
+
}
|
|
998
|
+
} catch (err) {
|
|
999
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
1000
|
+
process.exit(1);
|
|
1001
|
+
}
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
if (sub === 'export') {
|
|
1006
|
+
const { BusinessDeed } = await import('./vault/deed.js');
|
|
1007
|
+
const deed = new BusinessDeed();
|
|
1008
|
+
|
|
1009
|
+
const from = getFlag(args, '--from');
|
|
1010
|
+
if (!from) { console.log(`${c.red}Usage: deed export --from <file> --name <name> --passphrase <pass>${c.reset}`); process.exit(1); }
|
|
1011
|
+
|
|
1012
|
+
const name = getFlag(args, '--name') || await promptInput('Business name: ');
|
|
1013
|
+
const passphrase = getFlag(args, '--passphrase') || await promptInput('Passphrase: ');
|
|
1014
|
+
const output = getFlag(args, '--output');
|
|
1015
|
+
|
|
1016
|
+
console.log(`\n${c.bright}Exporting Business Deed from ${from}...${c.reset}`);
|
|
1017
|
+
try {
|
|
1018
|
+
const result = await deed.export(
|
|
1019
|
+
{ envFile: from, jsonFile: from, csvFile: from },
|
|
1020
|
+
{ name, passphrase, output }
|
|
1021
|
+
);
|
|
1022
|
+
|
|
1023
|
+
console.log(`\n${c.green}✓ Business Deed exported${c.reset}`);
|
|
1024
|
+
console.log(` Business: ${c.cyan}${name}${c.reset}`);
|
|
1025
|
+
console.log(` Transfer ID: ${c.cyan}${result.transferId}${c.reset}`);
|
|
1026
|
+
console.log(` Services: ${result.services.join(', ') || 'none'}`);
|
|
1027
|
+
console.log(` Credentials: ${result.credentialCount}`);
|
|
1028
|
+
console.log(` File: ${c.yellow}${result.file}${c.reset}`);
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
1031
|
+
process.exit(1);
|
|
1032
|
+
}
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
console.log(`${c.red}Unknown deed command: ${sub}${c.reset}`);
|
|
1037
|
+
console.log(`Run ${c.cyan}0nmcp deed help${c.reset} for usage.`);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
457
1040
|
async function handleEngine(args) {
|
|
458
1041
|
const sub = args[0];
|
|
459
1042
|
|
|
@@ -913,4 +1496,125 @@ async function handleApp(args) {
|
|
|
913
1496
|
process.exit(1);
|
|
914
1497
|
}
|
|
915
1498
|
|
|
1499
|
+
async function startShell() {
|
|
1500
|
+
const cmds = loadCommands();
|
|
1501
|
+
const aliases = [...cmds.keys()];
|
|
1502
|
+
const builtins = ['help', 'commands', 'list', 'exit', 'quit'];
|
|
1503
|
+
const allCompletions = [...builtins, ...aliases];
|
|
1504
|
+
|
|
1505
|
+
console.log(`${c.bright}0nMCP Interactive Shell${c.reset}`);
|
|
1506
|
+
console.log(`Type ${c.cyan}/command${c.reset} to run a named RUN, ${c.cyan}/commands${c.reset} to list, ${c.cyan}/exit${c.reset} to quit.\n`);
|
|
1507
|
+
|
|
1508
|
+
if (aliases.length > 0) {
|
|
1509
|
+
console.log(`${c.bright}Available commands:${c.reset} ${aliases.map(a => c.cyan + '/' + a + c.reset).join(', ')}\n`);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
const rl = readline.createInterface({
|
|
1513
|
+
input: process.stdin,
|
|
1514
|
+
output: process.stdout,
|
|
1515
|
+
prompt: `${c.green}0n>${c.reset} `,
|
|
1516
|
+
completer: (line) => {
|
|
1517
|
+
const input = line.startsWith('/') ? line.slice(1) : line;
|
|
1518
|
+
const hits = allCompletions.filter(c2 => c2.startsWith(input));
|
|
1519
|
+
return [hits.map(h => '/' + h), line];
|
|
1520
|
+
},
|
|
1521
|
+
});
|
|
1522
|
+
|
|
1523
|
+
rl.prompt();
|
|
1524
|
+
|
|
1525
|
+
rl.on('line', async (line) => {
|
|
1526
|
+
const input = line.trim();
|
|
1527
|
+
if (!input) {
|
|
1528
|
+
rl.prompt();
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
// Strip leading / if present
|
|
1533
|
+
const raw = input.startsWith('/') ? input.slice(1) : input;
|
|
1534
|
+
const parts = raw.split(/\s+/);
|
|
1535
|
+
const cmd = parts[0];
|
|
1536
|
+
const cmdArgs = parts.slice(1);
|
|
1537
|
+
|
|
1538
|
+
if (cmd === 'exit' || cmd === 'quit') {
|
|
1539
|
+
console.log(`\n${c.cyan}Goodbye!${c.reset}`);
|
|
1540
|
+
rl.close();
|
|
1541
|
+
process.exit(0);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
if (cmd === 'help') {
|
|
1545
|
+
console.log(`\n${c.bright}Shell Commands:${c.reset}`);
|
|
1546
|
+
console.log(` ${c.cyan}/commands${c.reset} List all named RUNs`);
|
|
1547
|
+
console.log(` ${c.cyan}/list${c.reset} Show connected services`);
|
|
1548
|
+
console.log(` ${c.cyan}/exit${c.reset} Exit the shell`);
|
|
1549
|
+
if (aliases.length > 0) {
|
|
1550
|
+
console.log(`\n${c.bright}Named RUNs:${c.reset}`);
|
|
1551
|
+
for (const a of aliases) {
|
|
1552
|
+
const def = cmds.get(a);
|
|
1553
|
+
console.log(` ${c.cyan}/${a}${c.reset} ${def.description || def.name || ''}`);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
console.log('');
|
|
1557
|
+
rl.prompt();
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
if (cmd === 'commands') {
|
|
1562
|
+
if (aliases.length === 0) {
|
|
1563
|
+
console.log(`\n${c.yellow}No commands defined in SWITCH file.${c.reset}\n`);
|
|
1564
|
+
} else {
|
|
1565
|
+
console.log(`\n${c.bright}Named RUNs:${c.reset}`);
|
|
1566
|
+
for (const a of aliases) {
|
|
1567
|
+
const def = cmds.get(a);
|
|
1568
|
+
console.log(` ${c.cyan}/${a}${c.reset} ${def.description || def.name || ''}`);
|
|
1569
|
+
}
|
|
1570
|
+
console.log('');
|
|
1571
|
+
}
|
|
1572
|
+
rl.prompt();
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
if (cmd === 'list') {
|
|
1577
|
+
const connectionsDir = path.join(DOT_ON_DIR, 'connections');
|
|
1578
|
+
if (fs.existsSync(connectionsDir)) {
|
|
1579
|
+
const files = fs.readdirSync(connectionsDir).filter(f => f.endsWith('.0n'));
|
|
1580
|
+
console.log(`\n${c.bright}Connected Services:${c.reset}`);
|
|
1581
|
+
for (const file of files) {
|
|
1582
|
+
try {
|
|
1583
|
+
const data = JSON.parse(fs.readFileSync(path.join(connectionsDir, file), 'utf8'));
|
|
1584
|
+
console.log(` ${c.green}●${c.reset} ${data.$0n?.name || data.service}`);
|
|
1585
|
+
} catch {
|
|
1586
|
+
console.log(` ${c.red}●${c.reset} ${file} (error)`);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
console.log('');
|
|
1590
|
+
} else {
|
|
1591
|
+
console.log(`\n${c.yellow}No connections found.${c.reset}\n`);
|
|
1592
|
+
}
|
|
1593
|
+
rl.prompt();
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// Try to run as a named command
|
|
1598
|
+
if (cmds.has(cmd)) {
|
|
1599
|
+
console.log('');
|
|
1600
|
+
try {
|
|
1601
|
+
await runCommand(cmd, cmds.get(cmd), cmdArgs);
|
|
1602
|
+
} catch (err) {
|
|
1603
|
+
console.log(`${c.red}Error: ${err.message}${c.reset}`);
|
|
1604
|
+
}
|
|
1605
|
+
console.log('');
|
|
1606
|
+
rl.prompt();
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
console.log(`${c.yellow}Unknown command: ${cmd}${c.reset}`);
|
|
1611
|
+
console.log(`Type ${c.cyan}/help${c.reset} for available commands.`);
|
|
1612
|
+
rl.prompt();
|
|
1613
|
+
});
|
|
1614
|
+
|
|
1615
|
+
rl.on('close', () => {
|
|
1616
|
+
process.exit(0);
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
|
|
916
1620
|
main().catch(console.error);
|