@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.
Files changed (2) hide show
  1. package/bin/0dai.js +37 -32
  2. 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.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
- console.log(`[0dai] ${created} created, ${updated} updated, ${unchanged} unchanged`);
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
- console.log(`[0dai] ai/ layer already exists (v${v}). Run '0dai sync' to update.`);
151
+ log(`ai/ layer already exists (v${v}). Run '0dai sync' to update.`);
148
152
  return;
149
153
  }
150
154
 
151
- console.log("[0dai] collecting project metadata...");
155
+ log("collecting project metadata...");
152
156
  const { projectFiles, fileContents, clis } = collectMetadata(target);
153
157
 
154
- console.log(`[0dai] sending to API (${projectFiles.length} files, ${clis.length} CLIs)...`);
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
- console.log(`\n[0dai] ${result.message || result.error}`);
167
+ log(`${result.message || result.error}`);
164
168
  console.log(` ${result.hint}\n`);
165
169
  } else {
166
- console.error(`[0dai] error: ${result.error}`);
170
+ log(`error: ${result.error}`);
167
171
  }
168
172
  process.exit(1);
169
173
  }
170
174
 
171
- console.log(`[0dai] detected: ${result.stack || "?"}`);
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
- console.log(`[0dai] initialized (${result.file_count || "?"} files)`);
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) { console.error(`[0dai] error: ${result.error}`); process.exit(1); }
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 console.log("[0dai] already up to date");
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) { console.error(`[0dai] error: ${result.error}`); return; }
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)) { console.log("[0dai] no ai/ layer. Run '0dai init' first."); return; }
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
- console.log(`[0dai] v${v} — ${ok ? "healthy" : "issues found"}`);
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
- console.log(`[0dai] v${v} | stack: ${stack}`);
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
- // Only check once per day
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 < 86400) return;
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
- console.log(`\n[0dai] Update available: ${VERSION} → ${result.version}`);
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) { console.error(`[0dai] error: ${result.error}`); process.exit(1); }
298
+ if (result.error) { log(`error: ${result.error}`); process.exit(1); }
294
299
 
295
- console.log(`[0dai] Open this URL in your browser:\n`);
300
+ log(`Open this URL in your browser:\n`);
296
301
  console.log(` ${result.verification_uri}\n`);
297
- console.log(`[0dai] Enter code: ${result.user_code}\n`);
298
- console.log("[0dai] Waiting for authorization...");
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
- console.log(`[0dai] Logged in as ${poll.email} (${poll.plan} plan)`);
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
- console.error(`[0dai] ${poll.error}`);
326
+ log(`${poll.error}`);
322
327
  process.exit(1);
323
328
  }
324
329
  process.stdout.write(".");
325
330
  }
326
- console.error("\n[0dai] Authorization timed out. Try again.");
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
- console.log("[0dai] Logged out");
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
- console.log(`[0dai] ${auth.email} (${auth.plan} plan)`);
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
- console.log("[0dai] Not logged in. Run: 0dai auth login");
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(`0dai ${VERSION}`); break;
382
+ case "--version": console.log(`${T}0dai${R} ${VERSION}`); break;
378
383
  case "help": case "--help": case "-h":
379
- console.log(`0dai v${VERSION} — One config for 5 AI agent CLIs\n`);
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
- console.error(`[0dai] unknown command: ${cmd}. Run '0dai --help'`);
398
+ log(`unknown command: ${cmd}. Run '0dai --help'`);
394
399
  process.exit(1);
395
400
  }
396
401
  }
397
402
 
398
- main().catch((e) => { console.error(`[0dai] ${e.message}`); process.exit(1); });
403
+ main().catch((e) => { log(`${e.message}`); process.exit(1); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0dai-dev/cli",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "One config layer for 5 AI agent CLIs — Claude Code, Codex, OpenCode, Gemini, Aider",
5
5
  "bin": {
6
6
  "0dai": "./bin/0dai.js"