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