dw-cli 0.1.3__tar.gz
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.
- dw_cli-0.1.3/MANIFEST.in +1 -0
- dw_cli-0.1.3/PKG-INFO +43 -0
- dw_cli-0.1.3/README.md +28 -0
- dw_cli-0.1.3/dw_cli/__init__.py +245 -0
- dw_cli-0.1.3/dw_cli.egg-info/PKG-INFO +43 -0
- dw_cli-0.1.3/dw_cli.egg-info/SOURCES.txt +9 -0
- dw_cli-0.1.3/dw_cli.egg-info/dependency_links.txt +1 -0
- dw_cli-0.1.3/dw_cli.egg-info/entry_points.txt +2 -0
- dw_cli-0.1.3/dw_cli.egg-info/top_level.txt +1 -0
- dw_cli-0.1.3/pyproject.toml +31 -0
- dw_cli-0.1.3/setup.cfg +4 -0
dw_cli-0.1.3/MANIFEST.in
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include README.md
|
dw_cli-0.1.3/PKG-INFO
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dw-cli
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Doubleword Batch Inference CLI
|
|
5
|
+
Author-email: Doubleword <engineering@doubleword.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://doubleword.ai
|
|
8
|
+
Project-URL: Repository, https://github.com/doublewordai/dw
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# dw-cli
|
|
17
|
+
|
|
18
|
+
Python wrapper for the [Doubleword CLI](https://github.com/doublewordai/dw) — a terminal tool for batch inference at scale.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install dw-cli
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This installs a `dw` command that automatically downloads the correct native binary for your platform on first run.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
dw login # Authenticate via browser
|
|
32
|
+
dw models list # List available models
|
|
33
|
+
dw files upload batch.jsonl # Upload a JSONL file
|
|
34
|
+
dw batches run batch.jsonl --watch # Upload, create batch, and watch progress
|
|
35
|
+
dw stream batch.jsonl > results.jsonl # Stream results as they complete
|
|
36
|
+
dw realtime model "prompt" # One-shot inference
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## How it works
|
|
40
|
+
|
|
41
|
+
This package is a thin wrapper. On first run it downloads the pre-built native binary from [GitHub Releases](https://github.com/doublewordai/dw/releases) to `~/.dw/bin/` and executes it. Updates are checked daily.
|
|
42
|
+
|
|
43
|
+
For direct binary installation without Python, see the [main repository](https://github.com/doublewordai/dw).
|
dw_cli-0.1.3/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# dw-cli
|
|
2
|
+
|
|
3
|
+
Python wrapper for the [Doubleword CLI](https://github.com/doublewordai/dw) — a terminal tool for batch inference at scale.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install dw-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This installs a `dw` command that automatically downloads the correct native binary for your platform on first run.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
dw login # Authenticate via browser
|
|
17
|
+
dw models list # List available models
|
|
18
|
+
dw files upload batch.jsonl # Upload a JSONL file
|
|
19
|
+
dw batches run batch.jsonl --watch # Upload, create batch, and watch progress
|
|
20
|
+
dw stream batch.jsonl > results.jsonl # Stream results as they complete
|
|
21
|
+
dw realtime model "prompt" # One-shot inference
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## How it works
|
|
25
|
+
|
|
26
|
+
This package is a thin wrapper. On first run it downloads the pre-built native binary from [GitHub Releases](https://github.com/doublewordai/dw/releases) to `~/.dw/bin/` and executes it. Updates are checked daily.
|
|
27
|
+
|
|
28
|
+
For direct binary installation without Python, see the [main repository](https://github.com/doublewordai/dw).
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Doubleword CLI — thin Python wrapper that downloads and runs the dw binary.
|
|
3
|
+
|
|
4
|
+
Install: pip install dw-cli
|
|
5
|
+
Usage: dw login / dw batches list / dw --help
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import platform
|
|
10
|
+
import stat
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import time
|
|
14
|
+
import urllib.request
|
|
15
|
+
import urllib.error
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
# Where the binary lives after download
|
|
20
|
+
CACHE_DIR = Path.home() / ".dw" / "bin"
|
|
21
|
+
BINARY_NAME = "dw"
|
|
22
|
+
REPO = "doublewordai/dw"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_platform_suffix():
|
|
26
|
+
# type: () -> str
|
|
27
|
+
"""Map current platform to the GitHub release artifact name suffix."""
|
|
28
|
+
system = platform.system().lower()
|
|
29
|
+
machine = platform.machine().lower()
|
|
30
|
+
|
|
31
|
+
if system == "linux":
|
|
32
|
+
os_name = "linux"
|
|
33
|
+
elif system == "darwin":
|
|
34
|
+
os_name = "darwin"
|
|
35
|
+
else:
|
|
36
|
+
raise RuntimeError(
|
|
37
|
+
"Unsupported OS: {}. dw-cli supports Linux and macOS. "
|
|
38
|
+
"See https://github.com/doublewordai/dw/releases".format(system)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if machine in ("x86_64", "amd64"):
|
|
42
|
+
arch = "amd64"
|
|
43
|
+
elif machine in ("aarch64", "arm64"):
|
|
44
|
+
arch = "arm64"
|
|
45
|
+
else:
|
|
46
|
+
raise RuntimeError(
|
|
47
|
+
"Unsupported architecture: {}. "
|
|
48
|
+
"See https://github.com/doublewordai/dw/releases".format(machine)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
return "{}-{}".format(os_name, arch)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _get_latest_version():
|
|
55
|
+
# type: () -> str
|
|
56
|
+
"""Fetch the latest release version from GitHub."""
|
|
57
|
+
url = "https://api.github.com/repos/{}/releases/latest".format(REPO)
|
|
58
|
+
req = urllib.request.Request(url, headers={"Accept": "application/vnd.github+json"})
|
|
59
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
60
|
+
data = json.loads(resp.read().decode())
|
|
61
|
+
tag = data["tag_name"]
|
|
62
|
+
# Strip leading 'v' if present
|
|
63
|
+
return tag.lstrip("v")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _get_installed_version(binary_path):
|
|
67
|
+
# type: (Path) -> Optional[str]
|
|
68
|
+
"""Get the version of the installed binary, if any."""
|
|
69
|
+
if not binary_path.exists():
|
|
70
|
+
return None
|
|
71
|
+
try:
|
|
72
|
+
result = subprocess.run(
|
|
73
|
+
[str(binary_path), "--version"],
|
|
74
|
+
capture_output=True,
|
|
75
|
+
text=True,
|
|
76
|
+
timeout=5,
|
|
77
|
+
)
|
|
78
|
+
# Output is like "dw 0.1.0"
|
|
79
|
+
parts = result.stdout.strip().split()
|
|
80
|
+
return parts[-1] if parts else None
|
|
81
|
+
except Exception:
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _verify_checksum(binary_path, version, suffix):
|
|
86
|
+
# type: (Path, str, str) -> None
|
|
87
|
+
"""Download checksums.txt and verify the binary's SHA256. Fails hard — will not run unverified binaries."""
|
|
88
|
+
import hashlib
|
|
89
|
+
|
|
90
|
+
checksum_url = "https://github.com/{}/releases/download/v{}/checksums.txt".format(REPO, version)
|
|
91
|
+
try:
|
|
92
|
+
req = urllib.request.Request(checksum_url)
|
|
93
|
+
with urllib.request.urlopen(req, timeout=15) as resp:
|
|
94
|
+
checksums = resp.read().decode()
|
|
95
|
+
except (urllib.error.URLError, urllib.error.HTTPError) as e:
|
|
96
|
+
binary_path.unlink(missing_ok=True)
|
|
97
|
+
print(
|
|
98
|
+
"Error: Could not download checksums for verification ({})\n"
|
|
99
|
+
"Install manually: https://github.com/doublewordai/dw/releases".format(e),
|
|
100
|
+
file=sys.stderr,
|
|
101
|
+
)
|
|
102
|
+
sys.exit(1)
|
|
103
|
+
|
|
104
|
+
artifact_name = "dw-{}".format(suffix)
|
|
105
|
+
expected = None
|
|
106
|
+
for line in checksums.strip().splitlines():
|
|
107
|
+
parts = line.split()
|
|
108
|
+
if len(parts) == 2 and parts[1] == artifact_name:
|
|
109
|
+
expected = parts[0]
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
if expected is None:
|
|
113
|
+
binary_path.unlink(missing_ok=True)
|
|
114
|
+
print(
|
|
115
|
+
"Error: No checksum entry found for {} in checksums.txt\n"
|
|
116
|
+
"Install manually: https://github.com/doublewordai/dw/releases".format(artifact_name),
|
|
117
|
+
file=sys.stderr,
|
|
118
|
+
)
|
|
119
|
+
sys.exit(1)
|
|
120
|
+
|
|
121
|
+
sha256 = hashlib.sha256()
|
|
122
|
+
with open(binary_path, "rb") as f:
|
|
123
|
+
for chunk in iter(lambda: f.read(8192), b""):
|
|
124
|
+
sha256.update(chunk)
|
|
125
|
+
actual = sha256.hexdigest()
|
|
126
|
+
|
|
127
|
+
if actual != expected:
|
|
128
|
+
binary_path.unlink(missing_ok=True)
|
|
129
|
+
print(
|
|
130
|
+
"Error: Checksum verification failed for dw v{}.\n"
|
|
131
|
+
" Expected: {}\n"
|
|
132
|
+
" Got: {}\n"
|
|
133
|
+
"Install manually: https://github.com/doublewordai/dw/releases".format(version, expected, actual),
|
|
134
|
+
file=sys.stderr,
|
|
135
|
+
)
|
|
136
|
+
sys.exit(1)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _download_binary(version):
|
|
140
|
+
# type: (str) -> Path
|
|
141
|
+
"""Download the correct binary for this platform from GitHub releases."""
|
|
142
|
+
suffix = _get_platform_suffix()
|
|
143
|
+
artifact = "dw-{}".format(suffix)
|
|
144
|
+
url = "https://github.com/{}/releases/download/v{}/{}".format(REPO, version, artifact)
|
|
145
|
+
|
|
146
|
+
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
147
|
+
binary_path = CACHE_DIR / BINARY_NAME
|
|
148
|
+
|
|
149
|
+
print("Downloading dw v{} for {}...".format(version, suffix), file=sys.stderr)
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
urllib.request.urlretrieve(url, binary_path)
|
|
153
|
+
except urllib.error.HTTPError as e:
|
|
154
|
+
print(
|
|
155
|
+
"Error: Failed to download dw v{} for {} (HTTP {}).\n"
|
|
156
|
+
"Install manually: https://github.com/doublewordai/dw/releases".format(version, suffix, e.code),
|
|
157
|
+
file=sys.stderr,
|
|
158
|
+
)
|
|
159
|
+
sys.exit(1)
|
|
160
|
+
except urllib.error.URLError as e:
|
|
161
|
+
print(
|
|
162
|
+
"Error: Network error downloading dw: {}\n"
|
|
163
|
+
"Install manually: https://github.com/doublewordai/dw/releases".format(e.reason),
|
|
164
|
+
file=sys.stderr,
|
|
165
|
+
)
|
|
166
|
+
sys.exit(1)
|
|
167
|
+
|
|
168
|
+
# Verify checksum
|
|
169
|
+
_verify_checksum(binary_path, version, suffix)
|
|
170
|
+
|
|
171
|
+
# Make executable
|
|
172
|
+
binary_path.chmod(binary_path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
|
173
|
+
|
|
174
|
+
return binary_path
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _ensure_binary():
|
|
178
|
+
# type: () -> Path
|
|
179
|
+
"""Ensure the dw binary is downloaded and up to date."""
|
|
180
|
+
binary_path = CACHE_DIR / BINARY_NAME
|
|
181
|
+
|
|
182
|
+
installed = _get_installed_version(binary_path)
|
|
183
|
+
|
|
184
|
+
# If binary exists and works, use it (check for updates periodically)
|
|
185
|
+
if installed is not None:
|
|
186
|
+
# Check for updates at most once per day
|
|
187
|
+
marker = CACHE_DIR / ".last-update-check"
|
|
188
|
+
|
|
189
|
+
should_check = True
|
|
190
|
+
if marker.exists():
|
|
191
|
+
age = time.time() - marker.stat().st_mtime
|
|
192
|
+
should_check = age > 86400 # 24 hours
|
|
193
|
+
|
|
194
|
+
if should_check:
|
|
195
|
+
try:
|
|
196
|
+
latest = _get_latest_version()
|
|
197
|
+
if latest != installed:
|
|
198
|
+
print(
|
|
199
|
+
"Updating dw: {} -> {}".format(installed, latest),
|
|
200
|
+
file=sys.stderr,
|
|
201
|
+
)
|
|
202
|
+
_download_binary(latest)
|
|
203
|
+
# Only mark check complete after success (version check or download)
|
|
204
|
+
marker.touch()
|
|
205
|
+
except (urllib.error.URLError, urllib.error.HTTPError) as e:
|
|
206
|
+
print(
|
|
207
|
+
"Warning: Update check failed ({}), using existing binary".format(e),
|
|
208
|
+
file=sys.stderr,
|
|
209
|
+
)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
print(
|
|
212
|
+
"Warning: Update check failed ({}), using existing binary".format(e),
|
|
213
|
+
file=sys.stderr,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
return binary_path
|
|
217
|
+
|
|
218
|
+
# No binary — download latest
|
|
219
|
+
try:
|
|
220
|
+
version = _get_latest_version()
|
|
221
|
+
except Exception as e:
|
|
222
|
+
print("Error: Could not fetch latest version: {}".format(e), file=sys.stderr)
|
|
223
|
+
print("Install manually: https://github.com/doublewordai/dw/releases", file=sys.stderr)
|
|
224
|
+
sys.exit(1)
|
|
225
|
+
|
|
226
|
+
return _download_binary(version)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def main():
|
|
230
|
+
"""Entry point — find/download the binary and run it with all args."""
|
|
231
|
+
binary = _ensure_binary()
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
result = subprocess.run([str(binary)] + sys.argv[1:])
|
|
235
|
+
sys.exit(result.returncode)
|
|
236
|
+
except KeyboardInterrupt:
|
|
237
|
+
sys.exit(130)
|
|
238
|
+
except FileNotFoundError:
|
|
239
|
+
print("Error: Binary not found at {}".format(binary), file=sys.stderr)
|
|
240
|
+
print("Try reinstalling: pip install --force-reinstall dw-cli", file=sys.stderr)
|
|
241
|
+
sys.exit(1)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
if __name__ == "__main__":
|
|
245
|
+
main()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dw-cli
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: Doubleword Batch Inference CLI
|
|
5
|
+
Author-email: Doubleword <engineering@doubleword.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://doubleword.ai
|
|
8
|
+
Project-URL: Repository, https://github.com/doublewordai/dw
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# dw-cli
|
|
17
|
+
|
|
18
|
+
Python wrapper for the [Doubleword CLI](https://github.com/doublewordai/dw) — a terminal tool for batch inference at scale.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install dw-cli
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This installs a `dw` command that automatically downloads the correct native binary for your platform on first run.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
dw login # Authenticate via browser
|
|
32
|
+
dw models list # List available models
|
|
33
|
+
dw files upload batch.jsonl # Upload a JSONL file
|
|
34
|
+
dw batches run batch.jsonl --watch # Upload, create batch, and watch progress
|
|
35
|
+
dw stream batch.jsonl > results.jsonl # Stream results as they complete
|
|
36
|
+
dw realtime model "prompt" # One-shot inference
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## How it works
|
|
40
|
+
|
|
41
|
+
This package is a thin wrapper. On first run it downloads the pre-built native binary from [GitHub Releases](https://github.com/doublewordai/dw/releases) to `~/.dw/bin/` and executes it. Updates are checked daily.
|
|
42
|
+
|
|
43
|
+
For direct binary installation without Python, see the [main repository](https://github.com/doublewordai/dw).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dw_cli
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dw-cli"
|
|
7
|
+
version = "0.1.3"
|
|
8
|
+
description = "Doubleword Batch Inference CLI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Doubleword", email = "engineering@doubleword.ai"},
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Topic :: Software Development :: Build Tools",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
dw = "dw_cli:main"
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://doubleword.ai"
|
|
27
|
+
Repository = "https://github.com/doublewordai/dw"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["."]
|
|
31
|
+
include = ["dw_cli*"]
|
dw_cli-0.1.3/setup.cfg
ADDED