@josfox/jos 4.0.2 → 4.0.6
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/LICENSE +21 -0
- package/README.md +93 -195
- package/bin/jos +9 -6
- package/package.json +3 -3
- package/src/commands/add.js +145 -0
- package/src/commands/init.js +162 -0
- package/src/commands/run.js +172 -153
- package/src/commands/validate.js +190 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JOS VALIDATE Command - Validate .jos artifacts
|
|
3
|
+
* Checks schema, required keys, and integrity
|
|
4
|
+
* Format version v0.0.7 — Specification maturity v0.1.0 (Alpha)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const crypto = require('crypto');
|
|
10
|
+
|
|
11
|
+
const C = {
|
|
12
|
+
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
|
|
13
|
+
purple: '\x1b[38;5;135m', cyan: '\x1b[38;5;51m',
|
|
14
|
+
green: '\x1b[38;5;78m', red: '\x1b[38;5;196m',
|
|
15
|
+
yellow: '\x1b[38;5;220m', gray: '\x1b[38;5;245m'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Witness Logging
|
|
19
|
+
function logWitness(home, eventType, data) {
|
|
20
|
+
const runId = global.JOS_VALIDATE_ID || new Date().toISOString().replace(/[:.]/g, '-');
|
|
21
|
+
global.JOS_VALIDATE_ID = runId;
|
|
22
|
+
|
|
23
|
+
const runDir = path.join(home, 'runs', runId);
|
|
24
|
+
if (!fs.existsSync(runDir)) fs.mkdirSync(runDir, { recursive: true });
|
|
25
|
+
|
|
26
|
+
const event = {
|
|
27
|
+
timestamp: new Date().toISOString(),
|
|
28
|
+
type: `validate:${eventType}`,
|
|
29
|
+
...data
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
fs.appendFileSync(path.join(runDir, 'events.jsonl'), JSON.stringify(event) + '\n');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Required keys for JOSFOXAI + MAGIC compliance
|
|
36
|
+
const JOSFOXAI_KEYS = ['jos', 'orchestration_contract', 'security', 'files', 'orchestration', 'x_run_params', 'adaptive_ai', 'id_jos'];
|
|
37
|
+
const MAGIC_KEYS = ['meta', 'artifacts', 'guardrails', 'intention', 'capabilities'];
|
|
38
|
+
const ALL_REQUIRED_KEYS = [...JOSFOXAI_KEYS, ...MAGIC_KEYS];
|
|
39
|
+
|
|
40
|
+
exports.execute = async (args, home) => {
|
|
41
|
+
const target = args[0];
|
|
42
|
+
const insecure = args.includes('--insecure');
|
|
43
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
44
|
+
|
|
45
|
+
if (!target || args.includes('--help')) {
|
|
46
|
+
console.log(`
|
|
47
|
+
${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}
|
|
48
|
+
${C.bold}JOS VALIDATE${C.reset} // Validate .jos artifacts
|
|
49
|
+
${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}
|
|
50
|
+
|
|
51
|
+
${C.bold}Usage:${C.reset}
|
|
52
|
+
jos validate <file.jos> [options]
|
|
53
|
+
|
|
54
|
+
${C.bold}Options:${C.reset}
|
|
55
|
+
--insecure Skip integrity verification
|
|
56
|
+
--verbose Show detailed validation information
|
|
57
|
+
|
|
58
|
+
${C.bold}Validates:${C.reset}
|
|
59
|
+
1. JSON syntax
|
|
60
|
+
2. JOSFOXAI keys: ${JOSFOXAI_KEYS.join(', ')}
|
|
61
|
+
3. MAGIC keys: ${MAGIC_KEYS.join(', ')}
|
|
62
|
+
4. Integrity manifest (unless --insecure)
|
|
63
|
+
`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(`\n${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}`);
|
|
68
|
+
console.log(`${C.bold}JOS VALIDATE${C.reset} // ${C.gray}Format v0.0.7 — Spec v0.1.0 (Alpha)${C.reset}`);
|
|
69
|
+
console.log(`${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}`);
|
|
70
|
+
console.log(`\n${C.cyan}📋 Artifact:${C.reset} ${target}`);
|
|
71
|
+
|
|
72
|
+
logWitness(home, 'start', { artifact: target });
|
|
73
|
+
|
|
74
|
+
let allPassed = true;
|
|
75
|
+
const results = [];
|
|
76
|
+
|
|
77
|
+
// 1. JSON Syntax Check
|
|
78
|
+
let content, artifact;
|
|
79
|
+
try {
|
|
80
|
+
content = fs.readFileSync(target, 'utf8');
|
|
81
|
+
artifact = JSON.parse(content);
|
|
82
|
+
console.log(`${C.green} ✓ JSON Syntax Valid${C.reset}`);
|
|
83
|
+
results.push({ check: 'json_syntax', passed: true });
|
|
84
|
+
logWitness(home, 'check_passed', { check: 'json_syntax' });
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.log(`${C.red} ✖ JSON Error: ${e.message}${C.reset}`);
|
|
87
|
+
results.push({ check: 'json_syntax', passed: false, error: e.message });
|
|
88
|
+
logWitness(home, 'check_failed', { check: 'json_syntax', error: e.message });
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 2. JOSFOXAI Keys Check
|
|
93
|
+
const missingJosfoxai = JOSFOXAI_KEYS.filter(k => !artifact[k]);
|
|
94
|
+
if (missingJosfoxai.length > 0) {
|
|
95
|
+
console.log(`${C.red} ✖ Missing JOSFOXAI Keys: ${missingJosfoxai.join(', ')}${C.reset}`);
|
|
96
|
+
results.push({ check: 'josfoxai_keys', passed: false, missing: missingJosfoxai });
|
|
97
|
+
logWitness(home, 'check_failed', { check: 'josfoxai_keys', missing: missingJosfoxai });
|
|
98
|
+
allPassed = false;
|
|
99
|
+
} else {
|
|
100
|
+
console.log(`${C.green} ✓ JOSFOXAI Keys Present (${JOSFOXAI_KEYS.length}/${JOSFOXAI_KEYS.length})${C.reset}`);
|
|
101
|
+
results.push({ check: 'josfoxai_keys', passed: true });
|
|
102
|
+
logWitness(home, 'check_passed', { check: 'josfoxai_keys' });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 3. MAGIC Keys Check
|
|
106
|
+
const missingMagic = MAGIC_KEYS.filter(k => !artifact[k]);
|
|
107
|
+
if (missingMagic.length > 0) {
|
|
108
|
+
console.log(`${C.red} ✖ Missing MAGIC Keys: ${missingMagic.join(', ')}${C.reset}`);
|
|
109
|
+
results.push({ check: 'magic_keys', passed: false, missing: missingMagic });
|
|
110
|
+
logWitness(home, 'check_failed', { check: 'magic_keys', missing: missingMagic });
|
|
111
|
+
allPassed = false;
|
|
112
|
+
} else {
|
|
113
|
+
console.log(`${C.green} ✓ MAGIC Keys Present (${MAGIC_KEYS.length}/${MAGIC_KEYS.length})${C.reset}`);
|
|
114
|
+
results.push({ check: 'magic_keys', passed: true });
|
|
115
|
+
logWitness(home, 'check_passed', { check: 'magic_keys' });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 4. Orchestration Structure Check
|
|
119
|
+
if (artifact.orchestration) {
|
|
120
|
+
const hasDefinitions = artifact.orchestration.definitions && Object.keys(artifact.orchestration.definitions).length > 0;
|
|
121
|
+
const hasFlows = artifact.orchestration.flows && Object.keys(artifact.orchestration.flows).length > 0;
|
|
122
|
+
|
|
123
|
+
if (hasDefinitions && hasFlows) {
|
|
124
|
+
const defCount = Object.keys(artifact.orchestration.definitions).length;
|
|
125
|
+
const flowCount = Object.keys(artifact.orchestration.flows).length;
|
|
126
|
+
console.log(`${C.green} ✓ Orchestration Structure (${defCount} definitions, ${flowCount} flows)${C.reset}`);
|
|
127
|
+
results.push({ check: 'orchestration_structure', passed: true, definitions: defCount, flows: flowCount });
|
|
128
|
+
} else {
|
|
129
|
+
console.log(`${C.yellow} ⚠ Orchestration may be incomplete${C.reset}`);
|
|
130
|
+
results.push({ check: 'orchestration_structure', passed: true, warning: 'may be incomplete' });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 5. Integrity Check
|
|
135
|
+
if (!insecure) {
|
|
136
|
+
if (artifact.security?.integrity_ref) {
|
|
137
|
+
const manifestPath = path.resolve(path.dirname(target), artifact.security.integrity_ref);
|
|
138
|
+
if (fs.existsSync(manifestPath)) {
|
|
139
|
+
try {
|
|
140
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
141
|
+
const hash = crypto.createHash('sha256').update(content).digest('hex');
|
|
142
|
+
if (hash === manifest.artifact_sha256) {
|
|
143
|
+
console.log(`${C.green} ✓ Integrity Verified (SHA-256 match)${C.reset}`);
|
|
144
|
+
if (verbose) {
|
|
145
|
+
console.log(`${C.gray} Hash: ${hash}${C.reset}`);
|
|
146
|
+
}
|
|
147
|
+
results.push({ check: 'integrity', passed: true, hash: hash });
|
|
148
|
+
logWitness(home, 'integrity_verified', { hash: hash, manifest: artifact.security.integrity_ref });
|
|
149
|
+
} else {
|
|
150
|
+
console.log(`${C.red} ✖ Integrity Mismatch!${C.reset}`);
|
|
151
|
+
if (verbose) {
|
|
152
|
+
console.log(`${C.gray} Expected: ${manifest.artifact_sha256}${C.reset}`);
|
|
153
|
+
console.log(`${C.gray} Actual: ${hash}${C.reset}`);
|
|
154
|
+
}
|
|
155
|
+
results.push({ check: 'integrity', passed: false, expected: manifest.artifact_sha256, actual: hash });
|
|
156
|
+
logWitness(home, 'integrity_failed', { expected: manifest.artifact_sha256, actual: hash });
|
|
157
|
+
allPassed = false;
|
|
158
|
+
}
|
|
159
|
+
} catch (e) {
|
|
160
|
+
console.log(`${C.red} ✖ Invalid Manifest: ${e.message}${C.reset}`);
|
|
161
|
+
results.push({ check: 'integrity', passed: false, error: e.message });
|
|
162
|
+
allPassed = false;
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
console.log(`${C.red} ✖ Manifest Not Found: ${artifact.security.integrity_ref}${C.reset}`);
|
|
166
|
+
results.push({ check: 'integrity', passed: false, error: 'manifest not found' });
|
|
167
|
+
logWitness(home, 'integrity_failed', { error: 'manifest not found' });
|
|
168
|
+
allPassed = false;
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
console.log(`${C.yellow} ⚠ No Integrity Reference (security.integrity_ref)${C.reset}`);
|
|
172
|
+
results.push({ check: 'integrity', passed: true, warning: 'no integrity_ref' });
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
console.log(`${C.yellow} ⚠ Integrity Check Skipped (--insecure)${C.reset}`);
|
|
176
|
+
results.push({ check: 'integrity', skipped: true });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Summary
|
|
180
|
+
console.log('');
|
|
181
|
+
if (allPassed) {
|
|
182
|
+
console.log(`${C.green}${C.bold}✓ VALIDATION PASSED${C.reset}`);
|
|
183
|
+
logWitness(home, 'success', { results: results });
|
|
184
|
+
} else {
|
|
185
|
+
console.log(`${C.red}${C.bold}✖ VALIDATION FAILED${C.reset}`);
|
|
186
|
+
logWitness(home, 'failed', { results: results });
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
console.log('');
|
|
190
|
+
};
|