@josfox/jos 4.1.0 → 5.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.
Files changed (64) hide show
  1. package/LICENSE +208 -21
  2. package/PROMISES.md +52 -0
  3. package/README.md +334 -138
  4. package/SECURITY-MCP.md +117 -0
  5. package/SECURITY-SECRETS.md +108 -0
  6. package/STYLE.md +23 -0
  7. package/bin/jos +170 -160
  8. package/examples/01-hello.jos +44 -0
  9. package/examples/02-criteria.jos +54 -0
  10. package/examples/03-flow-gate.jos +83 -0
  11. package/examples/04-use-template/README.md +22 -0
  12. package/examples/04-use-template/lib/greeting.jos +56 -0
  13. package/examples/04-use-template/use.jos +64 -0
  14. package/examples/05-sealed/.jos/manifest.jos +53 -0
  15. package/examples/05-sealed/README.md +25 -0
  16. package/examples/06-requires.jos +42 -0
  17. package/examples/07-sandbox.jos +39 -0
  18. package/examples/08-ids-ledger-meter.md +26 -0
  19. package/examples/10-ollama.jos +47 -0
  20. package/examples/11-openai.jos +47 -0
  21. package/examples/12-gemini.jos +47 -0
  22. package/examples/13-anthropic.jos +47 -0
  23. package/examples/14-serve.jos +88 -0
  24. package/examples/README.md +105 -0
  25. package/examples/hello.jos +47 -0
  26. package/package.json +57 -40
  27. package/promises.json +894 -0
  28. package/src/args.js +47 -0
  29. package/src/commands/_frozen/add.README.md +1 -0
  30. package/src/commands/_frozen/get.README.md +1 -0
  31. package/src/commands/_frozen/llm.README.md +1 -0
  32. package/src/commands/_frozen/prompts.README.md +1 -0
  33. package/src/commands/_frozen/repo.README.md +1 -0
  34. package/src/commands/_frozen/serve-explorer.README.md +1 -0
  35. package/src/commands/ids.js +101 -0
  36. package/src/commands/init.js +128 -162
  37. package/src/commands/ledger.js +94 -0
  38. package/src/commands/meter.js +96 -0
  39. package/src/commands/migrate.js +214 -0
  40. package/src/commands/overview.js +118 -0
  41. package/src/commands/run.js +194 -244
  42. package/src/commands/runs.js +102 -0
  43. package/src/commands/secrets.js +717 -137
  44. package/src/commands/start.js +210 -0
  45. package/src/commands/validate.js +178 -194
  46. package/src/commands/verify.js +56 -98
  47. package/src/commands/version.js +23 -0
  48. package/src/intention.js +114 -0
  49. package/src/kernel.js +69 -0
  50. package/src/ledgers.js +57 -0
  51. package/src/mcp-guide.js +138 -0
  52. package/src/mcp.js +734 -0
  53. package/src/serve.js +48 -781
  54. package/src/ui.js +98 -0
  55. package/src/adapters/index.js +0 -16
  56. package/src/adapters/native.js +0 -41
  57. package/src/adapters/ollama.js +0 -140
  58. package/src/commands/add.js +0 -145
  59. package/src/commands/get.js +0 -245
  60. package/src/commands/llm.js +0 -159
  61. package/src/commands/prompts.js +0 -229
  62. package/src/commands/repo.js +0 -139
  63. package/src/utils/crypto.js +0 -41
  64. package/src/utils/request.js +0 -41
