@zils/cli 0.0.0-stage → 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zils contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,102 @@
1
- # Temporary Holding Version
1
+ # Zils CLI
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Sign in with your Zils account, submit text training, follow runs, download
4
+ accepted models, and call the decision API.
5
+
6
+ Requires Node.js 20+ and **Python 3.11+**, including `venv` and `pip`. Install
7
+ Python from [python.org](https://www.python.org/downloads/). On Debian/Ubuntu,
8
+ install `python3-venv` if your Python installation does not include it.
9
+
10
+ ```sh
11
+ npm install -g @zils/cli
12
+ zils login
13
+ zils train list
14
+ ```
15
+
16
+ Or run without a global npm installation:
17
+
18
+ ```sh
19
+ npx @zils/cli login
20
+ ```
21
+
22
+ First use installs the bundled Python CLI and its dependencies from PyPI into a
23
+ private cache. It needs internet access, takes longer than subsequent starts,
24
+ and does not modify your system Python. npm installation itself runs no setup
25
+ scripts. Set `ZILS_PYTHON` to an executable path if Python is not on your PATH.
26
+
27
+ ## Commands
28
+
29
+ ```sh
30
+ zils --help
31
+ zils train submit --help
32
+ zils train list --json
33
+ zils train status JOB_ID --watch
34
+ zils train download JOB_ID --out ./accepted-model
35
+ zils train cancel JOB_ID
36
+ zils logout
37
+ ```
38
+
39
+ `zils login` opens browser sign-in for training. Browser sign-in requires a local
40
+ desktop browser and port 43187. Training uses your account's allowance/credit;
41
+ only an accepted result can be downloaded. Login does not create an inference key.
42
+
43
+ For inference, supply an existing key at the private prompt:
44
+
45
+ ```sh
46
+ zils login --api-key
47
+ zils models
48
+ zils decide --state "Please refund the duplicate charge." \
49
+ --questions '{"refund":{"type":"noul","instructions":"Is a refund requested?"}}'
50
+ ```
51
+
52
+ Keys are never supplied as command arguments. Existing `~/.zils` credentials and
53
+ training receipts are shared with the Python CLI. Automatic request retries are
54
+ off. `zils logout` removes local credentials; it does not revoke remote sessions.
55
+ Windows users should protect their account's cache and configuration directories.
56
+
57
+ ## Submit training data
58
+
59
+ Prepare separate UTF-8 JSONL files for training, calibration, and test cases.
60
+ Every line is one case, for example:
61
+
62
+ ```json
63
+ {"id":"train-1","group_id":"ticket-1","family":"refund","state":"Please refund the duplicate charge.","question":{"type":"noul","instructions":"Is a refund requested?"},"label":"true"}
64
+ ```
65
+
66
+ Each case needs `id`, `group_id`, `family`, `state`, `question`, and `label`.
67
+ IDs must be unique across all splits. Related groups and identical state/question
68
+ pairs must stay in one split. Calibration and test families must match and occur
69
+ in training. Labels are strings: `"false"`/`"true"` for `noul`, a criterion key for
70
+ `choice`, or a zero-based index for `score`. Image cases are not supported.
71
+
72
+ ```sh
73
+ zils train submit --name ticket-routing \
74
+ --train train.jsonl --calibration calibration.jsonl --test test.jsonl \
75
+ --min-accuracy 0.8 --min-brier-improvement 0.01 \
76
+ --allow-training-data-export --json
77
+ ```
78
+
79
+ This submits a real training job and permits data export to approved workers.
80
+ Both quality thresholds are required. Keep the returned job ID to follow the run.
81
+ Run `zils train submit --help` for the available model/profile options.
82
+ If an upload is interrupted, resume the same job with unchanged files:
83
+
84
+ ```sh
85
+ zils train resume JOB_ID \
86
+ --train train.jsonl --calibration calibration.jsonl --test test.jsonl
87
+ ```
88
+
89
+ Do not submit again after an ambiguous connection failure: first inspect
90
+ `zils train list` and the website. Local recovery receipts are retained under
91
+ `~/.zils/training`. Cancellation does not undo incurred costs or promise a refund.
92
+
93
+ ## Runtime cache
94
+
95
+ The runtime cache lives under `~/Library/Caches/zils/cli` on macOS,
96
+ `~/.cache/zils/cli` on Linux, or `%LOCALAPPDATA%\zils\cli` on Windows.
97
+ `XDG_CACHE_HOME`, when set, overrides the cache base. Removing the runtime cache
98
+ only forces setup on the next invocation; it does not remove credentials or jobs.
99
+
100
+ The CLI bundles the same Python implementation as `zils-sdk`; no model weights,
101
+ GPU runtime, or private repository access is required. The JavaScript/TypeScript
102
+ application client is available separately as `@zils/sdk`.
@@ -0,0 +1,31 @@
1
+ # Third-party notices
2
+
3
+ The strict MCP argument check in `zils_sdk/mcp.py` is adapted from
4
+ `nace-mcp` 0.1.0, https://pypi.org/project/nace-mcp/.
5
+ No hosted Nace service is used.
6
+
7
+ MIT License
8
+
9
+ Copyright (c) 2026 Nace AI
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+
30
+ TypeSafe and the official MCP SDK are installed dependencies with their own
31
+ license notices. Their implementations are not vendored in this package.
package/bin/zils.cjs ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { spawn, spawnSync } = require('node:child_process');
5
+ const { join } = require('node:path');
6
+
7
+ // Use an argument array throughout: dataset paths and questions are never shell code.
8
+ const candidates = process.env.ZILS_PYTHON
9
+ ? [[process.env.ZILS_PYTHON]]
10
+ : process.platform === 'win32' ? [['py', '-3'], ['python'], ['python3']] : [['python3'], ['python']];
11
+ const python = candidates.find(([command, ...prefix]) => {
12
+ const result = spawnSync(command, [...prefix, '-I', '-c',
13
+ 'import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)'],
14
+ { stdio: 'ignore', timeout: 10000, windowsHide: true });
15
+ return result.status === 0;
16
+ });
17
+
18
+ if (!python) {
19
+ console.error('Zils requires Python 3.11+. Install it from https://www.python.org/downloads/ and try again.');
20
+ console.error('If Python is elsewhere, set ZILS_PYTHON to its executable path.');
21
+ process.exitCode = 2;
22
+ } else {
23
+ const [command, ...prefix] = python;
24
+ const child = spawn(command, [...prefix, '-I', join(__dirname, '..', 'bootstrap.py'), ...process.argv.slice(2)],
25
+ { stdio: 'inherit' });
26
+ for (const signal of ['SIGINT', 'SIGTERM']) process.on(signal, () => child.kill(signal));
27
+ child.on('error', () => { console.error('Could not start the Zils Python runtime.'); process.exitCode = 2; });
28
+ child.on('exit', (code, signal) => { process.exitCode = code ?? (signal === 'SIGINT' ? 130 : 143); });
29
+ }
package/bootstrap.py ADDED
@@ -0,0 +1,122 @@
1
+ """Run the bundled CLI wheel in a private, versioned Python environment."""
2
+
3
+ import hashlib
4
+ import json
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ import time
10
+ from contextlib import contextmanager
11
+ from pathlib import Path
12
+
13
+
14
+ @contextmanager
15
+ def installation_lock(path):
16
+ with path.open("a+b") as lock:
17
+ if os.name == "nt":
18
+ import msvcrt
19
+
20
+ if path.stat().st_size == 0:
21
+ lock.write(b"0")
22
+ lock.flush()
23
+ deadline = time.monotonic() + 300
24
+ while True:
25
+ lock.seek(0)
26
+ try:
27
+ msvcrt.locking(lock.fileno(), msvcrt.LK_NBLCK, 1)
28
+ break
29
+ except OSError:
30
+ if time.monotonic() >= deadline:
31
+ raise RuntimeError("Another Zils setup is still running. Try again later.")
32
+ time.sleep(0.1)
33
+ try:
34
+ yield
35
+ finally:
36
+ lock.seek(0)
37
+ msvcrt.locking(lock.fileno(), msvcrt.LK_UNLCK, 1)
38
+ else:
39
+ import fcntl
40
+
41
+ fcntl.flock(lock, fcntl.LOCK_EX)
42
+ try:
43
+ yield
44
+ finally:
45
+ fcntl.flock(lock, fcntl.LOCK_UN)
46
+
47
+
48
+ def runtime():
49
+ package = Path(__file__).resolve().parent
50
+ version = json.loads((package / "package.json").read_text())["version"]
51
+ wheel = package / "vendor" / f"zils_sdk-{version}-py3-none-any.whl"
52
+ digest = hashlib.sha256(wheel.read_bytes()).hexdigest()
53
+ cache = os.environ.get("XDG_CACHE_HOME")
54
+ if not cache:
55
+ if os.name == "nt":
56
+ cache = os.environ.get("LOCALAPPDATA") or str(Path.home() / "AppData" / "Local")
57
+ else:
58
+ cache = str(Path.home() / ("Library/Caches" if sys.platform == "darwin" else ".cache"))
59
+ root = (
60
+ Path(cache)
61
+ / "zils"
62
+ / "cli"
63
+ / f"{version}-{digest[:16]}-py{sys.version_info.major}.{sys.version_info.minor}"
64
+ )
65
+ root.mkdir(parents=True, exist_ok=True, mode=0o700)
66
+ environment = root / "runtime"
67
+ python = environment / ("Scripts/python.exe" if os.name == "nt" else "bin/python")
68
+ ready = root / "ready"
69
+ with installation_lock(root / "install.lock"):
70
+ if not (ready.is_file() and python.is_file()):
71
+ print("Setting up Zils CLI dependencies (first use only)...", file=sys.stderr)
72
+ ready.unlink(missing_ok=True)
73
+ if environment.exists():
74
+ shutil.rmtree(environment)
75
+ subprocess.run(
76
+ [sys.executable, "-I", "-m", "venv", str(environment)],
77
+ stdout=sys.stderr,
78
+ check=True,
79
+ )
80
+ subprocess.run(
81
+ [
82
+ str(python),
83
+ "-I",
84
+ "-m",
85
+ "pip",
86
+ "--isolated",
87
+ "--disable-pip-version-check",
88
+ "install",
89
+ "--no-input",
90
+ "--only-binary=:all:",
91
+ "--index-url",
92
+ "https://pypi.org/simple",
93
+ str(wheel),
94
+ ],
95
+ stdout=sys.stderr,
96
+ check=True,
97
+ )
98
+ subprocess.run(
99
+ [str(python), "-I", "-c", "import zils_sdk; from zils_sdk.cli import main"],
100
+ stdout=sys.stderr,
101
+ check=True,
102
+ )
103
+ ready.write_text(digest + "\n")
104
+ return python
105
+
106
+
107
+ if __name__ == "__main__":
108
+ try:
109
+ executable = runtime()
110
+ command = [str(executable), "-I", "-m", "zils_sdk.cli", *sys.argv[1:]]
111
+ if os.name == "nt":
112
+ # Windows exec does not preserve the caller's process/exit-code chain.
113
+ raise SystemExit(subprocess.run(command).returncode)
114
+ os.execv(str(executable), command)
115
+ except KeyboardInterrupt:
116
+ raise SystemExit(130)
117
+ except (OSError, RuntimeError, subprocess.CalledProcessError) as error:
118
+ print(
119
+ f"Zils setup failed: {error}. Check Python's venv/pip installation and network, then retry.",
120
+ file=sys.stderr,
121
+ )
122
+ raise SystemExit(2)
package/package.json CHANGED
@@ -1,6 +1,20 @@
1
1
  {
2
2
  "name": "@zils/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Sign in, train, and call Zils decision models from your terminal",
5
+ "license": "MIT",
6
+ "homepage": "https://zils.ai",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/ooo-hq/zils-sdk.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
13
+ "engines": { "node": ">=20" },
14
+ "bin": { "zils": "bin/zils.cjs" },
15
+ "files": ["bin", "bootstrap.py", "vendor", "README.md", "LICENSE", "THIRD_PARTY_NOTICES.md"],
16
+ "scripts": {
17
+ "prepack": "node scripts/build.cjs",
18
+ "test": "node --test test/installed.test.cjs"
19
+ }
20
+ }