agendex-cli 0.3.2 → 0.5.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.
Files changed (3) hide show
  1. package/README.md +32 -8
  2. package/dist/cli.js +175 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,15 +14,39 @@ bun install -g agendex-cli
14
14
  ## Commands
15
15
 
16
16
  ```bash
17
- agendex login
18
- agendex login --url https://agendex.yourdomain.com
19
- agendex configure
20
- agendex start
21
- agendex stop
22
- agendex sync
23
- agendex status
24
- agendex logout
17
+ agendex login # Authenticate via browser OAuth (agendex.dev)
18
+ agendex login --url <url> # Login to a self-hosted instance
19
+ agendex logout # Clear stored cloud token
20
+ agendex configure # Select which agents/adapters to index
21
+ agendex start # Start daemon (backgrounds itself)
22
+ agendex stop # Stop the running daemon
23
+ agendex sync # One-shot scan + sync to cloud
24
+ agendex status # Show config state, daemon status, uptime & hostname
25
+ agendex help # Show help message
26
+ agendex --version / -v # Print CLI version
27
+ ```
28
+
29
+ ## Status Output
30
+
31
+ `agendex status` prints a rich overview:
32
+
33
+ - Config version, local/cloud token state, Convex URL
34
+ - Enabled adapters
35
+ - Daemon running state with PID
36
+ - **Uptime** — how long the daemon has been running
37
+ - **Hostname** — machine the daemon is running on
38
+ - CLI version
39
+
40
+ ## Auto-Update Check
41
+
42
+ Before running `start`, `configure`, or `sync`, the CLI checks for a newer published version. If an update is required the command is blocked and you are prompted to upgrade:
43
+
25
44
  ```
45
+ [agendex] update required: v0.1.0 → v0.2.0
46
+ [agendex] run: npm i -g agendex-cli
47
+ ```
48
+
49
+ The check is skipped for `stop`, `status`, `login`, `logout`, and `help`.
26
50
 
27
51
  ## Supported Runtime
28
52
 
