@0dai-dev/cli 1.3.0 → 2.0.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/bin/0dai.js +56 -5
- package/package.json +1 -1
package/bin/0dai.js
CHANGED
|
@@ -7,10 +7,11 @@ const fs = require("fs");
|
|
|
7
7
|
const path = require("path");
|
|
8
8
|
const os = require("os");
|
|
9
9
|
|
|
10
|
-
const VERSION = "
|
|
10
|
+
const VERSION = "2.0.0";
|
|
11
11
|
const API_URL = process.env.ODAI_API_URL || "https://api.0dai.dev";
|
|
12
12
|
const CONFIG_DIR = path.join(os.homedir(), ".0dai");
|
|
13
13
|
const AUTH_FILE = path.join(CONFIG_DIR, "auth.json");
|
|
14
|
+
const VERSION_CHECK_FILE = path.join(CONFIG_DIR, ".version_check");
|
|
14
15
|
|
|
15
16
|
const MANIFEST_FILES = [
|
|
16
17
|
"package.json", "go.mod", "pyproject.toml", "requirements.txt",
|
|
@@ -27,16 +28,37 @@ const PROBE_DIRS = [
|
|
|
27
28
|
"android", "ios", "linux", "macos", "windows",
|
|
28
29
|
];
|
|
29
30
|
|
|
31
|
+
function deviceFingerprint() {
|
|
32
|
+
const crypto = require("crypto");
|
|
33
|
+
const parts = [
|
|
34
|
+
os.hostname(),
|
|
35
|
+
os.userInfo().username,
|
|
36
|
+
os.platform(),
|
|
37
|
+
os.arch(),
|
|
38
|
+
os.cpus().length.toString(),
|
|
39
|
+
os.totalmem().toString(),
|
|
40
|
+
];
|
|
41
|
+
// Try machine-id
|
|
42
|
+
try {
|
|
43
|
+
if (os.platform() === "linux") parts.push(fs.readFileSync("/etc/machine-id", "utf8").trim());
|
|
44
|
+
else if (os.platform() === "darwin") {
|
|
45
|
+
const { execSync } = require("child_process");
|
|
46
|
+
parts.push(execSync("ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/'", { encoding: "utf8" }).trim());
|
|
47
|
+
}
|
|
48
|
+
} catch {}
|
|
49
|
+
return crypto.createHash("sha256").update(parts.join(":")).digest("hex").slice(0, 32);
|
|
50
|
+
}
|
|
51
|
+
|
|
30
52
|
function apiCall(endpoint, data) {
|
|
31
53
|
return new Promise((resolve, reject) => {
|
|
32
54
|
const url = new URL(endpoint, API_URL);
|
|
33
55
|
const mod = url.protocol === "https:" ? https : http;
|
|
34
56
|
const body = data ? JSON.stringify(data) : null;
|
|
35
|
-
const headers = { "Content-Type": "application/json" };
|
|
57
|
+
const headers = { "Content-Type": "application/json", "X-Device-ID": deviceFingerprint() };
|
|
36
58
|
|
|
37
59
|
try {
|
|
38
60
|
const auth = JSON.parse(fs.readFileSync(AUTH_FILE, "utf8"));
|
|
39
|
-
const token = auth.api_key || auth.token;
|
|
61
|
+
const token = auth.api_key || auth.access_token || auth.token;
|
|
40
62
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
41
63
|
} catch {}
|
|
42
64
|
|
|
@@ -46,7 +68,7 @@ function apiCall(endpoint, data) {
|
|
|
46
68
|
path: url.pathname,
|
|
47
69
|
method: body ? "POST" : "GET",
|
|
48
70
|
headers,
|
|
49
|
-
timeout:
|
|
71
|
+
timeout: 60000,
|
|
50
72
|
};
|
|
51
73
|
if (body) opts.headers["Content-Length"] = Buffer.byteLength(body);
|
|
52
74
|
|
|
@@ -136,7 +158,15 @@ async function cmdInit(target) {
|
|
|
136
158
|
available_clis: clis,
|
|
137
159
|
});
|
|
138
160
|
|
|
139
|
-
if (result.error) {
|
|
161
|
+
if (result.error) {
|
|
162
|
+
if (result.hint) {
|
|
163
|
+
console.log(`\n[0dai] ${result.message || result.error}`);
|
|
164
|
+
console.log(` ${result.hint}\n`);
|
|
165
|
+
} else {
|
|
166
|
+
console.error(`[0dai] error: ${result.error}`);
|
|
167
|
+
}
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
140
170
|
|
|
141
171
|
console.log(`[0dai] detected: ${result.stack || "?"}`);
|
|
142
172
|
writeFiles(target, result.files || {});
|
|
@@ -239,6 +269,24 @@ function cmdStatus(target) {
|
|
|
239
269
|
} catch {}
|
|
240
270
|
}
|
|
241
271
|
|
|
272
|
+
async function checkVersion() {
|
|
273
|
+
try {
|
|
274
|
+
// Only check once per day
|
|
275
|
+
let lastCheck = 0;
|
|
276
|
+
try { lastCheck = parseFloat(fs.readFileSync(VERSION_CHECK_FILE, "utf8")); } catch {}
|
|
277
|
+
if (Date.now() / 1000 - lastCheck < 86400) return;
|
|
278
|
+
|
|
279
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
280
|
+
fs.writeFileSync(VERSION_CHECK_FILE, String(Date.now() / 1000));
|
|
281
|
+
|
|
282
|
+
const result = await apiCall("/v1/version");
|
|
283
|
+
if (result.version && result.version !== VERSION) {
|
|
284
|
+
console.log(`\n[0dai] Update available: ${VERSION} → ${result.version}`);
|
|
285
|
+
console.log(` Run: npm update -g @0dai-dev/cli\n`);
|
|
286
|
+
}
|
|
287
|
+
} catch {}
|
|
288
|
+
}
|
|
289
|
+
|
|
242
290
|
async function cmdAuthLogin() {
|
|
243
291
|
// Step 1: request device code
|
|
244
292
|
const result = await apiCall("/v1/auth/device", { client_id: "cli" });
|
|
@@ -309,6 +357,9 @@ async function main() {
|
|
|
309
357
|
const cmd = args[0] || "help";
|
|
310
358
|
const sub = args[1] || "";
|
|
311
359
|
|
|
360
|
+
// Non-blocking version check (runs in background, once per day)
|
|
361
|
+
checkVersion();
|
|
362
|
+
|
|
312
363
|
switch (cmd) {
|
|
313
364
|
case "init": await cmdInit(target); break;
|
|
314
365
|
case "sync": await cmdSync(target); break;
|