@arkveil/cli 1.1.0 → 1.2.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.
package/dist/index.js CHANGED
@@ -5413,9 +5413,9 @@ var require_src = __commonJS({
5413
5413
  });
5414
5414
 
5415
5415
  // src/index.ts
5416
- import { readFileSync as readFileSync4 } from "fs";
5417
- import { fileURLToPath as fileURLToPath2 } from "url";
5418
- import { dirname, join as join2 } from "path";
5416
+ import { readFileSync as readFileSync5 } from "fs";
5417
+ import { fileURLToPath as fileURLToPath4 } from "url";
5418
+ import { dirname as dirname2, join as join3 } from "path";
5419
5419
 
5420
5420
  // node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
5421
5421
  var import_index = __toESM(require_commander(), 1);
@@ -14954,12 +14954,255 @@ function registerAdmin(program2) {
14954
14954
  });
14955
14955
  }
14956
14956
 
14957
+ // src/commands/update/update.ts
14958
+ import { execFile as execFile6 } from "child_process";
14959
+ import { fileURLToPath as fileURLToPath3 } from "url";
14960
+
14961
+ // src/commands/update/npm.ts
14962
+ import { existsSync, readFileSync as readFileSync4 } from "fs";
14963
+ import { fileURLToPath as fileURLToPath2 } from "url";
14964
+ import { dirname, join as join2, parse } from "path";
14965
+ function readPackageMeta() {
14966
+ let dir = dirname(fileURLToPath2(import.meta.url));
14967
+ const { root } = parse(dir);
14968
+ while (true) {
14969
+ const candidate = join2(dir, "package.json");
14970
+ if (existsSync(candidate)) {
14971
+ const pkg = JSON.parse(readFileSync4(candidate, "utf8"));
14972
+ return { name: pkg.name ?? "@arkveil/cli", version: pkg.version ?? "0.0.0" };
14973
+ }
14974
+ if (dir === root) break;
14975
+ dir = dirname(dir);
14976
+ }
14977
+ return { name: "@arkveil/cli", version: "0.0.0" };
14978
+ }
14979
+ function parseSemver(version) {
14980
+ const cleaned = version.trim().replace(/^v/, "");
14981
+ const [main3 = "", pre = null] = cleaned.split("-", 2);
14982
+ const core = main3.split(".").map((p) => Number.parseInt(p, 10) || 0);
14983
+ while (core.length < 3) core.push(0);
14984
+ return { core: core.slice(0, 3), pre: pre ?? null };
14985
+ }
14986
+ function compareSemver(a3, b3) {
14987
+ const pa = parseSemver(a3);
14988
+ const pb = parseSemver(b3);
14989
+ for (let i = 0; i < 3; i++) {
14990
+ const diff = (pa.core[i] ?? 0) - (pb.core[i] ?? 0);
14991
+ if (diff !== 0) return diff < 0 ? -1 : 1;
14992
+ }
14993
+ if (pa.pre === pb.pre) return 0;
14994
+ if (pa.pre === null) return 1;
14995
+ if (pb.pre === null) return -1;
14996
+ return pa.pre < pb.pre ? -1 : 1;
14997
+ }
14998
+ var PACKAGE_MANAGERS = ["pnpm", "yarn", "bun", "npm"];
14999
+ function detectPackageManager(modulePath, env2 = process.env) {
15000
+ const path2 = modulePath.toLowerCase();
15001
+ if (path2.includes("pnpm")) return "pnpm";
15002
+ if (path2.includes(`${"/"}.bun${"/"}`) || path2.includes("/bun/")) return "bun";
15003
+ if (path2.includes(".yarn") || path2.includes("/yarn/")) return "yarn";
15004
+ const agent = (env2.npm_config_user_agent ?? "").toLowerCase();
15005
+ const fromAgent = PACKAGE_MANAGERS.find((pm) => agent.startsWith(pm));
15006
+ if (fromAgent) return fromAgent;
15007
+ return "npm";
15008
+ }
15009
+ function installCommand(pm, spec) {
15010
+ switch (pm) {
15011
+ case "pnpm":
15012
+ return { cmd: "pnpm", args: ["add", "-g", spec] };
15013
+ case "yarn":
15014
+ return { cmd: "yarn", args: ["global", "add", spec] };
15015
+ case "bun":
15016
+ return { cmd: "bun", args: ["add", "-g", spec] };
15017
+ case "npm":
15018
+ default:
15019
+ return { cmd: "npm", args: ["install", "-g", spec] };
15020
+ }
15021
+ }
15022
+ async function fetchLatestVersion(opts) {
15023
+ const tag = opts.tag ?? "latest";
15024
+ const registry = (opts.registry ?? "https://registry.npmjs.org").replace(/\/+$/, "");
15025
+ const encoded = opts.name.replace("/", "%2f");
15026
+ const url = `${registry}/${encoded}/${tag}`;
15027
+ const controller = new AbortController();
15028
+ const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? 1e4);
15029
+ try {
15030
+ const res = await fetch(url, {
15031
+ headers: { accept: "application/json" },
15032
+ signal: controller.signal
15033
+ });
15034
+ if (!res.ok) {
15035
+ throw new NetworkError(
15036
+ `npm registry returned ${res.status} for ${opts.name}@${tag}.`,
15037
+ "Check your network connection and that the package/tag exists."
15038
+ );
15039
+ }
15040
+ const body = await res.json();
15041
+ if (!body.version) {
15042
+ throw new NetworkError(
15043
+ `npm registry response for ${opts.name}@${tag} had no version.`
15044
+ );
15045
+ }
15046
+ return body.version;
15047
+ } catch (err) {
15048
+ if (err instanceof NetworkError) throw err;
15049
+ if (err instanceof Error && err.name === "AbortError") {
15050
+ throw new NetworkError(
15051
+ `Timed out contacting the npm registry for ${opts.name}@${tag}.`,
15052
+ "Retry, or increase the timeout with --timeout <ms>.",
15053
+ err
15054
+ );
15055
+ }
15056
+ throw new NetworkError(
15057
+ `Could not reach the npm registry: ${err instanceof Error ? err.message : String(err)}`,
15058
+ "Check your network connection.",
15059
+ err
15060
+ );
15061
+ } finally {
15062
+ clearTimeout(timer);
15063
+ }
15064
+ }
15065
+
15066
+ // src/commands/update/update.ts
15067
+ var VALID_PMS = ["npm", "pnpm", "yarn", "bun"];
15068
+ async function selfUpdate(ctx, options) {
15069
+ const { out, config } = ctx;
15070
+ const meta = readPackageMeta();
15071
+ const tag = options.tag ?? "latest";
15072
+ if (options.use && !VALID_PMS.includes(options.use)) {
15073
+ throw new CliError(`Unknown package manager "${options.use}".`, {
15074
+ exitCode: ExitCode.Usage,
15075
+ hint: `Choose one of: ${VALID_PMS.join(", ")}.`
15076
+ });
15077
+ }
15078
+ const spinner = out.spinner(`Checking npm for ${meta.name}@${tag}\u2026`);
15079
+ let latest;
15080
+ try {
15081
+ latest = await fetchLatestVersion({
15082
+ name: meta.name,
15083
+ tag,
15084
+ timeoutMs: config.timeoutMs
15085
+ });
15086
+ spinner.stop();
15087
+ } catch (err) {
15088
+ spinner.fail("Could not check for updates.");
15089
+ throw err;
15090
+ }
15091
+ const cmp = compareSemver(meta.version, latest);
15092
+ const upToDate = cmp >= 0;
15093
+ const willUpdate = !upToDate || Boolean(options.force);
15094
+ const pm = options.use ?? detectPackageManager(fileURLToPath3(import.meta.url));
15095
+ const spec = `${meta.name}@${tag}`;
15096
+ const { cmd, args } = installCommand(pm, spec);
15097
+ const commandLine = `${cmd} ${args.join(" ")}`;
15098
+ if (options.check || options.dryRun || !willUpdate) {
15099
+ out.data(
15100
+ {
15101
+ name: meta.name,
15102
+ current: meta.version,
15103
+ latest,
15104
+ tag,
15105
+ updateAvailable: !upToDate,
15106
+ upToDate,
15107
+ packageManager: pm,
15108
+ command: commandLine,
15109
+ action: options.dryRun ? "dry-run" : options.check ? "check" : "none"
15110
+ },
15111
+ (o2) => {
15112
+ if (upToDate) {
15113
+ return `${o2.c.green("\u2714")} ${meta.name} is up to date (${meta.version}).`;
15114
+ }
15115
+ const lines = [
15116
+ `${o2.c.yellow("!")} Update available: ${o2.c.dim(meta.version)} \u2192 ${o2.c.green(latest)}`
15117
+ ];
15118
+ if (options.dryRun) {
15119
+ lines.push(` Would run: ${o2.c.cyan(commandLine)}`);
15120
+ } else {
15121
+ lines.push(` Run ${o2.c.cyan("arkveil update")} to upgrade (uses: ${o2.c.cyan(commandLine)}).`);
15122
+ }
15123
+ return lines.join("\n");
15124
+ }
15125
+ );
15126
+ return;
15127
+ }
15128
+ const label = upToDate ? `Reinstalling ${spec}\u2026` : `Updating ${meta.name} ${meta.version} \u2192 ${latest}\u2026`;
15129
+ out.verbose(`Running: ${commandLine}`);
15130
+ const install = out.spinner(label);
15131
+ try {
15132
+ await runInstall(cmd, args);
15133
+ install.succeed(`Updated to ${latest}. Run \`arkveil --version\` to confirm.`);
15134
+ } catch (err) {
15135
+ install.fail("Update failed.");
15136
+ throw toInstallError(err, pm, commandLine);
15137
+ }
15138
+ out.data(
15139
+ {
15140
+ name: meta.name,
15141
+ previous: meta.version,
15142
+ latest,
15143
+ tag,
15144
+ packageManager: pm,
15145
+ command: commandLine,
15146
+ action: "updated"
15147
+ },
15148
+ () => {
15149
+ }
15150
+ );
15151
+ }
15152
+ function runInstall(cmd, args) {
15153
+ return new Promise((resolve2, reject) => {
15154
+ execFile6(cmd, args, { timeout: 12e4 }, (err) => {
15155
+ if (err) reject(err);
15156
+ else resolve2();
15157
+ });
15158
+ });
15159
+ }
15160
+ function toInstallError(err, pm, commandLine) {
15161
+ const e2 = err;
15162
+ if (e2?.code === "ENOENT") {
15163
+ return new CliError(`${pm} was not found on your PATH.`, {
15164
+ exitCode: ExitCode.Generic,
15165
+ hint: `Install ${pm}, or pass --use <npm|pnpm|yarn|bun> to pick another package manager.`,
15166
+ cause: err
15167
+ });
15168
+ }
15169
+ const detail = (e2?.stderr ?? e2?.message ?? String(err)).trim().split("\n").slice(-3).join(" ");
15170
+ return new CliError(`Upgrade command failed: ${detail || commandLine}`, {
15171
+ exitCode: ExitCode.Generic,
15172
+ hint: `Try running it yourself: ${commandLine}. A global install may require elevated permissions (sudo).`,
15173
+ cause: err
15174
+ });
15175
+ }
15176
+
15177
+ // src/commands/update/index.ts
15178
+ function registerUpdate(program2) {
15179
+ program2.command("update").aliases(["upgrade", "self-update"]).description("Update the Arkveil CLI to the latest published version").option("--check", "only report whether an update is available; don't install").option("--dry-run", "print the install command that would run, without executing it").option("--tag <tag>", "npm dist-tag to install (default latest)", "latest").option("--use <pm>", "package manager to use: npm, pnpm, yarn, or bun").option("--force", "reinstall even if already on the target version").addHelpText(
15180
+ "after",
15181
+ `
15182
+ Checks the npm registry for a newer release of the CLI and upgrades this install
15183
+ in place using the package manager that installed it (auto-detected; override
15184
+ with --use). Use --check in scripts/CI to detect a pending update via the exit
15185
+ output without changing anything.
15186
+
15187
+ Examples:
15188
+ $ arkveil update # upgrade to the latest release
15189
+ $ arkveil update --check # is a newer version available?
15190
+ $ arkveil update --dry-run # show the command without running it
15191
+ $ arkveil update --tag next # install the "next" dist-tag
15192
+ $ arkveil update --use pnpm # force pnpm for the global install
15193
+ $ arkveil update --json # machine-readable result
15194
+ `
15195
+ ).action(async (options, command) => {
15196
+ await run(command, (ctx) => selfUpdate(ctx, options));
15197
+ });
15198
+ }
15199
+
14957
15200
  // src/index.ts