package/dist/cli.js CHANGED
@@ -2800,19 +2800,35 @@ function requestText(urlString, options) {
2800
2800
 
2801
2801
  // src/pid.ts
2802
2802
  import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
2803
- import { homedir as homedir9 } from "node:os";
2803
+ import { homedir as homedir9, hostname } from "node:os";
2804
2804
  import { dirname, join as join10 } from "node:path";
2805
2805
  var pidPath = join10(homedir9(), ".agendex", "daemon.pid");
2806
2806
  function writePid() {
2807
2807
  mkdirSync2(dirname(pidPath), { recursive: true });
2808
- writeFileSync2(pidPath, String(process.pid));
2808
+ const info = {
2809
+ pid: process.pid,
2810
+ startedAtMs: Date.now(),
2811
+ hostname: hostname()
2812
+ };
2813
+ writeFileSync2(pidPath, JSON.stringify(info));
2809
2814
  }
2810
- function readPid() {
2815
+ function readPidInfo() {
2811
2816
  if (!existsSync6(pidPath))
2812
2817
  return null;
2813
2818
  const raw = readFileSync3(pidPath, "utf-8").trim();
2814
- const pid = Number(raw);
2815
- return Number.isFinite(pid) && pid > 0 ? pid : null;
2819
+ const asNumber = Number(raw);
2820
+ if (Number.isFinite(asNumber) && asNumber > 0 && !raw.startsWith("{")) {
2821
+ return { pid: asNumber };
2822
+ }
2823
+ try {
2824
+ const parsed = JSON.parse(raw);
2825
+ if (Number.isFinite(parsed.pid) && parsed.pid > 0)
2826
+ return parsed;
2827
+ } catch {}
2828
+ return null;
2829
+ }
2830
+ function readPid() {
2831
+ return readPidInfo()?.pid ?? null;
2816
2832
  }
2817
2833
  function removePid() {
2818
2834
  try {
@@ -3006,11 +3022,132 @@ async function syncAll() {
3006
3022
  console.log(`[agendex] Sync complete: ${synced} synced, ${failed} failed`);
3007
3023
  }
3008
3024
 
3025
+ // src/version.ts
3026
+ import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
3027
+ import { tmpdir } from "node:os";
3028
+ import { join as join11 } from "node:path";
3029
+ // package.json
3030
+ var package_default = {
3031
+ name: "agendex-cli",
3032
+ version: "0.5.0",
3033
+ description: "Agendex CLI for login, sync, and daemon workflows",
3034
+ homepage: "https://github.com/Tyru5/Agendex#readme",
3035
+ repository: {
3036
+ type: "git",
3037
+ url: "git+https://github.com/Tyru5/Agendex.git",
3038
+ directory: "packages/cli"
3039
+ },
3040
+ bugs: {
3041
+ url: "https://github.com/Tyru5/Agendex/issues"
3042
+ },
3043
+ type: "module",
3044
+ license: "AGPL-3.0-only",
3045
+ bin: {
3046
+ agendex: "./dist/cli.js"
3047
+ },
3048
+ files: [
3049
+ "dist",
3050
+ "README.md",
3051
+ "LICENSE"
3052
+ ],
3053
+ publishConfig: {
3054
+ access: "public"
3055
+ },
3056
+ engines: {
3057
+ node: ">=20"
3058
+ },
3059
+ scripts: {
3060
+ build: "node ./scripts/build-release.mjs --dist-only",
3061
+ "build:release": "node ./scripts/build-release.mjs",
3062
+ prepack: "node ./scripts/build-release.mjs --dist-only"
3063
+ },
3064
+ dependencies: {
3065
+ "better-sqlite3": "^12.6.2"
3066
+ },
3067
+ devDependencies: {
3068
+ "@agendex/shared": "workspace:*",
3069
+ "@types/bun": "^1.3.9"
3070
+ }
3071
+ };
3072
+
3073
+ // src/version.ts
3074
+ var CLI_VERSION = package_default.version;
3075
+ var CACHE_FILE = join11(tmpdir(), ".agendex-update-cache.json");
3076
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1000;
3077
+ function readCache() {
3078
+ try {
3079
+ if (!existsSync7(CACHE_FILE))
3080
+ return null;
3081
+ const { result, ts } = JSON.parse(readFileSync4(CACHE_FILE, "utf8"));
3082
+ if (Date.now() - ts > CACHE_TTL_MS)
3083
+ return null;
3084
+ return result;
3085
+ } catch {
3086
+ return null;
3087
+ }
3088
+ }
3089
+ function writeCache(result) {
3090
+ try {
3091
+ writeFileSync3(CACHE_FILE, JSON.stringify({ result, ts: Date.now() }));
3092
+ } catch {}
3093
+ }
3094
+ async function checkForUpdate() {
3095
+ const current = CLI_VERSION;
3096
+ const cached = readCache();
3097
+ if (cached)
3098
+ return { ...cached, current };
3099
+ try {
3100
+ const controller = new AbortController;
3101
+ const timeout = setTimeout(() => controller.abort(), 3000);
3102
+ const res = await fetch("https://registry.npmjs.org/agendex-cli/latest", {
3103
+ signal: controller.signal
3104
+ });
3105
+ clearTimeout(timeout);
3106
+ if (!res.ok) {
3107
+ return { updateAvailable: false, current, latest: current };
3108
+ }
3109
+ const data = await res.json();
3110
+ const latest = data.version;
3111
+ const result = { updateAvailable: isNewer(latest, current), current, latest };
3112
+ writeCache(result);
3113
+ return result;
3114
+ } catch {
3115
+ return { updateAvailable: false, current, latest: current };
3116
+ }
3117
+ }
3118
+ function isNewer(latest, current) {
3119
+ const l = latest.split(".").map(Number);
3120
+ const c = current.split(".").map(Number);
3121
+ for (let i = 0;i < Math.max(l.length, c.length); i++) {
3122
+ const lv = l[i] ?? 0;
3123
+ const cv = c[i] ?? 0;
3124
+ if (lv > cv)
3125
+ return true;
3126
+ if (lv < cv)
3127
+ return false;
3128
+ }
3129
+ return false;
3130
+ }
3131
+
3009
3132
  // src/cli.ts
3010
3133
  var args = process.argv.slice(2);
3011
3134
  var command = args[0] ?? "start";
3012
3135
  var cliEntry = resolve5(process.argv[1] ?? fileURLToPath2(import.meta.url));
3013
3136
  async function main() {
3137
+ const isInternal = args.includes("--daemon") || args.includes("--worker");
3138
+ if (command === "--version" || command === "-v") {
3139
+ writeStdout(CLI_VERSION);
3140
+ return 0;
3141
+ }
3142
+ const isPassthrough = ["stop", "status", "login", "logout", "help", "--help", "-h"].includes(command);
3143
+ if (!isInternal && !isPassthrough) {
3144
+ const { updateAvailable, current, latest } = await checkForUpdate();
3145
+ if (updateAvailable) {
3146
+ writeStderr(`[agendex] update required: v${current} → v${latest}`);
3147
+ writeStderr(`[agendex] run: npm i -g agendex-cli`);
3148
+ return 1;
3149
+ }
3150
+ }
3014
3151
  switch (command) {
3015
3152
  case "start": {
3016
3153
  if (args.includes("--daemon")) {
@@ -3079,7 +3216,8 @@ async function main() {
3079
3216
  }
3080
3217
  case "status": {
3081
3218
  const config = loadConfig();
3082
- const pid = readPid();
3219
+ const pidInfo = readPidInfo();
3220
+ const pid = pidInfo?.pid ?? null;
3083
3221
  const running = pid ? isRunning(pid) : false;
3084
3222
  writeStdout(`[agendex] Config version: ${config?.configVersion ?? "none"}`);
3085
3223
  writeStdout(`[agendex] Local token: ${config?.token ? "set" : "not set"}`);
@@ -3087,6 +3225,21 @@ async function main() {
3087
3225
  writeStdout(`[agendex] Convex URL: ${config?.convexUrl ?? "not set"}`);
3088
3226
  writeStdout(`[agendex] Enabled adapters: ${config?.enabledAdapters.join(", ") || "none"}`);
3089
3227
  writeStdout(`[agendex] Daemon: ${running ? `running (PID ${pid})` : "not running"}`);
3228
+ if (running && pidInfo?.startedAtMs) {
3229
+ writeStdout(`[agendex] Uptime: ${formatDuration(Date.now() - pidInfo.startedAtMs)}`);
3230
+ } else if (running) {
3231
+ writeStdout(`[agendex] Uptime: unknown (restart daemon to populate)`);
3232
+ } else {
3233
+ writeStdout(`[agendex] Uptime: n/a`);
3234
+ }
3235
+ if (running && pidInfo?.hostname) {
3236
+ writeStdout(`[agendex] Hostname: ${pidInfo.hostname}`);
3237
+ } else if (running) {
3238
+ writeStdout(`[agendex] Hostname: unknown (restart daemon to populate)`);
3239
+ } else {
3240
+ writeStdout(`[agendex] Hostname: n/a`);
3241
+ }
3242
+ writeStdout(`[agendex] CLI version: ${CLI_VERSION}`);
3090
3243
  return 0;
3091
3244
  }
3092
3245
  case "help":
@@ -3106,6 +3259,8 @@ Usage:
3106
3259
  agendex sync One-shot scan + sync to cloud
3107
3260
  agendex status Show current config state + daemon status
3108
3261
  agendex help Show this help message
3262
+ agendex --version Print CLI version
3263
+ agendex -v Print CLI version
3109
3264
  `.trim());
3110
3265
  return 0;
3111
3266
  }
@@ -3146,3 +3301,17 @@ function writeStderr(message) {
3146
3301
  writeSync(process.stderr.fd, `${message}
3147
3302
  `);
3148
3303
  }
3304
+ function formatDuration(ms) {
3305
+ const totalSeconds = Math.max(0, Math.floor(ms / 1000));
3306
+ const days = Math.floor(totalSeconds / 86400);
3307
+ const hours = Math.floor(totalSeconds % 86400 / 3600);
3308
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
3309
+ const seconds = totalSeconds % 60;
3310
+ if (days > 0)
3311
+ return `${days}d ${hours}h ${minutes}m ${seconds}s`;
3312
+ if (hours > 0)
3313
+ return `${hours}h ${minutes}m ${seconds}s`;
3314
+ if (minutes > 0)
3315
+ return `${minutes}m ${seconds}s`;
3316
+ return `${seconds}s`;
3317
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agendex-cli",
3
- "version": "0.3.2",
3
+ "version": "0.5.0",
4
4
  "description": "Agendex CLI for login, sync, and daemon workflows",
5
5
  "homepage": "https://github.com/Tyru5/Agendex#readme",
6
6
  "repository": {