@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.
- package/LICENSE +208 -21
- package/PROMISES.md +52 -0
- package/README.md +294 -146
- package/SECURITY-MCP.md +117 -0
- package/SECURITY-SECRETS.md +108 -0
- package/STYLE.md +23 -0
- package/bin/jos +139 -67
- package/bin/jos.cmd +5 -0
- package/examples/01-hello.jos +44 -0
- package/examples/02-criteria.jos +54 -0
- package/examples/03-flow-gate.jos +83 -0
- package/examples/04-use-template/README.md +22 -0
- package/examples/04-use-template/lib/greeting.jos +56 -0
- package/examples/04-use-template/use.jos +64 -0
- package/examples/05-sealed/.jos/manifest.jos +53 -0
- package/examples/05-sealed/README.md +25 -0
- package/examples/06-requires.jos +42 -0
- package/examples/07-sandbox.jos +39 -0
- package/examples/08-ids-ledger-meter.md +26 -0
- package/examples/10-ollama.jos +47 -0
- package/examples/11-openai.jos +47 -0
- package/examples/12-gemini.jos +47 -0
- package/examples/13-anthropic.jos +47 -0
- package/examples/14-serve.jos +88 -0
- package/examples/README.md +105 -0
- package/examples/hello.jos +47 -0
- package/package.json +31 -9
- package/promises.json +894 -0
- package/src/args.js +47 -0
- package/src/commands/_frozen/add.README.md +1 -0
- package/src/commands/_frozen/get.README.md +1 -0
- package/src/commands/_frozen/llm.README.md +1 -0
- package/src/commands/_frozen/prompts.README.md +1 -0
- package/src/commands/_frozen/repo.README.md +1 -0
- package/src/commands/_frozen/serve-explorer.README.md +1 -0
- package/src/commands/ids.js +101 -0
- package/src/commands/init.js +113 -147
- package/src/commands/ledger.js +94 -0
- package/src/commands/meter.js +96 -0
- package/src/commands/migrate.js +214 -0
- package/src/commands/overview.js +118 -0
- package/src/commands/run.js +164 -214
- package/src/commands/runs.js +102 -0
- package/src/commands/secrets.js +695 -115
- package/src/commands/start.js +210 -0
- package/src/commands/validate.js +153 -165
- package/src/commands/verify.js +56 -0
- package/src/commands/version.js +23 -0
- package/src/intention.js +114 -0
- package/src/kernel.js +69 -0
- package/src/ledgers.js +57 -0
- package/src/mcp-guide.js +138 -0
- package/src/mcp.js +734 -0
- package/src/serve.js +38 -770
- package/src/ui.js +98 -0
- package/src/commands/add.js +0 -145
- package/src/commands/get.js +0 -245
- package/src/commands/repo.js +0 -139
- 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 };
|
package/src/commands/add.js
DELETED
|
@@ -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
|
-
};
|
package/src/commands/get.js
DELETED
|
@@ -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
|
-
};
|
package/src/commands/repo.js
DELETED
|
@@ -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
|