@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
@@ -0,0 +1,88 @@
1
+ {
2
+ "jos": {
3
+ "version": "2.0.0",
4
+ "kind": "flow"
5
+ },
6
+ "id": "tour.serve",
7
+ "meta": {
8
+ "name": "tour.serve",
9
+ "version": "1.0.0"
10
+ },
11
+ "intention": {
12
+ "objective": "Serve a read-only HTML index of ~/.jos/runs on http://127.0.0.1:8765/ for 60 s, from a temporary folder (never ~/.jos itself)",
13
+ "success_criteria": [
14
+ {
15
+ "exit": 0
16
+ },
17
+ {
18
+ "stdout_contains": "stopped and removed"
19
+ }
20
+ ]
21
+ },
22
+ "guardrails": {
23
+ "avoid": [
24
+ "rm -rf",
25
+ "curl",
26
+ "wget"
27
+ ],
28
+ "limit": {
29
+ "seconds": 900,
30
+ "output_bytes": 65536,
31
+ "retries": 0
32
+ }
33
+ },
34
+ "orchestration": {
35
+ "definitions": {
36
+ "index": {
37
+ "engine": "shell",
38
+ "run": [
39
+ "/bin/sh",
40
+ "-c",
41
+ "dir=$(mktemp -d \"${TMPDIR:-/tmp}/jos-runs-index.XXXXXX\") || exit 1\nnode -e 'const fs = require(\"fs\"), path = require(\"path\");\nconst [runs, out] = process.argv.slice(1);\nconst esc = (s) => String(s).replace(/[&<>\"]/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", \"\\\"\": \"&quot;\" })[c]);\nlet ids = [];\ntry { ids = fs.readdirSync(runs).filter((n) => /^run_[0-9A-Za-z]+$/.test(n)).sort().reverse(); } catch (e) { ids = []; }\nconst rows = ids.map((id) => {\n let es = [];\n try { es = fs.readFileSync(path.join(runs, id, \"ledger.jsonl\"), \"utf8\").split(\"\\n\").filter(Boolean).map((l) => JSON.parse(l)); } catch (e) { es = []; }\n const b = (e) => (e && e.body) || {};\n const end = b(es.find((e) => e.kind === \"end\"));\n const top = b(es.filter((e) => e.kind === \"receipt\").pop());\n const cells = [id, top.step || \"\", end.code === undefined ? \"\" : end.code, es.length ? es[0].ts : \"\"];\n return \"<tr>\" + cells.map((c) => \"<td>\" + esc(c) + \"</td>\").join(\"\") + \"</tr>\";\n});\nfs.writeFileSync(out, \"<!doctype html>\\n<meta charset=\\\"utf-8\\\">\\n<title>jos runs</title>\\n<h1>Runs in ~/.jos/runs</h1>\\n<p>A read-only snapshot. Check a run with <code>jos ledger verify &lt;run_id&gt;</code>.</p>\\n<table>\\n<tr><th>run</th><th>document</th><th>code</th><th>started</th></tr>\\n\" + rows.join(\"\\n\") + \"\\n</table>\\n\");\nconsole.error(ids.length + \" runs indexed\");' \"$HOME/.jos/runs\" \"$dir/index.html\" || { rmdir \"$dir\"; exit 1; }\nprintf %s \"$dir\"",
42
+ "index"
43
+ ],
44
+ "execution": {
45
+ "env": [
46
+ "PATH",
47
+ "HOME",
48
+ "TMPDIR"
49
+ ]
50
+ },
51
+ "as": "site",
52
+ "description": "Write index.html for ~/.jos/runs into a new temporary folder"
53
+ },
54
+ "serve": {
55
+ "engine": "shell",
56
+ "run": [
57
+ "/bin/sh",
58
+ "-c",
59
+ "dir=$(node -e 'let s = \"\"; process.stdin.on(\"data\", (d) => { s += d; }).on(\"end\", () => { process.stdout.write(String(JSON.parse(s).input.dir)); });') || exit 2\ncase \"$dir\" in \"\"|\"$HOME/.jos\"|\"$HOME/.jos/\"*) echo \"refusing to serve $dir\" >&2; exit 2;; esac\n[ -f \"$dir/index.html\" ] || { echo \"no index in $dir\" >&2; exit 2; }\ncommand -v python3 >/dev/null 2>&1 || { echo \"python3 is not installed\" >&2; rm -f \"$dir/index.html\"; rmdir \"$dir\"; exit 3; }\nport=\"${JOS_SERVE_PORT:-8765}\"\nsecs=\"${JOS_SERVE_SECONDS:-60}\"\nlog=\"$dir.log\"\npython3 -u -m http.server \"$port\" --bind 127.0.0.1 --directory \"$dir\" >\"$log\" 2>/dev/null &\npid=$!\nn=0\nuntil grep -q '^Serving HTTP on' \"$log\" 2>/dev/null; do\n if ! kill -0 \"$pid\" 2>/dev/null; then echo \"python3 could not serve on 127.0.0.1:$port (is the port in use?)\" >&2; rm -f \"$dir/index.html\" \"$log\"; rmdir \"$dir\"; exit 1; fi\n if [ \"$n\" -ge 600 ]; then echo \"python3 did not start serving on 127.0.0.1:$port within 60 s\" >&2; kill \"$pid\" 2>/dev/null; wait \"$pid\" 2>/dev/null; rm -f \"$dir/index.html\" \"$log\"; rmdir \"$dir\"; exit 1; fi\n n=$((n + 1))\n sleep 0.1\ndone\nsleep \"$secs\"\nkill \"$pid\" 2>/dev/null\nwait \"$pid\" 2>/dev/null\nrm -f \"$dir/index.html\" \"$log\"\nrmdir \"$dir\"\necho \"served a read-only index of ~/.jos/runs from $dir on http://127.0.0.1:$port/ for $secs s; stopped and removed it\"",
60
+ "serve"
61
+ ],
62
+ "execution": {
63
+ "env": [
64
+ "PATH",
65
+ "HOME",
66
+ "JOS_SERVE_PORT",
67
+ "JOS_SERVE_SECONDS"
68
+ ]
69
+ },
70
+ "needs": [
71
+ "index"
72
+ ],
73
+ "with": {
74
+ "dir": "{{ site }}"
75
+ },
76
+ "description": "python3 -m http.server --bind 127.0.0.1 on that folder, counted from the moment it says it is serving, then remove it"
77
+ }
78
+ },
79
+ "flows": {
80
+ "main": {
81
+ "steps": [
82
+ "index",
83
+ "serve"
84
+ ]
85
+ }
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,105 @@
1
+ # The tour
2
+
3
+ Each example is one capability of `jos`, and each one runs. Copy the folder first, so the runs do not write into
4
+ the installed package (a run of `05-sealed` keeps its ledger in `05-sealed/.jos/replays/`; the others in
5
+ `~/.jos/runs/`):
6
+
7
+ ```
8
+ cp -R "$(npm root -g)/@josfox/jos/examples" jos-tour && cd jos-tour
9
+ ```
10
+
11
+ Every `jos run` prints the document's intention before it runs (`Intention`, `Done when`, `Avoid`, `Limit`), then
12
+ the verdict of each criterion as jos-core evaluated it, then `Intention met` or `Intention NOT met (code N: …)`.
13
+ `--json` prints jos-core's result object instead, unchanged.
14
+
15
+ | Example | Command | Result |
16
+ |---|---|---|
17
+ | `01-hello.jos` | `jos run 01-hello.jos` | prints `hello, .jos`; `✓ Intention met`; exit 0 |
18
+ | `02-criteria.jos` | `jos run 02-criteria.jos` | one verdict line per criterion: `exit`, `stdout_contains`, `stdout_matches`, `stderr_empty`, `not`, and a descriptive one (recorded, never evaluated); exit 0. Change the `echo` text and it is exit 1 |
19
+ | `03-flow-gate.jos` | `jos run 03-flow-gate.jos` | `build` runs, the human gate stops the flow: `Intention NOT met (code 1: not done)` and the command to approve; exit 1 |
20
+ | | `jos run 03-flow-gate.jos --approve build` | `build`, then `publish`; prints `published`; exit 0 |
21
+ | | `jos run 03-flow-gate.jos --flow build-only` | runs the other flow of the document (`build` only); exit 0 |
22
+ | `04-use-template/` | `jos run 04-use-template/use.jos --library 04-use-template/lib` | one template, used twice with `with`, pinned by its digest; prints `hello, Leo`; exit 0. See [its README](04-use-template/README.md) |
23
+ | | `jos run 04-use-template/lib/greeting.jos --with '{"name":"Ana"}'` | the template run directly; prints `hello, Ana`; exit 0 |
24
+ | `05-sealed/` | `jos run 05-sealed` | the folder runs while its hash matches `integrity.hash`; exit 0. Edit it and it is exit 3. See [its README](05-sealed/README.md) |
25
+ | `06-requires.jos` | `jos run 06-requires.jos` | needs a `payments` service this runtime does not provide: refused before anything starts, `/requires/0: not satisfied`; exit 5 |
26
+ | `07-sandbox.jos` | `jos run 07-sandbox.jos` | `security.tier: sandbox`, and this kernel cannot confine a process: refused, `sandbox not available`; exit 5 |
27
+ | `08-ids-ledger-meter.md` | the commands in it | `jos ids`, `jos runs`, `jos ledger verify`, `jos meter` over the runs above |
28
+ | `10-ollama.jos` | `jos run 10-ollama.jos` | checks `http://localhost:11434`; without Ollama it says so and sends nothing (exit 1); with Ollama the objective goes to `ollama run ${OLLAMA_MODEL:-llama3.2}` and the answer is printed (exit 0) |
29
+ | `11-openai.jos` | `jos secrets exec --only OPENAI_API_KEY -- jos run 11-openai.jos` | the objective to the OpenAI Responses API (`${OPENAI_MODEL:-gpt-5-mini}`); prints the answer; exit 0. Without the key: `OPENAI_API_KEY is not set: no request was sent.`, exit 1 |
30
+ | `12-gemini.jos` | `jos secrets exec --only GEMINI_API_KEY -- jos run 12-gemini.jos` | the same with the Gemini API (`${GEMINI_MODEL:-gemini-2.5-flash}`) |
31
+ | `13-anthropic.jos` | `jos secrets exec --only ANTHROPIC_API_KEY -- jos run 13-anthropic.jos` | the same with the Anthropic Messages API (`${ANTHROPIC_MODEL:-claude-opus-5-5}`) |
32
+ | `14-serve.jos` | `jos run 14-serve.jos` | writes a read-only HTML index of `~/.jos/runs` into a new temporary folder and serves only that folder with `python3 -m http.server --bind 127.0.0.1` on port `${JOS_SERVE_PORT:-8765}` for `${JOS_SERVE_SECONDS:-60}` seconds, counted from the moment python3 says it is serving (it may take a few seconds to listen; at most 60 s), then removes it; exit 0 |
33
+
34
+ `hello.jos` is the document `jos verify` runs.
35
+
36
+ ## The provider examples and your keys
37
+
38
+ `11`, `12` and `13` are shell steps that call the provider's REST API with `curl`. They are paid calls on your
39
+ account. Store a key once, then give it only to the run that needs it:
40
+
41
+ ```
42
+ jos secrets set OPENAI_API_KEY --stdin # type or paste the key; it is not echoed
43
+ jos secrets exec --only OPENAI_API_KEY -- jos run 11-openai.jos
44
+ ```
45
+
46
+ What happens to the key: `jos secrets exec` puts it only in the environment of that `jos run`; the document's
47
+ `execution.env` lets it reach the step; the step hands it to `curl` on stdin (`-H @-`), so it is never on a command
48
+ line; the examples never print it, and an error message from the API is printed with the key replaced by `[key]`.
49
+ The key is not in the receipts or the ledger: a receipt records the step's code, its criteria verdicts and hashes
50
+ of its input and output, never the environment (the test suite checks the ledger, the result and the output for
51
+ the key). The answer itself is printed
52
+ to your terminal and not stored. The model's tokens and cost are not recorded either: the `shell` engine does not
53
+ report them, so `jos meter` shows them as "not reported".
54
+
55
+ ## What `jos run` prints (NO_COLOR)
56
+
57
+ ```
58
+ $ jos run 03-flow-gate.jos
59
+
60
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
61
+ JOS RUN // jos-core 2.0.0
62
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
63
+
64
+ File: 03-flow-gate.jos
65
+
66
+ Intention: Build, wait for a person to approve the build, then publish
67
+ Done when:
68
+ • exit 0
69
+ Avoid: "rm -rf"
70
+ Limit: 10 s, 65536 output bytes per stream, 0 retries
71
+
72
+ ID: art_572a58662e9e76761b6844bfd091ed08636023e39ce10cd13dc1cdf463741d96
73
+ Input version: 2.0.0
74
+ Capability: cap_01M5DX8R8WT15PS3SQV70CV2PM (local-trusted)
75
+ Ledger: ~/.jos/runs/run_01M5DX8R8TCEQ2D7DESRZQH8JG/ledger.jsonl
76
+
77
+ built
78
+
79
+ Verdict:
80
+ [1] build ✓ code 0
81
+ ✓ stdout_contains: found
82
+ [1] tour.flow-gate (flow main) ✖ code 1
83
+ ✓ exit: exit 0 (want 0)
84
+
85
+ ✖ Intention NOT met (code 1: not done) (run_01M5DX8R8TCEQ2D7DESRZQH8JG)
86
+ gate human: waiting for an approval of "build"
87
+ To approve it: jos run 03-flow-gate.jos --approve build
88
+
89
+ $ jos run 03-flow-gate.jos --approve build
90
+ …
91
+ published
92
+
93
+ Verdict:
94
+ [1] build ✓ code 0
95
+ ✓ stdout_contains: found
96
+ [1] publish ✓ code 0
97
+ ✓ stdout_contains: found
98
+ [1] tour.flow-gate (flow main) ✓ code 0
99
+ ✓ exit: exit 0 (want 0)
100
+
101
+ ✓ Intention met (code 0, run_01M5DX8R9YSZT1S7GKJJJNS9MM)
102
+ ```
103
+
104
+ The `run_` and `cap_` ids and the ledger path are yours; the `art_` is the same on every machine and in both
105
+ reference kernels, because it is the hash of the document's content.
@@ -0,0 +1,47 @@
1
+ {
2
+ "jos": {
3
+ "version": "2.0.0",
4
+ "kind": "atom"
5
+ },
6
+ "id": "hello",
7
+ "meta": {
8
+ "name": "hello",
9
+ "version": "1.0.0"
10
+ },
11
+ "intention": {
12
+ "objective": "Print a greeting and exit cleanly",
13
+ "success_criteria": [
14
+ {
15
+ "exit": 0
16
+ },
17
+ {
18
+ "stdout_contains": "hello"
19
+ }
20
+ ]
21
+ },
22
+ "guardrails": {
23
+ "avoid": [
24
+ "rm -rf",
25
+ "curl",
26
+ "wget"
27
+ ],
28
+ "limit": {
29
+ "seconds": 5,
30
+ "output_bytes": 65536,
31
+ "retries": 0
32
+ }
33
+ },
34
+ "execution": {
35
+ "engine": "shell",
36
+ "run": [
37
+ "echo",
38
+ "hello, .jos"
39
+ ],
40
+ "on_error": "fail",
41
+ "env": [
42
+ "PATH",
43
+ "HOME"
44
+ ],
45
+ "cwd": "."
46
+ }
47
+ }
package/package.json CHANGED
@@ -1,40 +1,57 @@
1
- {
2
- "name": "@josfox/jos",
3
- "version": "4.1.0",
4
- "description": "Stoic kernel for AI agent orchestration. Zero dependencies. Offline first.",
5
- "license": "MIT",
6
- "author": "JOS Open Standards Foundation",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/josfox-ai/jos.git"
10
- },
11
- "homepage": "https://josfox.ai",
12
- "keywords": [
13
- "cli",
14
- "ai",
15
- "agent",
16
- "orchestration",
17
- "jos",
18
- "josfox",
19
- "artifacts",
20
- "offline-first",
21
- "zero-dependencies"
22
- ],
23
- "bin": {
24
- "jos": "bin/jos",
25
- "josfox": "bin/jos"
26
- },
27
- "files": [
28
- "bin/",
29
- "src/"
30
- ],
31
- "type": "commonjs",
32
- "engines": {
33
- "node": ">=18"
34
- },
35
- "scripts": {
36
- "test": "node bin/jos verify",
37
- "test:cli": "node bin/jos verify",
38
- "test:all": "node bin/jos verify && node bin/jos validate ../examples/reference-v0.0.7.jos --no-strict"
39
- }
40
- }
1
+ {
2
+ "name": "@josfox/jos",
3
+ "version": "5.0.0",
4
+ "description": "Host CLI for the jos-core kernel: run and validate .jos documents (2.0.0, 1.1.0, 1.0.0, 0.0.7). Offline first.",
5
+ "license": "Apache-2.0",
6
+ "author": "Josué Emmanuel Gómez Carrillo (JOSFOX LLC)",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/josfox-jos/jos.git",
10
+ "directory": "packages/jos"
11
+ },
12
+ "homepage": "https://github.com/josfox-jos/jos/tree/main/packages/jos",
13
+ "keywords": [
14
+ "cli",
15
+ "ai",
16
+ "agent",
17
+ "orchestration",
18
+ "jos",
19
+ "josfox",
20
+ "artifacts",
21
+ "offline-first",
22
+ "zero-dependencies"
23
+ ],
24
+ "bin": {
25
+ "jos": "bin/jos"
26
+ },
27
+ "files": [
28
+ "bin/",
29
+ "src/",
30
+ "examples/",
31
+ "SECURITY-SECRETS.md",
32
+ "SECURITY-MCP.md",
33
+ "STYLE.md",
34
+ "PROMISES.md",
35
+ "promises.json",
36
+ "!src/commands/_frozen/",
37
+ "src/commands/_frozen/*.README.md"
38
+ ],
39
+ "type": "commonjs",
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "dependencies": {
44
+ "@josfox/jos-core": "^2.0.0"
45
+ },
46
+ "scripts": {
47
+ "test": "node test/gate.js",
48
+ "test:cli": "node bin/jos verify",
49
+ "test:coverage": "node test/coverage.js",
50
+ "test:host": "node --test test/host.test.js",
51
+ "test:promises": "node --test test/promises.test.js",
52
+ "promises:md": "node test/promises-md.js",
53
+ "promises:check": "node test/promises-md.js --check",
54
+ "test:secrets": "node --test --experimental-test-coverage --test-coverage-include=src/commands/secrets.js --test-coverage-lines=100 --test-coverage-branches=100 --test-coverage-functions=100 test/secrets.test.js",
55
+ "test:mcp": "node --test --experimental-test-coverage --test-coverage-include=src/mcp.js --test-coverage-lines=100 --test-coverage-branches=100 --test-coverage-functions=100 test/mcp.test.js"
56
+ }
57
+ }