drift-ml 0.1.1 → 0.1.4

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.
Files changed (2) hide show
  1. package/bin/drift.js +62 -155
  2. package/package.json +1 -1
package/bin/drift.js CHANGED
@@ -1,175 +1,82 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { spawnSync, spawn } = require("child_process");
4
- const fs = require("fs");
3
+ const { spawnSync } = require("child_process");
5
4
  const path = require("path");
6
- const https = require("https");
7
- const http = require("http");
8
-
9
- const ENGINE_PORT = process.env.DRIFT_ENGINE_PORT || "8000";
10
- const ENGINE_BASE = process.env.DRIFT_ENGINE_BASE_URL || "https://github.com/lakshitsachdeva/drift/releases/latest/download";
11
- const HEALTH_URL = `http://127.0.0.1:${ENGINE_PORT}/health`;
12
- const HEALTH_TIMEOUT_MS = 2000;
13
- const HEALTH_POLL_MS = 500;
14
- const HEALTH_POLL_MAX = 60; // 30s
15
-
16
- function which(cmd) {
17
- const { status, stdout } = spawnSync("which", [cmd], { encoding: "utf8" });
18
- return status === 0 ? (stdout || "").trim() : null;
19
- }
20
-
21
- function getPlatformKey() {
22
- const p = process.platform;
23
- const a = process.arch;
24
- const plat = p === "darwin" ? "macos" : p === "win32" ? "windows" : "linux";
25
- const arch = a === "arm64" || a === "aarch64" ? "arm64" : "x64";
26
- return { plat, arch };
27
- }
28
-
29
- function getEngineDir() {
30
- const home = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH;
31
- if (!home) return null;
32
- return path.join(home, ".drift", "bin");
33
- }
34
-
35
- function getEnginePath() {
36
- const dir = getEngineDir();
37
- if (!dir) return null;
38
- const { plat, arch } = getPlatformKey();
39
- const ext = process.platform === "win32" ? ".exe" : "";
40
- return path.join(dir, `drift-engine-${plat}-${arch}${ext}`);
41
- }
5
+ const fs = require("fs");
42
6
 
