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.
@@ -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,9 @@
1
+ MANIFEST.in
2
+ README.md
3
+ pyproject.toml
4
+ dw_cli/__init__.py
5
+ dw_cli.egg-info/PKG-INFO
6
+ dw_cli.egg-info/SOURCES.txt
7
+ dw_cli.egg-info/dependency_links.txt
8
+ dw_cli.egg-info/entry_points.txt
9
+ dw_cli.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ dw = dw_cli:main
@@ -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
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+