@zils/cli 0.0.0-stage → 0.1.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 +21 -0
- package/README.md +101 -2
- package/THIRD_PARTY_NOTICES.md +31 -0
- package/bin/zils.cjs +29 -0
- package/bootstrap.py +122 -0
- package/package.json +18 -4
- package/vendor/zils_sdk-0.1.0-py3-none-any.whl +0 -0
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
|
-
#
|
|
1
|
+
# Zils CLI
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.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
|
+
}
|
|
Binary file
|