@josfox/jos 4.0.8 → 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 (59) hide show
  1. package/LICENSE +208 -21
  2. package/PROMISES.md +52 -0
  3. package/README.md +294 -146
  4. package/SECURITY-MCP.md +117 -0
  5. package/SECURITY-SECRETS.md +108 -0
  6. package/STYLE.md +23 -0
  7. package/bin/jos +139 -67
  8. package/bin/jos.cmd +5 -0
  9. package/examples/01-hello.jos +44 -0
  10. package/examples/02-criteria.jos +54 -0
  11. package/examples/03-flow-gate.jos +83 -0
  12. package/examples/04-use-template/README.md +22 -0
  13. package/examples/04-use-template/lib/greeting.jos +56 -0
  14. package/examples/04-use-template/use.jos +64 -0
  15. package/examples/05-sealed/.jos/manifest.jos +53 -0
  16. package/examples/05-sealed/README.md +25 -0
  17. package/examples/06-requires.jos +42 -0
  18. package/examples/07-sandbox.jos +39 -0
  19. package/examples/08-ids-ledger-meter.md +26 -0
  20. package/examples/10-ollama.jos +47 -0
  21. package/examples/11-openai.jos +47 -0
  22. package/examples/12-gemini.jos +47 -0
  23. package/examples/13-anthropic.jos +47 -0
  24. package/examples/14-serve.jos +88 -0
  25. package/examples/README.md +105 -0
  26. package/examples/hello.jos +47 -0
  27. package/package.json +31 -9
  28. package/promises.json +894 -0
  29. package/src/args.js +47 -0
  30. package/src/commands/_frozen/add.README.md +1 -0
  31. package/src/commands/_frozen/get.README.md +1 -0
  32. package/src/commands/_frozen/llm.README.md +1 -0
  33. package/src/commands/_frozen/prompts.README.md +1 -0
  34. package/src/commands/_frozen/repo.README.md +1 -0
  35. package/src/commands/_frozen/serve-explorer.README.md +1 -0
  36. package/src/commands/ids.js +101 -0
  37. package/src/commands/init.js +113 -147
  38. package/src/commands/ledger.js +94 -0
  39. package/src/commands/meter.js +96 -0
  40. package/src/commands/migrate.js +214 -0
  41. package/src/commands/overview.js +118 -0
  42. package/src/commands/run.js +164 -214
  43. package/src/commands/runs.js +102 -0
  44. package/src/commands/secrets.js +695 -115
  45. package/src/commands/start.js +210 -0
  46. package/src/commands/validate.js +153 -165
  47. package/src/commands/verify.js +56 -0
  48. package/src/commands/version.js +23 -0
  49. package/src/intention.js +114 -0
  50. package/src/kernel.js +69 -0
  51. package/src/ledgers.js +57 -0
  52. package/src/mcp-guide.js +138 -0
  53. package/src/mcp.js +734 -0
  54. package/src/serve.js +38 -770
  55. package/src/ui.js +98 -0
  56. package/src/commands/add.js +0 -145
  57. package/src/commands/get.js +0 -245
  58. package/src/commands/repo.js +0 -139
  59. package/src/index.js +0 -0
