@ai-dossier/cli 0.2.1
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 +419 -0
- package/bin/ai-dossier +3 -0
- package/bin/dossier-verify +435 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +77 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/cache.d.ts +3 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +239 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/checksum.d.ts +3 -0
- package/dist/commands/checksum.d.ts.map +1 -0
- package/dist/commands/checksum.js +116 -0
- package/dist/commands/checksum.js.map +1 -0
- package/dist/commands/config-cmd.d.ts +3 -0
- package/dist/commands/config-cmd.d.ts.map +1 -0
- package/dist/commands/config-cmd.js +117 -0
- package/dist/commands/config-cmd.js.map +1 -0
- package/dist/commands/create.d.ts +3 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +130 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/export.d.ts +3 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +55 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/format.d.ts +3 -0
- package/dist/commands/format.d.ts.map +1 -0
- package/dist/commands/format.js +66 -0
- package/dist/commands/format.js.map +1 -0
- package/dist/commands/from-file.d.ts +3 -0
- package/dist/commands/from-file.d.ts.map +1 -0
- package/dist/commands/from-file.js +136 -0
- package/dist/commands/from-file.js.map +1 -0
- package/dist/commands/get.d.ts +3 -0
- package/dist/commands/get.d.ts.map +1 -0
- package/dist/commands/get.js +65 -0
- package/dist/commands/get.js.map +1 -0
- package/dist/commands/info.d.ts +3 -0
- package/dist/commands/info.d.ts.map +1 -0
- package/dist/commands/info.js +159 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +98 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install-skill.d.ts +3 -0
- package/dist/commands/install-skill.d.ts.map +1 -0
- package/dist/commands/install-skill.js +131 -0
- package/dist/commands/install-skill.js.map +1 -0
- package/dist/commands/keys.d.ts +3 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +170 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/lint.d.ts +3 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +105 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +173 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +33 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +3 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +19 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/prompt-hook.d.ts +3 -0
- package/dist/commands/prompt-hook.d.ts.map +1 -0
- package/dist/commands/prompt-hook.js +101 -0
- package/dist/commands/prompt-hook.js.map +1 -0
- package/dist/commands/publish.d.ts +3 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +142 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/pull.d.ts +3 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +69 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/remove.d.ts +3 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +65 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/reset-hooks.d.ts +3 -0
- package/dist/commands/reset-hooks.d.ts.map +1 -0
- package/dist/commands/reset-hooks.js +52 -0
- package/dist/commands/reset-hooks.js.map +1 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +281 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +137 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/sign.d.ts +3 -0
- package/dist/commands/sign.d.ts.map +1 -0
- package/dist/commands/sign.js +143 -0
- package/dist/commands/sign.js.map +1 -0
- package/dist/commands/validate.d.ts +3 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +152 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +41 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/whoami.d.ts +3 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +28 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +87 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials.d.ts +29 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +98 -0
- package/dist/credentials.js.map +1 -0
- package/dist/github-url.d.ts +22 -0
- package/dist/github-url.d.ts.map +1 -0
- package/dist/github-url.js +34 -0
- package/dist/github-url.js.map +1 -0
- package/dist/helpers.d.ts +145 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +597 -0
- package/dist/helpers.js.map +1 -0
- package/dist/hooks.d.ts +38 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +155 -0
- package/dist/hooks.js.map +1 -0
- package/dist/oauth.d.ts +22 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +118 -0
- package/dist/oauth.js.map +1 -0
- package/dist/registry-client.d.ts +86 -0
- package/dist/registry-client.d.ts.map +1 -0
- package/dist/registry-client.js +225 -0
- package/dist/registry-client.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dossier Verification CLI
|
|
5
|
+
*
|
|
6
|
+
* Enforces security verification of dossier files before execution.
|
|
7
|
+
* Provides command-line interface for checksum and signature validation.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* dossier-verify <file-or-url>
|
|
11
|
+
* dossier-verify --run <file-or-url>
|
|
12
|
+
* dossier-verify --verbose <file-or-url>
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const crypto = require('crypto');
|
|
18
|
+
const https = require('https');
|
|
19
|
+
const http = require('http');
|
|
20
|
+
const { execSync } = require('child_process');
|
|
21
|
+
const {
|
|
22
|
+
parseDossierContent,
|
|
23
|
+
verifyIntegrity,
|
|
24
|
+
loadTrustedKeys,
|
|
25
|
+
verifySignature
|
|
26
|
+
} = require('@ai-dossier/core');
|
|
27
|
+
const { convertGitHubBlobToRaw } = require('../dist/github-url');
|
|
28
|
+
|
|
29
|
+
// Colors for terminal output
|
|
30
|
+
const colors = {
|
|
31
|
+
reset: '\x1b[0m',
|
|
32
|
+
bright: '\x1b[1m',
|
|
33
|
+
red: '\x1b[31m',
|
|
34
|
+
green: '\x1b[32m',
|
|
35
|
+
yellow: '\x1b[33m',
|
|
36
|
+
blue: '\x1b[34m',
|
|
37
|
+
cyan: '\x1b[36m',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function log(message, color = 'reset') {
|
|
41
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function error(message) {
|
|
45
|
+
log(`❌ ${message}`, 'red');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function success(message) {
|
|
49
|
+
log(`✅ ${message}`, 'green');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function warning(message) {
|
|
53
|
+
log(`⚠️ ${message}`, 'yellow');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function info(message) {
|
|
57
|
+
log(`ℹ️ ${message}`, 'cyan');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Parse command line arguments
|
|
61
|
+
function parseArgs() {
|
|
62
|
+
const args = process.argv.slice(2);
|
|
63
|
+
const options = {
|
|
64
|
+
verbose: false,
|
|
65
|
+
run: false,
|
|
66
|
+
outputPath: false,
|
|
67
|
+
help: false,
|
|
68
|
+
input: null,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < args.length; i++) {
|
|
72
|
+
const arg = args[i];
|
|
73
|
+
if (arg === '--verbose' || arg === '-v') {
|
|
74
|
+
options.verbose = true;
|
|
75
|
+
} else if (arg === '--run') {
|
|
76
|
+
options.run = true;
|
|
77
|
+
} else if (arg === '--output-path') {
|
|
78
|
+
options.outputPath = true;
|
|
79
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
80
|
+
options.help = true;
|
|
81
|
+
} else if (!options.input) {
|
|
82
|
+
options.input = arg;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return options;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function showHelp() {
|
|
90
|
+
console.log(`
|
|
91
|
+
${colors.bright}Dossier Verification CLI${colors.reset}
|
|
92
|
+
|
|
93
|
+
${colors.bright}Usage:${colors.reset}
|
|
94
|
+
dossier-verify <file-or-url> Verify dossier security
|
|
95
|
+
dossier-verify --run <file-or-url> Verify and execute if safe
|
|
96
|
+
dossier-verify --verbose <file-or-url> Show detailed verification
|
|
97
|
+
dossier-verify --output-path <url> Download and output path
|
|
98
|
+
dossier-verify --help Show this help
|
|
99
|
+
|
|
100
|
+
${colors.bright}Exit Codes:${colors.reset}
|
|
101
|
+
0 - Verification passed (safe to execute)
|
|
102
|
+
1 - Verification failed (do not execute)
|
|
103
|
+
2 - Error occurred (cannot verify)
|
|
104
|
+
|
|
105
|
+
${colors.bright}Examples:${colors.reset}
|
|
106
|
+
# Verify local file
|
|
107
|
+
dossier-verify path/to/dossier.ds.md
|
|
108
|
+
|
|
109
|
+
# Verify remote dossier
|
|
110
|
+
dossier-verify https://example.com/dossier.ds.md
|
|
111
|
+
|
|
112
|
+
# Verify and run if safe
|
|
113
|
+
dossier-verify --run https://example.com/dossier.ds.md
|
|
114
|
+
|
|
115
|
+
# Use in shell script
|
|
116
|
+
if dossier-verify "$URL"; then
|
|
117
|
+
claude-code "run $URL"
|
|
118
|
+
else
|
|
119
|
+
echo "Security verification failed"
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
${colors.bright}Security Checks:${colors.reset}
|
|
123
|
+
✓ SHA256 checksum verification (required)
|
|
124
|
+
✓ Signature verification (if present)
|
|
125
|
+
✓ Trusted keys check
|
|
126
|
+
✓ Risk level assessment
|
|
127
|
+
|
|
128
|
+
${colors.bright}More Information:${colors.reset}
|
|
129
|
+
Documentation: https://github.com/imboard/ai-dossier
|
|
130
|
+
Security: SECURITY_STATUS.md
|
|
131
|
+
Protocol: PROTOCOL.md
|
|
132
|
+
`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Download file from URL
|
|
136
|
+
async function downloadFile(url) {
|
|
137
|
+
// Convert GitHub blob URLs to raw URLs
|
|
138
|
+
const resolvedUrl = convertGitHubBlobToRaw(url);
|
|
139
|
+
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
const protocol = resolvedUrl.startsWith('https://') ? https : http;
|
|
142
|
+
|
|
143
|
+
protocol.get(resolvedUrl, (res) => {
|
|
144
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
145
|
+
// Follow redirect
|
|
146
|
+
return downloadFile(res.headers.location).then(resolve).catch(reject);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (res.statusCode !== 200) {
|
|
150
|
+
return reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let data = '';
|
|
154
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
155
|
+
res.on('end', () => resolve(data));
|
|
156
|
+
}).on('error', reject);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Parse dossier frontmatter (using @ai-dossier/core)
|
|
161
|
+
function parseDossier(content) {
|
|
162
|
+
const parsed = parseDossierContent(content);
|
|
163
|
+
return {
|
|
164
|
+
frontmatter: parsed.frontmatter,
|
|
165
|
+
body: parsed.body
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Verify checksum (using @ai-dossier/core)
|
|
170
|
+
function verifyChecksum(body, declaredHash) {
|
|
171
|
+
const result = verifyIntegrity(body, declaredHash);
|
|
172
|
+
return {
|
|
173
|
+
passed: result.status === 'valid',
|
|
174
|
+
declared: result.expectedHash,
|
|
175
|
+
actual: result.actualHash,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Verify signature (using @ai-dossier/core for real cryptographic verification)
|
|
180
|
+
async function checkSignature(body, frontmatter) {
|
|
181
|
+
if (!frontmatter.signature) {
|
|
182
|
+
return {
|
|
183
|
+
present: false,
|
|
184
|
+
verified: false,
|
|
185
|
+
trusted: false,
|
|
186
|
+
message: 'No signature present',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const signature = frontmatter.signature;
|
|
191
|
+
const trustedKeys = loadTrustedKeys();
|
|
192
|
+
// Check both key_id and public_key for trusted status
|
|
193
|
+
// key_id is easier to manage in trusted-keys.txt file
|
|
194
|
+
const isTrusted = trustedKeys.has(signature.key_id) || trustedKeys.has(signature.public_key);
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const isValid = await verifySignature(body, signature);
|
|
198
|
+
|
|
199
|
+
if (!isValid) {
|
|
200
|
+
return {
|
|
201
|
+
present: true,
|
|
202
|
+
verified: false,
|
|
203
|
+
trusted: isTrusted,
|
|
204
|
+
message: 'Signature verification FAILED',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
present: true,
|
|
210
|
+
verified: true,
|
|
211
|
+
trusted: isTrusted,
|
|
212
|
+
message: isTrusted
|
|
213
|
+
? `Verified signature from trusted source: ${trustedKeys.get(signature.key_id) || trustedKeys.get(signature.public_key)}`
|
|
214
|
+
: 'Valid signature but key is not in trusted list',
|
|
215
|
+
};
|
|
216
|
+
} catch (err) {
|
|
217
|
+
return {
|
|
218
|
+
present: true,
|
|
219
|
+
verified: false,
|
|
220
|
+
trusted: false,
|
|
221
|
+
message: `Verification error: ${err.message}`,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Assess risk
|
|
227
|
+
function assessRisk(frontmatter, checksumResult, signatureResult) {
|
|
228
|
+
const issues = [];
|
|
229
|
+
let riskLevel = 'low';
|
|
230
|
+
let shouldBlock = false;
|
|
231
|
+
|
|
232
|
+
// Checksum failure is critical
|
|
233
|
+
if (!checksumResult.passed) {
|
|
234
|
+
issues.push('Checksum verification FAILED - content has been tampered with');
|
|
235
|
+
riskLevel = 'critical';
|
|
236
|
+
shouldBlock = true;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Signature issues
|
|
240
|
+
if (signatureResult.present && !signatureResult.verified) {
|
|
241
|
+
issues.push('Signature verification FAILED or could not be verified');
|
|
242
|
+
if (riskLevel !== 'critical') riskLevel = 'high';
|
|
243
|
+
shouldBlock = true;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Valid signature but not trusted - BLOCK execution
|
|
247
|
+
if (signatureResult.present && signatureResult.verified && !signatureResult.trusted) {
|
|
248
|
+
issues.push('Signature is valid but signer is not in your trusted keys list');
|
|
249
|
+
issues.push('Add the public key to ~/.dossier/trusted-keys.txt to trust this signer');
|
|
250
|
+
if (riskLevel === 'low') riskLevel = 'medium';
|
|
251
|
+
shouldBlock = true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// No signature on high-risk dossier
|
|
255
|
+
if (!signatureResult.present && frontmatter.risk_level === 'high') {
|
|
256
|
+
issues.push('High-risk dossier without signature');
|
|
257
|
+
if (riskLevel === 'low') riskLevel = 'medium';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (!signatureResult.present && frontmatter.risk_level === 'critical') {
|
|
261
|
+
issues.push('Critical-risk dossier without signature');
|
|
262
|
+
if (riskLevel !== 'critical') riskLevel = 'high';
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
level: riskLevel,
|
|
267
|
+
issues,
|
|
268
|
+
recommendation: shouldBlock ? 'BLOCK' : 'ALLOW',
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Main verification function
|
|
273
|
+
async function verifyDossier(input, options) {
|
|
274
|
+
try {
|
|
275
|
+
log(`\n${colors.bright}🔐 Dossier Verification Tool${colors.reset}\n`);
|
|
276
|
+
|
|
277
|
+
// Determine if input is URL or file
|
|
278
|
+
const isUrl = input.startsWith('http://') || input.startsWith('https://');
|
|
279
|
+
let content;
|
|
280
|
+
let displayPath = input;
|
|
281
|
+
|
|
282
|
+
if (isUrl) {
|
|
283
|
+
info(`Downloading: ${input}`);
|
|
284
|
+
content = await downloadFile(input);
|
|
285
|
+
success('Downloaded successfully');
|
|
286
|
+
} else {
|
|
287
|
+
info(`Reading: ${input}`);
|
|
288
|
+
content = fs.readFileSync(input, 'utf8');
|
|
289
|
+
displayPath = path.resolve(input);
|
|
290
|
+
success('File read successfully');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Parse dossier
|
|
294
|
+
info('Parsing dossier...');
|
|
295
|
+
const { frontmatter, body } = parseDossier(content);
|
|
296
|
+
success(`Parsed: ${frontmatter.title} v${frontmatter.version}`);
|
|
297
|
+
|
|
298
|
+
if (options.verbose) {
|
|
299
|
+
console.log(`\n${colors.bright}Dossier Metadata:${colors.reset}`);
|
|
300
|
+
console.log(` Title: ${frontmatter.title}`);
|
|
301
|
+
console.log(` Version: ${frontmatter.version}`);
|
|
302
|
+
console.log(` Risk Level: ${frontmatter.risk_level}`);
|
|
303
|
+
console.log(` Protocol: ${frontmatter.protocol_version}`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Verify checksum
|
|
307
|
+
console.log(`\n${colors.bright}📊 Integrity Check:${colors.reset}`);
|
|
308
|
+
const checksumResult = verifyChecksum(body, frontmatter.checksum?.hash);
|
|
309
|
+
|
|
310
|
+
if (checksumResult.passed) {
|
|
311
|
+
success('Checksum VALID - content has not been tampered with');
|
|
312
|
+
if (options.verbose) {
|
|
313
|
+
console.log(` Hash: ${checksumResult.actual}`);
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
error('Checksum INVALID - content has been modified!');
|
|
317
|
+
if (options.verbose) {
|
|
318
|
+
console.log(` Declared: ${checksumResult.declared}`);
|
|
319
|
+
console.log(` Actual: ${checksumResult.actual}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Verify signature
|
|
324
|
+
console.log(`\n${colors.bright}🔏 Authenticity Check:${colors.reset}`);
|
|
325
|
+
const signatureResult = await checkSignature(body, frontmatter);
|
|
326
|
+
|
|
327
|
+
if (signatureResult.present) {
|
|
328
|
+
if (signatureResult.verified && signatureResult.trusted) {
|
|
329
|
+
success('Signature VERIFIED - from trusted author');
|
|
330
|
+
} else if (signatureResult.verified && !signatureResult.trusted) {
|
|
331
|
+
warning(signatureResult.message);
|
|
332
|
+
if (frontmatter.signature?.signed_by) {
|
|
333
|
+
console.log(` Signed by: ${frontmatter.signature.signed_by}`);
|
|
334
|
+
}
|
|
335
|
+
console.log(`\n ${colors.cyan}To trust this signer, run:${colors.reset}`);
|
|
336
|
+
const publicKey = frontmatter.signature.public_key || frontmatter.signature.key_id;
|
|
337
|
+
const identifier = frontmatter.signature.signed_by
|
|
338
|
+
? frontmatter.signature.signed_by.split('<')[0].trim().toLowerCase().replace(/\s+/g, '-')
|
|
339
|
+
: 'unknown-signer';
|
|
340
|
+
console.log(` ${colors.bright}dossier keys add "${publicKey}" "${identifier}"${colors.reset}\n`);
|
|
341
|
+
} else {
|
|
342
|
+
warning(signatureResult.message);
|
|
343
|
+
if (frontmatter.signature?.signed_by) {
|
|
344
|
+
console.log(` Signed by: ${frontmatter.signature.signed_by}`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
} else {
|
|
348
|
+
warning('No signature present (dossier is unsigned)');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Assess risk
|
|
352
|
+
console.log(`\n${colors.bright}🔴 Risk Assessment:${colors.reset}`);
|
|
353
|
+
const risk = assessRisk(frontmatter, checksumResult, signatureResult);
|
|
354
|
+
|
|
355
|
+
const riskColors = {
|
|
356
|
+
low: 'green',
|
|
357
|
+
medium: 'yellow',
|
|
358
|
+
high: 'yellow',
|
|
359
|
+
critical: 'red',
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
log(` Risk Level: ${risk.level.toUpperCase()}`, riskColors[risk.level]);
|
|
363
|
+
|
|
364
|
+
if (risk.issues.length > 0) {
|
|
365
|
+
console.log(`\n Issues Found:`);
|
|
366
|
+
risk.issues.forEach(issue => {
|
|
367
|
+
console.log(` - ${issue}`);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Recommendation
|
|
372
|
+
console.log(`\n${colors.bright}Recommendation:${colors.reset}`, risk.recommendation);
|
|
373
|
+
|
|
374
|
+
if (risk.recommendation === 'BLOCK') {
|
|
375
|
+
error('\nDO NOT EXECUTE this dossier');
|
|
376
|
+
console.log(' Security verification failed.');
|
|
377
|
+
console.log(' This dossier may have been tampered with or is from an untrusted source.\n');
|
|
378
|
+
return false;
|
|
379
|
+
} else if (risk.level === 'medium' || risk.level === 'high') {
|
|
380
|
+
warning('\nProceed with CAUTION');
|
|
381
|
+
console.log(' Review the dossier code before executing.');
|
|
382
|
+
console.log(' Consider the risk level and your trust in the source.\n');
|
|
383
|
+
return true;
|
|
384
|
+
} else {
|
|
385
|
+
success('\nSafe to execute');
|
|
386
|
+
console.log(' Dossier passed security verification.\n');
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
} catch (err) {
|
|
391
|
+
error(`\nVerification failed: ${err.message}`);
|
|
392
|
+
if (options.verbose) {
|
|
393
|
+
console.error(err);
|
|
394
|
+
}
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Main entry point
|
|
400
|
+
async function main() {
|
|
401
|
+
const options = parseArgs();
|
|
402
|
+
|
|
403
|
+
if (options.help || !options.input) {
|
|
404
|
+
showHelp();
|
|
405
|
+
process.exit(options.help ? 0 : 2);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const passed = await verifyDossier(options.input, options);
|
|
409
|
+
|
|
410
|
+
if (!passed) {
|
|
411
|
+
process.exit(1); // Verification failed
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (options.run) {
|
|
415
|
+
warning('\n--run flag not yet implemented');
|
|
416
|
+
warning('For now, manually execute the dossier if verification passed');
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (options.outputPath) {
|
|
420
|
+
// Would output the path to downloaded file
|
|
421
|
+
warning('\n--output-path flag not yet implemented');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
process.exit(0); // Success
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Run if called directly
|
|
428
|
+
if (require.main === module) {
|
|
429
|
+
main().catch(err => {
|
|
430
|
+
error(`Fatal error: ${err.message}`);
|
|
431
|
+
process.exit(2);
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
module.exports = { verifyDossier, parseDossier, verifyChecksum };
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dossier CLI - Main entry point (TypeScript)
|
|
4
|
+
* Sets up Commander program and registers all commands.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
// Package info
|
|
9
|
+
const pkg = require('../package.json');
|
|
10
|
+
const cache_1 = require("./commands/cache");
|
|
11
|
+
const checksum_1 = require("./commands/checksum");
|
|
12
|
+
const config_cmd_1 = require("./commands/config-cmd");
|
|
13
|
+
const create_1 = require("./commands/create");
|
|
14
|
+
const export_1 = require("./commands/export");
|
|
15
|
+
const format_1 = require("./commands/format");
|
|
16
|
+
const from_file_1 = require("./commands/from-file");
|
|
17
|
+
const get_1 = require("./commands/get");
|
|
18
|
+
const info_1 = require("./commands/info");
|
|
19
|
+
const init_1 = require("./commands/init");
|
|
20
|
+
const install_skill_1 = require("./commands/install-skill");
|
|
21
|
+
const keys_1 = require("./commands/keys");
|
|
22
|
+
const lint_1 = require("./commands/lint");
|
|
23
|
+
const list_1 = require("./commands/list");
|
|
24
|
+
const login_1 = require("./commands/login");
|
|
25
|
+
const logout_1 = require("./commands/logout");
|
|
26
|
+
const prompt_hook_1 = require("./commands/prompt-hook");
|
|
27
|
+
const publish_1 = require("./commands/publish");
|
|
28
|
+
const pull_1 = require("./commands/pull");
|
|
29
|
+
const remove_1 = require("./commands/remove");
|
|
30
|
+
const reset_hooks_1 = require("./commands/reset-hooks");
|
|
31
|
+
const run_1 = require("./commands/run");
|
|
32
|
+
const search_1 = require("./commands/search");
|
|
33
|
+
const sign_1 = require("./commands/sign");
|
|
34
|
+
const validate_1 = require("./commands/validate");
|
|
35
|
+
// Import command registrations
|
|
36
|
+
const verify_1 = require("./commands/verify");
|
|
37
|
+
const whoami_1 = require("./commands/whoami");
|
|
38
|
+
// Setup program
|
|
39
|
+
commander_1.program
|
|
40
|
+
.name('ai-dossier')
|
|
41
|
+
.description('CLI tool for creating, verifying, and executing dossiers')
|
|
42
|
+
.version(pkg.version);
|
|
43
|
+
// Register all commands
|
|
44
|
+
(0, verify_1.registerVerifyCommand)(commander_1.program);
|
|
45
|
+
(0, run_1.registerRunCommand)(commander_1.program);
|
|
46
|
+
(0, create_1.registerCreateCommand)(commander_1.program);
|
|
47
|
+
(0, config_cmd_1.registerConfigCommand)(commander_1.program);
|
|
48
|
+
(0, list_1.registerListCommand)(commander_1.program);
|
|
49
|
+
(0, search_1.registerSearchCommand)(commander_1.program);
|
|
50
|
+
(0, info_1.registerInfoCommand)(commander_1.program);
|
|
51
|
+
(0, sign_1.registerSignCommand)(commander_1.program);
|
|
52
|
+
(0, publish_1.registerPublishCommand)(commander_1.program);
|
|
53
|
+
(0, remove_1.registerRemoveCommand)(commander_1.program);
|
|
54
|
+
(0, login_1.registerLoginCommand)(commander_1.program);
|
|
55
|
+
(0, logout_1.registerLogoutCommand)(commander_1.program);
|
|
56
|
+
(0, whoami_1.registerWhoamiCommand)(commander_1.program);
|
|
57
|
+
(0, checksum_1.registerChecksumCommand)(commander_1.program);
|
|
58
|
+
(0, validate_1.registerValidateCommand)(commander_1.program);
|
|
59
|
+
(0, init_1.registerInitCommand)(commander_1.program);
|
|
60
|
+
(0, reset_hooks_1.registerResetHooksCommand)(commander_1.program);
|
|
61
|
+
(0, prompt_hook_1.registerPromptHookCommand)(commander_1.program);
|
|
62
|
+
(0, pull_1.registerPullCommand)(commander_1.program);
|
|
63
|
+
(0, export_1.registerExportCommand)(commander_1.program);
|
|
64
|
+
(0, cache_1.registerCacheCommand)(commander_1.program);
|
|
65
|
+
(0, install_skill_1.registerInstallSkillCommand)(commander_1.program);
|
|
66
|
+
(0, keys_1.registerKeysCommand)(commander_1.program);
|
|
67
|
+
(0, lint_1.registerLintCommand)(commander_1.program);
|
|
68
|
+
(0, format_1.registerFormatCommand)(commander_1.program);
|
|
69
|
+
(0, from_file_1.registerFromFileCommand)(commander_1.program);
|
|
70
|
+
(0, get_1.registerGetCommand)(commander_1.program);
|
|
71
|
+
// Parse and execute
|
|
72
|
+
commander_1.program.parse(process.argv);
|
|
73
|
+
// Show help if no command provided
|
|
74
|
+
if (!process.argv.slice(2).length) {
|
|
75
|
+
commander_1.program.outputHelp();
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAEH,yCAAoC;AAEpC,eAAe;AACf,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEvC,4CAAwD;AACxD,kDAA8D;AAC9D,sDAA8D;AAC9D,8CAA0D;AAC1D,8CAA0D;AAC1D,8CAA0D;AAC1D,oDAA+D;AAC/D,wCAAoD;AACpD,0CAAsD;AACtD,0CAAsD;AACtD,4DAAuE;AACvE,0CAAsD;AACtD,0CAAsD;AACtD,0CAAsD;AACtD,4CAAwD;AACxD,8CAA0D;AAC1D,wDAAmE;AACnE,gDAA4D;AAC5D,0CAAsD;AACtD,8CAA0D;AAC1D,wDAAmE;AACnE,wCAAoD;AACpD,8CAA0D;AAC1D,0CAAsD;AACtD,kDAA8D;AAC9D,+BAA+B;AAC/B,8CAA0D;AAC1D,8CAA0D;AAE1D,gBAAgB;AAChB,mBAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,0DAA0D,CAAC;KACvE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,wBAAwB;AACxB,IAAA,8BAAqB,EAAC,mBAAO,CAAC,CAAC;AAC/B,IAAA,wBAAkB,EAAC,mBAAO,CAAC,CAAC;AAC5B,IAAA,8BAAqB,EAAC,mBAAO,CAAC,CAAC;AAC/B,IAAA,kCAAqB,EAAC,mBAAO,CAAC,CAAC;AAC/B,IAAA,0BAAmB,EAAC,mBAAO,CAAC,CAAC;AAC7B,IAAA,8BAAqB,EAAC,mBAAO,CAAC,CAAC;AAC/B,IAAA,0BAAmB,EAAC,mBAAO,CAAC,CAAC;AAC7B,IAAA,0BAAmB,EAAC,mBAAO,CAAC,CAAC;AAC7B,IAAA,gCAAsB,EAAC,mBAAO,CAAC,CAAC;AAChC,IAAA,8BAAqB,EAAC,mBAAO,CAAC,CAAC;AAC/B,IAAA,4BAAoB,EAAC,mBAAO,CAAC,CAAC;AAC9B,IAAA,8BAAqB,EAAC,mBAAO,CAAC,CAAC;AAC/B,IAAA,8BAAqB,EAAC,mBAAO,CAAC,CAAC;AAC/B,IAAA,kCAAuB,EAAC,mBAAO,CAAC,CAAC;AACjC,IAAA,kCAAuB,EAAC,mBAAO,CAAC,CAAC;AACjC,IAAA,0BAAmB,EAAC,mBAAO,CAAC,CAAC;AAC7B,IAAA,uCAAyB,EAAC,mBAAO,CAAC,CAAC;AACnC,IAAA,uCAAyB,EAAC,mBAAO,CAAC,CAAC;AACnC,IAAA,0BAAmB,EAAC,mBAAO,CAAC,CAAC;AAC7B,IAAA,8BAAqB,EAAC,mBAAO,CAAC,CAAC;AAC/B,IAAA,4BAAoB,EAAC,mBAAO,CAAC,CAAC;AAC9B,IAAA,2CAA2B,EAAC,mBAAO,CAAC,CAAC;AACrC,IAAA,0BAAmB,EAAC,mBAAO,CAAC,CAAC;AAC7B,IAAA,0BAAmB,EAAC,mBAAO,CAAC,CAAC;AAC7B,IAAA,8BAAqB,EAAC,mBAAO,CAAC,CAAC;AAC/B,IAAA,mCAAuB,EAAC,mBAAO,CAAC,CAAC;AACjC,IAAA,wBAAkB,EAAC,mBAAO,CAAC,CAAC;AAE5B,oBAAoB;AACpB,mBAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5B,mCAAmC;AACnC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,mBAAO,CAAC,UAAU,EAAE,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/commands/cache.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuO3D"}
|