drift-ml 0.1.15 → 0.2.3
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/README.md +27 -53
- package/bin/drift.js +26 -65
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,86 +1,60 @@
|
|
|
1
|
-
# drift
|
|
1
|
+
# drift
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Terminal-first, chat-based AutoML.** Open source. No tokens. No auth. Works fully local.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Install
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
Or: `npm install -g drift-ml` (requires `pipx install drift-ml` for the CLI).
|
|
14
|
-
|
|
15
|
-
2. **Run drift**
|
|
16
|
-
```bash
|
|
17
|
-
drift
|
|
18
|
-
```
|
|
19
|
-
On first run, the CLI detects your OS and architecture, downloads the correct engine binary into `~/.drift/bin/`, and starts the engine in the background. You’ll see a short welcome and instructions in the terminal.
|
|
20
|
-
|
|
21
|
-
3. **Use a local LLM**
|
|
22
|
-
Training and planning use an LLM. You need one of:
|
|
23
|
-
- **Gemini CLI** — install it and set `GEMINI_API_KEY` or have `gemini` on your PATH.
|
|
24
|
-
- **Ollama** — run `ollama run llama2` (or another model).
|
|
25
|
-
- Another local LLM the engine supports.
|
|
26
|
-
|
|
27
|
-
4. **Chat**
|
|
28
|
-
- `load path/to/data.csv`
|
|
29
|
-
- `predict price` (or any column)
|
|
30
|
-
- `try something stronger`
|
|
31
|
-
- `why is accuracy capped`
|
|
32
|
-
- `quit` to exit.
|
|
33
|
-
|
|
34
|
-
That’s it. The engine runs locally. The web app (if you use it) can be hosted on Vercel; the engine stays on your machine.
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## What is drift?
|
|
9
|
+
```bash
|
|
10
|
+
pipx install drift-ml
|
|
11
|
+
```
|
|
39
12
|
|
|
40
|
-
|
|
41
|
-
- **Terminal-first, chat-based** — Same engine as the web app. No commands to memorize; chat in natural language.
|
|
42
|
-
- **Engine** — On first run the CLI downloads and starts the engine from `~/.drift/bin/`. Or set `DRIFT_BACKEND_URL` to a running engine URL.
|
|
13
|
+
Or with npm (also requires pipx for the CLI):
|
|
43
14
|
|
|
44
|
-
|
|
15
|
+
```bash
|
|
16
|
+
pipx install drift-ml
|
|
17
|
+
npm install -g drift-ml
|
|
18
|
+
```
|
|
45
19
|
|
|
46
20
|
---
|
|
47
21
|
|
|
48
|
-
##
|
|
22
|
+
## Run
|
|
49
23
|
|
|
50
24
|
```bash
|
|
51
|
-
npm install -g drift-ml
|
|
52
25
|
drift
|
|
53
26
|
```
|
|
54
27
|
|
|
55
|
-
|
|
28
|
+
That's it. On first run, drift downloads and starts the engine automatically. No backend setup. No config.
|
|
56
29
|
|
|
57
|
-
|
|
30
|
+
---
|
|
58
31
|
|
|
59
|
-
|
|
60
|
-
pipx install drift-ml
|
|
61
|
-
drift
|
|
62
|
-
```
|
|
32
|
+
## How it works
|
|
63
33
|
|
|
64
|
-
**
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
(PowerShell on Windows: same command.)
|
|
34
|
+
- **Local-first** — Engine runs on your machine. Data never leaves.
|
|
35
|
+
- **Chat-based** — `load data.csv`, `predict price`, `try something stronger`
|
|
36
|
+
- **Auto-start** — Engine downloads and starts in the background. You never touch it.
|
|
37
|
+
- **No tokens** — No API keys for drift. (You need an LLM for training: Ollama, Gemini CLI, etc.)
|
|
69
38
|
|
|
70
39
|
---
|
|
71
40
|
|
|
72
|
-
## Example
|
|
41
|
+
## Example
|
|
73
42
|
|
|
74
43
|
```text
|
|
75
44
|
drift › load iris.csv
|
|
76
|
-
drift › predict
|
|
45
|
+
drift › predict variety
|
|
77
46
|
drift › try something stronger
|
|
78
|
-
drift › why is accuracy capped
|
|
79
47
|
drift › quit
|
|
80
48
|
```
|
|
81
49
|
|
|
82
50
|
---
|
|
83
51
|
|
|
52
|
+
## Philosophy
|
|
53
|
+
|
|
54
|
+
drift should feel like `git`, `docker`, `brew` — a tool you trust immediately. Zero friction. Open source.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
84
58
|
## License
|
|
85
59
|
|
|
86
60
|
MIT
|
package/bin/drift.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// FINAL FLOW: (1) If no DRIFT_BACKEND_URL, ensure engine at 127.0.0.1:8000 (download + start if needed).
|
|
7
|
-
// (2) Locate Python drift ONLY in ~/.local/bin (macOS/Linux) or %USERPROFILE%\.local\bin (Windows).
|
|
8
|
-
// (3) Spawn that binary with DRIFT_BACKEND_URL set; never run drift.cmd/drift.ps1/drift.js (self).
|
|
3
|
+
// drift — terminal-first AutoML. Engine auto-starts locally.
|
|
4
|
+
// No tokens. No auth. No backend setup.
|
|
9
5
|
//
|
|
10
6
|
|
|
11
7
|
const { spawnSync, spawn } = require("child_process");
|
|
@@ -16,16 +12,13 @@ const http = require("http");
|
|
|
16
12
|
|
|
17
13
|
const isWindows = process.platform === "win32";
|
|
18
14
|
const ENGINE_PORT = process.env.DRIFT_ENGINE_PORT || "8000";
|
|
19
|
-
|
|
20
|
-
const ENGINE_TAG = "v0.
|
|
21
|
-
const
|
|
22
|
-
const ENGINE_BASE_URL = process.env.DRIFT_ENGINE_BASE_URL || `https://github.com/${GITHUB_REPO}/releases/download/${ENGINE_TAG}`;
|
|
23
|
-
const GITHUB_API_RELEASE = `https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${ENGINE_TAG}`;
|
|
24
|
-
const GITHUB_TOKEN = process.env.DRIFT_GITHUB_TOKEN || process.env.GITHUB_TOKEN;
|
|
15
|
+
const GITHUB_REPO = "lakshitsachdeva/drift"; // Engine binaries
|
|
16
|
+
const ENGINE_TAG = "v0.2.0"; // Pinned — direct URL, no API, no rate limits
|
|
17
|
+
const ENGINE_BASE_URL = `https://github.com/${GITHUB_REPO}/releases/download/${ENGINE_TAG}`;
|
|
25
18
|
const HEALTH_URL = `http://127.0.0.1:${ENGINE_PORT}/health`;
|
|
26
19
|
const HEALTH_TIMEOUT_MS = 2000;
|
|
27
20
|
const HEALTH_POLL_MS = 500;
|
|
28
|
-
const HEALTH_POLL_MAX = 60;
|
|
21
|
+
const HEALTH_POLL_MAX = 60;
|
|
29
22
|
const isMac = process.platform === "darwin";
|
|
30
23
|
|
|
31
24
|
function getPlatformKey() {
|
|
@@ -66,55 +59,32 @@ function fetchOk(url) {
|
|
|
66
59
|
});
|
|
67
60
|
}
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Accept: "application/octet-stream",
|
|
77
|
-
...(GITHUB_TOKEN && { Authorization: `Bearer ${GITHUB_TOKEN}` }),
|
|
78
|
-
};
|
|
62
|
+
function getAssetUrl(assetName) {
|
|
63
|
+
const baseUrl = process.env.DRIFT_ENGINE_BASE_URL;
|
|
64
|
+
if (baseUrl) {
|
|
65
|
+
return `${baseUrl.replace(/\/$/, "")}/${assetName}`;
|
|
66
|
+
}
|
|
67
|
+
return `${ENGINE_BASE_URL}/${assetName}`;
|
|
68
|
+
}
|
|
79
69
|
|
|
80
|
-
|
|
81
|
-
function getGitHubAssetUrl(assetName) {
|
|
70
|
+
function downloadWithCurl(url, destPath) {
|
|
82
71
|
return new Promise((resolve, reject) => {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"Release not found (404). If the repo is private, set DRIFT_GITHUB_TOKEN with a token that has repo read access."
|
|
87
|
-
));
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (res.statusCode !== 200) {
|
|
91
|
-
reject(new Error(`Release not found: ${res.statusCode}`));
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
let body = "";
|
|
95
|
-
res.on("data", (chunk) => { body += chunk; });
|
|
96
|
-
res.on("end", () => {
|
|
97
|
-
try {
|
|
98
|
-
const data = JSON.parse(body);
|
|
99
|
-
const asset = (data.assets || []).find((a) => a.name === assetName);
|
|
100
|
-
if (!asset || !asset.url) {
|
|
101
|
-
reject(new Error(`Asset not found: ${assetName}`));
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
resolve(asset.url);
|
|
105
|
-
} catch (e) {
|
|
106
|
-
reject(e);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
72
|
+
const result = spawnSync("curl", ["-fsSL", "-o", destPath, url], {
|
|
73
|
+
stdio: "pipe",
|
|
74
|
+
timeout: 120000,
|
|
109
75
|
});
|
|
110
|
-
|
|
76
|
+
if (result.status !== 0) {
|
|
77
|
+
reject(new Error("Download failed"));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
resolve();
|
|
111
81
|
});
|
|
112
82
|
}
|
|
113
83
|
|
|
114
84
|
function downloadFile(url, destPath) {
|
|
115
85
|
return new Promise((resolve, reject) => {
|
|
116
86
|
const client = url.startsWith("https") ? https : http;
|
|
117
|
-
const req = client.get(url, { headers:
|
|
87
|
+
const req = client.get(url, { headers: { "User-Agent": "Drift/1.0" } }, (res) => {
|
|
118
88
|
const redirect = res.statusCode >= 301 && res.statusCode <= 302 && res.headers.location;
|
|
119
89
|
if (redirect) {
|
|
120
90
|
downloadFile(redirect, destPath).then(resolve).catch(reject);
|
|
@@ -166,16 +136,12 @@ async function ensureEngine() {
|
|
|
166
136
|
const { plat, arch } = getPlatformKey();
|
|
167
137
|
const ext = isWindows ? ".exe" : "";
|
|
168
138
|
const asset = `drift-engine-${plat}-${arch}${ext}`;
|
|
169
|
-
const
|
|
170
|
-
const url = isDefaultGitHub
|
|
171
|
-
? await getGitHubAssetUrl(asset)
|
|
172
|
-
: `${ENGINE_BASE_URL.replace(/\/$/, "")}/${asset}`;
|
|
139
|
+
const url = getAssetUrl(asset);
|
|
173
140
|
process.stderr.write(`drift: Downloading engine (${asset})...\n`);
|
|
174
141
|
try {
|
|
175
|
-
await downloadFile(url, binPath);
|
|
142
|
+
await downloadWithCurl(url, binPath).catch(() => downloadFile(url, binPath));
|
|
176
143
|
} catch (e) {
|
|
177
144
|
console.error("drift: Download failed.", e.message);
|
|
178
|
-
console.error("drift: Set DRIFT_ENGINE_BASE_URL or run the engine manually.");
|
|
179
145
|
return false;
|
|
180
146
|
}
|
|
181
147
|
if (!isWindows) {
|
|
@@ -187,7 +153,6 @@ async function ensureEngine() {
|
|
|
187
153
|
} catch (_) {}
|
|
188
154
|
}
|
|
189
155
|
}
|
|
190
|
-
// On macOS, always ensure binary is executable and not quarantined before spawn (covers existing binaries).
|
|
191
156
|
if (isMac && binPath) {
|
|
192
157
|
try {
|
|
193
158
|
fs.chmodSync(binPath, 0o755);
|
|
@@ -204,7 +169,6 @@ async function ensureEngine() {
|
|
|
204
169
|
return waitForEngine();
|
|
205
170
|
}
|
|
206
171
|
|
|
207
|
-
// Locate Python drift ONLY in pipx bin dir. Never search PATH (avoids running drift.cmd/drift.ps1/drift.js).
|
|
208
172
|
function findPythonDrift() {
|
|
209
173
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
210
174
|
if (!home) return null;
|
|
@@ -230,7 +194,6 @@ async function main() {
|
|
|
230
194
|
});
|
|
231
195
|
if (!started) {
|
|
232
196
|
console.error("Failed to start drift engine.");
|
|
233
|
-
console.error("Please check permissions or download the engine manually.");
|
|
234
197
|
process.exit(1);
|
|
235
198
|
}
|
|
236
199
|
}
|
|
@@ -239,9 +202,7 @@ async function main() {
|
|
|
239
202
|
const driftPath = findPythonDrift();
|
|
240
203
|
if (!driftPath) {
|
|
241
204
|
console.error(`
|
|
242
|
-
drift is not installed.
|
|
243
|
-
|
|
244
|
-
Install it with:
|
|
205
|
+
drift is not installed. Install it with:
|
|
245
206
|
|
|
246
207
|
pipx install drift-ml
|
|
247
208
|
`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drift-ml",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "drift — terminal-first, chat-based AutoML. Open source. No tokens. No auth.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"drift": "bin/drift.js"
|
|
7
7
|
},
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"engines": {
|
|
10
10
|
"node": ">=18"
|
|
11
11
|
},
|
|
12
|
-
"keywords": ["drift", "automl", "cli", "ml", "terminal", "local-first"],
|
|
12
|
+
"keywords": ["drift", "automl", "cli", "ml", "terminal", "local-first", "ai", "open-source"],
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": "Lakshit Sachdeva",
|
|
15
15
|
"repository": {
|