0nmcp 1.7.0 → 2.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.
package/README.md CHANGED
@@ -20,19 +20,19 @@
20
20
  [![MCP](https://img.shields.io/badge/MCP-Compatible-blueviolet?style=flat-square)](https://modelcontextprotocol.io)
21
21
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
22
22
  [![Services](https://img.shields.io/badge/services-26+-blue?style=flat-square)](#-supported-services)
23
- [![Tools](https://img.shields.io/badge/tools-535-orange?style=flat-square)](#-all-tools)
23
+ [![Tools](https://img.shields.io/badge/tools-550-orange?style=flat-square)](#-all-tools)
24
24
  [![Community](https://img.shields.io/badge/community-1000%2B_devs-ff6600?style=flat-square)](#-community)
25
25
  [![GitHub Discussions](https://img.shields.io/github/discussions/0nork/0nMCP?style=flat-square&label=discussions)](https://github.com/0nork/0nMCP/discussions)
26
26
 
27
- **535 tools. 26 services. Zero configuration. One natural language interface.**
27
+ **550 tools. 26 services. Zero configuration. One natural language interface.**
28
28
 
29
- [Quick Start](#-installation) · [Services](#-supported-services) · [535 Tools](#-all-tools) · [.0n Standard](#-the-0n-standard) · [Unlocks](#-unlocks) · [Community](#-community) · [Contributing](#-contributing)
29
+ [Quick Start](#-installation) · [Services](#-supported-services) · [550 Tools](#-all-tools) · [.0n Standard](#-the-0n-standard) · [Unlocks](#-unlocks) · [Community](#-community) · [Contributing](#-contributing)
30
30
 
31
31
  </div>
32
32
 
33
33
  ---
34
34
 
35
- > **v1.3.0** — 535 tools across 26 services in 13 categories. Gmail, Google Sheets, Google Drive, Jira, Zendesk, Mailchimp, Zoom, Microsoft 365, and MongoDB now live. Full CRM coverage (245 tools), rate limiting, webhooks, TypeScript definitions, and a growing open source community. [See what's new](#-whats-new-in-v13).
35
+ > **v1.7.0** — 550 tools across 26 services in 13 categories. Now with the **.0n Conversion Engine** (portable AI Brain bundles), **Vault** (machine-bound encrypted credential storage), **Application Engine** (build + distribute .0n apps), **Workflow Runtime** + **HTTP Server**, and a **CLI with named runs**. [See what's new](#-whats-new-in-v17).
36
36
 
37
37
  ---
38
38
 
@@ -157,7 +157,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (Mac) o
157
157
  | **MongoDB** | Database | Find, insert, update, delete, aggregate documents via Atlas Data API |
158
158
  | **CRM** | CRM | **245 tools** — contacts, conversations, calendars, invoices, payments, products, pipelines, social media, custom objects, and more |
159
159
 
160
- **26 services. 535 tools. 13 categories. One interface.**
160
+ **26 services. 550 tools. 13 categories. One interface.**
161
161
 
162
162
  > **More coming:** QuickBooks, Asana, Intercom, AWS S3, Vercel, Cloudflare, Firebase, Figma...
163
163
 
@@ -267,24 +267,25 @@ The deepest CRM integration available in any MCP server. 245 tools across 12 mod
267
267
  | **Users** | 24 | Users, forms, surveys, funnels, media, companies, businesses |
268
268
  | **Objects** | 34 | Custom objects, associations, email, workflows, snapshots, links, campaigns, courses, SaaS |
269
269
 
270
- **535 total tools.** Universal orchestration (290 catalog tools across 26 services) + the most comprehensive CRM integration in the MCP ecosystem (245 dedicated tools).
270
+ **550 total tools.** Universal orchestration (290 catalog tools across 26 services) + the most comprehensive CRM integration in the MCP ecosystem (245 dedicated tools).
271
271
 
272
272
  > Every CRM tool is data-driven — defined as configuration, not code. Adding new endpoints takes minutes, not hours. See [CONTRIBUTING.md](CONTRIBUTING.md).
273
273
 
274
274
  ---
275
275
 
276
- ## What's New in v1.3
277
-
278
- - **26 services, 535 tools** up from 17 services and 252 tools
279
- - **9 new integrations** — Gmail, Google Sheets, Google Drive, Jira, Zendesk, Mailchimp, Zoom, Microsoft 365, MongoDB
280
- - **13 categories** — added Storage, Support, and Marketing categories
281
- - **Three-Level Execution Hierarchy** (Patent Pending) Pipeline Assembly Line Radial Burst
276
+ ## What's New in v1.7
277
+
278
+ - **550 tools across 26 services** in 13 categories 708 total capabilities
279
+ - **.0n Conversion Engine** — import credentials from .env/CSV/JSON, auto-map to 26 services, verify API keys, generate configs for 7 AI platforms (Claude Desktop, Cursor, Windsurf, Gemini, Continue, Cline, OpenAI)
280
+ - **Vault** — machine-bound encrypted credential storage (AES-256-GCM + PBKDF2-SHA512 + hardware fingerprint)
281
+ - **Application Engine** build, distribute, inspect, and validate portable .0n application bundles
282
+ - **Workflow Runtime** — load and execute `.0n` workflow files with template engine, conditions, and step chaining
283
+ - **HTTP Server** — Express-based REST API, MCP over HTTP, and webhook receivers
284
+ - **Named Runs / Hotkeys** — define command aliases in your SWITCH file, run them as `0nmcp launch`, `0nmcp hello`
285
+ - **Interactive Shell** — `0nmcp shell` starts a REPL for `/command` execution
286
+ - **Portable encryption** — passphrase-only AES-256-GCM (no machine fingerprint, works anywhere)
282
287
  - **245 CRM tools** — full API coverage across 12 modules
283
- - **Data-driven tool factory** every tool is a config object, new endpoints in minutes
284
- - **Rate limiting** — built-in per-service rate limits with automatic retry
285
- - **Webhook support** — receive and process external events
286
- - **TypeScript definitions** — full type coverage for all exports
287
- - **CLI improvements** — interactive setup, legacy migration, colored output
288
+ - **Three-Level Execution** (Patent Pending) Pipeline Assembly Line Radial Burst
288
289
 
289
290
  ---
290
291
 
@@ -323,7 +324,7 @@ The orchestrator uses keyword matching to route tasks to the right service. Less
323
324
  | **Flexibility** | Say what you want | Triggers/actions only | Unlimited but complex |
324
325
  | **Maintenance** | Zero | Update broken zaps | Fix API changes |
325
326
  | **Open source** | Yes (MIT) | No | Depends |
326
- | **Tools available** | 535 | Varies | Whatever you build |
327
+ | **Tools available** | 550 | Varies | Whatever you build |
327
328
 
328
329
  ---
329
330
 
@@ -505,7 +506,7 @@ The tool factory handles registration, validation, API calls, error handling —
505
506
 
506
507
  ### Phase 0 — Foundation (Current)
507
508
 
508
- - [x] **26 services, 535 tools, 13 categories**
509
+ - [x] **26 services, 550 tools, 13 categories**
509
510
  - [x] Core orchestration engine with AI planning
510
511
  - [x] **245 CRM tools** — full API coverage across 12 modules
511
512
  - [x] Gmail, Google Sheets, Google Drive, Jira, Zendesk, Mailchimp, Zoom, Microsoft 365, MongoDB
@@ -588,7 +589,7 @@ We ship weekly. The codebase is active. The community is real. If you're buildin
588
589
 
589
590
  | Metric | |
590
591
  |--------|---|
591
- | **Tools shipped** | 535 |
592
+ | **Tools shipped** | 550 |
592
593
  | **Services integrated** | 26 |
593
594
  | **Categories** | 13 |
594
595
  | **CRM endpoints covered** | 245 / 245 (100%) |
@@ -639,7 +640,7 @@ node index.js
639
640
 
640
641
  | Project | Description |
641
642
  |---------|-------------|
642
- | **[0nMCP](https://github.com/0nork/0nMCP)** | Universal AI API Orchestrator — 535 tools, 26 services, natural language interface |
643
+ | **[0nMCP](https://github.com/0nork/0nMCP)** | Universal AI API Orchestrator — 550 tools, 26 services, natural language interface |
643
644
  | **[0n-spec](https://github.com/0nork/0n-spec)** | The .0n Standard — universal configuration format for AI orchestration |
644
645
  | **[0nork](https://github.com/0nork/0nork)** | The parent org — AI orchestration infrastructure |
645
646
 
@@ -654,7 +655,7 @@ node index.js
654
655
 
655
656
  <div align="center">
656
657
 
657
- **[Sponsor on GitHub](https://github.com/sponsors/0nork)** · **[Star the repo](https://github.com/0nork/0nMCP)** · **[Tell a friend](https://twitter.com/intent/tweet?text=0nMCP%20-%20535%20tools,%2026%20services,%20zero%20config.%20The%20universal%20AI%20API%20orchestrator.%20Free%20and%20open%20source.&url=https://github.com/0nork/0nMCP)**
658
+ **[Sponsor on GitHub](https://github.com/sponsors/0nork)** · **[Star the repo](https://github.com/0nork/0nMCP)** · **[Tell a friend](https://twitter.com/intent/tweet?text=0nMCP%20-%20550%20tools,%2026%20services,%20zero%20config.%20The%20universal%20AI%20API%20orchestrator.%20Free%20and%20open%20source.&url=https://github.com/0nork/0nMCP)**
658
659
 
659
660
  </div>
660
661
 
@@ -664,7 +665,7 @@ node index.js
664
665
 
665
666
  ### Stop building workflows. Start describing outcomes.
666
667
 
667
- **535 tools. 26 services. Zero config. MIT licensed. Community driven.**
668
+ **550 tools. 26 services. Zero config. MIT licensed. Community driven.**
668
669
 
669
670
  **[Get Started](https://github.com/0nork/0nMCP)** · **[Join the Community](https://0nmcp.com/community)** · **[Unlock Schedule](https://0nmcp.com/sponsor)** · **[Read the Docs](https://github.com/0nork/0n-spec)**
670
671
 
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);
@@ -249,9 +287,57 @@ ${c.bright}Links:${c.reset}
249
287
  return;
250
288
  }
251
289
 
290
+ // Commands — list named RUNs from SWITCH file
291
+ if (command === 'commands' || command === 'aliases') {
292
+ console.log(BANNER);
293
+ const cmds = listCommands();
294
+ if (cmds.length === 0) {
295
+ console.log(`${c.yellow}No commands defined.${c.reset}`);
296
+ console.log(`\nAdd a ${c.cyan}"commands"${c.reset} section to ${c.cyan}~/.0n/0n-setup.0n${c.reset}`);
297
+ console.log(`\nExample:`);
298
+ console.log(` {`);
299
+ console.log(` "commands": {`);
300
+ console.log(` "launch": { "type": "pipeline", "steps": [{ "action": "verify" }] }`);
301
+ console.log(` }`);
302
+ console.log(` }`);
303
+ return;
304
+ }
305
+ console.log(`${c.bright}Named Runs:${c.reset}\n`);
306
+ for (const cmd of cmds) {
307
+ const typeTag = cmd.type === 'pipeline' ? `${c.blue}pipeline${c.reset}` : `${c.green}workflow${c.reset}`;
308
+ console.log(` ${c.cyan}0nmcp ${cmd.alias}${c.reset} ${cmd.description || cmd.name} [${typeTag}]`);
309
+ }
310
+ 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.`);
311
+ return;
312
+ }
313
+
314
+ // Shell — interactive REPL
315
+ if (command === 'shell' || command === 'repl') {
316
+ console.log(BANNER);
317
+ await startShell();
318
+ return;
319
+ }
320
+
321
+ // Dynamic command aliases from SWITCH file
322
+ const switchCommands = loadCommands();
323
+ if (switchCommands.has(command)) {
324
+ console.log(BANNER);
325
+ const def = switchCommands.get(command);
326
+ const cliArgs = args.slice(1);
327
+ await runCommand(command, def, cliArgs);
328
+ return;
329
+ }
330
+
252
331
  // Unknown command
253
332
  console.log(`${c.red}Unknown command: ${command}${c.reset}`);
254
- console.log(`Run ${c.cyan}npx 0nmcp help${c.reset} for usage`);
333
+ const switchCmds = listCommands();
334
+ if (switchCmds.length > 0) {
335
+ console.log(`\n${c.bright}Available named RUNs:${c.reset}`);
336
+ for (const cmd of switchCmds) {
337
+ console.log(` ${c.cyan}0nmcp ${cmd.alias}${c.reset} ${cmd.description || cmd.name}`);
338
+ }
339
+ }
340
+ console.log(`\nRun ${c.cyan}npx 0nmcp help${c.reset} for usage`);
255
341
  process.exit(1);
256
342
  }
257
343
 
@@ -454,6 +540,229 @@ async function interactiveConnect() {
454
540
  console.log(` Saved to: ${filePath}`);
455
541
  }
456
542
 
543
+ // ═══════════════════════════════════════════════════════════
544
+ // Vault Container Commands (Patent Pending #63/990,046)
545
+ // ═══════════════════════════════════════════════════════════
546
+
547
+ function promptInput(prompt) {
548
+ return new Promise(resolve => {
549
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
550
+ rl.question(prompt, answer => { rl.close(); resolve(answer); });
551
+ });
552
+ }
553
+
554
+ async function handleVault(args) {
555
+ const sub = args[0];
556
+
557
+ if (!sub || sub === 'help') {
558
+ console.log(`
559
+ ${c.bright}Vault Container Commands${c.reset} ${c.yellow}(Patent Pending #63/990,046)${c.reset}
560
+
561
+ ${c.cyan}vault create${c.reset} Create a .0nv vault container
562
+ --layers <names> Comma-separated layer names (default: all)
563
+ --passphrase <pass> Encryption passphrase
564
+ --output <file> Output file path
565
+
566
+ ${c.cyan}vault open <file>${c.reset} Open/decrypt a .0nv container
567
+ --passphrase <pass> Decryption passphrase
568
+ --layer <name> Only decrypt this layer
569
+
570
+ ${c.cyan}vault inspect <file>${c.reset} Inspect without decrypting
571
+
572
+ ${c.cyan}vault verify <file>${c.reset} Verify Seal of Truth + Ed25519 signature
573
+
574
+ ${c.cyan}vault escrow create${c.reset} Generate escrow keypairs
575
+ --parties <n> Number of parties (1-8)
576
+
577
+ ${c.cyan}vault transfer <file>${c.reset} Register a transfer
578
+ --to <recipient> Recipient identifier
579
+
580
+ ${c.cyan}vault revoke <id>${c.reset} Revoke a transfer ID
581
+
582
+ ${c.bright}7 Semantic Layers:${c.reset}
583
+ workflows, credentials, env_vars, mcp_configs,
584
+ site_profiles, ai_brain, audit_trail
585
+ `);
586
+ return;
587
+ }
588
+
589
+ if (sub === 'create') {
590
+ const { assembleContainer, saveContainer } = await import('./vault/container.js');
591
+ const { LAYER_NAMES } = await import('./vault/layers.js');
592
+
593
+ const passphrase = getFlag(args, '--passphrase') || await promptInput('Passphrase: ');
594
+ const output = getFlag(args, '--output');
595
+ const layerStr = getFlag(args, '--layers');
596
+ const requestedLayers = layerStr ? layerStr.split(',').map(s => s.trim()) : LAYER_NAMES;
597
+
598
+ // Build layer data from ~/.0n directory
599
+ const layers = {};
600
+ for (const name of requestedLayers) {
601
+ if (!LAYER_NAMES.includes(name)) {
602
+ console.log(`${c.red}Invalid layer: ${name}${c.reset}`);
603
+ process.exit(1);
604
+ }
605
+ layers[name] = { placeholder: true, created: new Date().toISOString() };
606
+ }
607
+
608
+ console.log(`\n${c.bright}Creating vault container...${c.reset}`);
609
+ console.log(` Layers: ${Object.keys(layers).join(', ')}`);
610
+
611
+ try {
612
+ const result = await assembleContainer({ layers, passphrase });
613
+ const homePath = path.join(os.homedir(), '.0n', 'vault');
614
+ if (!fs.existsSync(homePath)) fs.mkdirSync(homePath, { recursive: true });
615
+ const filePath = output || path.join(homePath, `${result.transferId}.0nv`);
616
+ saveContainer(result.buffer, filePath);
617
+
618
+ console.log(`\n${c.green}✓ Container created${c.reset}`);
619
+ console.log(` Transfer ID: ${c.cyan}${result.transferId}${c.reset}`);
620
+ console.log(` Seal of Truth: ${c.cyan}${result.sealHex}${c.reset}`);
621
+ console.log(` Layers: ${result.layerCount}`);
622
+ console.log(` Size: ${result.buffer.length} bytes`);
623
+ console.log(` File: ${c.yellow}${filePath}${c.reset}`);
624
+ } catch (err) {
625
+ console.log(`${c.red}Error: ${err.message}${c.reset}`);
626
+ process.exit(1);
627
+ }
628
+ return;
629
+ }
630
+
631
+ if (sub === 'open') {
632
+ const file = args[1];
633
+ if (!file) { console.log(`${c.red}Usage: vault open <file.0nv>${c.reset}`); process.exit(1); }
634
+
635
+ const { disassembleContainer, loadContainer } = await import('./vault/container.js');
636
+ const passphrase = getFlag(args, '--passphrase') || await promptInput('Passphrase: ');
637
+ const layer = getFlag(args, '--layer');
638
+
639
+ try {
640
+ const buffer = loadContainer(file);
641
+ const result = await disassembleContainer(buffer, passphrase, layer ? [layer] : null);
642
+
643
+ console.log(`\n${c.green}✓ Container opened${c.reset}`);
644
+ console.log(` Transfer ID: ${c.cyan}${result.metadata.transferId}${c.reset}`);
645
+ console.log(` Seal valid: ${result.seal.valid ? c.green + '✓' : c.red + '✗'}${c.reset}`);
646
+ console.log(` Sig valid: ${result.signature.valid ? c.green + '✓' : c.red + '✗'}${c.reset}`);
647
+ console.log(` Layers:`);
648
+ for (const [name, data] of Object.entries(result.layers)) {
649
+ const preview = JSON.stringify(data).substring(0, 80);
650
+ console.log(` ${c.cyan}${name}${c.reset}: ${preview}...`);
651
+ }
652
+ } catch (err) {
653
+ console.log(`${c.red}Error: ${err.message}${c.reset}`);
654
+ process.exit(1);
655
+ }
656
+ return;
657
+ }
658
+
659
+ if (sub === 'inspect') {
660
+ const file = args[1];
661
+ if (!file) { console.log(`${c.red}Usage: vault inspect <file.0nv>${c.reset}`); process.exit(1); }
662
+
663
+ const { inspectContainer, loadContainer } = await import('./vault/container.js');
664
+ try {
665
+ const buffer = loadContainer(file);
666
+ const info = inspectContainer(buffer);
667
+ console.log(`\n${c.bright}Container Inspection${c.reset}`);
668
+ console.log(JSON.stringify(info, null, 2));
669
+ } catch (err) {
670
+ console.log(`${c.red}Error: ${err.message}${c.reset}`);
671
+ process.exit(1);
672
+ }
673
+ return;
674
+ }
675
+
676
+ if (sub === 'verify') {
677
+ const file = args[1];
678
+ if (!file) { console.log(`${c.red}Usage: vault verify <file.0nv>${c.reset}`); process.exit(1); }
679
+
680
+ const { inspectContainer, loadContainer } = await import('./vault/container.js');
681
+ try {
682
+ const buffer = loadContainer(file);
683
+ const info = inspectContainer(buffer);
684
+ const verified = info.seal.valid && info.signature.valid;
685
+
686
+ console.log(`\n${verified ? c.green + '✓ VERIFIED' : c.red + '✗ FAILED'}${c.reset}`);
687
+ console.log(` Seal of Truth: ${info.seal.valid ? c.green + 'Valid' : c.red + 'Invalid'}${c.reset} (${info.seal.algorithm})`);
688
+ console.log(` Signature: ${info.signature.valid ? c.green + 'Valid' : c.red + 'Invalid'}${c.reset} (${info.signature.algorithm})`);
689
+ console.log(` Transfer ID: ${info.metadata.transferId}`);
690
+ console.log(` Created: ${info.metadata.created}`);
691
+ console.log(` Patent: ${info.patent}`);
692
+ } catch (err) {
693
+ console.log(`${c.red}Error: ${err.message}${c.reset}`);
694
+ process.exit(1);
695
+ }
696
+ return;
697
+ }
698
+
699
+ if (sub === 'escrow') {
700
+ const escrowSub = args[1];
701
+ if (escrowSub === 'create') {
702
+ const { generatePartyKeys } = await import('./vault/escrow.js');
703
+ const count = parseInt(getFlag(args, '--parties') || '3');
704
+
705
+ console.log(`\n${c.bright}Generating ${count} escrow keypairs...${c.reset}`);
706
+ const parties = [];
707
+ for (let i = 0; i < count; i++) {
708
+ const party = generatePartyKeys();
709
+ parties.push(party);
710
+ console.log(`\n ${c.cyan}Party ${i + 1}${c.reset} (${party.partyId}):`);
711
+ console.log(` Public: ${party.publicKey.toString('hex').substring(0, 32)}...`);
712
+ console.log(` Secret: ${party.secretKey.toString('hex').substring(0, 32)}...`);
713
+ }
714
+ console.log(`\n${c.green}✓ ${count} keypairs generated${c.reset}`);
715
+ console.log(` ${c.yellow}Save secret keys securely. Share public keys for container creation.${c.reset}`);
716
+ }
717
+ return;
718
+ }
719
+
720
+ if (sub === 'transfer') {
721
+ const file = args[1];
722
+ if (!file) { console.log(`${c.red}Usage: vault transfer <file.0nv>${c.reset}`); process.exit(1); }
723
+
724
+ const { inspectContainer, loadContainer } = await import('./vault/container.js');
725
+ const { registerTransfer } = await import('./vault/registry.js');
726
+ const recipient = getFlag(args, '--to');
727
+
728
+ try {
729
+ const buffer = loadContainer(file);
730
+ const info = inspectContainer(buffer);
731
+ const result = registerTransfer(info.metadata.transferId, info.seal.hash, { recipient });
732
+
733
+ if (result.success) {
734
+ console.log(`\n${c.green}✓ Transfer registered${c.reset}`);
735
+ console.log(` Transfer ID: ${c.cyan}${info.metadata.transferId}${c.reset}`);
736
+ if (recipient) console.log(` Recipient: ${recipient}`);
737
+ } else {
738
+ console.log(`\n${c.red}✗ ${result.error}${c.reset}`);
739
+ }
740
+ } catch (err) {
741
+ console.log(`${c.red}Error: ${err.message}${c.reset}`);
742
+ process.exit(1);
743
+ }
744
+ return;
745
+ }
746
+
747
+ if (sub === 'revoke') {
748
+ const transferId = args[1];
749
+ if (!transferId) { console.log(`${c.red}Usage: vault revoke <transferId>${c.reset}`); process.exit(1); }
750
+
751
+ const { revokeTransfer } = await import('./vault/registry.js');
752
+ const result = revokeTransfer(transferId);
753
+
754
+ if (result.success) {
755
+ console.log(`\n${c.green}✓ Transfer revoked: ${transferId}${c.reset}`);
756
+ } else {
757
+ console.log(`\n${c.red}✗ ${result.error}${c.reset}`);
758
+ }
759
+ return;
760
+ }
761
+
762
+ console.log(`${c.red}Unknown vault command: ${sub}${c.reset}`);
763
+ console.log(`Run ${c.cyan}0nmcp vault help${c.reset} for usage.`);
764
+ }
765
+
457
766
  async function handleEngine(args) {
458
767
  const sub = args[0];
459
768
 
@@ -913,4 +1222,125 @@ async function handleApp(args) {
913
1222
  process.exit(1);
914
1223
  }
915
1224
 
1225
+ async function startShell() {
1226
+ const cmds = loadCommands();
1227
+ const aliases = [...cmds.keys()];
1228
+ const builtins = ['help', 'commands', 'list', 'exit', 'quit'];
1229
+ const allCompletions = [...builtins, ...aliases];
1230
+
1231
+ console.log(`${c.bright}0nMCP Interactive Shell${c.reset}`);
1232
+ 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`);
1233
+
1234
+ if (aliases.length > 0) {
1235
+ console.log(`${c.bright}Available commands:${c.reset} ${aliases.map(a => c.cyan + '/' + a + c.reset).join(', ')}\n`);
1236
+ }
1237
+
1238
+ const rl = readline.createInterface({
1239
+ input: process.stdin,
1240
+ output: process.stdout,
1241
+ prompt: `${c.green}0n>${c.reset} `,
1242
+ completer: (line) => {
1243
+ const input = line.startsWith('/') ? line.slice(1) : line;
1244
+ const hits = allCompletions.filter(c2 => c2.startsWith(input));
1245
+ return [hits.map(h => '/' + h), line];
1246
+ },
1247
+ });
1248
+
1249
+ rl.prompt();
1250
+
1251
+ rl.on('line', async (line) => {
1252
+ const input = line.trim();
1253
+ if (!input) {
1254
+ rl.prompt();
1255
+ return;
1256
+ }
1257
+
1258
+ // Strip leading / if present
1259
+ const raw = input.startsWith('/') ? input.slice(1) : input;
1260
+ const parts = raw.split(/\s+/);
1261
+ const cmd = parts[0];
1262
+ const cmdArgs = parts.slice(1);
1263
+
1264
+ if (cmd === 'exit' || cmd === 'quit') {
1265
+ console.log(`\n${c.cyan}Goodbye!${c.reset}`);
1266
+ rl.close();
1267
+ process.exit(0);
1268
+ }
1269
+
1270
+ if (cmd === 'help') {
1271
+ console.log(`\n${c.bright}Shell Commands:${c.reset}`);
1272
+ console.log(` ${c.cyan}/commands${c.reset} List all named RUNs`);
1273
+ console.log(` ${c.cyan}/list${c.reset} Show connected services`);
1274
+ console.log(` ${c.cyan}/exit${c.reset} Exit the shell`);
1275
+ if (aliases.length > 0) {
1276
+ console.log(`\n${c.bright}Named RUNs:${c.reset}`);
1277
+ for (const a of aliases) {
1278
+ const def = cmds.get(a);
1279
+ console.log(` ${c.cyan}/${a}${c.reset} ${def.description || def.name || ''}`);
1280
+ }
1281
+ }
1282
+ console.log('');
1283
+ rl.prompt();
1284
+ return;
1285
+ }
1286
+
1287
+ if (cmd === 'commands') {
1288
+ if (aliases.length === 0) {
1289
+ console.log(`\n${c.yellow}No commands defined in SWITCH file.${c.reset}\n`);
1290
+ } else {
1291
+ console.log(`\n${c.bright}Named RUNs:${c.reset}`);
1292
+ for (const a of aliases) {
1293
+ const def = cmds.get(a);
1294
+ console.log(` ${c.cyan}/${a}${c.reset} ${def.description || def.name || ''}`);
1295
+ }
1296
+ console.log('');
1297
+ }
1298
+ rl.prompt();
1299
+ return;
1300
+ }
1301
+
1302
+ if (cmd === 'list') {
1303
+ const connectionsDir = path.join(DOT_ON_DIR, 'connections');
1304
+ if (fs.existsSync(connectionsDir)) {
1305
+ const files = fs.readdirSync(connectionsDir).filter(f => f.endsWith('.0n'));
1306
+ console.log(`\n${c.bright}Connected Services:${c.reset}`);
1307
+ for (const file of files) {
1308
+ try {
1309
+ const data = JSON.parse(fs.readFileSync(path.join(connectionsDir, file), 'utf8'));
1310
+ console.log(` ${c.green}●${c.reset} ${data.$0n?.name || data.service}`);
1311
+ } catch {
1312
+ console.log(` ${c.red}●${c.reset} ${file} (error)`);
1313
+ }
1314
+ }
1315
+ console.log('');
1316
+ } else {
1317
+ console.log(`\n${c.yellow}No connections found.${c.reset}\n`);
1318
+ }
1319
+ rl.prompt();
1320
+ return;
1321
+ }
1322
+
1323
+ // Try to run as a named command
1324
+ if (cmds.has(cmd)) {
1325
+ console.log('');
1326
+ try {
1327
+ await runCommand(cmd, cmds.get(cmd), cmdArgs);
1328
+ } catch (err) {
1329
+ console.log(`${c.red}Error: ${err.message}${c.reset}`);
1330
+ }
1331
+ console.log('');
1332
+ rl.prompt();
1333
+ return;
1334
+ }
1335
+
1336
+ console.log(`${c.yellow}Unknown command: ${cmd}${c.reset}`);
1337
+ console.log(`Type ${c.cyan}/help${c.reset} for available commands.`);
1338
+ rl.prompt();
1339
+ });
1340
+
1341
+ rl.on('close', () => {
1342
+ process.exit(0);
1343
+ });
1344
+ }
1345
+
916
1346
  main().catch(console.error);