rshow 0.0.7 → 0.0.9

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/main.js CHANGED
@@ -26,8 +26,8 @@
26
26
  */
27
27
 
28
28
  // src/main.ts
29
- import path26 from "node:path";
30
- import process23 from "node:process";
29
+ import path28 from "node:path";
30
+ import process25 from "node:process";
31
31
 
32
32
  // src/binary.ts
33
33
  import { mkdir, mkdtemp, rename, rm, stat, writeFile } from "node:fs/promises";
@@ -38,7 +38,7 @@ import process from "node:process";
38
38
  // src/version.ts
39
39
  import { readFileSync } from "node:fs";
40
40
  import { fileURLToPath } from "node:url";
41
- var VERSION = true ? "0.0.7" : versionFromPackage();
41
+ var VERSION = true ? "0.0.9" : versionFromPackage();
42
42
 
43
43
  // src/binary.ts
44
44
  var ASSET_MANIFEST = "assets.json";
@@ -382,8 +382,12 @@ var Client = class {
382
382
  async patchArtifact(slug, patch) {
383
383
  return (await this.send("PATCH", `/api/artifacts/${encodeURIComponent(slug)}`, { json: patch })).body;
384
384
  }
385
- async deleteArtifact(slug) {
386
- await this.send("DELETE", `/api/artifacts/${encodeURIComponent(slug)}`);
385
+ async deleteArtifact(slug, force = false) {
386
+ await this.send("DELETE", `/api/artifacts/${encodeURIComponent(slug)}${force ? "?force=1" : ""}`);
387
+ }
388
+ async deleteVersion(slug, n, force = false) {
389
+ const pathname = `/api/artifacts/${encodeURIComponent(slug)}/versions/${n}${force ? "?force=1" : ""}`;
390
+ return (await this.send("DELETE", pathname)).body;
387
391
  }
388
392
  async putShare(slug, email, role) {
389
393
  const pathname = `/api/artifacts/${encodeURIComponent(slug)}/shares/${encodeURIComponent(email)}`;
@@ -444,6 +448,7 @@ var SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
444
448
  var TAG_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
445
449
  var THEME_NAME_PATTERN = /^[a-z0-9-]{1,40}$/;
446
450
  var THEME_VERSION_PATTERN = /^([a-z0-9-]{1,40})@([0-9]{1,9})$/;
451
+ var THEME_LATEST_PATTERN = /^([a-z0-9-]{1,40})@latest$/;
447
452
  var DATA_KEY_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
448
453
  var EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
449
454
  var TOKEN_PREFIX = "rsh_";
@@ -991,7 +996,9 @@ commands:
991
996
  show <slug> artifact, versions and shares
992
997
  share <slug> set mode or change shares
993
998
  pin <slug> <n|live> serve version n, or live for the newest
994
- delete <slug> --yes delete an artifact
999
+ delete <slug> [--version n] [--force] --yes
1000
+ delete an artifact, or one version of it
1001
+ upgrade [--check] update rshow to the newest release
995
1002
  login [--url u] save the API token for this account
996
1003
  logout [--url u] remove the saved token for a server
997
1004
  completion <shell> print a completion script for bash, zsh or fish
@@ -1041,8 +1048,10 @@ usage error. --offline skips the theme version lookup on the art host.
1041
1048
  errors:
1042
1049
  no index.html at the root, unless theme.json makes the folder a theme
1043
1050
  a local href, src, poster, srcset, CSS url() or @import with no file behind it
1044
- a root-relative reference other than /_themes/<name>@<n>/<path>
1045
- a /_themes/<name>@<n>/ path whose version the art host does not have
1051
+ a root-relative reference other than /_themes/<name>@<n>/<path> or
1052
+ /_themes/<name>@latest/<path>
1053
+ a /_themes/<name>@<n>/ path whose version the art host does not have, or an
1054
+ @latest whose theme it does not have
1046
1055
  a script from a host other than cdnjs.cloudflare.com, cdn.jsdelivr.net,
1047
1056
  unpkg.com, or one of those without an exact version (@1.2.3, /1.2.3/ on cdnjs)
1048
1057
  a stylesheet from a host other than fonts.googleapis.com, cdn.jsdelivr.net,
@@ -1101,9 +1110,9 @@ Style warnings:
1101
1110
  var PREVIEW_USAGE = `usage: rshow preview <dir> [--theme <dir>] [--offline] [--no-reload] [--port 4321]
1102
1111
 
1103
1112
  Serves <dir> on 127.0.0.1: index.html for a folder, content types for files.
1104
- Works offline: /_themes/<name>@<n>/ is served from --theme when it names that
1105
- theme, then from the built-in themes on disk, and only then proxied to the art
1106
- URL from config, which --offline turns off.
1113
+ Works offline: /_themes/<name>@<n>/ and /_themes/<name>@latest/ are served
1114
+ from --theme when it names that theme, then from the built-in themes on disk,
1115
+ and only then proxied to the art URL from config, which --offline turns off.
1107
1116
  The page is reloaded on its own when a file changes; --no-reload turns that off.
1108
1117
  Scripts that use <artifact>/_data/<key> get a local store in that same session.`;
1109
1118
  var CONFIG_USAGE = `usage: rshow config [dir] [--offline]
@@ -1112,9 +1121,10 @@ Prints the merged config as JSON: ~/.config/rshow/config.json first, then
1112
1121
  <dir>/rshow.json (default: the current folder). RSHOW_URL overrides url and
1113
1122
  RSHOW_ART overrides art. <dir>/rshow.json also takes project and tags; later
1114
1123
  files win. Run rshow init to write url and art.
1115
- A theme with no version, or @latest, is looked up on the art host and printed
1116
- as <name>@<n>; when that fails a warning goes to stderr. --offline skips it, and
1117
- so does a config with no art host.
1124
+ A theme with no version, or @latest, prints as <name>@latest, and with the
1125
+ network its lookup adds theme_resolved as <name>@<n>; when the lookup fails a
1126
+ warning goes to stderr and the key is left out. --offline prints the loaded
1127
+ config as it is, and so does a config with no art host.
1118
1128
  The token is RSHOW_TOKEN, else the token rshow login saved in the macOS
1119
1129
  Keychain, the Linux keyring or a file with mode 600.`;
1120
1130
  var INIT_USAGE = `usage: rshow init --url <admin-url> --art <viewer-url> [--theme <name>] [--force]
@@ -1174,11 +1184,11 @@ var THEME_PREVIEW_USAGE = `usage: rshow theme preview <dir> [--offline] [--no-re
1174
1184
 
1175
1185
  Builds the theme folder into a temporary folder, serves that on 127.0.0.1 and
1176
1186
  prints the URL of index.html, the preview page that shows every layout. The
1177
- built folder answers /_themes/<name>@<n>/ and is rebuilt, and the page reloaded,
1178
- when the folder changes; --no-reload turns the reload off. A /_themes/ path for
1179
- another theme comes from the built-in themes on disk, then from the art URL
1180
- from config, which --offline turns off. The temporary folder goes when the
1181
- server stops.`;
1187
+ built folder answers /_themes/<name>@<n>/ and /_themes/<name>@latest/ and is
1188
+ rebuilt, and the page reloaded, when the folder changes; --no-reload turns the
1189
+ reload off. A /_themes/ path for another theme comes from the built-in themes
1190
+ on disk, then from the art URL from config, which --offline turns off. The
1191
+ temporary folder goes when the server stops.`;
1182
1192
  var THEME_PUBLISH_USAGE = `usage: rshow theme publish <dir> [--label l] [--build cmd] [--no-check] [--force]
1183
1193
 
1184
1194
  <dir> needs theme.json with a name. The folder is published as theme-<name>,
@@ -1190,7 +1200,8 @@ built on top of its base theme, and the built folder is what gets published.
1190
1200
  When the newest version already holds the built folder's paths and hashes, it
1191
1201
  prints unchanged: theme-<name> is already v<n> and publishes nothing.
1192
1202
  --force publishes a new version anyway.
1193
- Prints /_themes/<name>@<n>/.`;
1203
+ Prints /_themes/<name>@<n>/. Pages link /_themes/<name>@latest/ by default, so
1204
+ they follow every version you publish here.`;
1194
1205
  var THEME_LIST_USAGE = `usage: rshow theme list
1195
1206
 
1196
1207
  Lists artifacts whose slug starts with theme- and their served version.`;
@@ -1258,9 +1269,38 @@ var SHARE_USAGE = `usage: rshow share <slug> [--mode private|people|public] [--a
1258
1269
  var PIN_USAGE = `usage: rshow pin <slug> <n|live>
1259
1270
 
1260
1271
  Pins version n, or live to serve the newest version again.`;
1261
- var DELETE_USAGE = `usage: rshow delete <slug> --yes
1272
+ var DELETE_USAGE = `usage: rshow delete <slug> [--version n] [--force] --yes
1273
+
1274
+ Deletes the artifact, or with --version n only that version, and needs --yes.
1275
+ A theme or a theme version also needs --force, because pages link to it.
1276
+ A pinned version and the only version of an artifact cannot be deleted.
1277
+ A public version can stay visible for up to 5 minutes after the delete, and a
1278
+ theme file for longer.`;
1279
+ var UPGRADE_USAGE = `usage: rshow upgrade [--check] [--version <x.y.z>] [--force]
1262
1280
 
1263
- Deletes the artifact and needs --yes.`;
1281
+ Updates rshow to the newest release, or to the release --version names, which
1282
+ may be an older one.
1283
+ A single executable is replaced in place, and an npm global install runs
1284
+ npm install -g rshow@<version>.
1285
+ Any other install, such as a source checkout, an npx cache or a local
1286
+ dependency, is left alone and named with the tool that owns it.
1287
+ Running the command is the consent: there is no --yes.
1288
+ No config and no token are read, so it works before rshow init.
1289
+
1290
+ --check prints the current and the newest version and changes nothing, and
1291
+ exits 0 either way; --json prints {current, latest, upgrade_available}.
1292
+ A run prints upgraded <old> -> <new>, or already at <version>. --json prints
1293
+ {previous, version, upgraded, kind}, where kind is binary or npm.
1294
+ --version takes x.y.z with an optional leading v; --force reinstalls the same
1295
+ version, which is how a broken binary comes back.
1296
+ Skills that rshow installed are refreshed by the new rshow, and a bash, zsh or
1297
+ fish completion that is already there is replaced; RSHOW_NO_COMPLETIONS=1
1298
+ leaves the completions alone.
1299
+ RSHOW_DOWNLOAD_BASE downloads from a mirror instead of the release, and takes
1300
+ an http or https url; it cannot be used with --check.
1301
+
1302
+ A release older than this one has no upgrade command, so after a downgrade
1303
+ come back with curl -fsSL https://rslib.github.io/rshow/install.sh | sh.`;
1264
1304
  var LOGIN_USAGE = `usage: rshow login [--url u]
1265
1305
 
1266
1306
  Reads the token from stdin, checks it with the API, then saves it in the macOS
@@ -1954,27 +1994,27 @@ var sqrtTall = function sqrtTall2(extraVinculum, hLinePad2, viewBoxHeight) {
1954
1994
  };
1955
1995
  var sqrtPath = function sqrtPath2(size, extraVinculum, viewBoxHeight) {
1956
1996
  extraVinculum = 1e3 * extraVinculum;
1957
- var path27 = "";
1997
+ var path29 = "";
1958
1998
  switch (size) {
1959
1999
  case "sqrtMain":
1960
- path27 = sqrtMain(extraVinculum, hLinePad);
2000
+ path29 = sqrtMain(extraVinculum, hLinePad);
1961
2001
  break;
1962
2002
  case "sqrtSize1":
1963
- path27 = sqrtSize1(extraVinculum, hLinePad);
2003
+ path29 = sqrtSize1(extraVinculum, hLinePad);
1964
2004
  break;
1965
2005
  case "sqrtSize2":
1966
- path27 = sqrtSize2(extraVinculum, hLinePad);
2006
+ path29 = sqrtSize2(extraVinculum, hLinePad);
1967
2007
  break;
1968
2008
  case "sqrtSize3":
1969
- path27 = sqrtSize3(extraVinculum, hLinePad);
2009
+ path29 = sqrtSize3(extraVinculum, hLinePad);
1970
2010
  break;
1971
2011
  case "sqrtSize4":
1972
- path27 = sqrtSize4(extraVinculum, hLinePad);
2012
+ path29 = sqrtSize4(extraVinculum, hLinePad);
1973
2013
  break;
1974
2014
  case "sqrtTall":
1975
- path27 = sqrtTall(extraVinculum, hLinePad, viewBoxHeight);
2015
+ path29 = sqrtTall(extraVinculum, hLinePad, viewBoxHeight);
1976
2016
  }
1977
- return path27;
2017
+ return path29;
1978
2018
  };
1979
2019
  var innerPath = function innerPath2(name, height) {
1980
2020
  switch (name) {
@@ -6188,8 +6228,8 @@ var svgData = {
6188
6228
  };
6189
6229
  var staticSvg = function staticSvg2(value, options) {
6190
6230
  var _svgData$value = svgData[value], pathName = _svgData$value[0], width = _svgData$value[1], height = _svgData$value[2];
6191
- var path27 = new PathNode(pathName);
6192
- var svgNode = new SvgNode([path27], {
6231
+ var path29 = new PathNode(pathName);
6232
+ var svgNode = new SvgNode([path29], {
6193
6233
  "width": makeEm(width),
6194
6234
  "height": makeEm(height),
6195
6235
  // Override CSS rule `.katex svg { width: 100% }`
@@ -7332,8 +7372,8 @@ var stretchySvg = function stretchySvg2(group, options) {
7332
7372
  pathName = "tilde" + imgIndex;
7333
7373
  }
7334
7374
  }
7335
- var path27 = new PathNode(pathName);
7336
- var svgNode = new SvgNode([path27], {
7375
+ var path29 = new PathNode(pathName);
7376
+ var svgNode = new SvgNode([path29], {
7337
7377
  "width": "100%",
7338
7378
  "height": makeEm(_height),
7339
7379
  "viewBox": "0 0 " + viewBoxWidth + " " + viewBoxHeight,
@@ -8547,8 +8587,8 @@ var makeGlyphSpan = function makeGlyphSpan2(symbol, font, mode, options) {
8547
8587
  };
8548
8588
  var makeInner = function makeInner2(ch2, height, options) {
8549
8589
  var width = fontMetricsData["Size4-Regular"][ch2.charCodeAt(0)] ? fontMetricsData["Size4-Regular"][ch2.charCodeAt(0)][4] : fontMetricsData["Size1-Regular"][ch2.charCodeAt(0)][4];
8550
- var path27 = new PathNode("inner", innerPath(ch2, Math.round(1e3 * height)));
8551
- var svgNode = new SvgNode([path27], {
8590
+ var path29 = new PathNode("inner", innerPath(ch2, Math.round(1e3 * height)));
8591
+ var svgNode = new SvgNode([path29], {
8552
8592
  "width": makeEm(width),
8553
8593
  "height": makeEm(height),
8554
8594
  // Override CSS rule `.katex svg { width: 100% }`
@@ -8717,10 +8757,10 @@ var makeStackedDelim = function makeStackedDelim2(delim, heightTotal, center, op
8717
8757
  var midHeight = realHeightTotal - topHeightTotal - bottomHeightTotal;
8718
8758
  var viewBoxHeight = Math.round(realHeightTotal * 1e3);
8719
8759
  var pathStr = tallDelim(svgLabel, Math.round(midHeight * 1e3));
8720
- var path27 = new PathNode(svgLabel, pathStr);
8760
+ var path29 = new PathNode(svgLabel, pathStr);
8721
8761
  var width = makeEm(viewBoxWidth / 1e3);
8722
8762
  var height = makeEm(viewBoxHeight / 1e3);
8723
- var svg = new SvgNode([path27], {
8763
+ var svg = new SvgNode([path29], {
8724
8764
  "width": width,
8725
8765
  "height": height,
8726
8766
  "viewBox": "0 0 " + viewBoxWidth + " " + viewBoxHeight
@@ -8761,8 +8801,8 @@ var makeStackedDelim = function makeStackedDelim2(delim, heightTotal, center, op
8761
8801
  var vbPad = 80;
8762
8802
  var emPad = 0.08;
8763
8803
  var sqrtSvg = function sqrtSvg2(sqrtName, height, viewBoxHeight, extraVinculum, options) {
8764
- var path27 = sqrtPath(sqrtName, extraVinculum, viewBoxHeight);
8765
- var pathNode = new PathNode(sqrtName, path27);
8804
+ var path29 = sqrtPath(sqrtName, extraVinculum, viewBoxHeight);
8805
+ var pathNode = new PathNode(sqrtName, path29);
8766
8806
  var svg = new SvgNode([pathNode], {
8767
8807
  // Note: 1000:1 ratio of viewBox to document em width.
8768
8808
  "width": "400em",
@@ -9261,8 +9301,8 @@ var htmlBuilder$7 = (group, options) => {
9261
9301
  var angleHeight = inner2.height + inner2.depth + lineWeight + clearance;
9262
9302
  inner2.style.paddingLeft = makeEm(angleHeight / 2 + lineWeight);
9263
9303
  var viewBoxHeight = Math.floor(1e3 * angleHeight * scale);
9264
- var path27 = phasePath(viewBoxHeight);
9265
- var svgNode = new SvgNode([new PathNode("phase", path27)], {
9304
+ var path29 = phasePath(viewBoxHeight);
9305
+ var svgNode = new SvgNode([new PathNode("phase", path29)], {
9266
9306
  "width": "400em",
9267
9307
  "height": makeEm(viewBoxHeight / 1e3),
9268
9308
  "viewBox": "0 0 400000 " + viewBoxHeight,
@@ -13291,11 +13331,11 @@ var newcommand = (context, existsOK, nonexistsOK, skipIfExists) => {
13291
13331
  throw new ParseError("\\newcommand's first argument must be a macro name");
13292
13332
  }
13293
13333
  var name = arg[0].text;
13294
- var exists2 = context.isDefined(name);
13295
- if (exists2 && !existsOK) {
13334
+ var exists3 = context.isDefined(name);
13335
+ if (exists3 && !existsOK) {
13296
13336
  throw new ParseError("\\newcommand{" + name + "} attempting to redefine " + (name + "; use \\renewcommand"));
13297
13337
  }
13298
- if (!exists2 && !nonexistsOK) {
13338
+ if (!exists3 && !nonexistsOK) {
13299
13339
  throw new ParseError("\\renewcommand{" + name + "} when command " + name + " does not yet exist; use \\newcommand");
13300
13340
  }
13301
13341
  var numArgs = 0;
@@ -13313,7 +13353,7 @@ var newcommand = (context, existsOK, nonexistsOK, skipIfExists) => {
13313
13353
  numArgs = parseInt(argText);
13314
13354
  arg = context.consumeArg().tokens;
13315
13355
  }
13316
- if (!(exists2 && skipIfExists)) {
13356
+ if (!(exists3 && skipIfExists)) {
13317
13357
  context.macros.set(name, {
13318
13358
  tokens: arg,
13319
13359
  numArgs
@@ -15984,7 +16024,7 @@ var EMOJI = new RegExp("\\p{Emoji_Presentation}", "gu");
15984
16024
  var COLOR = /#[0-9a-f]{3,8}\b|\b(?:rgba?|hsla?)\([^()]*\)/gi;
15985
16025
  var GOOGLE_URL = /fonts\.googleapis\.com\/[^"'\s)>]*/gi;
15986
16026
  var GOOGLE_FAMILY = /[?&]family=([^&"'\s]+)/gi;
15987
- var THEME_LINK = /\/_themes\/[a-z0-9][a-z0-9-]*@[0-9]+\//i;
16027
+ var THEME_LINK = /\/_themes\/[a-z0-9][a-z0-9-]*@(?:[0-9]+|latest)\//i;
15988
16028
  function stylePathSkipped(rel2) {
15989
16029
  const parts2 = rel2.toLowerCase().split("/");
15990
16030
  const name = parts2[parts2.length - 1] ?? "";
@@ -16372,7 +16412,7 @@ var VERSION_AT = /@[0-9]+\.[0-9]+\.[0-9]+/;
16372
16412
  var VERSION_PATH = /\/[0-9]+\.[0-9]+\.[0-9]+\//;
16373
16413
  var EXTERNAL = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
16374
16414
  var IGNORED = /^(?:#|data:|blob:|mailto:|tel:|javascript:)/i;
16375
- var THEME_REF = /^\/_themes\/([a-z0-9][a-z0-9-]*)@([0-9]+)\//;
16415
+ var THEME_REF = /^\/_themes\/([a-z0-9][a-z0-9-]*)@(latest|[0-9]+)\//;
16376
16416
  var TAG_PATTERN2 = /<([a-zA-Z][a-zA-Z0-9:-]*)((?:[^>"']|"[^"]*"|'[^']*')*)>/g;
16377
16417
  var ATTR_PATTERN = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
16378
16418
  var CSS_URL = /url\(\s*(?:"([^"]*)"|'([^']*)'|([^)'"\s]*))\s*\)/gi;
@@ -16498,7 +16538,7 @@ function resolveRef(scan, file, raw, line) {
16498
16538
  file,
16499
16539
  line,
16500
16540
  "error",
16501
- `root-relative reference ${value} (only /_themes/<name>@<n>/ paths may start with a slash)`
16541
+ `root-relative reference ${value} (only /_themes/<name>@<n>/ or /_themes/<name>@latest/ paths may start with a slash)`
16502
16542
  );
16503
16543
  return;
16504
16544
  }
@@ -17032,7 +17072,7 @@ function sameFiles(local, remote) {
17032
17072
  const paths = Object.keys(local);
17033
17073
  const other = remote;
17034
17074
  if (paths.length !== Object.keys(other).length) return false;
17035
- return paths.every((path27) => other[path27]?.hash === local[path27]?.hash);
17075
+ return paths.every((path29) => other[path29]?.hash === local[path29]?.hash);
17036
17076
  }
17037
17077
  function reportUnchanged(out, result) {
17038
17078
  out.data({ unchanged: true, slug: result.slug, version: result.version.n });
@@ -17770,8 +17810,8 @@ function keyLine(text2, key2, mode) {
17770
17810
  }
17771
17811
  return 0;
17772
17812
  }
17773
- function finding(path27, line, severity, message) {
17774
- return { path: path27, line: Math.max(1, line), severity, message };
17813
+ function finding(path29, line, severity, message) {
17814
+ return { path: path29, line: Math.max(1, line), severity, message };
17775
17815
  }
17776
17816
  function resolveValue(name, map, fallback) {
17777
17817
  let current = map[name] ?? fallback[name];
@@ -18093,7 +18133,7 @@ async function handleTheme(req, res, session, raw, pathname, method) {
18093
18133
  const rest = pathname.slice(PROXY_PREFIX.length);
18094
18134
  const slash = rest.indexOf("/");
18095
18135
  const segment = slash < 0 ? rest : rest.slice(0, slash);
18096
- const match = THEME_VERSION_PATTERN.exec(segment);
18136
+ const match = THEME_VERSION_PATTERN.exec(segment) ?? THEME_LATEST_PATTERN.exec(segment);
18097
18137
  if (match === null) {
18098
18138
  if (session.art !== "" && !session.offline) {
18099
18139
  await proxy(session.art, raw, method, res);
@@ -18488,15 +18528,16 @@ async function resolveTheme2(config) {
18488
18528
  const theme = config.theme ?? "default";
18489
18529
  const ref = themeRefOf(theme);
18490
18530
  if (ref.version !== void 0 && ref.version !== "latest") return config;
18531
+ const latest = { ...config, theme: `${ref.name}@latest` };
18491
18532
  if (config.art === void 0) {
18492
18533
  warn("no art host configured; skipping theme version lookup");
18493
- return config;
18534
+ return config.theme === void 0 ? config : latest;
18494
18535
  }
18495
18536
  try {
18496
- return { ...config, theme: await resolvedTheme(config.art, theme) };
18537
+ return { ...latest, theme_resolved: await resolvedTheme(config.art, theme) };
18497
18538
  } catch (err) {
18498
18539
  warn(`cannot resolve theme ${ref.name}: ${messageOf8(err)}`);
18499
- return config.theme === void 0 ? config : { ...config, theme: ref.name };
18540
+ return config.theme === void 0 ? config : latest;
18500
18541
  }
18501
18542
  }
18502
18543
  async function run4(argv, ctx) {
@@ -19156,8 +19197,8 @@ async function create(argv, ctx) {
19156
19197
  if (baseDir === void 0) throw new UsageError(`no built-in theme: ${from}`);
19157
19198
  const parent = path18.resolve(stringValue(args, "dir") ?? ".");
19158
19199
  const target = path18.join(parent, name);
19159
- const exists2 = await stat13(target).catch(() => void 0);
19160
- if (exists2 !== void 0) throw new UsageError(`${target} already exists`);
19200
+ const exists3 = await stat13(target).catch(() => void 0);
19201
+ if (exists3 !== void 0) throw new UsageError(`${target} already exists`);
19161
19202
  const values = await baseRoleValues(baseDir, ROLE_TOKENS);
19162
19203
  const doc = { name, extends: from, tokens: {}, modeTokens: values };
19163
19204
  await mkdir6(target, { recursive: true });
@@ -19473,13 +19514,35 @@ async function run12(argv, ctx) {
19473
19514
 
19474
19515
  // src/commands/delete.ts
19475
19516
  async function run13(argv, ctx) {
19476
- const args = parseCommandArgs(argv, { yes: { type: "boolean" } }, 1, 1, DELETE_USAGE);
19517
+ const args = parseCommandArgs(
19518
+ argv,
19519
+ { version: { type: "string" }, force: { type: "boolean" }, yes: { type: "boolean" } },
19520
+ 1,
19521
+ 1,
19522
+ DELETE_USAGE
19523
+ );
19477
19524
  const slug = validateSlug(args.positionals[0]);
19525
+ const raw = stringValue(args, "version");
19526
+ let version2 = null;
19527
+ if (raw !== void 0) {
19528
+ if (!/^[0-9]+$/.test(raw) || Number(raw) < 1) {
19529
+ throw new UsageError(`expected a version number, got: ${raw}`);
19530
+ }
19531
+ version2 = Number(raw);
19532
+ }
19478
19533
  if (!boolValue(args, "yes")) throw new UsageError(`delete needs --yes (try: ${usageLine(DELETE_USAGE)})`);
19479
19534
  const api = await ctx.api();
19480
- await api.deleteArtifact(slug);
19481
- ctx.out.data(null);
19482
- ctx.out.line(`deleted: ${slug}`);
19535
+ const force = boolValue(args, "force");
19536
+ if (version2 === null) {
19537
+ await api.deleteArtifact(slug, force);
19538
+ ctx.out.data(null);
19539
+ ctx.out.line(`deleted: ${slug}`);
19540
+ return;
19541
+ }
19542
+ const artifact = await api.deleteVersion(slug, version2, force);
19543
+ ctx.out.data(artifact);
19544
+ ctx.out.line(`deleted: ${slug} v${version2}`);
19545
+ ctx.out.line(`served: ${artifact?.served === null || artifact?.served === void 0 ? "-" : artifact.served}`);
19483
19546
  }
19484
19547
 
19485
19548
  // src/commands/project.ts
@@ -22692,6 +22755,7 @@ var USAGE = {
22692
22755
  delete: DELETE_USAGE,
22693
22756
  login: LOGIN_USAGE,
22694
22757
  logout: LOGOUT_USAGE,
22758
+ upgrade: UPGRADE_USAGE,
22695
22759
  completion: COMPLETION_USAGE
22696
22760
  };
22697
22761
  var WHEN = {
@@ -22713,8 +22777,15 @@ function listLines(usage) {
22713
22777
  if (start < 0) return [];
22714
22778
  const out = [];
22715
22779
  for (const line of lines.slice(start)) {
22716
- if (!/^\s{2}\S/.test(line)) break;
22717
- out.push(line);
22780
+ if (/^\s{2}\S/.test(line)) {
22781
+ out.push(line);
22782
+ continue;
22783
+ }
22784
+ if (out.length > 0 && /^\s{3,}\S/.test(line)) {
22785
+ out[out.length - 1] = `${out[out.length - 1].trimEnd()} ${line.trim()}`;
22786
+ continue;
22787
+ }
22788
+ break;
22718
22789
  }
22719
22790
  return out;
22720
22791
  }
@@ -23061,6 +23132,7 @@ var SPECS = {
23061
23132
  delete: { usage: DELETE_USAGE },
23062
23133
  login: { usage: LOGIN_USAGE },
23063
23134
  logout: { usage: LOGOUT_USAGE },
23135
+ upgrade: { usage: UPGRADE_USAGE },
23064
23136
  completion: { usage: COMPLETION_USAGE, choices: [...SHELLS] }
23065
23137
  };
23066
23138
  function optionsOf(usage) {
@@ -23426,15 +23498,507 @@ async function run20(argv, ctx, platform = process21.platform) {
23426
23498
  ctx.out.line(`logged in as ${email} (scope ${scope}) for ${url}, saved in ${store2.description}`);
23427
23499
  }
23428
23500
 
23429
- // src/commands/logout.ts
23501
+ // src/commands/upgrade.ts
23502
+ import { realpath as realpath2 } from "node:fs/promises";
23503
+ import path27 from "node:path";
23504
+ import process23 from "node:process";
23505
+
23506
+ // src/upgrade.ts
23507
+ import { execFile, spawn as spawn6 } from "node:child_process";
23508
+ import { createHash as createHash3 } from "node:crypto";
23509
+ import { createReadStream, createWriteStream } from "node:fs";
23510
+ import { chmod as chmod2, copyFile as copyFile2, lstat as lstat2, mkdtemp as mkdtemp5, readFile as readFile19, realpath, rename as rename3, rm as rm8, stat as stat15 } from "node:fs/promises";
23511
+ import path26 from "node:path";
23430
23512
  import process22 from "node:process";
23431
- async function run21(argv, ctx, platform = process22.platform) {
23513
+ import { Readable } from "node:stream";
23514
+ import { pipeline } from "node:stream/promises";
23515
+ var REPO = "rslib/rshow";
23516
+ var NPM_HINT = "npm i -g rshow";
23517
+ var INSTALLER_ELSEWHERE = `curl -fsSL https://rslib.github.io/rshow/install.sh | RSHOW_INSTALL_DIR=$HOME/.local/bin sh`;
23518
+ var LATEST_URL = `https://github.com/${REPO}/releases/latest`;
23519
+ var RELEASES = `https://github.com/${REPO}/releases/download/v`;
23520
+ var TARGETS = ["linux-x64", "linux-arm64", "darwin-arm64"];
23521
+ var VERSION_PATTERN = /^v?([0-9]+\.[0-9]+\.[0-9]+)$/;
23522
+ var TAG_PATH = /\/tag\/v([0-9]+\.[0-9]+\.[0-9]+)$/;
23523
+ var LOOKUP_TIMEOUT_MS = 15e3;
23524
+ var DOWNLOAD_TIMEOUT_MS = 6e5;
23525
+ var UNPACK_TIMEOUT_MS = 12e4;
23526
+ var RUN_TIMEOUT_MS = 2e4;
23527
+ var NPM_TIMEOUT_MS = 3e5;
23528
+ var SKILLS_TIMEOUT_MS = 12e4;
23529
+ var UpgradeFailure = class extends Error {
23530
+ exit;
23531
+ constructor(message, exit = EXIT.api) {
23532
+ super(message);
23533
+ this.name = "UpgradeFailure";
23534
+ this.exit = exit;
23535
+ }
23536
+ };
23537
+ function messageOf12(err) {
23538
+ return err instanceof Error ? err.message : String(err);
23539
+ }
23540
+ function codeOf(err) {
23541
+ const code = typeof err === "object" && err !== null ? err.code : void 0;
23542
+ if (typeof code === "number") return `exit ${code}`;
23543
+ if (typeof code === "string" && code !== "") return code;
23544
+ return messageOf12(err);
23545
+ }
23546
+ function runProgram(file, args, options = {}) {
23547
+ return new Promise((resolve2, reject) => {
23548
+ const run23 = { timeout: options.timeoutMs, maxBuffer: 16 * 1024 * 1024 };
23549
+ execFile(file, [...args], run23, (err, stdout) => {
23550
+ if (err !== null) {
23551
+ reject(err);
23552
+ return;
23553
+ }
23554
+ resolve2(stdout);
23555
+ });
23556
+ });
23557
+ }
23558
+ function parseVersion(raw) {
23559
+ const found = VERSION_PATTERN.exec(raw.trim());
23560
+ if (found === null) throw new UsageError(`--version needs x.y.z, got: ${raw}`);
23561
+ return found[1];
23562
+ }
23563
+ function compareVersions(left, right) {
23564
+ const a = left.split(".").map(Number);
23565
+ const b = right.split(".").map(Number);
23566
+ for (let index = 0; index < 3; index += 1) {
23567
+ const diff = (a[index] ?? 0) - (b[index] ?? 0);
23568
+ if (diff !== 0) return diff < 0 ? -1 : 1;
23569
+ }
23570
+ return 0;
23571
+ }
23572
+ function targetOf2(platform, arch) {
23573
+ const name = `${platform}-${arch}`;
23574
+ return TARGETS.includes(name) ? name : void 0;
23575
+ }
23576
+ function releaseTagOf(location) {
23577
+ if (location === null || location.trim() === "") return void 0;
23578
+ let pathname;
23579
+ try {
23580
+ pathname = new URL(location, LATEST_URL).pathname;
23581
+ } catch {
23582
+ return void 0;
23583
+ }
23584
+ return TAG_PATH.exec(pathname)?.[1];
23585
+ }
23586
+ async function latestVersion(options) {
23587
+ const reply = await sendRequest(
23588
+ options.fetch,
23589
+ LATEST_URL,
23590
+ { redirect: "manual", signal: AbortSignal.timeout(LOOKUP_TIMEOUT_MS) },
23591
+ options.env
23592
+ );
23593
+ const location = reply.headers.get("location");
23594
+ const version2 = releaseTagOf(location);
23595
+ if (version2 === void 0) {
23596
+ throw new UpgradeFailure(`${LATEST_URL} did not name a release (location: ${location ?? "none"})`);
23597
+ }
23598
+ return version2;
23599
+ }
23600
+ function mirrorOf(options) {
23601
+ const raw = (options.env.RSHOW_DOWNLOAD_BASE ?? "").trim();
23602
+ if (raw === "") return void 0;
23603
+ const base = raw.replace(/\/+$/, "");
23604
+ let protocol;
23605
+ try {
23606
+ protocol = new URL(base).protocol;
23607
+ } catch {
23608
+ throw new UsageError(`RSHOW_DOWNLOAD_BASE is not a url: ${raw}`);
23609
+ }
23610
+ if (protocol !== "http:" && protocol !== "https:") {
23611
+ throw new UsageError(`RSHOW_DOWNLOAD_BASE needs an http or https url, got: ${raw}`);
23612
+ }
23613
+ return base;
23614
+ }
23615
+ function otherError(options) {
23616
+ const from = options.install.argv1 ?? options.install.execPath;
23617
+ const version2 = options.wanted ?? "<version>";
23618
+ const where = from.split(path26.sep).join("/");
23619
+ if (where.includes("/_npx/")) {
23620
+ return new UsageError(`rshow runs from ${from}, an npx cache that npm owns; run npx rshow@${version2}`);
23621
+ }
23622
+ if (where.includes("/node_modules/")) {
23623
+ return new UsageError(
23624
+ `rshow runs from ${from}, a local dependency that npm owns; run npm install rshow@${version2} to update it`
23625
+ );
23626
+ }
23627
+ return new UsageError(`rshow runs from ${from}, a source checkout; update it with git pull and npm run build`);
23628
+ }
23629
+ function dataHome(env) {
23630
+ const xdg = (env.XDG_DATA_HOME ?? "").trim();
23631
+ return xdg !== "" ? xdg : path26.join(homeDir(env), ".local", "share");
23632
+ }
23633
+ function configHome(env) {
23634
+ const xdg = (env.XDG_CONFIG_HOME ?? "").trim();
23635
+ return xdg !== "" ? xdg : path26.join(homeDir(env), ".config");
23636
+ }
23637
+ function completionPaths(env) {
23638
+ const data = dataHome(env);
23639
+ const config = configHome(env);
23640
+ return [
23641
+ { name: "rshow.bash", dst: path26.join(data, "bash-completion", "completions", "rshow") },
23642
+ { name: "_rshow", dst: path26.join(data, "zsh", "site-functions", "_rshow") },
23643
+ { name: "rshow.fish", dst: path26.join(config, "fish", "completions", "rshow.fish") }
23644
+ ];
23645
+ }
23646
+ async function exists2(file) {
23647
+ return await stat15(file).catch(() => void 0) !== void 0;
23648
+ }
23649
+ function missingFile(name, url, named) {
23650
+ if (named === void 0) {
23651
+ return new UpgradeFailure(
23652
+ `this release has no ${name} (${url}); a new release attaches its binaries a few minutes after it appears, so try again`
23653
+ );
23654
+ }
23655
+ return new UpgradeFailure(
23656
+ `release v${named} has no ${name} (${url}); it may not exist, and a release before 0.0.6 has no tarball, so use the installer with RSHOW_VERSION=${named}`
23657
+ );
23658
+ }
23659
+ async function download(options, url, file, name, named) {
23660
+ const reply = await sendRequest(options.fetch, url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) }, options.env);
23661
+ if (reply.status === 404) throw missingFile(name, url, named);
23662
+ if (reply.status !== 200 || reply.body === null) {
23663
+ throw new UpgradeFailure(`cannot download ${url}: HTTP ${reply.status}`);
23664
+ }
23665
+ try {
23666
+ await pipeline(Readable.fromWeb(reply.body), createWriteStream(file));
23667
+ } catch (err) {
23668
+ throw new UpgradeFailure(`cannot write ${file}: ${messageOf12(err)}`);
23669
+ }
23670
+ }
23671
+ function sumOf(text2, name) {
23672
+ for (const line of text2.split("\n")) {
23673
+ const parts2 = line.trim().split(/\s+/);
23674
+ if (parts2.length < 2) continue;
23675
+ if (parts2[1].replace(/^\*/, "") === name) return parts2[0];
23676
+ }
23677
+ return void 0;
23678
+ }
23679
+ async function sha256Of(file) {
23680
+ const hash = createHash3("sha256");
23681
+ await pipeline(createReadStream(file), hash);
23682
+ return hash.digest("hex");
23683
+ }
23684
+ async function checkSum(file, name, sumsFile) {
23685
+ const expected = sumOf(await readFile19(sumsFile, "utf8"), name);
23686
+ const actual = await sha256Of(file);
23687
+ if (expected === void 0) {
23688
+ throw new UpgradeFailure(`SHA256SUMS holds no line for ${name}, and the download is ${actual}`);
23689
+ }
23690
+ if (expected !== actual) {
23691
+ throw new UpgradeFailure(`checksum mismatch for ${name}: SHA256SUMS says ${expected}, the download is ${actual}`);
23692
+ }
23693
+ }
23694
+ async function unpack2(file, dir, tarball) {
23695
+ try {
23696
+ await runProgram("tar", ["-xzf", file, "-C", dir], { timeoutMs: UNPACK_TIMEOUT_MS });
23697
+ } catch (err) {
23698
+ throw new UpgradeFailure(`cannot unpack ${tarball}: ${codeOf(err)}`);
23699
+ }
23700
+ }
23701
+ async function printedVersion(file) {
23702
+ try {
23703
+ return (await runProgram(file, ["--version"], { timeoutMs: RUN_TIMEOUT_MS })).trim();
23704
+ } catch (err) {
23705
+ throw new UpgradeFailure(`the new rshow does not run (${codeOf(err)}); the old rshow is still in place`);
23706
+ }
23707
+ }
23708
+ async function replaceCompletions(options, dir, target) {
23709
+ if ((options.env.RSHOW_NO_COMPLETIONS ?? "").trim() === "1") return;
23710
+ for (const entry of completionPaths(options.env)) {
23711
+ const src = path26.join(dir, `rshow-${target}`, "completions", entry.name);
23712
+ try {
23713
+ if (!await exists2(entry.dst) || !await exists2(src)) continue;
23714
+ await copyFile2(src, entry.dst);
23715
+ } catch (err) {
23716
+ warn(`cannot replace the completion ${entry.dst}: ${messageOf12(err)}`);
23717
+ }
23718
+ }
23719
+ }
23720
+ function modeOf(target, statuses) {
23721
+ const kinds = statuses.filter((entry) => entry.target === target).map((entry) => entry.state.kind);
23722
+ if (kinds.includes("copy")) return "copy";
23723
+ if (kinds.includes("link")) return "link";
23724
+ return void 0;
23725
+ }
23726
+ async function refreshSkills(options, file, prefix) {
23727
+ const targets = await detectedTargets(options.home);
23728
+ if (targets.length === 0) return;
23729
+ let source;
23730
+ try {
23731
+ source = await skillsSource(options.env);
23732
+ } catch {
23733
+ return;
23734
+ }
23735
+ if (source === void 0) return;
23736
+ const statuses = await skillsStatus({ home: options.home, source, targets });
23737
+ const groups = { copy: [], link: [] };
23738
+ for (const target of targets) {
23739
+ const mode = modeOf(target, statuses);
23740
+ if (mode !== void 0) groups[mode].push(target);
23741
+ }
23742
+ for (const mode of ["copy", "link"]) {
23743
+ if (groups[mode].length === 0) continue;
23744
+ try {
23745
+ await runProgram(file, [...prefix, "skills", "install", `--${mode}`, ...groups[mode]], {
23746
+ timeoutMs: SKILLS_TIMEOUT_MS
23747
+ });
23748
+ } catch (err) {
23749
+ warn(`the new rshow could not refresh the skills (${codeOf(err)}); run rshow skills install`);
23750
+ }
23751
+ }
23752
+ }
23753
+ function versionIn(printed) {
23754
+ return /^rshow ([0-9]+\.[0-9]+\.[0-9]+)/.exec(printed)?.[1];
23755
+ }
23756
+ async function replaceBinary(job, dest) {
23757
+ const dir = path26.dirname(dest);
23758
+ let tmp;
23759
+ try {
23760
+ tmp = await mkdtemp5(path26.join(dir, ".rshow-upgrade-"));
23761
+ } catch (err) {
23762
+ const code = err.code;
23763
+ if (code === "EACCES" || code === "EPERM" || code === "EROFS") {
23764
+ throw new UpgradeFailure(`${dir} is not writable; install elsewhere instead: ${INSTALLER_ELSEWHERE}`);
23765
+ }
23766
+ throw new UpgradeFailure(`cannot write next to ${dest}: ${messageOf12(err)}`);
23767
+ }
23768
+ try {
23769
+ const tarball = `rshow-${job.target}.tar.gz`;
23770
+ const file = path26.join(tmp, tarball);
23771
+ const sums = path26.join(tmp, "SHA256SUMS");
23772
+ const url = `${job.base}/${tarball}`;
23773
+ process22.stderr.write(`rshow: downloading ${url}
23774
+ `);
23775
+ await download(job.options, url, file, tarball, job.named);
23776
+ await download(job.options, `${job.base}/SHA256SUMS`, sums, "SHA256SUMS", job.named);
23777
+ await checkSum(file, tarball, sums);
23778
+ await unpack2(file, tmp, tarball);
23779
+ const folder = `rshow-${job.target}`;
23780
+ const unpacked = path26.join(tmp, folder, "rshow");
23781
+ const info = await lstat2(unpacked).catch(() => void 0);
23782
+ if (info === void 0) {
23783
+ throw new UpgradeFailure(`${tarball} holds no ${folder}/rshow; the old rshow is still in place`);
23784
+ }
23785
+ if (!info.isFile()) {
23786
+ throw new UpgradeFailure(`${tarball} holds a ${folder}/rshow that is not a file; the old rshow is still in place`);
23787
+ }
23788
+ await chmod2(unpacked, 493);
23789
+ const printed = await printedVersion(unpacked);
23790
+ let version2;
23791
+ if (job.version === void 0) {
23792
+ const found = versionIn(printed);
23793
+ if (found === void 0) {
23794
+ throw new UpgradeFailure(`the new rshow printed ${printed || "no version"}; the old rshow is still in place`);
23795
+ }
23796
+ if (!job.force && found === job.options.current) return { replaced: false, version: found };
23797
+ version2 = found;
23798
+ } else {
23799
+ if (!printed.startsWith(`rshow ${job.version}`)) {
23800
+ throw new UpgradeFailure(
23801
+ `the new rshow printed ${printed || "no version"}, not rshow ${job.version}; the old rshow is still in place`
23802
+ );
23803
+ }
23804
+ version2 = job.version;
23805
+ }
23806
+ await rename3(unpacked, dest);
23807
+ await replaceCompletions(job.options, tmp, job.target);
23808
+ return { replaced: true, version: version2 };
23809
+ } finally {
23810
+ await rm8(tmp, { recursive: true, force: true });
23811
+ }
23812
+ }
23813
+ async function installPath(options) {
23814
+ try {
23815
+ return await realpath(options.install.execPath);
23816
+ } catch (err) {
23817
+ throw new UpgradeFailure(`cannot read ${options.install.execPath}: ${messageOf12(err)}`);
23818
+ }
23819
+ }
23820
+ function targetOrFail(options) {
23821
+ const platform = options.platform ?? process22.platform;
23822
+ const arch = options.arch ?? process22.arch;
23823
+ const target = targetOf2(platform, arch);
23824
+ if (target === void 0) {
23825
+ throw new UsageError(`no rshow single executable for ${platform}-${arch}; run ${NPM_HINT}`);
23826
+ }
23827
+ return target;
23828
+ }
23829
+ async function installBinary(job) {
23830
+ const dest = await installPath(job.options);
23831
+ const outcome = await replaceBinary(job, dest);
23832
+ if (!outcome.replaced) return { status: "current", kind: "binary", version: outcome.version };
23833
+ await refreshSkills(job.options, dest, []);
23834
+ return { status: "upgraded", kind: "binary", previous: job.options.current, version: outcome.version };
23835
+ }
23836
+ function runNpm(args) {
23837
+ return new Promise((resolve2, reject) => {
23838
+ const child = spawn6("npm", [...args], { stdio: ["ignore", 2, 2], timeout: NPM_TIMEOUT_MS });
23839
+ child.on("error", (err) => {
23840
+ child.emit("exit", null, null);
23841
+ reject(err);
23842
+ });
23843
+ child.on("close", (code, signal) => {
23844
+ if (code === 0) {
23845
+ resolve2();
23846
+ return;
23847
+ }
23848
+ if (code !== null) {
23849
+ const err = Object.assign(new Error(`npm exited ${code}`), { code });
23850
+ reject(err);
23851
+ return;
23852
+ }
23853
+ reject(new Error(`npm stopped on ${signal ?? "a timeout"}`));
23854
+ });
23855
+ });
23856
+ }
23857
+ async function npmInstall(version2) {
23858
+ const command = `npm install -g rshow@${version2}`;
23859
+ try {
23860
+ await runNpm(["install", "-g", `rshow@${version2}`]);
23861
+ } catch (err) {
23862
+ const code = err.code;
23863
+ if (typeof code === "number") throw new UpgradeFailure(`${command} failed (exit ${code})`, code);
23864
+ throw new UpgradeFailure(`cannot run npm (${codeOf(err)}); run ${command}`, EXIT.usage);
23865
+ }
23866
+ }
23867
+ async function upgrade(options) {
23868
+ const mirror = mirrorOf(options);
23869
+ const force = options.force === true;
23870
+ const kind = options.install.kind;
23871
+ if (options.check === true) {
23872
+ if (mirror !== void 0) {
23873
+ throw new UsageError(`--check cannot be used with RSHOW_DOWNLOAD_BASE (${mirror}), which names no release`);
23874
+ }
23875
+ const latest = options.wanted ?? await latestVersion(options);
23876
+ return {
23877
+ status: "check",
23878
+ current: options.current,
23879
+ latest,
23880
+ upgrade: compareVersions(latest, options.current) > 0
23881
+ };
23882
+ }
23883
+ if (kind === "other") throw otherError(options);
23884
+ if (kind === "binary" && mirror !== void 0 && options.wanted === void 0) {
23885
+ return installBinary({ options, base: mirror, target: targetOrFail(options), force });
23886
+ }
23887
+ let version2 = options.wanted;
23888
+ if (version2 === void 0) {
23889
+ const latest = await latestVersion(options);
23890
+ if (compareVersions(latest, options.current) < 0) {
23891
+ return { status: "newer", kind, current: options.current, latest };
23892
+ }
23893
+ version2 = latest;
23894
+ }
23895
+ if (version2 === options.current && !force) {
23896
+ return { status: "current", kind, version: version2 };
23897
+ }
23898
+ if (kind === "binary") {
23899
+ const base = mirror ?? `${RELEASES}${version2}`;
23900
+ return installBinary({ options, base, target: targetOrFail(options), version: version2, named: options.wanted, force });
23901
+ }
23902
+ await npmInstall(version2);
23903
+ const argv1 = options.install.argv1;
23904
+ if (argv1 !== void 0) await refreshSkills(options, process22.execPath, [argv1]);
23905
+ return { status: "upgraded", kind: "npm", previous: options.current, version: version2 };
23906
+ }
23907
+ function reportOf(result) {
23908
+ if (result.status === "check") {
23909
+ return {
23910
+ data: { current: result.current, latest: result.latest, upgrade_available: result.upgrade },
23911
+ lines: [`current: ${result.current}`, `latest: ${result.latest}`]
23912
+ };
23913
+ }
23914
+ if (result.status === "newer") {
23915
+ return {
23916
+ data: { previous: result.current, version: result.current, upgraded: false, kind: result.kind },
23917
+ lines: [`rshow: ${result.current} is newer than the newest release, ${result.latest}`]
23918
+ };
23919
+ }
23920
+ if (result.status === "current") {
23921
+ return {
23922
+ data: { previous: result.version, version: result.version, upgraded: false, kind: result.kind },
23923
+ lines: [`rshow: already at ${result.version}`]
23924
+ };
23925
+ }
23926
+ return {
23927
+ data: { previous: result.previous, version: result.version, upgraded: true, kind: result.kind },
23928
+ lines: [`rshow: upgraded ${result.previous} -> ${result.version}`]
23929
+ };
23930
+ }
23931
+
23932
+ // src/commands/upgrade.ts
23933
+ var TIMEOUT_MS2 = 6e4;
23934
+ function isUnder2(file, dir) {
23935
+ const rel2 = path27.relative(dir, file);
23936
+ return rel2 !== "" && rel2 !== ".." && !rel2.startsWith(`..${path27.sep}`) && !path27.isAbsolute(rel2);
23937
+ }
23938
+ async function npmRoot() {
23939
+ try {
23940
+ const root2 = (await runProgram("npm", ["root", "-g"], { timeoutMs: TIMEOUT_MS2 })).trim();
23941
+ return root2 === "" ? void 0 : await realpath2(root2);
23942
+ } catch {
23943
+ return void 0;
23944
+ }
23945
+ }
23946
+ async function installKind() {
23947
+ if (await isBinary()) return { kind: "binary", execPath: process23.execPath };
23948
+ const argv1 = process23.argv[1];
23949
+ const root2 = await npmRoot();
23950
+ if (root2 !== void 0 && argv1 !== void 0) {
23951
+ const script3 = await realpath2(argv1).catch(() => void 0);
23952
+ if (script3 !== void 0 && isUnder2(script3, root2)) {
23953
+ return { kind: "npm", execPath: process23.execPath, argv1: script3 };
23954
+ }
23955
+ }
23956
+ return { kind: "other", execPath: process23.execPath, argv1 };
23957
+ }
23958
+ async function run21(argv, ctx) {
23959
+ const args = parseCommandArgs(
23960
+ argv,
23961
+ { check: { type: "boolean" }, version: { type: "string" }, force: { type: "boolean" } },
23962
+ 0,
23963
+ 0,
23964
+ UPGRADE_USAGE
23965
+ );
23966
+ const raw = stringValue(args, "version");
23967
+ const wanted = raw === void 0 ? void 0 : parseVersion(raw);
23968
+ const env = process23.env;
23969
+ let result;
23970
+ try {
23971
+ result = await upgrade({
23972
+ current: VERSION,
23973
+ wanted,
23974
+ check: boolValue(args, "check"),
23975
+ force: boolValue(args, "force"),
23976
+ install: await installKind(),
23977
+ fetch,
23978
+ env,
23979
+ home: homeDir(env)
23980
+ });
23981
+ } catch (err) {
23982
+ if (!(err instanceof UpgradeFailure)) throw err;
23983
+ process23.stderr.write(`rshow: ${err.message}
23984
+ `);
23985
+ return err.exit;
23986
+ }
23987
+ const report = reportOf(result);
23988
+ ctx.out.data(report.data);
23989
+ for (const line of report.lines) ctx.out.line(line);
23990
+ return EXIT.ok;
23991
+ }
23992
+
23993
+ // src/commands/logout.ts
23994
+ import process24 from "node:process";
23995
+ async function run22(argv, ctx, platform = process24.platform) {
23432
23996
  const args = parseCommandArgs(argv, { url: { type: "string" } }, 0, 0, LOGOUT_USAGE);
23433
23997
  const settings = await ctx.config();
23434
23998
  const given = (stringValue(args, "url") ?? "").trim();
23435
23999
  const url = (given === "" ? requireUrl(settings) : given).replace(/\/+$/, "");
23436
24000
  const removed = [];
23437
- for (const store2 of await every(process22.env, platform)) {
24001
+ for (const store2 of await every(process24.env, platform)) {
23438
24002
  if (await store2.remove(url)) removed.push(store2.description);
23439
24003
  }
23440
24004
  if (removed.length === 0) {
@@ -23464,14 +24028,15 @@ var COMMANDS = {
23464
24028
  pin: { usage: PIN_USAGE, run: run12 },
23465
24029
  delete: { usage: DELETE_USAGE, run: run13 },
23466
24030
  login: { usage: LOGIN_USAGE, run: run20 },
23467
- logout: { usage: LOGOUT_USAGE, run: run21 },
24031
+ logout: { usage: LOGOUT_USAGE, run: run22 },
24032
+ upgrade: { usage: UPGRADE_USAGE, run: run21 },
23468
24033
  completion: { usage: COMPLETION_USAGE, run: run19 },
23469
24034
  // Kept out of the general help: the docs site runs it, a person rarely does.
23470
24035
  docs: { usage: DOCS_USAGE, run: run18 }
23471
24036
  };
23472
24037
  function fail(err) {
23473
24038
  const message = err instanceof Error ? err.message : String(err);
23474
- process23.stderr.write(`rshow: ${message}
24039
+ process25.stderr.write(`rshow: ${message}
23475
24040
  `);
23476
24041
  if (err instanceof UsageError) return EXIT.usage;
23477
24042
  if (err instanceof CheckError) return EXIT.check;
@@ -23483,39 +24048,40 @@ function fail(err) {
23483
24048
  async function main2(argv) {
23484
24049
  const wantsJson = argv.includes("--json");
23485
24050
  const args = argv.filter((arg) => arg !== "--json");
23486
- const wantsVersion = args.includes("--version") || args.includes("-v");
23487
24051
  const wantsHelp = args.includes("--help") || args.includes("-h");
23488
- const name = args.find((arg) => !arg.startsWith("-"));
24052
+ const commandAt = args.findIndex((arg) => !arg.startsWith("-"));
24053
+ const name = commandAt < 0 ? void 0 : args[commandAt];
23489
24054
  const spec = name === void 0 ? void 0 : COMMANDS[name];
24055
+ const versionAt = args.findIndex((arg) => arg === "--version" || arg === "-v");
24056
+ const wantsVersion = versionAt >= 0 && (commandAt < 0 || versionAt < commandAt);
23490
24057
  if (wantsVersion) {
23491
24058
  const suffix = await isBinary() ? " (binary)" : "";
23492
- process23.stdout.write(`rshow ${VERSION}${suffix}
24059
+ process25.stdout.write(`rshow ${VERSION}${suffix}
23493
24060
  `);
23494
24061
  return EXIT.ok;
23495
24062
  }
23496
24063
  if (wantsHelp) {
23497
- process23.stdout.write(`${spec === void 0 ? GENERAL_USAGE : spec.usage}
24064
+ process25.stdout.write(`${spec === void 0 ? GENERAL_USAGE : spec.usage}
23498
24065
  `);
23499
24066
  return EXIT.ok;
23500
24067
  }
23501
24068
  if (name === void 0) {
23502
- process23.stderr.write(`${GENERAL_USAGE}
24069
+ process25.stderr.write(`${GENERAL_USAGE}
23503
24070
  `);
23504
24071
  return EXIT.usage;
23505
24072
  }
23506
24073
  if (spec === void 0) {
23507
24074
  throw new UsageError(`unknown command: ${name} (try: rshow --help)`);
23508
24075
  }
23509
- const index = args.indexOf(name);
23510
- const rest = [...args.slice(0, index), ...args.slice(index + 1)];
24076
+ const rest = [...args.slice(0, commandAt), ...args.slice(commandAt + 1)];
23511
24077
  const configs = /* @__PURE__ */ new Map();
23512
24078
  const clients = /* @__PURE__ */ new Map();
23513
- const keyOf = (dir) => dir === void 0 ? "" : path26.resolve(dir);
24079
+ const keyOf = (dir) => dir === void 0 ? "" : path28.resolve(dir);
23514
24080
  const configFor = (dir) => {
23515
24081
  const key2 = keyOf(dir);
23516
24082
  let pending = configs.get(key2);
23517
24083
  if (pending === void 0) {
23518
- pending = loadConfig(process23.env, dir);
24084
+ pending = loadConfig(process25.env, dir);
23519
24085
  configs.set(key2, pending);
23520
24086
  }
23521
24087
  return pending;
@@ -23528,7 +24094,7 @@ async function main2(argv) {
23528
24094
  if (cached !== void 0) return cached;
23529
24095
  const settings = await configFor(dir);
23530
24096
  const url = requireUrl(settings);
23531
- const client = new Client(url, await readToken(process23.env, url));
24097
+ const client = new Client(url, await readToken(process25.env, url));
23532
24098
  clients.set(key2, client);
23533
24099
  return client;
23534
24100
  },
@@ -23537,11 +24103,11 @@ async function main2(argv) {
23537
24103
  const code = await spec.run(rest, ctx);
23538
24104
  return code ?? EXIT.ok;
23539
24105
  }
23540
- main2(process23.argv.slice(2)).then(
24106
+ main2(process25.argv.slice(2)).then(
23541
24107
  (code) => {
23542
- process23.exitCode = code;
24108
+ process25.exitCode = code;
23543
24109
  },
23544
24110
  (err) => {
23545
- process23.exitCode = fail(err);
24111
+ process25.exitCode = fail(err);
23546
24112
  }
23547
24113
  );