14958
15201
  function readVersion() {
14959
15202
  try {
14960
- const here = dirname(fileURLToPath2(import.meta.url));
15203
+ const here = dirname2(fileURLToPath4(import.meta.url));
14961
15204
  const pkg = JSON.parse(
14962
- readFileSync4(join2(here, "..", "package.json"), "utf8")
15205
+ readFileSync5(join3(here, "..", "package.json"), "utf8")
14963
15206
  );
14964
15207
  return pkg.version ?? "0.0.0";
14965
15208
  } catch {
@@ -14993,6 +15236,7 @@ function buildProgram() {
14993
15236
  registerEval(program2);
14994
15237
  registerAbac(program2);
14995
15238
  registerAdmin(program2);
15239
+ registerUpdate(program2);
14996
15240
  program2.addHelpText(
14997
15241
  "after",
14998
15242
  `
@@ -15002,6 +15246,7 @@ Examples:
15002
15246
  $ arkveil tags list --json List tags as JSON
15003
15247
  $ arkveil trees forest Show the full navigation forest
15004
15248
  $ arkveil sdk info How to install & use the SDK
15249
+ $ arkveil update Update the CLI to the latest release
15005
15250
  $ arkveil formula syntax Print the formula DSL reference
15006
15251
  $ arkveil eval explain -a orders:read \\
15007
15252
  --user '{"role":"admin"}' --context '{}' Explain an access decision