agendex-cli 0.15.0 → 0.16.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 (2) hide show
  1. package/dist/cli.js +236 -23
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1058,10 +1058,10 @@ var init_cleanup = __esm(() => {
1058
1058
  });
1059
1059
 
1060
1060
  // src/cli.ts
1061
- import { spawn as spawn3 } from "node:child_process";
1061
+ import { spawn as spawn4 } from "node:child_process";
1062
1062
  import { existsSync as existsSync11, statSync as statSync2, writeSync } from "node:fs";
1063
- import { resolve as resolve7 } from "node:path";
1064
- import { fileURLToPath as fileURLToPath2 } from "node:url";
1063
+ import { resolve as resolve8 } from "node:path";
1064
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
1065
1065
 
1066
1066
  // ../shared/src/adapters/catalog.ts
1067
1067
  import { homedir as homedir7 } from "node:os";
@@ -3946,6 +3946,7 @@ async function refreshToken(currentToken, convexUrl) {
3946
3946
  return null;
3947
3947
  return { token: body.token, expiresAt: body.expiresAt ?? 0 };
3948
3948
  }
3949
+ var REQUEST_TIMEOUT_MS2 = Number.parseInt(process.env.AGENDEX_HTTP_TIMEOUT_MS ?? "", 10) || 1e4;
3949
3950
  function requestText(urlString, options) {
3950
3951
  const url = new URL(urlString);
3951
3952
  const request = url.protocol === "https:" ? httpsRequest : httpRequest;
@@ -3957,7 +3958,8 @@ function requestText(urlString, options) {
3957
3958
  const req = request(url, {
3958
3959
  agent: false,
3959
3960
  headers,
3960
- method: options.method
3961
+ method: options.method,
3962
+ timeout: REQUEST_TIMEOUT_MS2
3961
3963
  }, (res) => {
3962
3964
  let body = "";
3963
3965
  res.setEncoding("utf8");
@@ -3973,6 +3975,9 @@ function requestText(urlString, options) {
3973
3975
  res.on("error", reject);
3974
3976
  });
3975
3977
  req.on("error", reject);
3978
+ req.on("timeout", () => {
3979
+ req.destroy(new Error(`Request to ${url.host} timed out after ${REQUEST_TIMEOUT_MS2}ms`));
3980
+ });
3976
3981
  if (options.body) {
3977
3982
  req.write(options.body);
3978
3983
  }
@@ -4780,6 +4785,12 @@ async function syncAll(force = false) {
4780
4785
  console.log(`[agendex] Sync complete: ${synced} synced${lowValueSuffix}, ${skipped} unchanged, ${failed} failed`);
4781
4786
  }
4782
4787
 
4788
+ // src/upgrade.ts
4789
+ import { spawn as spawn3, spawnSync } from "node:child_process";
4790
+ import { realpathSync as realpathSync2 } from "node:fs";
4791
+ import { dirname as dirname4, resolve as resolve7, sep as sep4 } from "node:path";
4792
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
4793
+
4783
4794
  // src/version.ts
4784
4795
  import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
4785
4796
  import { tmpdir } from "node:os";
@@ -4787,7 +4798,7 @@ import { join as join14 } from "node:path";
4787
4798
  // package.json
4788
4799
  var package_default = {
4789
4800
  name: "agendex-cli",
4790
- version: "0.15.0",
4801
+ version: "0.16.0",
4791
4802
  description: "Agendex CLI for login, sync, and daemon workflows",
4792
4803
  homepage: "https://github.com/Tyru5/Agendex#readme",
4793
4804
  repository: {
@@ -4851,11 +4862,13 @@ function writeCache(result) {
4851
4862
  writeFileSync5(CACHE_FILE, JSON.stringify({ result, ts: Date.now() }));
4852
4863
  } catch {}
4853
4864
  }
4854
- async function checkForUpdate() {
4865
+ async function checkForUpdate(options = {}) {
4855
4866
  const current = CLI_VERSION;
4856
- const cached = readCache(current);
4857
- if (cached)
4858
- return cached;
4867
+ if (!options.forceRefresh) {
4868
+ const cached = readCache(current);
4869
+ if (cached)
4870
+ return cached;
4871
+ }
4859
4872
  try {
4860
4873
  const controller = new AbortController;
4861
4874
  const timeout = setTimeout(() => controller.abort(), 3000);
@@ -4863,21 +4876,27 @@ async function checkForUpdate() {
4863
4876
  signal: controller.signal
4864
4877
  }).finally(() => clearTimeout(timeout));
4865
4878
  if (!res.ok) {
4866
- return { updateAvailable: false, current, latest: current };
4879
+ return { checked: false, updateAvailable: false, current, latest: current };
4867
4880
  }
4868
4881
  const data = await res.json();
4869
4882
  const latest = data.version;
4870
- const result = { updateAvailable: isNewer(latest, current), current, latest };
4883
+ const result = {
4884
+ checked: true,
4885
+ updateAvailable: isNewer(latest, current),
4886
+ current,
4887
+ latest
4888
+ };
4871
4889
  writeCache(result);
4872
4890
  return result;
4873
4891
  } catch {
4874
- return { updateAvailable: false, current, latest: current };
4892
+ return { checked: false, updateAvailable: false, current, latest: current };
4875
4893
  }
4876
4894
  }
4877
4895
  function normalizeResult(result, current) {
4878
4896
  if (typeof result.latest !== "string")
4879
4897
  return null;
4880
4898
  return {
4899
+ checked: true,
4881
4900
  updateAvailable: isNewer(result.latest, current),
4882
4901
  current,
4883
4902
  latest: result.latest
@@ -4897,6 +4916,195 @@ function isNewer(latest, current) {
4897
4916
  return false;
4898
4917
  }
4899
4918
 
4919
+ // src/upgrade.ts
4920
+ var PACKAGE_NAME = "agendex-cli";
4921
+ var moduleDir = dirname4(fileURLToPath2(import.meta.url));
4922
+ function getPackageRoot() {
4923
+ try {
4924
+ return realpathSync2(resolve7(moduleDir, ".."));
4925
+ } catch {
4926
+ return resolve7(moduleDir, "..");
4927
+ }
4928
+ }
4929
+ function detectPackageManager(packageRoot) {
4930
+ const userAgent = process.env.npm_config_user_agent ?? "";
4931
+ const execpath = process.env.npm_execpath ?? "";
4932
+ if (userAgent.startsWith("bun/") || execpath.includes("bun"))
4933
+ return "bun";
4934
+ if (userAgent.startsWith("pnpm/") || execpath.includes("pnpm"))
4935
+ return "pnpm";
4936
+ if (userAgent.startsWith("yarn/") || execpath.includes("yarn"))
4937
+ return "yarn";
4938
+ const lower = packageRoot.toLowerCase();
4939
+ if (lower.includes(`${sep4}.bun${sep4}`) || lower.includes("/.bun/"))
4940
+ return "bun";
4941
+ if (lower.includes(`${sep4}pnpm${sep4}`) || lower.includes("/pnpm/"))
4942
+ return "pnpm";
4943
+ if (lower.includes(`${sep4}yarn${sep4}`) || lower.includes("/yarn/"))
4944
+ return "yarn";
4945
+ if (typeof globalThis.Bun !== "undefined" || process.versions.bun) {
4946
+ return "bun";
4947
+ }
4948
+ return "npm";
4949
+ }
4950
+ function readYarnVersion() {
4951
+ const result = spawnSync("yarn", ["--version"], {
4952
+ encoding: "utf8",
4953
+ shell: process.platform === "win32"
4954
+ });
4955
+ if (result.error || result.status !== 0)
4956
+ return null;
4957
+ return result.stdout.trim() || null;
4958
+ }
4959
+ function parseMajorVersion(version) {
4960
+ const match = version.match(/^(\d+)/);
4961
+ if (!match)
4962
+ return null;
4963
+ const major = Number(match[1]);
4964
+ return Number.isFinite(major) ? major : null;
4965
+ }
4966
+ function buildGlobalInstallCommand(pm) {
4967
+ const pkgSpec = `${PACKAGE_NAME}@latest`;
4968
+ switch (pm) {
4969
+ case "bun":
4970
+ return {
4971
+ supported: true,
4972
+ command: { bin: "bun", args: ["add", "-g", pkgSpec], display: `bun add -g ${pkgSpec}` }
4973
+ };
4974
+ case "pnpm":
4975
+ return {
4976
+ supported: true,
4977
+ command: {
4978
+ bin: "pnpm",
4979
+ args: ["add", "-g", pkgSpec],
4980
+ display: `pnpm add -g ${pkgSpec}`
4981
+ }
4982
+ };
4983
+ case "yarn": {
4984
+ const yarnVersion = readYarnVersion();
4985
+ const yarnMajorVersion = yarnVersion ? parseMajorVersion(yarnVersion) : null;
4986
+ if (yarnMajorVersion !== null && yarnMajorVersion >= 2) {
4987
+ return {
4988
+ supported: false,
4989
+ reason: `automatic upgrade with Yarn only supports Yarn Classic (v1); detected Yarn v${yarnVersion}.`,
4990
+ manualCommand: `npm install -g ${pkgSpec}`
4991
+ };
4992
+ }
4993
+ return {
4994
+ supported: true,
4995
+ command: {
4996
+ bin: "yarn",
4997
+ args: ["global", "add", pkgSpec],
4998
+ display: `yarn global add ${pkgSpec}`
4999
+ }
5000
+ };
5001
+ }
5002
+ default:
5003
+ return {
5004
+ supported: true,
5005
+ command: {
5006
+ bin: "npm",
5007
+ args: ["install", "-g", pkgSpec],
5008
+ display: `npm install -g ${pkgSpec}`
5009
+ }
5010
+ };
5011
+ }
5012
+ }
5013
+ function isLikelyGlobalInstall(packageRoot) {
5014
+ const normalized = packageRoot.replace(/\\/g, "/");
5015
+ if (!normalized.includes("/node_modules/")) {
5016
+ return false;
5017
+ }
5018
+ const globalMarkers = [
5019
+ "/lib/node_modules/",
5020
+ "/pnpm/global/",
5021
+ "/yarn/global/",
5022
+ "/.bun/install/global/",
5023
+ "/.bun/install/",
5024
+ "/.config/yarn/global/",
5025
+ "/AppData/Roaming/npm/",
5026
+ "/AppData/Local/Yarn/",
5027
+ "/AppData/Local/pnpm/"
5028
+ ];
5029
+ return globalMarkers.some((marker) => normalized.includes(marker));
5030
+ }
5031
+ async function runUpgrade(opts) {
5032
+ const packageRoot = getPackageRoot();
5033
+ const pm = detectPackageManager(packageRoot);
5034
+ const isGlobal = isLikelyGlobalInstall(packageRoot);
5035
+ if (!isGlobal) {
5036
+ process.stderr.write(`[agendex] this CLI appears to be running from a local checkout or linked install:
5037
+ ` + `[agendex] ${packageRoot}
5038
+ ` + `[agendex] automatic upgrade only supports global installs.
5039
+ ` + `[agendex] reinstall globally (e.g. \`npm install -g ${PACKAGE_NAME}@latest\`) or update the source repo manually.
5040
+ `);
5041
+ return 1;
5042
+ }
5043
+ const { checked, updateAvailable, current, latest } = await checkForUpdate({
5044
+ forceRefresh: true
5045
+ });
5046
+ if (checked && !updateAvailable && !opts.force) {
5047
+ process.stdout.write(`[agendex] already up to date (v${current})
5048
+ `);
5049
+ return 0;
5050
+ }
5051
+ if (!checked) {
5052
+ process.stderr.write(`[agendex] could not verify the latest version; attempting upgrade anyway...
5053
+ `);
5054
+ }
5055
+ const commandResult = buildGlobalInstallCommand(pm);
5056
+ if (!commandResult.supported) {
5057
+ process.stderr.write(`[agendex] ${commandResult.reason}
5058
+ `);
5059
+ process.stderr.write(`[agendex] install the latest CLI manually, e.g. \`${commandResult.manualCommand}\`.
5060
+ `);
5061
+ return 1;
5062
+ }
5063
+ const cmd = commandResult.command;
5064
+ if (checked && updateAvailable) {
5065
+ process.stdout.write(`[agendex] upgrading: v${current} → v${latest}
5066
+ `);
5067
+ } else if (opts.force) {
5068
+ process.stdout.write(`[agendex] reinstalling v${CLI_VERSION} (forced)
5069
+ `);
5070
+ }
5071
+ process.stdout.write(`[agendex] running: ${cmd.display}
5072
+ `);
5073
+ return await new Promise((resolveExit) => {
5074
+ const child = spawn3(cmd.bin, cmd.args, {
5075
+ stdio: "inherit",
5076
+ shell: process.platform === "win32",
5077
+ env: process.env
5078
+ });
5079
+ let didError = false;
5080
+ child.on("error", (err) => {
5081
+ didError = true;
5082
+ process.stderr.write(`[agendex] failed to run ${cmd.bin}: ${err instanceof Error ? err.message : String(err)}
5083
+ `);
5084
+ process.stderr.write(`[agendex] you can run it manually: ${cmd.display}
5085
+ `);
5086
+ resolveExit(1);
5087
+ });
5088
+ child.on("close", (code) => {
5089
+ if (didError)
5090
+ return;
5091
+ if (code === 0) {
5092
+ process.stdout.write(`[agendex] upgrade complete.
5093
+ `);
5094
+ process.stdout.write(`[agendex] note: if the daemon is running, restart it: \`agendex stop && agendex start\`
5095
+ `);
5096
+ resolveExit(0);
5097
+ return;
5098
+ }
5099
+ process.stderr.write(`[agendex] upgrade failed with exit code ${code ?? "unknown"}
5100
+ `);
5101
+ process.stderr.write(`[agendex] you can run it manually: ${cmd.display}
5102
+ `);
5103
+ resolveExit(code ?? 1);
5104
+ });
5105
+ });
5106
+ }
5107
+
4900
5108
  // src/web.ts
4901
5109
  async function openAgendexWeb(siteUrlOverride) {
4902
5110
  const base = siteUrlOverride ?? getDefaultSiteUrl();
@@ -4934,7 +5142,7 @@ function firstCommandToken(argv) {
4934
5142
  return;
4935
5143
  }
4936
5144
  var command = firstCommandToken(args) ?? "start";
4937
- var cliEntry = resolve7(process.argv[1] ?? fileURLToPath2(import.meta.url));
5145
+ var cliEntry = resolve8(process.argv[1] ?? fileURLToPath3(import.meta.url));
4938
5146
  async function main() {
4939
5147
  const isInternal = args.includes("--daemon") || args.includes("--worker");
4940
5148
  if (command === "--version" || command === "-v") {
@@ -4952,16 +5160,16 @@ async function main() {
4952
5160
  "add-dir",
4953
5161
  "remove-dir",
4954
5162
  "list-dirs",
5163
+ "upgrade",
4955
5164
  "help",
4956
5165
  "--help",
4957
5166
  "-h"
4958
5167
  ].includes(command);
4959
5168
  if (!isInternal && !isPassthrough) {
4960
- const { updateAvailable, current, latest } = await checkForUpdate();
4961
- if (updateAvailable) {
4962
- writeStderr(`[agendex] update required: v${current} → v${latest}`);
4963
- writeStderr(`[agendex] run: npm i -g agendex-cli`);
4964
- return 1;
5169
+ const { checked, updateAvailable, current, latest } = await checkForUpdate();
5170
+ if (checked && updateAvailable) {
5171
+ writeStderr(`[agendex] update available: v${current} → v${latest}`);
5172
+ writeStderr(`[agendex] run: agendex upgrade`);
4965
5173
  }
4966
5174
  }
4967
5175
  switch (command) {
@@ -5000,7 +5208,7 @@ async function main() {
5000
5208
  const daemonArgs = [cliEntry, "start", "--daemon"];
5001
5209
  if (devFlag)
5002
5210
  daemonArgs.push("--dev");
5003
- const child = spawn3(process.execPath, daemonArgs, {
5211
+ const child = spawn4(process.execPath, daemonArgs, {
5004
5212
  detached: true,
5005
5213
  stdio: "ignore",
5006
5214
  env: { ...process.env, ...devFlag ? { AGENDEX_DEV: "1" } : {} }
@@ -5153,7 +5361,7 @@ async function main() {
5153
5361
  const { request } = await import("node:http");
5154
5362
  const body = JSON.stringify({ path: resolved });
5155
5363
  try {
5156
- const res = await new Promise((resolve8, reject) => {
5364
+ const res = await new Promise((resolve9, reject) => {
5157
5365
  const req = request(`http://localhost:${port}/api/v1/plan-sources`, {
5158
5366
  method: "POST",
5159
5367
  headers: {
@@ -5167,7 +5375,7 @@ async function main() {
5167
5375
  res2.on("data", (chunk) => {
5168
5376
  data += chunk;
5169
5377
  });
5170
- res2.on("end", () => resolve8({ status: res2.statusCode ?? 0, body: data }));
5378
+ res2.on("end", () => resolve9({ status: res2.statusCode ?? 0, body: data }));
5171
5379
  res2.on("error", reject);
5172
5380
  });
5173
5381
  req.on("error", reject);
@@ -5295,6 +5503,9 @@ async function main() {
5295
5503
  }
5296
5504
  return 0;
5297
5505
  }
5506
+ case "upgrade": {
5507
+ return await runUpgrade({ force: args.includes("--force") });
5508
+ }
5298
5509
  case "help":
5299
5510
  case "--help":
5300
5511
  case "-h": {
@@ -5321,6 +5532,8 @@ Usage:
5321
5532
  agendex cleanup Interactively remove cloud daemons
5322
5533
  agendex cleanup --stale Auto-remove all stale daemons
5323
5534
  agendex status Show current config state + daemon status
5535
+ agendex upgrade Upgrade the globally installed CLI to the latest version
5536
+ agendex upgrade --force Reinstall latest even if already up to date
5324
5537
  agendex help Show this help message
5325
5538
  agendex --version Print CLI version
5326
5539
  agendex -v Print CLI version
@@ -5349,13 +5562,13 @@ function flushStream(stream) {
5349
5562
  if (stream.destroyed || !stream.writable) {
5350
5563
  return Promise.resolve();
5351
5564
  }
5352
- return new Promise((resolve8, reject) => {
5565
+ return new Promise((resolve9, reject) => {
5353
5566
  stream.write("", (error) => {
5354
5567
  if (error) {
5355
5568
  reject(error);
5356
5569
  return;
5357
5570
  }
5358
- resolve8();
5571
+ resolve9();
5359
5572
  });
5360
5573
  });
5361
5574
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agendex-cli",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Agendex CLI for login, sync, and daemon workflows",
5
5
  "homepage": "https://github.com/Tyru5/Agendex#readme",
6
6
  "repository": {