@@ -1,159 +0,0 @@
1
- /**
2
- * JOS LLM Command - Execute artifacts via LLM
3
- */
4
- const fs = require('fs');
5
- const path = require('path');
6
- const AdapterFactory = require('../adapters');
7
- const { calculateChecksum, verifyIntegrity } = require('../utils/crypto');
8
-
9
- // AURORA colors
10
- const C = {
11
- reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
12
- purple: '\x1b[38;5;135m', cyan: '\x1b[38;5;51m',
13
- green: '\x1b[38;5;78m', red: '\x1b[38;5;196m',
14
- yellow: '\x1b[38;5;220m', white: '\x1b[38;5;255m',
15
- gray: '\x1b[38;5;245m', blue: '\x1b[38;5;39m'
16
- };
17
-
18
- const DEFAULT_PROVIDER = 'https://api.josfox.cloud/prompts';
19
-
20
- function getProviderConfig(home) {
21
- const configPath = path.join(home, 'prompts', 'providers.json');
22
- if (fs.existsSync(configPath)) {
23
- return JSON.parse(fs.readFileSync(configPath, 'utf8'));
24
- }
25
- return {
26
- providers: {
27
- 'josfox-cloud': { url: DEFAULT_PROVIDER, type: 'native', description: 'Official JOSFOX Cloud provider' }
28
- },
29
- default: 'josfox-cloud'
30
- };
31
- }
32
-
33
- function getApiKey(home) {
34
- const secretsPath = path.join(home, 'secrets', 'vault.json');
35
- if (fs.existsSync(secretsPath)) {
36
- try {
37
- const secrets = JSON.parse(fs.readFileSync(secretsPath, 'utf8'));
38
- return secrets.JOSFOX_API_KEY || secrets.OPENAI_API_KEY || null;
39
- } catch { return null; }
40
- }
41
- return process.env.JOSFOX_API_KEY || process.env.OPENAI_API_KEY || null;
42
- }
43
-
44
- exports.execute = async (args, home) => {
45
- if (args.includes('--help') || args.length === 0) {
46
- console.log(`
47
- ${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}
48
- ${C.bold}JOS LLM EXECUTION${C.reset} // Execute artifacts via LLM
49
- ${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}
50
-
51
- ${C.bold}Usage:${C.reset}
52
- jos llm <artifact.jos|.fox> [options]
53
-
54
- ${C.bold}Options:${C.reset}
55
- --model <name> Target model
56
- --provider <url> API endpoint or Ollama URL
57
- --type <native|ollama> Provider type
58
- --production Enforce strict SHA integrity for .jos
59
-
60
- ${C.bold}Notes:${C.reset}
61
- • .fox artifacts ALWAYS require valid SHA checksums.
62
- • .jos artifacts require SHA only in --production mode.
63
- `);
64
- return;
65
- }
66
-
67
- const target = args[0];
68
- const absPath = path.resolve(target);
69
-
70
- if (!fs.existsSync(absPath)) {
71
- console.log(`${C.red}✖ File not found: ${target}${C.reset}`);
72
- return;
73
- }
74
-
75
- const ext = path.extname(absPath);
76
- if (ext !== '.jos' && ext !== '.fox') {
77
- console.log(`${C.red}✖ Invalid file type. Must be .jos or .fox${C.reset}`);
78
- return;
79
- }
80
-
81
- console.log(`\n${C.cyan}${C.bold}JOS LLM EXECUTION${C.reset}`);
82
- console.log(`${C.dim}Artifact: ${path.basename(absPath)}${C.reset}`);
83
-
84
- // READ & PARSE
85
- const rawContent = fs.readFileSync(absPath, 'utf8');
86
- let artifact;
87
- try {
88
- artifact = JSON.parse(rawContent);
89
- } catch (e) {
90
- console.log(`${C.red}✖ Invalid JSON: ${e.message}${C.reset}`);
91
- return;
92
- }
93
-
94
- // INTEGRITY CHECK
95
- const isProduction = args.includes('--production') || process.env.NODE_ENV === 'production';
96
- const enforceSha = ext === '.fox' || isProduction;
97
-
98
- if (enforceSha) {
99
- // TODO: Strict canonical verification. For now, we check if meta.checksum exists.
100
- if (!artifact.meta || !artifact.meta.checksum) {
101
- console.log(`${C.red}✖ Integrity Check Failed: Missing checksum in ${ext} file${C.reset}`);
102
- console.log(`${C.gray} (.fox files and production .jos require signed checksums)${C.reset}`);
103
- return;
104
- }
105
- // const isValid = verifyIntegrity(artifact, rawContent); // Placeholder for strict check
106
- // if (!isValid) { ... }
107
- console.log(`${C.green}✓ Integrity Validated (Signature present)${C.reset}`);
108
- } else {
109
- console.log(`${C.yellow}⚠ Integrity Check Skipped (Dev Mode)${C.reset}`);
110
- }
111
-
112
- // RESOLVE PROVIDER
113
- const config = getProviderConfig(home);
114
- const providerKey = config.default;
115
- const providerCfg = config.providers[providerKey] || config.providers['josfox-cloud'];
116
-
117
- let providerUrl = providerCfg.url;
118
- let providerType = providerCfg.type || 'native';
119
-
120
- if (args.includes('--provider')) {
121
- const pName = args[args.indexOf('--provider') + 1];
122
- if (config.providers[pName]) {
123
- providerUrl = config.providers[pName].url;
124
- providerType = config.providers[pName].type || 'native';
125
- } else {
126
- providerUrl = pName;
127
- }
128
- }
129
- if (args.includes('--type')) {
130
- providerType = args[args.indexOf('--type') + 1];
131
- }
132
-
133
- const model = args.includes('--model') ? args[args.indexOf('--model') + 1] : null;
134
- const apiKey = getApiKey(home);
135
-
136
- console.log(`${C.dim}Provider: ${providerUrl} (${providerType})${C.reset}`);
137
- console.log(`${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}\n`);
138
-
139
- // EXECUTE
140
- const Adapter = AdapterFactory.getAdapter(providerType);
141
-
142
- try {
143
- console.log(`${C.yellow}⏳ Executing via LLM...${C.reset}`);
144
-
145
- const result = await Adapter.execute(artifact, providerUrl, model, apiKey);
146
-
147
- console.log(`\n${C.green}✓ Execution Complete${C.reset}`);
148
- console.log(`${C.cyan}Status:${C.reset} ${result.status}`);
149
- console.log(`\n${C.bold}Output:${C.reset}\n`);
150
- console.log(result.output);
151
- console.log('');
152
-
153
- } catch (e) {
154
- console.log(`${C.red}✖ Execution Failed: ${e.message}${C.reset}`);
155
- if (e.message && e.message.includes('ECONNREFUSED')) {
156
- console.log(`${C.gray} Is the provider running?${C.reset}`);
157
- }
158
- }
159
- };
@@ -1,229 +0,0 @@
1
- /**
2
- * JOS PROMPTS Command - Prompt optimization via open API or Local LLM
3
- * Complies with JOS Prompts API Specification
4
- * Format version v0.0.8 — Specification maturity v0.1.0 (Alpha)
5
- */
6
-
7
- const fs = require('fs');
8
- const path = require('path');
9
- const AdapterFactory = require('../adapters');
10
-
11
- // AURORA colors
12
- const C = {
13
- reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
14
- purple: '\x1b[38;5;135m', cyan: '\x1b[38;5;51m',
15
- green: '\x1b[38;5;78m', red: '\x1b[38;5;196m',
16
- yellow: '\x1b[38;5;220m', white: '\x1b[38;5;255m',
17
- gray: '\x1b[38;5;245m', blue: '\x1b[38;5;39m'
18
- };
19
-
20
- const DEFAULT_PROVIDER = 'https://api.josfox.cloud/prompts';
21
-
22
- function getProviderConfig(home) {
23
- const configPath = path.join(home, 'prompts', 'providers.json');
24
- if (fs.existsSync(configPath)) {
25
- return JSON.parse(fs.readFileSync(configPath, 'utf8'));
26
- }
27
- return {
28
- providers: {
29
- 'josfox-cloud': { url: DEFAULT_PROVIDER, type: 'native', description: 'Official JOSFOX Cloud provider' }
30
- },
31
- default: 'josfox-cloud'
32
- };
33
- }
34
-
35
- function saveProviderConfig(home, config) {
36
- const configDir = path.join(home, 'prompts');
37
- if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true });
38
- fs.writeFileSync(path.join(configDir, 'providers.json'), JSON.stringify(config, null, 2));
39
- }
40
-
41
- function getApiKey(home) {
42
- const secretsPath = path.join(home, 'secrets', 'vault.json');
43
- if (fs.existsSync(secretsPath)) {
44
- try {
45
- const secrets = JSON.parse(fs.readFileSync(secretsPath, 'utf8'));
46
- return secrets.JOSFOX_API_KEY || secrets.OPENAI_API_KEY || null;
47
- } catch { return null; }
48
- }
49
- return process.env.JOSFOX_API_KEY || process.env.OPENAI_API_KEY || null;
50
- }
51
-
52
- exports.execute = async (args, home) => {
53
- const subcommand = args[0];
54
-
55
- if (args.includes('--help') || !subcommand) {
56
- console.log(`
57
- ${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}
58
- ${C.bold}JOS PROMPTS${C.reset} // Prompt optimization via API or Local LLM
59
- ${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}
60
-
61
- ${C.bold}Usage:${C.reset}
62
- jos prompts <subcommand> [options]
63
-
64
- ${C.bold}Subcommands:${C.reset}
65
- optimize <file.jos> Optimize prompts in artifact
66
- validate <file.jos> Check prompt quality
67
- generate "<intention>" Create new .jos from text
68
- provider Manage API providers
69
-
70
- ${C.bold}Options:${C.reset}
71
- --model <name> Target model (e.g., gpt-4o, llama3)
72
- --provider <url> API endpoint or Ollama URL
73
- --type <native|ollama> Provider type (default: native)
74
- --dry-run Show changes without writing
75
- --output <file> Output file path
76
-
77
- ${C.bold}Experimental:${C.reset}
78
- Ollama Adapter supported via --type ollama
79
-
80
- ${C.bold}Examples:${C.reset}
81
- jos prompts optimize flow.jos --model llama3 --provider local
82
- jos prompts provider add local http://localhost:11434 ollama
83
- jos prompts generate "Deploy app"
84
- `);
85
- return;
86
- }
87
-
88
- // Provider management
89
- if (subcommand === 'provider') {
90
- const action = args[1];
91
- const config = getProviderConfig(home);
92
-
93
- if (action === 'list' || !action) {
94
- console.log(`\n${C.cyan}${C.bold}Configured Providers${C.reset}\n`);
95
- for (const [name, info] of Object.entries(config.providers)) {
96
- const isDefault = config.default === name;
97
- const marker = isDefault ? `${C.green}★${C.reset}` : ' ';
98
- const typeLabel = info.type === 'ollama' ? `${C.yellow}[Ollama]${C.reset}` : `${C.blue}[Native]${C.reset}`;
99
- console.log(` ${marker} ${C.bold}${name}${C.reset} ${typeLabel}`);
100
- console.log(` ${C.gray}${info.url}${C.reset}`);
101
- }
102
- console.log('');
103
- return;
104
- }
105
-
106
- if (action === 'add') {
107
- const name = args[2];
108
- const url = args[3];
109
- const type = args[4] || 'native'; // 'native' or 'ollama'
110
- if (!name || !url) {
111
- console.log(`${C.red}✖ Usage: jos prompts provider add <name> <url> [type]${C.reset}`);
112
- return;
113
- }
114
- config.providers[name] = { url, type };
115
- saveProviderConfig(home, config);
116
- console.log(`${C.green}✓ Added provider: ${name} (${type})${C.reset}`);
117
- return;
118
- }
119
-
120
- if (action === 'remove') {
121
- const name = args[2];
122
- if (!config.providers[name]) return console.log(`${C.red}✖ Not found${C.reset}`);
123
- delete config.providers[name];
124
- if (config.default === name) config.default = 'josfox-cloud';
125
- saveProviderConfig(home, config);
126
- console.log(`${C.green}✓ Removed${C.reset}`);
127
- return;
128
- }
129
- if (action === 'default') {
130
- const name = args[2];
131
- if (!config.providers[name]) return console.log(`${C.red}✖ Not found${C.reset}`);
132
- config.default = name;
133
- saveProviderConfig(home, config);
134
- console.log(`${C.green}✓ Default: ${name}${C.reset}`);
135
- return;
136
- }
137
- return;
138
- }
139
-
140
- // Resolve Provider
141
- const config = getProviderConfig(home);
142
- const providerKey = config.default;
143
- const providerCfg = config.providers[providerKey] || config.providers['josfox-cloud'];
144
-
145
- // Allow overrides
146
- let providerUrl = providerCfg.url;
147
- let providerType = providerCfg.type || 'native';
148
-
149
- if (args.includes('--provider')) {
150
- const pName = args[args.indexOf('--provider') + 1];
151
- if (config.providers[pName]) {
152
- providerUrl = config.providers[pName].url;
153
- providerType = config.providers[pName].type || 'native';
154
- } else {
155
- // Assume direct URL, default to native unless --type specified
156
- providerUrl = pName;
157
- }
158
- }
159
- if (args.includes('--type')) {
160
- providerType = args[args.indexOf('--type') + 1];
161
- }
162
-
163
- const apiKey = getApiKey(home);
164
- const model = args.includes('--model') ? args[args.indexOf('--model') + 1] : null;
165
-
166
- // Use Factory
167
- const Adapter = AdapterFactory.getAdapter(providerType);
168
-
169
- console.log(`\n${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}`);
170
- console.log(`${C.cyan}${C.bold}JOS PROMPTS${C.reset} [${subcommand.toUpperCase()}]`);
171
- console.log(`${C.dim}Provider: ${providerUrl} (${providerType})${C.reset}`);
172
- if (model) console.log(`${C.dim}Model: ${model}${C.reset}`);
173
- console.log(`${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}\n`);
174
-
175
- try {
176
- if (subcommand === 'optimize') {
177
- const target = args[1];
178
- if (!target) return console.log(`${C.red}✖ Target file required${C.reset}`);
179
-
180
- const artifactPath = path.resolve(target.endsWith('.jos') ? target : target + '.jos');
181
- const artifact = JSON.parse(fs.readFileSync(artifactPath, 'utf8'));
182
-
183
- console.log(`${C.yellow}⏳ Optimizing...${C.reset}`);
184
- const result = await Adapter.optimize(artifact, { optimization_goals: ['clarity'] }, providerUrl, model, apiKey);
185
-
186
- if (args.includes('--dry-run')) {
187
- console.log(JSON.stringify(result.optimized_artifact, null, 2));
188
- } else {
189
- fs.writeFileSync(artifactPath, JSON.stringify(result.optimized_artifact, null, 2));
190
- console.log(`${C.green}✓ Optimized artifact saved to ${path.basename(artifactPath)}${C.reset}`);
191
- }
192
- }
193
- else if (subcommand === 'validate') {
194
- const target = args[1];
195
- if (!target) return console.log(`${C.red}✖ Target file required${C.reset}`);
196
- const artifactPath = path.resolve(target.endsWith('.jos') ? target : target + '.jos');
197
- const artifact = JSON.parse(fs.readFileSync(artifactPath, 'utf8'));
198
-
199
- console.log(`${C.yellow}⏳ Validating...${C.reset}`);
200
- const result = await Adapter.validate(artifact, providerUrl, model, apiKey);
201
-
202
- console.log(`${C.cyan}Quality Score:${C.reset} ${result.quality_score}`);
203
- console.log(`${C.cyan}Valid:${C.reset} ${result.valid}`);
204
- if (result.issues) result.issues.forEach(i => console.log(` ${C.red}✖ ${i.message}${C.reset}`));
205
- }
206
- else if (subcommand === 'generate') {
207
- const intention = args[1];
208
- if (!intention) return console.log(`${C.red}✖ Intention required${C.reset}`);
209
-
210
- console.log(`${C.yellow}⏳ Generating...${C.reset}`);
211
- const result = await Adapter.generate(intention, { type: 'task' }, providerUrl, model, apiKey);
212
-
213
- const outputPath = args.includes('--output') ? args[args.indexOf('--output') + 1] : null;
214
- if (outputPath) {
215
- fs.writeFileSync(path.resolve(outputPath), JSON.stringify(result.artifact, null, 2));
216
- console.log(`${C.green}✓ Saved to ${outputPath}${C.reset}`);
217
- } else {
218
- console.log(JSON.stringify(result.artifact, null, 2));
219
- }
220
- } else {
221
- console.log(`${C.red}✖ Unknown subcommand${C.reset}`);
222
- }
223
- } catch (e) {
224
- console.error(`${C.red}✖ Error: ${e.message}${C.reset}`);
225
- if (e.message && e.message.includes('ECONNREFUSED')) {
226
- console.log(`${C.gray} Is Ollama running? (Try 'ollama serve')${C.reset}`);
227
- }
228
- }
229
- };
@@ -1,139 +0,0 @@
1
- /**
2
- * JOS REPO Command - Manage package repositories
3
- */
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
-
8
- // AURORA colors
9
- const C = {
10
- reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
11
- purple: '\x1b[38;5;135m', magenta: '\x1b[38;5;198m', cyan: '\x1b[38;5;51m',
12
- green: '\x1b[38;5;78m', red: '\x1b[38;5;196m', gray: '\x1b[38;5;245m',
13
- white: '\x1b[38;5;255m', yellow: '\x1b[38;5;220m'
14
- };
15
-
16
- const DEFAULT_REPOS = {
17
- default: 'https://registry.josfox.ai',
18
- local: '~/.jos/artifacts'
19
- };
20
-
21
- function getReposPath(home) {
22
- return path.join(home, 'repos.json');
23
- }
24
-
25
- function loadRepos(home) {
26
- const reposPath = getReposPath(home);
27
- if (fs.existsSync(reposPath)) {
28
- return { ...DEFAULT_REPOS, ...JSON.parse(fs.readFileSync(reposPath, 'utf8')) };
29
- }
30
- return { ...DEFAULT_REPOS };
31
- }
32
-
33
- function saveRepos(home, repos) {
34
- const reposPath = getReposPath(home);
35
- fs.writeFileSync(reposPath, JSON.stringify(repos, null, 2));
36
- }
37
-
38
- exports.execute = async (args, home) => {
39
- const action = args[0];
40
- const name = args[1];
41
- const url = args[2];
42
- const showHelp = args.includes('--help') || args.includes('-h');
43
-
44
- if (showHelp || !action) {
45
- console.log(`
46
- ${C.cyan}${C.bold}JOS REPO${C.reset} - Manage package repositories
47
-
48
- ${C.white}Usage:${C.reset} jos repo <action> [name] [url]
49
-
50
- ${C.white}Actions:${C.reset}
51
- list Show all configured repos
52
- add <name> <url> Add a new repository
53
- remove <name> Remove a repository
54
- default <name> Set default repository
55
-
56
- ${C.white}Examples:${C.reset}
57
- jos repo list
58
- jos repo add myserver http://192.168.1.10:1111
59
- jos repo add company https://artifacts.company.com
60
- jos repo default myserver
61
- jos repo remove myserver
62
-
63
- ${C.white}Using repos:${C.reset}
64
- jos get myserver:package-name
65
- jos get package --from 192.168.1.10
66
- `);
67
- return;
68
- }
69
-
70
- const repos = loadRepos(home);
71
-
72
- switch (action) {
73
- case 'list':
74
- console.log(`\n${C.cyan}${C.bold}📦 Configured Repositories${C.reset}\n`);
75
- Object.entries(repos).forEach(([name, url]) => {
76
- const isDefault = name === 'default';
77
- const isLocal = name === 'local';
78
- console.log(` ${C.white}${name}${C.reset}`);
79
- console.log(` ${C.gray}${url}${C.reset}`);
80
- if (isDefault) console.log(` ${C.green}★ Default registry${C.reset}`);
81
- if (isLocal) console.log(` ${C.cyan}📁 Local artifacts${C.reset}`);
82
- console.log();
83
- });
84
- console.log(`${C.dim}Config: ~/.jos/repos.json${C.reset}\n`);
85
- break;
86
-
87
- case 'add':
88
- if (!name || !url) {
89
- console.log(`${C.red}✖ Usage: jos repo add <name> <url>${C.reset}`);
90
- process.exit(1);
91
- }
92
- if (repos[name] && name !== 'default' && name !== 'local') {
93
- console.log(`${C.yellow}⚠ Repository '${name}' already exists. Updating...${C.reset}`);
94
- }
95
- repos[name] = url;
96
- saveRepos(home, repos);
97
- console.log(`${C.green}✓ Repository '${name}' added: ${url}${C.reset}`);
98
- console.log(`${C.dim} Use: jos get ${name}:package-name${C.reset}`);
99
- break;
100
-
101
- case 'remove':
102
- case 'rm':
103
- if (!name) {
104
- console.log(`${C.red}✖ Usage: jos repo remove <name>${C.reset}`);
105
- process.exit(1);
106
- }
107
- if (name === 'default' || name === 'local') {
108
- console.log(`${C.red}✖ Cannot remove built-in repository '${name}'${C.reset}`);
109
- process.exit(1);
110
- }
111
- if (!repos[name]) {
112
- console.log(`${C.yellow}⚠ Repository '${name}' not found${C.reset}`);
113
- process.exit(1);
114
- }
115
- delete repos[name];
116
- saveRepos(home, repos);
117
- console.log(`${C.green}✓ Repository '${name}' removed${C.reset}`);
118
- break;
119
-
120
- case 'default':
121
- if (!name) {
122
- console.log(`${C.white}Current default: ${repos.default}${C.reset}`);
123
- return;
124
- }
125
- if (!repos[name] && !name.startsWith('http')) {
126
- console.log(`${C.red}✖ Repository '${name}' not found${C.reset}`);
127
- process.exit(1);
128
- }
129
- repos.default = repos[name] || name;
130
- saveRepos(home, repos);
131
- console.log(`${C.green}✓ Default repository set to: ${repos.default}${C.reset}`);
132
- break;
133
-
134
- default:
135
- console.log(`${C.red}✖ Unknown action: ${action}${C.reset}`);
136
- console.log(`${C.dim}Run 'jos repo --help' for usage${C.reset}`);
137
- process.exit(1);
138
- }
139
- };
@@ -1,41 +0,0 @@
1
- const crypto = require('crypto');
2
- const fs = require('fs');
3
-
4
- /**
5
- * Calculate SHA-256 checksum of detailed content
6
- * @param {string} content
7
- * @returns {string} Hex digest
8
- */
9
- exports.calculateChecksum = (content) => {
10
- return crypto.createHash('sha256').update(content).digest('hex');
11
- };
12
-
13
- /**
14
- * Verify artifact integrity
15
- * @param {object} artifact Parsed JSON artifact
16
- * @param {string} rawContent Raw file content string
17
- * @returns {boolean} True if valid
18
- */
19
- exports.verifyIntegrity = (artifact, rawContent) => {
20
- const expected = artifact.meta?.checksum;
21
- if (!expected) return false;
22
-
23
- // To verify, we need to reproduce how the checksum was calculated.
24
- // Standard JOS: Checksum is of the minified JSON structure *excluding* the checksum field itself.
25
- // For simplicity in this v0.0.7 alpha, we'll assume it's a file-hash if provided externally,
26
- // OR we just trust the meta if we aren't doing strict reproduction yet.
27
-
28
- // STRICT MODE:
29
- // 1. Remove meta.checksum from object
30
- // 2. Canonicalize JSON
31
- // 3. Hash
32
-
33
- const clone = JSON.parse(JSON.stringify(artifact));
34
- if (clone.meta) delete clone.meta.checksum;
35
-
36
- // Canonical stringify (basic)
37
- const canonical = JSON.stringify(clone);
38
- const calculated = exports.calculateChecksum(canonical);
39
-
40
- return calculated === expected;
41
- };
@@ -1,41 +0,0 @@
1
- const https = require('https');
2
- const http = require('http');
3
-
4
- /**
5
- * Shared HTTP Request Helper
6
- */
7
- exports.apiRequest = async (url, method, body, apiKey) => {
8
- return new Promise((resolve, reject) => {
9
- const urlObj = new URL(url);
10
- const client = urlObj.protocol === 'https:' ? https : http;
11
-
12
- const options = {
13
- hostname: urlObj.hostname,
14
- port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
15
- path: urlObj.pathname,
16
- method: method,
17
- headers: {
18
- 'Content-Type': 'application/json',
19
- 'User-Agent': 'jos-cli/1.0'
20
- }
21
- };
22
-
23
- if (apiKey) options.headers['Authorization'] = `Bearer ${apiKey}`;
24
-
25
- const req = client.request(options, (res) => {
26
- let data = '';
27
- res.on('data', chunk => data += chunk);
28
- res.on('end', () => {
29
- try {
30
- resolve({ status: res.statusCode, data: JSON.parse(data) });
31
- } catch {
32
- resolve({ status: res.statusCode, data: data });
33
- }
34
- });
35
- });
36
-
37
- req.on('error', reject);
38
- if (body) req.write(JSON.stringify(body));
39
- req.end();
40
- });
41
- };