@0dai-dev/cli 1.3.1 → 2.0.1
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 -29
- package/package.json +1 -1
package/bin/0dai.js
CHANGED
|
@@ -7,10 +7,15 @@ 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.1";
|
|
11
11
|
const API_URL = process.env.ODAI_API_URL || "https://api.0dai.dev";
|
|
12
|
+
const T = process.stdout.isTTY ? "\x1b[38;2;45;212;168m" : ""; // teal
|
|
13
|
+
const R = process.stdout.isTTY ? "\x1b[0m" : ""; // reset
|
|
14
|
+
const D = process.stdout.isTTY ? "\x1b[2m" : ""; // dim
|
|
15
|
+
const log = (msg) => console.log(`${T}[0dai]${R} ${msg}`);
|
|
12
16
|
const CONFIG_DIR = path.join(os.homedir(), ".0dai");
|
|
13
17
|
const AUTH_FILE = path.join(CONFIG_DIR, "auth.json");
|
|
18
|
+
const VERSION_CHECK_FILE = path.join(CONFIG_DIR, ".version_check");
|
|
14
19
|
|
|
15
20
|
const MANIFEST_FILES = [
|
|
16
21
|
"package.json", "go.mod", "pyproject.toml", "requirements.txt",
|
|
@@ -136,21 +141,21 @@ function writeFiles(target, files) {
|
|
|
136
141
|
} catch { created++; }
|
|
137
142
|
fs.writeFileSync(p, content, "utf8");
|
|
138
143
|
}
|
|
139
|
-
|
|
144
|
+
log(`${created} created, ${updated} updated, ${unchanged} unchanged`);
|
|
140
145
|
return created + updated;
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
async function cmdInit(target) {
|
|
144
149
|
if (fs.existsSync(path.join(target, "ai", "VERSION"))) {
|
|
145
150
|
const v = fs.readFileSync(path.join(target, "ai", "VERSION"), "utf8").trim();
|
|
146
|
-
|
|
151
|
+
log(`ai/ layer already exists (v${v}). Run '0dai sync' to update.`);
|
|
147
152
|
return;
|
|
148
153
|
}
|
|
149
154
|
|
|
150
|
-
|
|
155
|
+
log("collecting project metadata...");
|
|
151
156
|
const { projectFiles, fileContents, clis } = collectMetadata(target);
|
|
152
157
|
|
|
153
|
-
|
|
158
|
+
log(`sending to API (${projectFiles.length} files, ${clis.length} CLIs)...`);
|
|
154
159
|
const result = await apiCall("/v1/init", {
|
|
155
160
|
project_files: projectFiles,
|
|
156
161
|
file_contents: fileContents,
|
|
@@ -159,15 +164,15 @@ async function cmdInit(target) {
|
|
|
159
164
|
|
|
160
165
|
if (result.error) {
|
|
161
166
|
if (result.hint) {
|
|
162
|
-
|
|
167
|
+
log(`${result.message || result.error}`);
|
|
163
168
|
console.log(` ${result.hint}\n`);
|
|
164
169
|
} else {
|
|
165
|
-
|
|
170
|
+
log(`error: ${result.error}`);
|
|
166
171
|
}
|
|
167
172
|
process.exit(1);
|
|
168
173
|
}
|
|
169
174
|
|
|
170
|
-
|
|
175
|
+
log(`detected: ${result.stack || "?"}`);
|
|
171
176
|
writeFiles(target, result.files || {});
|
|
172
177
|
|
|
173
178
|
// Add to .gitignore
|
|
@@ -177,7 +182,7 @@ async function cmdInit(target) {
|
|
|
177
182
|
if (!text.includes(".0dai")) fs.appendFileSync(gi, "\n.0dai/\n");
|
|
178
183
|
} catch {}
|
|
179
184
|
|
|
180
|
-
|
|
185
|
+
log(`initialized (${result.file_count || "?"} files)`);
|
|
181
186
|
console.log(" skills: /build /review /status /feedback /bugfix /delegate");
|
|
182
187
|
}
|
|
183
188
|
|
|
@@ -216,24 +221,24 @@ async function cmdSync(target) {
|
|
|
216
221
|
current_files: currentFiles, file_contents: fileContents,
|
|
217
222
|
});
|
|
218
223
|
|
|
219
|
-
if (result.error) {
|
|
224
|
+
if (result.error) { log(`error: ${result.error}`); process.exit(1); }
|
|
220
225
|
|
|
221
226
|
const updated = result.files_updated || {};
|
|
222
227
|
if (Object.keys(updated).length) writeFiles(target, updated);
|
|
223
|
-
else
|
|
228
|
+
else log("already up to date");
|
|
224
229
|
}
|
|
225
230
|
|
|
226
231
|
async function cmdDetect(target) {
|
|
227
232
|
const { projectFiles } = collectMetadata(target);
|
|
228
233
|
const result = await apiCall("/v1/detect", { files: projectFiles });
|
|
229
|
-
if (result.error) {
|
|
234
|
+
if (result.error) { log(`error: ${result.error}`); return; }
|
|
230
235
|
console.log(`stack: ${result.stack || "?"}`);
|
|
231
236
|
console.log(`clis: ${(result.available_clis || []).join(",")}`);
|
|
232
237
|
}
|
|
233
238
|
|
|
234
239
|
function cmdDoctor(target) {
|
|
235
240
|
const ai = path.join(target, "ai");
|
|
236
|
-
if (!fs.existsSync(ai)) {
|
|
241
|
+
if (!fs.existsSync(ai)) { log("no ai/ layer. Run '0dai init' first."); return; }
|
|
237
242
|
let v = "?";
|
|
238
243
|
try { v = fs.readFileSync(path.join(ai, "VERSION"), "utf8").trim(); } catch {}
|
|
239
244
|
const checks = {
|
|
@@ -245,7 +250,7 @@ function cmdDoctor(target) {
|
|
|
245
250
|
"AGENTS.md": fs.existsSync(path.join(target, "AGENTS.md")),
|
|
246
251
|
};
|
|
247
252
|
const ok = Object.values(checks).every(Boolean);
|
|
248
|
-
|
|
253
|
+
log(`v${v} — ${ok ? "healthy" : "issues found"}`);
|
|
249
254
|
for (const [name, exists] of Object.entries(checks)) console.log(` ${exists ? "ok" : "MISSING"}: ${name}`);
|
|
250
255
|
}
|
|
251
256
|
|
|
@@ -254,7 +259,7 @@ function cmdStatus(target) {
|
|
|
254
259
|
let v = "?", stack = "?";
|
|
255
260
|
try { v = fs.readFileSync(path.join(ai, "VERSION"), "utf8").trim(); } catch {}
|
|
256
261
|
try { stack = JSON.parse(fs.readFileSync(path.join(ai, "manifest", "discovery.json"), "utf8")).stack || "?"; } catch {}
|
|
257
|
-
|
|
262
|
+
log(`v${v} | stack: ${stack}`);
|
|
258
263
|
|
|
259
264
|
const count = (dir) => { try { return fs.readdirSync(dir).filter(f => f.endsWith(".json")).length; } catch { return 0; } };
|
|
260
265
|
const q = count(path.join(ai, "swarm", "queue"));
|
|
@@ -268,15 +273,34 @@ function cmdStatus(target) {
|
|
|
268
273
|
} catch {}
|
|
269
274
|
}
|
|
270
275
|
|
|
276
|
+
async function checkVersion() {
|
|
277
|
+
try {
|
|
278
|
+
// Check interval: 1 hour during debug, configurable via env
|
|
279
|
+
const intervalSec = parseInt(process.env.ODAI_UPDATE_CHECK_INTERVAL || "3600");
|
|
280
|
+
let lastCheck = 0;
|
|
281
|
+
try { lastCheck = parseFloat(fs.readFileSync(VERSION_CHECK_FILE, "utf8")); } catch {}
|
|
282
|
+
if (Date.now() / 1000 - lastCheck < intervalSec) return;
|
|
283
|
+
|
|
284
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
285
|
+
fs.writeFileSync(VERSION_CHECK_FILE, String(Date.now() / 1000));
|
|
286
|
+
|
|
287
|
+
const result = await apiCall("/v1/version");
|
|
288
|
+
if (result.version && result.version !== VERSION) {
|
|
289
|
+
log(`Update available: ${VERSION} → ${result.version}`);
|
|
290
|
+
console.log(` Run: npm update -g @0dai-dev/cli\n`);
|
|
291
|
+
}
|
|
292
|
+
} catch {}
|
|
293
|
+
}
|
|
294
|
+
|
|
271
295
|
async function cmdAuthLogin() {
|
|
272
296
|
// Step 1: request device code
|
|
273
297
|
const result = await apiCall("/v1/auth/device", { client_id: "cli" });
|
|
274
|
-
if (result.error) {
|
|
298
|
+
if (result.error) { log(`error: ${result.error}`); process.exit(1); }
|
|
275
299
|
|
|
276
|
-
|
|
300
|
+
log(`Open this URL in your browser:\n`);
|
|
277
301
|
console.log(` ${result.verification_uri}\n`);
|
|
278
|
-
|
|
279
|
-
|
|
302
|
+
log(`Enter code: ${result.user_code}\n`);
|
|
303
|
+
log("Waiting for authorization...");
|
|
280
304
|
|
|
281
305
|
// Step 2: poll for token
|
|
282
306
|
const interval = (result.interval || 5) * 1000;
|
|
@@ -295,28 +319,28 @@ async function cmdAuthLogin() {
|
|
|
295
319
|
authenticated_at: new Date().toISOString(),
|
|
296
320
|
expires_at: poll.expires_at,
|
|
297
321
|
}, null, 2) + "\n", { mode: 0o600 });
|
|
298
|
-
|
|
322
|
+
log(`Logged in as ${poll.email} (${poll.plan} plan)`);
|
|
299
323
|
return;
|
|
300
324
|
}
|
|
301
325
|
if (poll.error && poll.error !== "authorization_pending") {
|
|
302
|
-
|
|
326
|
+
log(`${poll.error}`);
|
|
303
327
|
process.exit(1);
|
|
304
328
|
}
|
|
305
329
|
process.stdout.write(".");
|
|
306
330
|
}
|
|
307
|
-
|
|
331
|
+
log("Authorization timed out. Try again.");
|
|
308
332
|
process.exit(1);
|
|
309
333
|
}
|
|
310
334
|
|
|
311
335
|
function cmdAuthLogout() {
|
|
312
336
|
try { fs.unlinkSync(AUTH_FILE); } catch {}
|
|
313
|
-
|
|
337
|
+
log("Logged out");
|
|
314
338
|
}
|
|
315
339
|
|
|
316
340
|
async function cmdAuthStatus() {
|
|
317
341
|
try {
|
|
318
342
|
const auth = JSON.parse(fs.readFileSync(AUTH_FILE, "utf8"));
|
|
319
|
-
|
|
343
|
+
log(`${auth.email} (${auth.plan} plan)`);
|
|
320
344
|
// Get usage from API
|
|
321
345
|
const status = await apiCall("/v1/auth/status");
|
|
322
346
|
if (status.usage_today) {
|
|
@@ -325,7 +349,7 @@ async function cmdAuthStatus() {
|
|
|
325
349
|
console.log(` ${k}: ${v} / ${status.limits[k]}`);
|
|
326
350
|
}
|
|
327
351
|
} catch {
|
|
328
|
-
|
|
352
|
+
log("Not logged in. Run: 0dai auth login");
|
|
329
353
|
}
|
|
330
354
|
}
|
|
331
355
|
|
|
@@ -338,6 +362,9 @@ async function main() {
|
|
|
338
362
|
const cmd = args[0] || "help";
|
|
339
363
|
const sub = args[1] || "";
|
|
340
364
|
|
|
365
|
+
// Non-blocking version check (runs in background, once per day)
|
|
366
|
+
checkVersion();
|
|
367
|
+
|
|
341
368
|
switch (cmd) {
|
|
342
369
|
case "init": await cmdInit(target); break;
|
|
343
370
|
case "sync": await cmdSync(target); break;
|
|
@@ -352,9 +379,9 @@ async function main() {
|
|
|
352
379
|
console.log("Usage: 0dai auth [login|logout|status]");
|
|
353
380
|
}
|
|
354
381
|
break;
|
|
355
|
-
case "--version": console.log(
|
|
382
|
+
case "--version": console.log(`${T}0dai${R} ${VERSION}`); break;
|
|
356
383
|
case "help": case "--help": case "-h":
|
|
357
|
-
console.log(
|
|
384
|
+
console.log(`\n ${T}0dai${R} v${VERSION} — One config for 5 AI agent CLIs\n`);
|
|
358
385
|
console.log("Commands:");
|
|
359
386
|
console.log(" init Initialize ai/ layer (via API)");
|
|
360
387
|
console.log(" sync Update ai/ layer (via API)");
|
|
@@ -368,9 +395,9 @@ async function main() {
|
|
|
368
395
|
console.log("https://0dai.dev");
|
|
369
396
|
break;
|
|
370
397
|
default:
|
|
371
|
-
|
|
398
|
+
log(`unknown command: ${cmd}. Run '0dai --help'`);
|
|
372
399
|
process.exit(1);
|
|
373
400
|
}
|
|
374
401
|
}
|
|
375
402
|
|
|
376
|
-
main().catch((e) => {
|
|
403
|
+
main().catch((e) => { log(`${e.message}`); process.exit(1); });
|