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 +24 -23
- package/cli.js +431 -1
- package/command-runner.js +224 -0
- package/commands.js +115 -0
- package/index.js +8 -1
- package/lib/stats.json +1 -1
- package/package.json +26 -3
- package/vault/container.js +479 -0
- package/vault/crypto-container.js +278 -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/README.md
CHANGED
|
@@ -20,19 +20,19 @@
|
|
|
20
20
|
[](https://modelcontextprotocol.io)
|
|
21
21
|
[](http://makeapullrequest.com)
|
|
22
22
|
[](#-supported-services)
|
|
23
|
-
[](#-all-tools)
|
|
24
24
|
[](#-community)
|
|
25
25
|
[](https://github.com/0nork/0nMCP/discussions)
|
|
26
26
|
|
|
27
|
-
**
|
|
27
|
+
**550 tools. 26 services. Zero configuration. One natural language interface.**
|
|
28
28
|
|
|
29
|
-
[Quick Start](#-installation) · [Services](#-supported-services) · [
|
|
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.
|
|
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.
|
|
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
|
-
**
|
|
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.
|
|
277
|
-
|
|
278
|
-
- **26 services
|
|
279
|
-
-
|
|
280
|
-
- **
|
|
281
|
-
- **
|
|
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
|
-
- **
|
|
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** |
|
|
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,
|
|
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** |
|
|
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 —
|
|
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-%
|
|
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
|
-
**
|
|
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
|
-
|
|
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);
|