43
- function fetchOk(url) {
44
- return new Promise((resolve, reject) => {
45
- const client = url.startsWith("https") ? https : http;
46
- const req = client.get(url, { timeout: HEALTH_TIMEOUT_MS }, (res) => {
47
- const redirect = res.statusCode >= 301 && res.statusCode <= 302 && res.headers.location;
48
- if (redirect) {
49
- fetchOk(redirect).then(resolve).catch(reject);
50
- return;
51
- }
52
- resolve(res.statusCode === 200);
53
- });
54
- req.on("error", () => resolve(false));
55
- req.on("timeout", () => { req.destroy(); resolve(false); });
56
- });
7
+ // Known pipx install locations
8
+ function getPipxBinPaths() {
9
+ const home = process.env.HOME || process.env.USERPROFILE || "";
10
+ return [
11
+ path.join(home, ".local", "bin", "drift"),
12
+ "/usr/local/bin/drift",
13
+ ];
57
14
  }
58
15
 
59
- function downloadFile(url, destPath) {
60
- return new Promise((resolve, reject) => {
61
- const client = url.startsWith("https") ? https : http;
62
- const req = client.get(url, (res) => {
63
- const redirect = res.statusCode >= 301 && res.statusCode <= 302 && res.headers.location;
64
- if (redirect) {
65
- downloadFile(redirect, destPath).then(resolve).catch(reject);
66
- return;
67
- }
68
- if (res.statusCode !== 200) {
69
- reject(new Error(`Download failed: ${res.statusCode}`));
70
- return;
16
+ // Find Python-based drift (not this Node script)
17
+ function findPythonDrift() {
18
+ // First check pipx standard locations
19
+ for (const p of getPipxBinPaths()) {
20
+ if (fs.existsSync(p)) {
21
+ // Verify it's not a Node script (i.e., not us)
22
+ try {
23
+ const content = fs.readFileSync(p, "utf8").slice(0, 100);
24
+ if (!content.includes("#!/usr/bin/env node")) {
25
+ return p;
26
+ }
27
+ } catch (_) {}
28
+ }
29
+ }
30
+
31
+ // Fallback: search PATH but skip Node scripts
32
+ const pathEnv = process.env.PATH || "";
33
+ const dirs = pathEnv.split(path.delimiter);
34
+
35
+ for (const dir of dirs) {
36
+ const candidate = path.join(dir, "drift");
37
+ if (fs.existsSync(candidate)) {
38
+ try {
39
+ const content = fs.readFileSync(candidate, "utf8").slice(0, 100);
40
+ // Skip if it's a Node script (that's us or another Node launcher)
41
+ if (content.includes("#!/usr/bin/env node")) {
42
+ continue;
43
+ }
44
+ return candidate;
45
+ } catch (_) {
46
+ // Binary file or unreadable - might be the real one
47
+ return candidate;
71
48
  }
72
- const dir = path.dirname(destPath);
73
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
74
- const file = fs.createWriteStream(destPath);
75
- res.pipe(file);
76
- file.on("finish", () => { file.close(); resolve(); });
77
- file.on("error", reject);
78
- });
79
- req.on("error", reject);
80
- });
49
+ }
50
+ }
51
+
52
+ return null;
81
53
  }
82
54
 
83
- function engineRunning() {
84
- return fetchOk(HEALTH_URL);
85
- }
55
+ function main() {
56
+ const driftPath = findPythonDrift();
57
+
58
+ if (!driftPath) {
59
+ console.error(`
60
+ drift is not installed.
86
61
 
87
- function waitForEngine() {
88
- return new Promise((resolve) => {
89
- let n = 0;
90
- const t = setInterval(() => {
91
- n++;
92
- fetchOk(HEALTH_URL).then((ok) => {
93
- if (ok) { clearInterval(t); resolve(true); }
94
- else if (n >= HEALTH_POLL_MAX) { clearInterval(t); resolve(false); }
95
- });
96
- }, HEALTH_POLL_MS);
97
- });
98
- }
62
+ Install it with:
99
63
 
100
- async function ensureEngine() {
101
- const binPath = getEnginePath();
102
- const binDir = getEngineDir();
103
- if (!binPath || !binDir) {
104
- console.error("drift: Could not resolve engine directory (~/.drift/bin). Set HOME or USERPROFILE.");
105
- return false;
106
- }
107
- if (!fs.existsSync(binPath)) {
108
- const { plat, arch } = getPlatformKey();
109
- const ext = process.platform === "win32" ? ".exe" : "";
110
- const asset = `drift-engine-${plat}-${arch}${ext}`;
111
- const url = `${ENGINE_BASE}/${asset}`;
112
- process.stderr.write(`drift: Downloading engine (${asset})...\n`);
113
- try {
114
- await downloadFile(url, binPath);
115
- } catch (e) {
116
- console.error("drift: Download failed.", e.message);
117
- console.error("drift: Run the engine manually or set DRIFT_ENGINE_BASE_URL.");
118
- return false;
119
- }
120
- if (process.platform !== "win32") {
121
- try { fs.chmodSync(binPath, 0o755); } catch (_) {}
122
- }
123
- }
124
- const child = spawn(binPath, [], {
125
- detached: true,
126
- stdio: "ignore",
127
- cwd: binDir,
128
- env: { ...process.env, DRIFT_ENGINE_PORT: ENGINE_PORT },
129
- });
130
- child.unref();
131
- return waitForEngine();
132
- }
64
+ pipx install drift-ml
133
65
 
134
- async function main() {
135
- const userBackend = process.env.DRIFT_BACKEND_URL;
136
- if (userBackend) {
137
- // User runs engine elsewhere; skip download/start and connect to their URL.
138
- } else if (await engineRunning()) {
139
- // Engine already running locally.
140
- } else {
141
- const engineStarted = await ensureEngine().catch(() => false);
142
- if (!engineStarted) {
143
- console.error("drift: Engine did not start. Run the engine manually or set DRIFT_BACKEND_URL to a running engine URL.");
144
- process.exit(1);
145
- }
146
- }
66
+ Then run:
147
67
 
148
- const python = which("python3") || which("python");
149
- if (!python) {
150
- console.error("drift: Python is required for the chat CLI. Install Python 3 and ensure `python` or `python3` is on your PATH.");
68
+ drift
69
+ `);
151
70
  process.exit(1);
152
71
  }
153
72
 
154
- const pip = spawnSync(python, ["-m", "pip", "install", "--quiet", "--upgrade", "drift"], {
155
- encoding: "utf8",
156
- stdio: ["inherit", "pipe", "inherit"],
157
- });
158
- if (pip.status !== 0) {
159
- console.error("drift: Failed to install or upgrade the drift Python package.");
160
- process.exit(pip.status === null ? 1 : pip.status);
161
- }
162
-
163
- const backendUrl = userBackend || `http://127.0.0.1:${ENGINE_PORT}`;
164
- const run = spawnSync(python, ["-m", "drift"], {
165
- encoding: "utf8",
73
+ // Run the Python drift and forward stdin/stdout
74
+ const result = spawnSync(driftPath, process.argv.slice(2), {
166
75
  stdio: "inherit",
167
- env: { ...process.env, DRIFT_BACKEND_URL: backendUrl },
76
+ env: process.env,
168
77
  });
169
- process.exit(run.status === null ? (run.signal ? 128 + 9 : 1) : run.status);
78
+
79
+ process.exit(result.status === null ? (result.signal ? 128 + 9 : 1) : result.status);
170
80
  }
171
81
 
172
- main().catch((e) => {
173
- console.error("drift:", e.message);
174
- process.exit(1);
175
- });
82
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drift-ml",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "Drift — terminal-first, chat-based AutoML. Same engine as the web app. On first run: downloads and starts the engine locally (never exposes engine source).",
5
5
  "bin": {
6
6
  "drift": "./bin/drift.js"