provenance-protocol 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +5 -5
  2. package/package.json +1 -1
  3. package/src/cli.js +249 -151
package/README.md CHANGED
@@ -264,11 +264,11 @@ await fetch('https://getprovenance.dev/api/agents/revoke', {
264
264
  ## CLI
265
265
 
266
266
  ```bash
267
- npx provenance keygen
268
- npx provenance register --id provenance:github:your-org/your-agent --url https://github.com/...
269
- npx provenance status provenance:github:alice/my-agent
270
- npx provenance validate PROVENANCE.yml
271
- npx provenance revoke --id provenance:github:your-org/your-agent
267
+ npx provenance-protocol keygen
268
+ npx provenance-protocol register --id provenance:github:your-org/your-agent --url https://github.com/...
269
+ npx provenance-protocol status provenance:github:alice/my-agent
270
+ npx provenance-protocol validate PROVENANCE.yml
271
+ npx provenance-protocol revoke --id provenance:github:your-org/your-agent
272
272
  ```
273
273
 
274
274
  Full CLI reference: [getprovenance.dev/docs#cli](https://getprovenance.dev/docs#cli)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "provenance-protocol",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "The Provenance Protocol \u2014 open standard for AI agent identity \u2014 and its reference SDK",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/cli.js CHANGED
@@ -1,196 +1,294 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * provenance CLI
3
+ * provenance — Provenance Protocol identity CLI (the `provenance` bin of provenance-protocol)
4
4
  *
5
- * npx provenance keygen
6
- * npx provenance register --id provenance:github:org/agent --url https://...
7
- * npx provenance status provenance:github:org/agent
8
- * npx provenance revoke --id provenance:github:org/agent
5
+ * Usage:
6
+ * provenance keygen
7
+ * provenance register --id <id> --url <url> [options]
8
+ * provenance status <id>
9
+ * provenance validate [file]
10
+ * provenance revoke --id <id> [--private-key <key>]
9
11
  */
10
12
 
11
- import { writeFileSync, readFileSync, existsSync } from 'fs';
12
- import { execSync } from 'child_process';
13
- import { generateProvenanceKeyPair, signChallenge, signForProvenance, signRevocation } from './keygen.js';
13
+ import { createPrivateKey, createPublicKey, generateKeyPairSync, sign as nodeSign } from 'crypto';
14
+ import { readFileSync, existsSync } from 'fs';
15
+ import { resolve } from 'path';
16
+ import { createRequire } from 'module';
14
17
 
15
- const API = 'https://getprovenance.dev/api/agents';
16
- const KEY_FILE = '.provenance-key';
18
+ const API = process.env.PROVENANCE_API_URL || 'https://getprovenance.dev';
19
+ const VERSION = createRequire(import.meta.url)('../package.json').version;
17
20
 
18
- function readPrivateKey() {
19
- if (process.env.PROVENANCE_PRIVATE_KEY) return process.env.PROVENANCE_PRIVATE_KEY.trim();
20
- if (existsSync(KEY_FILE)) return readFileSync(KEY_FILE, 'utf8').trim();
21
- console.error(`No private key found. Set PROVENANCE_PRIVATE_KEY or run: npx provenance keygen`);
22
- process.exit(1);
23
- }
21
+ // ── Colours ───────────────────────────────────────────────────────────────────
22
+
23
+ const c = {
24
+ reset: '\x1b[0m', dim: '\x1b[2m', bold: '\x1b[1m',
25
+ green: '\x1b[32m', amber: '\x1b[33m', red: '\x1b[31m', white: '\x1b[97m',
26
+ };
27
+ const ok = s => `${c.green}✓${c.reset} ${s}`;
28
+ const err = s => `${c.red}✗${c.reset} ${s}`;
29
+ const dim = s => `${c.dim}${s}${c.reset}`;
30
+ const hi = s => `${c.white}${c.bold}${s}${c.reset}`;
31
+ const amb = s => `${c.amber}${s}${c.reset}`;
32
+
33
+ // ── Arg parsing ───────────────────────────────────────────────────────────────
24
34
 
25
35
  function parseArgs(argv) {
26
- const args = {};
27
- for (let i = 0; i < argv.length; i++) {
28
- if (argv[i].startsWith('--')) {
29
- args[argv[i].slice(2)] = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : true;
30
- } else {
31
- args._ = args._ || [];
32
- args._.push(argv[i]);
33
- }
36
+ const args = { _: [] };
37
+ let i = 0;
38
+ while (i < argv.length) {
39
+ const a = argv[i];
40
+ if (a.startsWith('--')) {
41
+ const key = a.slice(2);
42
+ const next = argv[i + 1];
43
+ if (next && !next.startsWith('--')) { args[key] = next; i += 2; }
44
+ else { args[key] = true; i++; }
45
+ } else { args._.push(a); i++; }
34
46
  }
35
47
  return args;
36
48
  }
37
49
 
38
- const [,, command, ...rest] = process.argv;
39
- const args = parseArgs(rest);
50
+ // ── Crypto helpers ────────────────────────────────────────────────────────────
40
51
 
41
- // ── keygen ──────────────────────────────────────────────────────────────────
52
+ function generateKeyPair() {
53
+ const { publicKey, privateKey } = generateKeyPairSync('ed25519', {
54
+ publicKeyEncoding: { type: 'spki', format: 'der' },
55
+ privateKeyEncoding: { type: 'pkcs8', format: 'der' },
56
+ });
57
+ return {
58
+ publicKey: Buffer.from(publicKey).toString('base64'),
59
+ privateKey: Buffer.from(privateKey).toString('base64'),
60
+ };
61
+ }
42
62
 
43
- if (command === 'keygen') {
44
- const { publicKey, privateKey } = generateProvenanceKeyPair();
63
+ function signMessage(privateKeyBase64, message) {
64
+ const key = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });
65
+ return nodeSign(null, Buffer.from(message, 'utf8'), key).toString('base64');
66
+ }
67
+
68
+ function derivePublicKey(privateKeyBase64) {
69
+ const priv = createPrivateKey({ key: Buffer.from(privateKeyBase64, 'base64'), format: 'der', type: 'pkcs8' });
70
+ return Buffer.from(createPublicKey(priv).export({ type: 'spki', format: 'der' })).toString('base64');
71
+ }
45
72
 
46
- writeFileSync(KEY_FILE, privateKey, { mode: 0o600 });
47
- try { execSync(`grep -qxF "${KEY_FILE}" .gitignore 2>/dev/null || echo "${KEY_FILE}" >> .gitignore`); } catch {}
73
+ // ── Commands ──────────────────────────────────────────────────────────────────
48
74
 
49
- console.log(`\nKeypair generated.\n`);
50
- console.log(`Private key → ${KEY_FILE} (chmod 600, added to .gitignore)`);
51
- console.log(`\nPublic key (add to PROVENANCE.yml):\n`);
75
+ async function cmdKeygen() {
76
+ console.log(`\n${amb('Generating Ed25519 keypair...')}\n`);
77
+ const { publicKey, privateKey } = generateKeyPair();
78
+
79
+ console.log(`${hi('Public key')} ${dim('(add to PROVENANCE.yml identity.public_key)')}`);
80
+ console.log(`${c.green}${publicKey}${c.reset}\n`);
81
+ console.log(`${hi('Private key')} ${dim('(store as PROVENANCE_PRIVATE_KEY — never commit)')}`);
82
+ console.log(`${c.amber}${privateKey}${c.reset}\n`);
83
+ console.log(dim('─'.repeat(60)));
84
+ console.log(dim('Add to your environment:'));
85
+ console.log(` PROVENANCE_PRIVATE_KEY=${privateKey}\n`);
86
+ console.log(dim('Add to PROVENANCE.yml:'));
52
87
  console.log(` identity:`);
53
88
  console.log(` public_key: "${publicKey}"`);
54
- console.log(` algorithm: ed25519`);
55
- console.log(`\nNext: npx provenance register --id provenance:<platform>:<org>/<name>\n`);
56
- process.exit(0);
89
+ console.log(` algorithm: ed25519\n`);
57
90
  }
58
91
 
59
- // ── register ─────────────────────────────────────────────────────────────────
60
-
61
- if (command === 'register') {
62
- const id = args.id || args._?.[0];
63
- if (!id) { console.error('Usage: npx provenance register --id provenance:<platform>:<org>/<name> [options]'); process.exit(1); }
64
-
65
- const privateKey = readPrivateKey();
66
- const { publicKey } = (() => {
67
- // derive public key from private key for display — we just use the stored one
68
- // We can't derive public from private easily here, so read from PROVENANCE.yml or require --public-key
69
- return { publicKey: args['public-key'] || null };
70
- })();
71
-
72
- // If no public key arg, re-generate won't work — need the stored public key
73
- // Best path: require keygen was run, read public key from PROVENANCE.yml if present
74
- let pubKey = args['public-key'];
75
- if (!pubKey) {
76
- if (existsSync('PROVENANCE.yml')) {
77
- const yml = readFileSync('PROVENANCE.yml', 'utf8');
78
- const match = yml.match(/public_key:\s*["']?([A-Za-z0-9+/=]+)["']?/);
79
- if (match) pubKey = match[1];
80
- }
81
- }
82
- if (!pubKey) {
83
- console.error('Public key required. Pass --public-key or add identity.public_key to PROVENANCE.yml first.');
84
- process.exit(1);
85
- }
92
+ async function cmdRegister(args) {
93
+ const id = args.id;
94
+ const url = args.url;
95
+ const name = args.name;
96
+ const description = args.description || args.desc;
97
+ const caps = args.capabilities ? args.capabilities.split(',').map(s => s.trim()) : [];
98
+ const cons = args.constraints ? args.constraints.split(',').map(s => s.trim()) : [];
99
+ const model = args.model;
100
+ const modelId = args['model-id'];
101
+ const ajpEndpoint = args['ajp-endpoint'];
102
+ const privateKey = args['private-key'] || process.env.PROVENANCE_PRIVATE_KEY;
103
+
104
+ if (!id) { console.error(err('--id required')); process.exit(1); }
105
+ if (!url) { console.error(err('--url required')); process.exit(1); }
86
106
 
87
- const signed_challenge = signChallenge(privateKey, id, 'REGISTER');
107
+ console.log(`\n${amb('Registering')} ${hi(id)}...\n`);
108
+
109
+ let pubKey, signedChallenge;
110
+ if (privateKey) {
111
+ pubKey = args['public-key'] || process.env.PROVENANCE_PUBLIC_KEY || derivePublicKey(privateKey);
112
+ signedChallenge = signMessage(privateKey, `${id}:REGISTER`);
113
+ console.log(ok('Signing with private key'));
114
+ }
88
115
 
89
116
  const body = {
90
- provenance_id: id,
91
- public_key: pubKey,
92
- signed_challenge,
93
- ...(args.url ? { url: args.url } : {}),
94
- ...(args.name ? { name: args.name } : {}),
95
- ...(args.description ? { description: args.description } : {}),
96
- ...(args.capabilities ? { capabilities: args.capabilities.split(',').map(s => s.trim()) } : {}),
97
- ...(args.constraints ? { constraints: args.constraints.split(',').map(s => s.trim()) } : {}),
98
- ...(args.model ? { model_provider: args.model } : {}),
99
- ...(args['model-id'] ? { model_id: args['model-id'] } : {}),
117
+ provenance_id: id, url,
118
+ ...(name && { name }),
119
+ ...(description && { description }),
120
+ ...(caps.length && { capabilities: caps }),
121
+ ...(cons.length && { constraints: cons }),
122
+ ...(model && { model_provider: model }),
123
+ ...(modelId && { model_id: modelId }),
124
+ ...(ajpEndpoint && { ajp_endpoint: ajpEndpoint }),
125
+ ...(pubKey && { public_key: pubKey }),
126
+ ...(signedChallenge && { signed_challenge: signedChallenge }),
100
127
  };
101
128
 
102
- try {
103
- const res = await fetch(`${API}/register`, {
104
- method: 'POST',
105
- headers: { 'Content-Type': 'application/json' },
106
- body: JSON.stringify(body),
107
- });
108
- const data = await res.json();
109
- if (!res.ok) {
110
- console.error(`\nRegistration failed: ${data.error}`);
111
- if (data.hint) console.error(`Hint: ${data.hint}`);
112
- process.exit(1);
113
- }
114
- console.log(`\n${data.created ? 'Registered' : 'Updated'}: ${id}`);
115
- console.log(`identity: ${data.agent?.identity}`);
116
- console.log(`profile: https://getprovenance.dev/agent/${id.replace('provenance:', '').replace(':', '/')}\n`);
117
- } catch (e) {
118
- console.error('Network error:', e.message);
119
- process.exit(1);
120
- }
121
- process.exit(0);
129
+ const res = await fetch(`${API}/api/agents/register`, {
130
+ method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),
131
+ });
132
+ const data = await res.json();
133
+
134
+ if (!res.ok) { console.error(err(data.error || `HTTP ${res.status}`)); process.exit(1); }
135
+
136
+ const agent = data.agent || data;
137
+ console.log(ok(data.created ? 'Agent registered' : 'Agent updated'));
138
+ console.log(` ${dim('confidence:')} ${c.green}${agent.confidence}${c.reset}`);
139
+ console.log(` ${dim('identity_verified:')} ${agent.identity_verified ? c.green + 'true' : c.amber + 'false'}${c.reset}`);
140
+ if (ajpEndpoint) console.log(` ${dim('ajp_endpoint:')} ${ajpEndpoint}`);
141
+ if (!agent.identity_verified)
142
+ console.log(`\n${c.amber}Tip:${c.reset} Run with ${hi('--private-key')} to get identity_verified status`);
143
+ console.log();
122
144
  }
123
145
 
124
- // ── status ───────────────────────────────────────────────────────────────────
146
+ async function cmdStatus(args) {
147
+ const id = args._[1];
148
+ if (!id) { console.error(err('Usage: provenance status <provenance_id>')); process.exit(1); }
125
149
 
126
- if (command === 'status') {
127
- const id = args._?.[0] || args.id;
128
- if (!id) { console.error('Usage: npx provenance status <provenance_id>'); process.exit(1); }
150
+ console.log(`\n${amb('Checking')} ${hi(id)}...\n`);
129
151
 
130
- try {
131
- const res = await fetch(`${API}/register?provenance_id=${encodeURIComponent(id)}`);
132
- const data = await res.json();
133
- if (!data.registered) { console.log(`\nNot registered: ${id}\n`); process.exit(0); }
134
- const a = data.agent;
135
- console.log(`\n${id}`);
136
- console.log(`name: ${a.name || '—'}`);
137
- console.log(`identity: ${a.identity}`);
138
- console.log(`status: ${a.status}`);
139
- console.log(`profile: https://getprovenance.dev/agent/${id.replace('provenance:', '').replace(':', '/')}\n`);
140
- } catch (e) {
141
- console.error('Network error:', e.message);
142
- process.exit(1);
143
- }
144
- process.exit(0);
145
- }
152
+ const res = await fetch(`${API}/api/agent/${id.replace('provenance:', '').replace(':', '/')}`);
153
+ const data = await res.json();
154
+
155
+ if (!res.ok || data.error) { console.error(err(data.error || 'Not found')); process.exit(1); }
146
156
 
147
- // ── revoke ───────────────────────────────────────────────────────────────────
157
+ const trust = Math.round((data.confidence || 0) * 100);
158
+ const trustColor = trust >= 80 ? c.green : trust >= 50 ? c.amber : c.red;
148
159
 
149
- if (command === 'revoke') {
150
- const id = args.id || args._?.[0];
151
- if (!id) { console.error('Usage: npx provenance revoke --id <provenance_id>'); process.exit(1); }
160
+ console.log(`${hi(data.name || id)}`);
161
+ console.log(`${dim(data.provenance_id)}\n`);
162
+ console.log(`${dim('Trust score:')} ${trustColor}${trust}/100${c.reset}`);
163
+ console.log(`${dim('Declared:')} ${data.declared ? c.green + 'yes' : c.amber + 'no'}${c.reset}`);
164
+ console.log(`${dim('Identity verified:')} ${data.identity_verified ? c.green + 'yes' : c.amber + 'no'}${c.reset}`);
165
+ console.log(`${dim('AJP endpoint:')} ${data.ajp?.endpoint ? c.green + data.ajp.endpoint : c.dim + 'not set'}${c.reset}`);
166
+ console.log(`${dim('Incidents:')} ${(data.incident_count || 0) === 0 ? c.green + '0' : c.red + data.incident_count}${c.reset}`);
167
+ if (data.capabilities?.length) console.log(`${dim('Capabilities:')} ${data.capabilities.join(', ')}`);
168
+ if (data.constraints?.length) console.log(`${dim('Constraints:')} ${data.constraints.join(', ')}`);
152
169
 
153
- const privateKey = readPrivateKey();
154
- const signed_challenge = signRevocation(privateKey, id);
170
+ console.log();
171
+ for (const [pass, label] of [
172
+ [data.declared, 'PROVENANCE.yml declared'],
173
+ [data.identity_verified, 'Identity verified (Ed25519)'],
174
+ [!!data.ajp?.endpoint, 'AJP endpoint configured'],
175
+ [(data.incident_count||0)===0, 'No open incidents'],
176
+ ]) console.log(` ${pass ? ok(label) : dim('○ ' + label)}`);
177
+ console.log();
178
+ }
155
179
 
180
+ async function cmdValidate(args) {
181
+ const file = args._[1] || 'PROVENANCE.yml';
182
+ const path = resolve(process.cwd(), file);
183
+ if (!existsSync(path)) { console.error(err(`File not found: ${path}`)); process.exit(1); }
184
+
185
+ console.log(`\n${amb('Validating')} ${hi(file)}...\n`);
186
+ const content = readFileSync(path, 'utf8');
187
+
188
+ // "Could not check" and "checked, and it is invalid" must not look alike:
189
+ // this runs in CI, where a validator that cannot reach the service and
190
+ // reports failure would be indistinguishable from a broken declaration.
191
+ // Transport trouble exits 2; a genuinely invalid file exits 1.
192
+ let data;
156
193
  try {
157
- const res = await fetch(`${API}/revoke`, {
158
- method: 'POST',
159
- headers: { 'Content-Type': 'application/json' },
160
- body: JSON.stringify({ provenance_id: id, signed_challenge }),
194
+ const res = await fetch(`${API}/api/mcp`, {
195
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
196
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call',
197
+ params: { name: 'validate_provenance_yml', arguments: { content } } }),
161
198
  });
162
- const data = await res.json();
163
- if (!res.ok) { console.error(`Revocation failed: ${data.error}`); process.exit(1); }
164
- console.log(`\nKey revoked for ${id}.`);
165
- console.log(`Run npx provenance keygen && npx provenance register --id ${id} to re-register with a new key.\n`);
199
+ if (!res.ok) throw new Error(`validation service returned HTTP ${res.status}`);
200
+ const rpc = await res.json();
201
+ const text = rpc.result?.content?.[0]?.text;
202
+ if (!text) throw new Error('validation service returned no result');
203
+ data = JSON.parse(text);
204
+ if (typeof data.valid !== 'boolean') throw new Error('validation service returned no verdict');
166
205
  } catch (e) {
167
- console.error('Network error:', e.message);
168
- process.exit(1);
206
+ console.error(err(`Could not validate: ${e.message}`));
207
+ console.error(dim(`The file was not checked. This is not a validation failure.`));
208
+ console.log();
209
+ process.exit(2);
169
210
  }
170
- process.exit(0);
211
+
212
+ if (data.valid) console.log(ok('Valid PROVENANCE.yml'));
213
+ else { console.log(err('Validation failed')); for (const e of data.errors || []) console.log(` ${c.red}✗${c.reset} ${e}`); }
214
+ for (const w of data.warnings || []) console.log(` ${c.amber}⚠${c.reset} ${w}`);
215
+ console.log();
216
+
217
+ // Exit non-zero so `provenance validate` can gate a pipeline. It previously
218
+ // exited 0 on an invalid file, which made every CI check that used it pass.
219
+ if (!data.valid) process.exit(1);
171
220
  }
172
221
 
173
- // ── help ─────────────────────────────────────────────────────────────────────
222
+ async function cmdRevoke(args) {
223
+ const id = args.id;
224
+ const privateKey = args['private-key'] || process.env.PROVENANCE_PRIVATE_KEY;
225
+ if (!id) { console.error(err('--id required')); process.exit(1); }
226
+ if (!privateKey) { console.error(err('--private-key or PROVENANCE_PRIVATE_KEY required')); process.exit(1); }
174
227
 
175
- console.log(`
176
- provenance <command>
228
+ console.log(`\n${c.red}Revoking identity for${c.reset} ${hi(id)}...\n`);
177
229
 
178
- keygen Generate an Ed25519 keypair
179
- register --id <id> [options] Register or update your agent
180
- status <id> Check registration status
181
- revoke --id <id> Revoke your registered key
230
+ const res = await fetch(`${API}/api/agents/revoke`, {
231
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
232
+ body: JSON.stringify({ provenance_id: id, signed_challenge: signMessage(privateKey, `${id}:REVOKE`) }),
233
+ });
234
+ const data = await res.json();
235
+ if (!res.ok || !data.success) { console.error(err(data.error || `HTTP ${res.status}`)); process.exit(1); }
182
236
 
183
- register options:
184
- --id provenance:<platform>:<org>/<name> (required)
185
- --url URL to your PROVENANCE.yml (for independent verification)
186
- --name Agent display name
187
- --description One-sentence description
188
- --capabilities read:web,write:summaries
189
- --constraints no:pii,no:financial:transact
190
- --model anthropic / openai / etc.
191
- --model-id claude-sonnet-4-6 / gpt-4o / etc.
192
- --public-key Base64 public key (auto-read from PROVENANCE.yml if present)
237
+ console.log(ok('Identity revoked'));
238
+ console.log(dim('Run `provenance register` with a new keypair to re-establish.\n'));
239
+ }
193
240
 
194
- Full docs: https://getprovenance.dev/docs
241
+ function cmdHelp() {
242
+ console.log(`
243
+ ${hi('provenance')} ${dim(`v${VERSION}`)} — Provenance Protocol identity CLI
244
+
245
+ ${amb('Commands:')}
246
+ ${hi('keygen')} Generate an Ed25519 keypair
247
+ ${hi('register')} --id <id> --url <url> Register or update your agent
248
+ [--name <name>]
249
+ [--description <text>]
250
+ [--capabilities read:web,write:code]
251
+ [--constraints no:pii,no:financial:transact]
252
+ [--model anthropic] [--model-id claude-sonnet-4-6]
253
+ [--ajp-endpoint <url>]
254
+ [--private-key <key>]
255
+ ${hi('status')} <provenance_id> Check trust score and checklist
256
+ ${hi('validate')} [file] Validate PROVENANCE.yml (default: ./PROVENANCE.yml)
257
+ ${hi('revoke')} --id <id> Revoke cryptographic identity
258
+ [--private-key <key>]
259
+
260
+ ${amb('Environment variables:')}
261
+ PROVENANCE_ID Your agent's Provenance ID
262
+ PROVENANCE_PRIVATE_KEY Your Ed25519 private key (base64 PKCS8 DER)
263
+ PROVENANCE_API_URL Override API base (default: https://getprovenance.dev)
264
+
265
+ ${amb('For AJP job delegation:')}
266
+ ${dim('npm install -g ajp-cli')}
267
+ ${dim('npx ajp hire <id> --instruction "..."')}
268
+
269
+ ${amb('Examples:')}
270
+ provenance keygen
271
+ provenance register --id provenance:github:alice/my-agent --url https://github.com/alice/my-agent
272
+ provenance status provenance:github:alice/my-agent
273
+ provenance validate
195
274
  `);
196
- process.exit(0);
275
+ }
276
+
277
+ // ── Main ──────────────────────────────────────────────────────────────────────
278
+
279
+ const argv = process.argv.slice(2);
280
+ const args = parseArgs(argv);
281
+ const cmd = args._[0];
282
+
283
+ try {
284
+ if (!cmd || cmd === 'help' || args.help) cmdHelp();
285
+ else if (cmd === 'keygen') await cmdKeygen();
286
+ else if (cmd === 'register') await cmdRegister(args);
287
+ else if (cmd === 'status') await cmdStatus(args);
288
+ else if (cmd === 'validate') await cmdValidate(args);
289
+ else if (cmd === 'revoke') await cmdRevoke(args);
290
+ else { console.error(err(`Unknown command: ${cmd}\nRun \`provenance help\` for usage.`)); process.exit(1); }
291
+ } catch (e) {
292
+ console.error(err(e.message));
293
+ process.exit(1);
294
+ }