package/src/ui.js ADDED
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+ // The one style module of the host (STYLE.md). Every line written for a person goes through a printer of this file:
3
+ // the header with rules, the marks ✓ ✖ ⚠, the colors and the indentation live here and nowhere else. Colors are off
4
+ // when NO_COLOR is set or the stream is not a terminal, so piped output carries no escape codes. Machine output
5
+ // (`--json`) and data that is not a message (a child's output, a revealed value) also leave through this file, by
6
+ // json() and data(), which never add color or decoration.
7
+
8
+ const FULL = {
9
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
10
+ purple: '\x1b[38;5;135m', magenta: '\x1b[38;5;198m', cyan: '\x1b[38;5;51m',
11
+ blue: '\x1b[38;5;39m', white: '\x1b[38;5;255m', gray: '\x1b[38;5;245m',
12
+ pink: '\x1b[38;5;213m', teal: '\x1b[38;5;44m',
13
+ green: '\x1b[38;5;78m', red: '\x1b[38;5;196m', yellow: '\x1b[38;5;220m',
14
+ };
15
+
16
+ const BASIC = {
17
+ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
18
+ purple: '\x1b[35m', magenta: '\x1b[35m', cyan: '\x1b[36m',
19
+ blue: '\x1b[34m', white: '\x1b[37m', gray: '\x1b[90m',
20
+ pink: '\x1b[35m', teal: '\x1b[36m',
21
+ green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m',
22
+ };
23
+
24
+ function supports256Color() {
25
+ const term = process.env.TERM || '';
26
+ const colorterm = process.env.COLORTERM || '';
27
+ return term.includes('256color') || colorterm === 'truecolor' ||
28
+ colorterm === '24bit' || process.platform === 'darwin';
29
+ }
30
+
31
+ function supportsUnicode() {
32
+ const term = process.env.TERM || '';
33
+ const lang = process.env.LANG || '';
34
+ if (lang && !lang.includes('UTF-8') && !lang.includes('utf8')) return false;
35
+ return lang.includes('UTF-8') || lang.includes('utf8') ||
36
+ term.includes('xterm') || term.includes('256color') ||
37
+ (process.platform === 'darwin' && !lang);
38
+ }
39
+
40
+ function palette(stream) {
41
+ const on = !process.env.NO_COLOR && stream && stream.isTTY;
42
+ const src = supports256Color() ? FULL : BASIC;
43
+ const out = {};
44
+ for (const k of Object.keys(FULL)) out[k] = on ? src[k] : '';
45
+ return out;
46
+ }
47
+
48
+ const RULE = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
49
+
50
+ // A printer bound to one stream (process.stdout, process.stderr or a stream a test passes in). The stream's write
51
+ // is looked up on every call, so a test that replaces process.stdout.write sees every line.
52
+ // header(title, sub) blank line, rule, "TITLE // sub", rule, blank line
53
+ // ok / fail / warn "✓ text", "✖ text", "⚠ text" in green / red / yellow, then an optional gray detail
54
+ // step "▶ text" in cyan: something that starts now (a command the MCP client runs)
55
+ // hint(text) an indented, dimmed follow-up line (usage, next step, why)
56
+ // field(label, value) an indented "label: value" line
57
+ // item(text) an indented "• text" line
58
+ // cmd(text) an indented command to copy: " $ text"
59
+ // line(s) / text(s) a line, or a block or prompt written as given (composed with C, this printer's palette)
60
+ function printer(stream) {
61
+ const C = palette(stream);
62
+ const text = (s) => { stream.write(s); };
63
+ const line = (s) => text((s === undefined ? '' : s) + '\n');
64
+ const mark = (color, sign) => (s, detail) => line(`${C[color]}${sign} ${s}${C.reset}` + (detail ? ` ${C.gray}${detail}${C.reset}` : ''));
65
+ return {
66
+ C,
67
+ text,
68
+ line,
69
+ header(title, sub) {
70
+ line();
71
+ line(`${C.purple}${RULE}${C.reset}`);
72
+ line(`${C.bold}${title}${C.reset}` + (sub ? ` // ${C.gray}${sub}${C.reset}` : ''));
73
+ line(`${C.purple}${RULE}${C.reset}`);
74
+ line();
75
+ },
76
+ ok: mark('green', '✓'),
77
+ fail: mark('red', '✖'),
78
+ warn: mark('yellow', '⚠'),
79
+ step: mark('cyan', '▶'),
80
+ hint: (s) => line(` ${C.dim}${s}${C.reset}`),
81
+ field: (label, value) => line(` ${C.cyan}${label}:${C.reset} ${value}`),
82
+ item: (s) => line(` ${C.white}•${C.reset} ${s}`),
83
+ cmd: (s) => line(` ${C.gray}$${C.reset} ${C.cyan}${s}${C.reset}`),
84
+ };
85
+ }
86
+
87
+ // Machine output (--json): plain JSON, two-space indent, one final newline; never colored, whatever the terminal.
88
+ function json(stream, value) { stream.write(JSON.stringify(value, null, 2) + '\n'); }
89
+
90
+ // Data that is not a message (a child's stdout or stderr, a value revealed on a terminal): written byte for byte.
91
+ function data(stream, chunk) { stream.write(chunk); }
92
+
93
+ // One argument as a POSIX shell word, for commands printed to be copied: left as is when it is plain, else quoted.
94
+ function shellArg(s) {
95
+ return /^[A-Za-z0-9_@%+=:,./-]+$/.test(s) ? s : "'" + s.replace(/'/g, "'\\''") + "'";
96
+ }
97
+
98
+ module.exports = { palette, supportsUnicode, RULE, printer, json, data, shellArg };
@@ -1,145 +0,0 @@
1
- /**
2
- * JOS ADD Command - Add task definitions to .jos artifacts
3
- * Format version v0.0.7 — Specification maturity v0.1.0 (Alpha)
4
- */
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
- const crypto = require('crypto');
9
-
10
- const C = {
11
- reset: '\x1b[0m', bold: '\x1b[1m',
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', gray: '\x1b[38;5;245m'
15
- };
16
-
17
- exports.execute = async (args, home) => {
18
- const help = args.includes('--help') || args.includes('-h');
19
-
20
- if (help || args.length < 2) {
21
- console.log(`
22
- ${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}
23
- ${C.bold}JOS ADD${C.reset} // Add task definitions to .jos artifacts
24
- ${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}
25
-
26
- ${C.bold}Usage:${C.reset}
27
- jos add <task-name> <file.jos> [options]
28
-
29
- ${C.bold}Options:${C.reset}
30
- --type <shell|jos> Task type (default: shell)
31
- --command <cmd> Shell command to execute
32
- --artifact <path> Path to sub-artifact (for type=jos)
33
- --description <desc> Task description
34
- --flow <name> Also add to this flow's steps
35
-
36
- ${C.bold}Examples:${C.reset}
37
- jos add build my-artifact.jos --command "npm run build"
38
- jos add deploy my-artifact.jos --type jos --artifact ./deploy.jos
39
- jos add test my-artifact.jos --command "npm test" --flow main
40
- `);
41
- return;
42
- }
43
-
44
- const taskName = args[0];
45
- const targetFile = args[1];
46
-
47
- // Parse options
48
- const getArg = (flag) => {
49
- const idx = args.indexOf(flag);
50
- return idx !== -1 && args[idx + 1] ? args[idx + 1] : null;
51
- };
52
-
53
- const taskType = getArg('--type') || 'shell';
54
- const command = getArg('--command');
55
- const artifact = getArg('--artifact');
56
- const description = getArg('--description') || `Task: ${taskName}`;
57
- const flowName = getArg('--flow');
58
-
59
- // Validate
60
- if (taskType === 'shell' && !command) {
61
- console.log(`${C.red}✖ Error: --command is required for shell tasks${C.reset}`);
62
- process.exit(1);
63
- }
64
- if (taskType === 'jos' && !artifact) {
65
- console.log(`${C.red}✖ Error: --artifact is required for jos tasks${C.reset}`);
66
- process.exit(1);
67
- }
68
-
69
- // Load artifact
70
- let artifactPath = path.resolve(targetFile);
71
- if (!fs.existsSync(artifactPath)) {
72
- console.log(`${C.red}✖ Artifact not found: ${targetFile}${C.reset}`);
73
- process.exit(1);
74
- }
75
-
76
- let content;
77
- try {
78
- content = JSON.parse(fs.readFileSync(artifactPath, 'utf8'));
79
- } catch (e) {
80
- console.log(`${C.red}✖ Invalid JSON: ${e.message}${C.reset}`);
81
- process.exit(1);
82
- }
83
-
84
- // Ensure orchestration structure exists
85
- if (!content.orchestration) content.orchestration = {};
86
- if (!content.orchestration.definitions) content.orchestration.definitions = {};
87
- if (!content.orchestration.flows) content.orchestration.flows = {};
88
- if (!content.orchestration.flows.main) content.orchestration.flows.main = { steps: [] };
89
-
90
- // Check for duplicate
91
- if (content.orchestration.definitions[taskName]) {
92
- console.log(`${C.yellow}⚠ Task '${taskName}' already exists. Overwriting...${C.reset}`);
93
- }
94
-
95
- // Add task definition
96
- const taskDef = {
97
- type: taskType,
98
- description: description
99
- };
100
-
101
- if (taskType === 'shell') {
102
- taskDef.command = command;
103
- } else if (taskType === 'jos') {
104
- taskDef.artifact = artifact;
105
- }
106
-
107
- content.orchestration.definitions[taskName] = taskDef;
108
- console.log(`${C.green}✓ Added task definition: ${taskName}${C.reset}`);
109
-
110
- // Add to flow if specified
111
- if (flowName) {
112
- if (!content.orchestration.flows[flowName]) {
113
- content.orchestration.flows[flowName] = { steps: [] };
114
- }
115
- if (!content.orchestration.flows[flowName].steps.includes(taskName)) {
116
- content.orchestration.flows[flowName].steps.push(taskName);
117
- console.log(`${C.green}✓ Added to flow: ${flowName}${C.reset}`);
118
- }
119
- }
120
-
121
- // Write updated artifact
122
- fs.writeFileSync(artifactPath, JSON.stringify(content, null, 2));
123
- console.log(`${C.green}✓ Updated: ${targetFile}${C.reset}`);
124
-
125
- // Regenerate manifest
126
- const manifestPath = artifactPath + '.sig.json';
127
- const newContent = fs.readFileSync(artifactPath, 'utf8');
128
- const sha256 = crypto.createHash('sha256').update(newContent).digest('hex');
129
- const stats = fs.statSync(artifactPath);
130
-
131
- const manifest = {
132
- "$schema": "https://josfox.ai/schemas/jos-manifest-0.0.7.json",
133
- "manifest_version": "1.0.0",
134
- "artifact_ref": path.basename(artifactPath),
135
- "artifact_sha256": sha256,
136
- "artifact_size_bytes": stats.size,
137
- "timestamp": new Date().toISOString(),
138
- "generated_by": "jos add command",
139
- "canonical_version": "Format version v0.0.7 — Specification maturity v0.1.0 (Alpha)"
140
- };
141
-
142
- fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
143
- console.log(`${C.green}✓ Updated manifest: ${path.basename(manifestPath)}${C.reset}`);
144
- console.log(`${C.gray} SHA256: ${sha256}${C.reset}`);
145
- };
@@ -1,245 +0,0 @@
1
- /**
2
- * JOS GET Command - Fetch .jos packages from repos
3
- * Architecture similar to npm/brew/apt but offline-first
4
- */
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
- const https = require('https');
9
- const http = require('http');
10
- const crypto = require('crypto');
11
-
12
- // AURORA colors
13
- const C = {
14
- reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
15
- purple: '\x1b[38;5;135m', magenta: '\x1b[38;5;198m', cyan: '\x1b[38;5;51m',
16
- green: '\x1b[38;5;78m', red: '\x1b[38;5;196m', gray: '\x1b[38;5;245m',
17
- white: '\x1b[38;5;255m', yellow: '\x1b[38;5;220m'
18
- };
19
-
20
- // Default repos configuration
21
- const DEFAULT_REPOS = {
22
- default: 'https://registry.josfox.ai',
23
- local: '~/.jos/artifacts'
24
- };
25
-
26
- function getReposConfig(home) {
27
- const reposPath = path.join(home, 'repos.json');
28
- if (fs.existsSync(reposPath)) {
29
- return { ...DEFAULT_REPOS, ...JSON.parse(fs.readFileSync(reposPath)) };
30
- }
31
- return DEFAULT_REPOS;
32
- }
33
-
34
- function computeIntegrity(content) {
35
- return crypto.createHash('sha256').update(content).digest('hex');
36
- }
37
-
38
- // Lock file management
39
- function getLockPath(home) {
40
- return path.join(home, 'lock.json');
41
- }
42
-
43
- function loadLock(home) {
44
- const lockPath = getLockPath(home);
45
- if (fs.existsSync(lockPath)) {
46
- return JSON.parse(fs.readFileSync(lockPath, 'utf8'));
47
- }
48
- return { locked: null, packages: {} };
49
- }
50
-
51
- function saveLock(home, lock) {
52
- const lockPath = getLockPath(home);
53
- lock.locked = new Date().toISOString();
54
- fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
55
- }
56
-
57
- function updateLock(home, pkgName, version, integrity) {
58
- const lock = loadLock(home);
59
- lock.packages[pkgName] = { version, integrity: `sha256-${integrity}` };
60
- saveLock(home, lock);
61
- }
62
-
63
- // Parse package reference: "pkg", "repo:pkg", "./path", "http://..."
64
- function parsePackageRef(ref) {
65
- // URL
66
- if (ref.startsWith('http://') || ref.startsWith('https://')) {
67
- return { type: 'url', url: ref };
68
- }
69
- // Local path
70
- if (ref.startsWith('./') || ref.startsWith('/') || ref.startsWith('~')) {
71
- return { type: 'local', path: ref };
72
- }
73
- // Named repo: "myrepo:package"
74
- if (ref.includes(':')) {
75
- const [repo, pkg] = ref.split(':');
76
- return { type: 'repo', repo, package: pkg };
77
- }
78
- // Default: package name
79
- return { type: 'default', package: ref };
80
- }
81
-
82
- // Fetch from URL
83
- function fetchUrl(url) {
84
- return new Promise((resolve, reject) => {
85
- const protocol = url.startsWith('https') ? https : http;
86
- protocol.get(url, (res) => {
87
- if (res.statusCode === 301 || res.statusCode === 302) {
88
- return fetchUrl(res.headers.location).then(resolve).catch(reject);
89
- }
90
- if (res.statusCode !== 200) {
91
- return reject(new Error(`HTTP ${res.statusCode}`));
92
- }
93
- let data = '';
94
- res.on('data', chunk => data += chunk);
95
- res.on('end', () => resolve(data));
96
- }).on('error', reject);
97
- });
98
- }
99
-
100
- exports.execute = async (args, home) => {
101
- const target = args[0];
102
- const fromRepo = args.find((a, i) => args[i - 1] === '--from');
103
- const showHelp = args.includes('--help') || args.includes('-h');
104
-
105
- if (showHelp || !target) {
106
- console.log(`
107
- ${C.cyan}${C.bold}JOS GET${C.reset} - Fetch .jos packages
108
-
109
- ${C.white}Usage:${C.reset} jos get <package> [options]
110
-
111
- ${C.white}Package formats:${C.reset}
112
- jos get hello ${C.dim}# From default registry${C.reset}
113
- jos get ./my-package ${C.dim}# From local folder${C.reset}
114
- jos get myrepo:package ${C.dim}# From named repo${C.reset}
115
- jos get http://host/p.jos ${C.dim}# From URL${C.reset}
116
-
117
- ${C.white}Options:${C.reset}
118
- --from <host> Override source (e.g., --from 192.168.1.10:1111)
119
- --help, -h Show this help
120
-
121
- ${C.white}Configuration:${C.reset}
122
- Repos defined in: ~/.jos/repos.json
123
-
124
- ${C.white}Example repos.json:${C.reset}
125
- {
126
- "default": "https://registry.josfox.ai",
127
- "local": "~/.jos/artifacts",
128
- "myrepo": "http://192.168.1.10:1111"
129
- }
130
- `);
131
- return;
132
- }
133
-
134
- console.log(`\n${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}`);
135
- console.log(`${C.cyan}${C.bold}JOS GET${C.reset} // ${C.gray}Package Manager${C.reset}`);
136
- console.log(`${C.purple}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${C.reset}\n`);
137
-
138
- const repos = getReposConfig(home);
139
- const artifactsDir = path.join(home, 'artifacts');
140
- if (!fs.existsSync(artifactsDir)) fs.mkdirSync(artifactsDir, { recursive: true });
141
-
142
- // Parse reference
143
- let ref = parsePackageRef(target);
144
-
145
- // Override with --from
146
- if (fromRepo) {
147
- const host = fromRepo.includes(':') ? fromRepo : `${fromRepo}:1111`;
148
- ref = { type: 'url', url: `http://${host}/${target}.jos` };
149
- console.log(`${C.white}📡 Source:${C.reset} ${host}`);
150
- }
151
-
152
- console.log(`${C.white}📦 Package:${C.reset} ${target}`);
153
- console.log(`${C.white}📁 Destination:${C.reset} ${artifactsDir}`);
154
-
155
- let content, sourcePath;
156
-
157
- try {
158
- switch (ref.type) {
159
- case 'local':
160
- // Local folder/file
161
- sourcePath = ref.path.replace('~', process.env.HOME);
162
- if (!sourcePath.endsWith('.jos')) sourcePath += '.jos';
163
- if (!fs.existsSync(sourcePath)) throw new Error(`Not found: ${sourcePath}`);
164
- content = fs.readFileSync(sourcePath, 'utf8');
165
- console.log(`${C.green}✓ Found locally:${C.reset} ${sourcePath}`);
166
- break;
167
-
168
- case 'url':
169
- // Direct URL
170
- console.log(`${C.dim}⏳ Fetching from URL...${C.reset}`);
171
- content = await fetchUrl(ref.url);
172
- console.log(`${C.green}✓ Downloaded from:${C.reset} ${ref.url}`);
173
- break;
174
-
175
- case 'repo':
176
- // Named repo
177
- const repoUrl = repos[ref.repo];
178
- if (!repoUrl) throw new Error(`Unknown repo: ${ref.repo}`);
179
- const pkgUrl = repoUrl.startsWith('http')
180
- ? `${repoUrl}/${ref.package}.jos`
181
- : path.join(repoUrl.replace('~', process.env.HOME), `${ref.package}.jos`);
182
-
183
- if (pkgUrl.startsWith('http')) {
184
- console.log(`${C.dim}⏳ Fetching from ${ref.repo}...${C.reset}`);
185
- content = await fetchUrl(pkgUrl);
186
- } else {
187
- if (!fs.existsSync(pkgUrl)) throw new Error(`Not found: ${pkgUrl}`);
188
- content = fs.readFileSync(pkgUrl, 'utf8');
189
- }
190
- console.log(`${C.green}✓ Found in ${ref.repo}:${C.reset} ${ref.package}`);
191
- break;
192
-
193
- case 'default':
194
- // Try local first, then default registry
195
- const localPath = path.join(artifactsDir, `${ref.package}.jos`);
196
- if (fs.existsSync(localPath)) {
197
- console.log(`${C.green}✓ Already cached:${C.reset} ${localPath}`);
198
- return;
199
- }
200
-
201
- // Try default registry
202
- if (repos.default && repos.default.startsWith('http')) {
203
- console.log(`${C.dim}⏳ Fetching from registry...${C.reset}`);
204
- try {
205
- content = await fetchUrl(`${repos.default}/${ref.package}.jos`);
206
- console.log(`${C.green}✓ Downloaded:${C.reset} ${ref.package}`);
207
- } catch (e) {
208
- throw new Error(`Package not found: ${ref.package}`);
209
- }
210
- } else {
211
- throw new Error(`Package not found: ${ref.package}`);
212
- }
213
- break;
214
- }
215
-
216
- // Validate JSON
217
- let artifact;
218
- try {
219
- artifact = JSON.parse(content);
220
- } catch (e) {
221
- throw new Error('Invalid .jos file (not valid JSON)');
222
- }
223
-
224
- // Compute integrity
225
- const integrity = computeIntegrity(content);
226
- console.log(`${C.white}🔐 Integrity:${C.reset} ${C.green}${integrity.substring(0, 16)}...${C.reset}`);
227
-
228
- // Save artifact
229
- const pkgName = artifact.meta?.name || artifact.name || target.replace(/[^a-z0-9]/gi, '_');
230
- const pkgVersion = artifact.meta?.version || artifact.orchestration_contract?.version || '1.0.0';
231
- const destPath = path.join(artifactsDir, `${pkgName}.jos`);
232
- fs.writeFileSync(destPath, content);
233
-
234
- // Update lock file
235
- updateLock(home, pkgName, pkgVersion, integrity);
236
- console.log(`${C.dim}📋 Lock file updated${C.reset}`);
237
-
238
- console.log(`\n${C.green}✓ Saved to:${C.reset} ${destPath}`);
239
- console.log(`${C.dim} Run with: jos run ${pkgName}.jos${C.reset}\n`);
240
-
241
- } catch (e) {
242
- console.log(`\n${C.red}✖ Error:${C.reset} ${e.message}\n`);
243
- process.exit(1);
244
- }
245
- };
@@ -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
- };
package/src/index.js DELETED
File without changes