@penvhq/launcher 0.10.0 → 0.12.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.cjs CHANGED
@@ -24,10 +24,10 @@ __export(src_exports, {
24
24
  AddLocalFlagError: () => AddLocalFlagError,
25
25
  AddLocalInCiError: () => AddLocalInCiError,
26
26
  AddNoDownloadError: () => AddNoDownloadError,
27
- AddNotInteractiveError: () => AddNotInteractiveError,
28
27
  AddPackageNameError: () => AddPackageNameError,
29
28
  AddRegistryError: () => AddRegistryError,
30
29
  AddSubjectError: () => AddSubjectError,
30
+ AddTrustUnattendedError: () => AddTrustUnattendedError,
31
31
  ArchiveError: () => ArchiveError,
32
32
  BUNDLED_ENGINE_PIN: () => BUNDLED_ENGINE_PIN,
33
33
  DEFAULT_REGISTRY: () => DEFAULT_REGISTRY,
@@ -63,7 +63,15 @@ __export(src_exports, {
63
63
  TrustDeclinedError: () => TrustDeclinedError,
64
64
  TrustPublisherMissingError: () => TrustPublisherMissingError,
65
65
  TrustReasonMissingError: () => TrustReasonMissingError,
66
+ UPGRADE_COMMAND: () => UPGRADE_COMMAND,
67
+ UpgradeDeclinedError: () => UpgradeDeclinedError,
68
+ UpgradeFlagError: () => UpgradeFlagError,
69
+ UpgradeInstallFailedError: () => UpgradeInstallFailedError,
70
+ UpgradeNoDownloadError: () => UpgradeNoDownloadError,
71
+ UpgradeSubjectError: () => UpgradeSubjectError,
72
+ UpgradeUnattendedError: () => UpgradeUnattendedError,
66
73
  VersionUnknownError: () => VersionUnknownError,
74
+ YES_FLAG: () => YES_FLAG,
67
75
  add: () => add,
68
76
  assertReleasePin: () => assertReleasePin,
69
77
  bundledEngine: () => bundledEngine,
@@ -87,6 +95,7 @@ __export(src_exports, {
87
95
  runLauncher: () => runLauncher,
88
96
  setProviderType: () => setProviderType,
89
97
  tarballUrl: () => tarballUrl,
98
+ upgrade: () => upgrade,
90
99
  writeDeclaration: () => writeDeclaration
91
100
  });
92
101
  module.exports = __toCommonJS(src_exports);
@@ -268,6 +277,8 @@ var import_core2 = require("@penvhq/core");
268
277
  // src/errors.ts
269
278
  var import_core = require("@penvhq/core");
270
279
  var INSTALL_COMMAND = "penv install";
280
+ var UPGRADE_COMMAND = "penv upgrade";
281
+ var YES_FLAG = "--yes";
271
282
  var MIN_PACKAGE_AGE_DAYS = 7;
272
283
  var TRUST_YOUNG_FLAG = "--trust-young";
273
284
  function addCommand(name, version, extra) {
@@ -373,7 +384,7 @@ var AddFlagError = class extends import_core.PenvError {
373
384
  super(
374
385
  "PENV_ADD_FLAG",
375
386
  `\`penv add\` does not understand \`${flag}\``,
376
- `Run \`penv add <package>\` with \`${TRUST_YOUNG_FLAG}\`, \`--registry <url>\`, or \`${LOCAL_FLAG}\` \u2014 those are the three it takes.`
387
+ `Run \`penv add <package>\` with \`${TRUST_YOUNG_FLAG}\`, \`--registry <url>\`, \`${LOCAL_FLAG}\`, or \`${YES_FLAG}\` \u2014 those are the four it takes.`
377
388
  );
378
389
  }
379
390
  };
@@ -450,11 +461,11 @@ var OfficialRegistryError = class extends import_core.PenvError {
450
461
  };
451
462
  var RegistryUnreadableError = class extends import_core.PenvError {
452
463
  name = "RegistryUnreadableError";
453
- constructor(name, url, detail) {
464
+ constructor(name, url, detail, retry = addCommand(name)) {
454
465
  super(
455
466
  "PENV_REGISTRY_UNREADABLE",
456
467
  `Reading ${name} from ${url} failed: ${detail}`,
457
- `Run \`${addCommand(name)}\` again when the registry is reachable.`
468
+ `Run \`${retry}\` again when the registry is reachable.`
458
469
  );
459
470
  }
460
471
  };
@@ -470,11 +481,11 @@ var PackageUnknownError = class extends import_core.PenvError {
470
481
  };
471
482
  var VersionUnknownError = class extends import_core.PenvError {
472
483
  name = "VersionUnknownError";
473
- constructor(name, version, url) {
484
+ constructor(name, version, url, retry = addCommand(name)) {
474
485
  super(
475
486
  "PENV_VERSION_UNKNOWN",
476
487
  `${url} publishes no ${version} of ${name}`,
477
- `Run \`${addCommand(name)}\` to take the version \`latest\` points at.`
488
+ `Run \`${retry}\` to take the version \`latest\` points at.`
478
489
  );
479
490
  }
480
491
  };
@@ -519,13 +530,13 @@ var AddNoDownloadError = class extends import_core.PenvError {
519
530
  );
520
531
  }
521
532
  };
522
- var AddNotInteractiveError = class extends import_core.PenvError {
523
- name = "AddNotInteractiveError";
524
- constructor(name) {
533
+ var AddTrustUnattendedError = class extends import_core.PenvError {
534
+ name = "AddTrustUnattendedError";
535
+ constructor(name, ceremony) {
525
536
  super(
526
- "PENV_ADD_NOT_INTERACTIVE",
527
- `Adding ${name} rewrites ${import_core.MANIFEST_PATH}, and this run has nobody to decide that`,
528
- `Run \`${addCommand(name)}\` from a terminal and commit what it writes. In CI, run \`${INSTALL_COMMAND}\` \u2014 it installs the versions the committed manifest already pins.`
537
+ "PENV_ADD_TRUST_UNATTENDED",
538
+ `Adding ${name} records ${ceremony}, and this run has nobody to write it`,
539
+ `Run \`${addCommand(name)}\` from a terminal, without \`${YES_FLAG}\` \u2014 the trust block is a line only a person can write. In CI, run \`${INSTALL_COMMAND}\`: it installs the versions the committed manifest already pins.`
529
540
  );
530
541
  }
531
542
  };
@@ -599,6 +610,66 @@ var EnginePinMismatchError = class extends import_core.PenvError {
599
610
  );
600
611
  }
601
612
  };
613
+ var UpgradeSubjectError = class extends import_core.PenvError {
614
+ name = "UpgradeSubjectError";
615
+ constructor() {
616
+ super(
617
+ "PENV_UPGRADE_SUBJECT",
618
+ "`penv upgrade` takes one version, or none",
619
+ `Run \`${UPGRADE_COMMAND}\` to move to whatever \`latest\` points at, or \`${UPGRADE_COMMAND} 0.9.6\` to move to an exact release.`
620
+ );
621
+ }
622
+ };
623
+ var UpgradeFlagError = class extends import_core.PenvError {
624
+ name = "UpgradeFlagError";
625
+ constructor(flag) {
626
+ super(
627
+ "PENV_UPGRADE_FLAG",
628
+ `\`${UPGRADE_COMMAND}\` does not understand \`${flag}\``,
629
+ `Run \`${UPGRADE_COMMAND} [version]\` with \`${YES_FLAG}\` \u2014 that is the one flag it takes.`
630
+ );
631
+ }
632
+ };
633
+ var UpgradeNoDownloadError = class extends import_core.PenvError {
634
+ name = "UpgradeNoDownloadError";
635
+ constructor() {
636
+ super(
637
+ "PENV_UPGRADE_NO_DOWNLOAD",
638
+ `Upgrading means reading the registry for the version and integrity to pin, and \`--no-download\` says this run does not`,
639
+ `Run \`${UPGRADE_COMMAND}\` without \`--no-download\`. Nothing was fetched or written.`
640
+ );
641
+ }
642
+ };
643
+ var UpgradeUnattendedError = class extends import_core.PenvError {
644
+ name = "UpgradeUnattendedError";
645
+ constructor() {
646
+ super(
647
+ "PENV_UPGRADE_UNATTENDED",
648
+ `Upgrading rewrites ${import_core.MANIFEST_PATH} and package.json, and this run has nobody to decide that`,
649
+ `Run \`${UPGRADE_COMMAND} <version> ${YES_FLAG}\` \u2014 unattended, penv moves the pin only to a version you named. In CI, run \`${INSTALL_COMMAND}\`: it installs what the committed manifest already pins.`
650
+ );
651
+ }
652
+ };
653
+ var UpgradeDeclinedError = class extends import_core.PenvError {
654
+ name = "UpgradeDeclinedError";
655
+ constructor(version) {
656
+ super(
657
+ "PENV_UPGRADE_DECLINED",
658
+ `${import_core.ENGINE_PACKAGE} ${version} was not installed, so ${import_core.MANIFEST_PATH} and package.json are unchanged`,
659
+ `Run \`${UPGRADE_COMMAND} ${version}\` again when you want both of them to move.`
660
+ );
661
+ }
662
+ };
663
+ var UpgradeInstallFailedError = class extends import_core.PenvError {
664
+ name = "UpgradeInstallFailedError";
665
+ constructor(manager, version) {
666
+ super(
667
+ "PENV_UPGRADE_INSTALL_FAILED",
668
+ `${manager} did not finish moving this project to ${import_core.ENGINE_PACKAGE} ${version}, so ${import_core.MANIFEST_PATH} still pins the engine it pinned before`,
669
+ `Read what ${manager} printed above \u2014 it names what it refused. Fix that and run \`${UPGRADE_COMMAND} ${version}\` again; the pin and the dependency move together or not at all.`
670
+ );
671
+ }
672
+ };
602
673
  var EngineEntryError = class extends import_core.PenvError {
603
674
  name = "EngineEntryError";
604
675
  constructor(name, version, dir) {
@@ -900,7 +971,8 @@ async function fetchRelease(query) {
900
971
  throw new RegistryUnreadableError(
901
972
  query.name,
902
973
  url,
903
- cause instanceof Error ? cause.message : String(cause)
974
+ cause instanceof Error ? cause.message : String(cause),
975
+ query.retry
904
976
  );
905
977
  }
906
978
  let parsed;
@@ -910,7 +982,8 @@ async function fetchRelease(query) {
910
982
  throw new RegistryUnreadableError(
911
983
  query.name,
912
984
  url,
913
- cause instanceof Error ? cause.message : String(cause)
985
+ cause instanceof Error ? cause.message : String(cause),
986
+ query.retry
914
987
  );
915
988
  }
916
989
  const packument = record(parsed);
@@ -926,7 +999,7 @@ async function fetchRelease(query) {
926
999
  const version = record(at(versions, asked)) === void 0 ? text3(at(tags, asked)) ?? asked : asked;
927
1000
  const release = record(at(versions, version));
928
1001
  if (release === void 0) {
929
- throw new VersionUnknownError(query.name, asked, url);
1002
+ throw new VersionUnknownError(query.name, asked, url, query.retry);
930
1003
  }
931
1004
  const dist = record(at(release, "dist"));
932
1005
  const integrity = text3(at(dist, "integrity"));
@@ -1010,12 +1083,17 @@ function parseRequest(argv) {
1010
1083
  let registry;
1011
1084
  let trustYoung = false;
1012
1085
  let local = false;
1086
+ let yes = false;
1013
1087
  for (let index = 0; index < argv.length; index += 1) {
1014
1088
  const token = argv[index] ?? "";
1015
1089
  if (token === LOCAL_FLAG) {
1016
1090
  local = true;
1017
1091
  continue;
1018
1092
  }
1093
+ if (token === YES_FLAG) {
1094
+ yes = true;
1095
+ continue;
1096
+ }
1019
1097
  if (token === TRUST_YOUNG_FLAG) {
1020
1098
  trustYoung = true;
1021
1099
  continue;
@@ -1055,7 +1133,10 @@ function parseRequest(argv) {
1055
1133
  throw new AddLocalFlagError(`\`${TRUST_YOUNG_FLAG}\``);
1056
1134
  }
1057
1135
  }
1058
- return { name, version, registry, trustYoung, local };
1136
+ return { name, version, registry, trustYoung, local, yes };
1137
+ }
1138
+ function ceremonyFor(tier) {
1139
+ return tier === "official" ? void 0 : "who publishes it and why you trust it";
1059
1140
  }
1060
1141
  function tierOf(request) {
1061
1142
  if (request.name.startsWith(import_core5.OFFICIAL_SCOPE)) {
@@ -1134,7 +1215,29 @@ function recordExtension(manifestFile, name, entry) {
1134
1215
  };
1135
1216
  return (0, import_core5.serializeManifest)(next);
1136
1217
  }
1137
- async function offerConfigEdit(options, name) {
1218
+ var NONE = /* @__PURE__ */ new Set(["", "none", "no", "n"]);
1219
+ var ALL = /* @__PURE__ */ new Set(["all", "a", "yes", "y"]);
1220
+ function chooseEnvironments(answer, offered) {
1221
+ const normalized = answer.trim().toLowerCase();
1222
+ if (NONE.has(normalized)) {
1223
+ return { chosen: [], unknown: [] };
1224
+ }
1225
+ if (ALL.has(normalized)) {
1226
+ return { chosen: offered, unknown: [] };
1227
+ }
1228
+ const chosen = [];
1229
+ const unknown = [];
1230
+ for (const token of normalized.split(/[\s,]+/).filter((part) => part !== "")) {
1231
+ const match = offered.find((environment) => environment.toLowerCase() === token);
1232
+ if (match === void 0) {
1233
+ unknown.push(token);
1234
+ } else {
1235
+ chosen.push(match);
1236
+ }
1237
+ }
1238
+ return { chosen, unknown };
1239
+ }
1240
+ async function offerConfigEdit(options, name, ask) {
1138
1241
  const { io, root } = options;
1139
1242
  const configFile = (0, import_core5.findConfigFile)(root);
1140
1243
  const line = `Add \`type: ${JSON.stringify(name)}\` to an environment in penv.config.ts.`;
@@ -1149,31 +1252,35 @@ async function offerConfigEdit(options, name) {
1149
1252
  io.out(line);
1150
1253
  return;
1151
1254
  }
1152
- if (entries.every((entry) => entry.type === name)) {
1255
+ const offered = entries.filter((entry) => entry.type !== name).map((entry) => entry.environment);
1256
+ if (offered.length === 0) {
1153
1257
  return;
1154
1258
  }
1155
- if (!io.interactive) {
1259
+ if (!ask) {
1156
1260
  io.out(line);
1157
1261
  return;
1158
1262
  }
1159
- for (const entry of entries) {
1160
- if (entry.type === name) {
1161
- continue;
1162
- }
1163
- if (!await io.confirm(`Point \`${entry.environment}\` at ${name} in ${shown}?`)) {
1164
- continue;
1165
- }
1166
- const next = setProviderType(current, entry.environment, name);
1263
+ const { chosen, unknown } = chooseEnvironments(
1264
+ await io.ask(`Point which of ${offered.join(", ")} at ${name} in ${shown}? all / none / names`),
1265
+ offered
1266
+ );
1267
+ if (unknown.length > 0) {
1268
+ io.out(`${shown} declares no ${unknown.join(", ")}, so nothing was repointed.`);
1269
+ io.out(line);
1270
+ return;
1271
+ }
1272
+ for (const environment of chosen) {
1273
+ const next = setProviderType(current, environment, name);
1167
1274
  if (next === void 0) {
1168
- io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${entry.environment}\` in ${shown}.`);
1275
+ io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${environment}\` in ${shown}.`);
1169
1276
  continue;
1170
1277
  }
1171
1278
  current = next;
1172
1279
  (0, import_node_fs4.writeFileSync)(configFile, current);
1173
- io.out(`\u2713 ${shown} points \`${entry.environment}\` at ${name}`);
1280
+ io.out(`\u2713 ${shown} points \`${environment}\` at ${name}`);
1174
1281
  }
1175
1282
  }
1176
- async function offerOnboarding(io, name, onboard) {
1283
+ async function offerOnboarding(io, name, onboard, ask) {
1177
1284
  if (onboard === void 0) {
1178
1285
  return void 0;
1179
1286
  }
@@ -1182,7 +1289,7 @@ async function offerOnboarding(io, name, onboard) {
1182
1289
  return void 0;
1183
1290
  }
1184
1291
  const command = `penv ${args.join(" ")}`;
1185
- if (io.interactive && await io.confirm(`Run \`${command}\` now?`)) {
1292
+ if (ask && await io.confirm(`Run \`${command}\` now?`)) {
1186
1293
  return args;
1187
1294
  }
1188
1295
  io.out(`Run \`${command}\` to finish setting ${name} up.`);
@@ -1263,8 +1370,9 @@ async function addLocal(options, request) {
1263
1370
  io.out(`\u2713 ${request.name} resolves from this project and imports \u2014 nothing is pinned`);
1264
1371
  io.out(`\u2713 ${import_core5.LOCAL_EXTENSIONS_PATH} records it`);
1265
1372
  io.out(`\u2713 ${declaration} declares its config type`);
1266
- await offerConfigEdit(options, request.name);
1267
- return { onboard: await offerOnboarding(io, request.name, installed.onboard) };
1373
+ const ask = io.interactive && !request.yes;
1374
+ await offerConfigEdit(options, request.name, ask);
1375
+ return { onboard: await offerOnboarding(io, request.name, installed.onboard, ask) };
1268
1376
  }
1269
1377
  async function add(options) {
1270
1378
  const { io, fetcher, home, root, manifestFile } = options;
@@ -1274,11 +1382,13 @@ async function add(options) {
1274
1382
  }
1275
1383
  const tier = tierOf(request);
1276
1384
  const now = (options.now ?? (() => /* @__PURE__ */ new Date()))();
1385
+ const ask = io.interactive && !request.yes;
1277
1386
  if (options.noDownload === true) {
1278
1387
  throw new AddNoDownloadError(request.name);
1279
1388
  }
1280
- if (options.ci === true || !io.interactive) {
1281
- throw new AddNotInteractiveError(request.name);
1389
+ const ceremony = ceremonyFor(tier);
1390
+ if (ceremony !== void 0 && (options.ci === true || !ask)) {
1391
+ throw new AddTrustUnattendedError(request.name, ceremony);
1282
1392
  }
1283
1393
  const release = await fetchRelease({
1284
1394
  name: request.name,
@@ -1320,8 +1430,8 @@ async function add(options) {
1320
1430
  io.out(`\u2713 ${release.name} ${release.version} installed \u2014 ${provenance}`);
1321
1431
  io.out(`\u2713 ${import_core5.MANIFEST_PATH} pins it`);
1322
1432
  io.out(`\u2713 ${declaration} declares its config type`);
1323
- await offerConfigEdit(options, release.name);
1324
- return { onboard: await offerOnboarding(io, release.name, installed.onboard) };
1433
+ await offerConfigEdit(options, release.name, ask);
1434
+ return { onboard: await offerOnboarding(io, release.name, installed.onboard, ask) };
1325
1435
  }
1326
1436
 
1327
1437
  // src/delegate.ts
@@ -1436,11 +1546,12 @@ function httpFetcher() {
1436
1546
  }
1437
1547
 
1438
1548
  // src/launcher.ts
1439
- var import_node_fs7 = require("fs");
1549
+ var import_node_fs8 = require("fs");
1440
1550
  var import_node_path7 = require("path");
1441
- var import_core10 = require("@penvhq/core");
1551
+ var import_core11 = require("@penvhq/core");
1442
1552
 
1443
1553
  // src/help.ts
1554
+ var import_install = require("@penvhq/cli/install");
1444
1555
  var import_core7 = require("@penvhq/core");
1445
1556
  function printLauncherCommands(io) {
1446
1557
  io.out("");
@@ -1449,6 +1560,7 @@ function printLauncherCommands(io) {
1449
1560
  io.out(` install Installs everything ${import_core7.MANIFEST_PATH} pins`);
1450
1561
  io.out(" add <package> Pins a provider extension, and commits the decision");
1451
1562
  io.out(` add ${LOCAL_FLAG} <package> Adds the one this repository builds \u2014 nothing is pinned`);
1563
+ io.out(` upgrade [version] Moves the engine pin and ${import_install.RUNTIME_PACKAGE} together`);
1452
1564
  io.out("");
1453
1565
  }
1454
1566
  function printInstallHelp(io) {
@@ -1466,10 +1578,26 @@ function printAddHelp(io) {
1466
1578
  io.out(
1467
1579
  ` ${TRUST_YOUNG_FLAG} Add a release published less than ${MIN_PACKAGE_AGE_DAYS} days ago`
1468
1580
  );
1581
+ io.out(` ${YES_FLAG} Ask nothing \u2014 the config and onboarding offers print instead`);
1469
1582
  io.out("");
1470
1583
  io.out(`Pins the release in ${import_core7.MANIFEST_PATH} and writes its type declaration to`);
1471
1584
  io.out(`${import_core7.EXTENSIONS_PATH}/. Both are committed, so \`${INSTALL_COMMAND}\` gives every machine`);
1472
1585
  io.out(`the bytes you reviewed. \`${LOCAL_FLAG}\` pins nothing and touches no manifest.`);
1586
+ io.out("");
1587
+ io.out(`An \`${import_core7.OFFICIAL_SCOPE}*\` package asks nothing, so it adds unattended. Anything else`);
1588
+ io.out("records who publishes it and why you trust it, which needs a person at a terminal \u2014");
1589
+ io.out(`\`${YES_FLAG}\` cannot write that line for you.`);
1590
+ }
1591
+ function printUpgradeHelp(io) {
1592
+ io.out("penv upgrade [version]");
1593
+ io.out("");
1594
+ io.out(` ${YES_FLAG} Skip the question \u2014 unattended runs need it and a version`);
1595
+ io.out("");
1596
+ io.out(`Moves the ${import_core7.ENGINE_PACKAGE} pin in ${import_core7.MANIFEST_PATH} and this project's`);
1597
+ io.out(`${import_install.RUNTIME_PACKAGE} dependency to the same exact version. No version takes whatever`);
1598
+ io.out("`latest` points at; the integrity is the one the registry states, never one penv");
1599
+ io.out("computed. Both files are shown before either moves, and they move together or not");
1600
+ io.out("at all. Extensions keep their own pins.");
1473
1601
  }
1474
1602
 
1475
1603
  // src/pins.ts
@@ -1478,8 +1606,8 @@ var DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
1478
1606
  var DEV_PIN_VERSION = "0.0.0-dev";
1479
1607
  var BUNDLED_ENGINE_PIN = {
1480
1608
  package: import_core8.ENGINE_PACKAGE,
1481
- version: "0.10.0",
1482
- integrity: "sha512-b+U0c3xWbk1ILtMfPjJW8x5XBxSLAaw/IAOLMBmGvT2uuYv41JPED8PrenEj4s3ZJY5BxpWU9mQcJHEBouaw/Q=="
1609
+ version: "0.12.0",
1610
+ integrity: "sha512-9HUylEIbFq0BGTOW54Vxhkzv8AvuB7H3bImTFP5J+dfglkR7udP2KUsr8b0I2mf45YMTQO5xsYpI5+nhLERGtA=="
1483
1611
  };
1484
1612
  function assertReleasePin(pin) {
1485
1613
  if (pin.integrity === DEV_PIN_INTEGRITY || pin.version === DEV_PIN_VERSION) {
@@ -1520,9 +1648,112 @@ function findAdoptedRoot(cwd) {
1520
1648
  return findUp(cwd, (dir) => (0, import_node_fs6.existsSync)((0, import_core9.stateDir)(dir)));
1521
1649
  }
1522
1650
 
1651
+ // src/upgrade.ts
1652
+ var import_node_fs7 = require("fs");
1653
+ var import_install2 = require("@penvhq/cli/install");
1654
+ var import_core10 = require("@penvhq/core");
1655
+ function parseRequest2(argv) {
1656
+ let version;
1657
+ let yes = false;
1658
+ for (const token of argv) {
1659
+ if (token === YES_FLAG) {
1660
+ yes = true;
1661
+ continue;
1662
+ }
1663
+ if (token.startsWith("-")) {
1664
+ throw new UpgradeFlagError(token);
1665
+ }
1666
+ if (version !== void 0 || token === "") {
1667
+ throw new UpgradeSubjectError();
1668
+ }
1669
+ version = token;
1670
+ }
1671
+ return { version, yes };
1672
+ }
1673
+ function renderPinChange(engine, release) {
1674
+ return [
1675
+ import_core10.MANIFEST_PATH,
1676
+ ` - "integrity": "${engine.integrity}"`,
1677
+ ` - "version": "${engine.version}"`,
1678
+ ` + "integrity": "${release.integrity}"`,
1679
+ ` + "version": "${release.version}"`
1680
+ ];
1681
+ }
1682
+ function pinnedElsewhere(manifest, version) {
1683
+ return Object.entries(manifest.extensions).filter(([, entry]) => entry.version !== version).map(([name]) => name);
1684
+ }
1685
+ async function consent(options, release, plan, yes) {
1686
+ const { io, manifest } = options;
1687
+ io.out(`${import_core10.ENGINE_PACKAGE} ${manifest.engine.version} \u2192 ${release.version}`);
1688
+ io.out("");
1689
+ for (const line of renderPinChange(manifest.engine, release)) {
1690
+ io.out(line);
1691
+ }
1692
+ for (const line of (0, import_install2.renderInstallPlan)(plan)) {
1693
+ io.out(line);
1694
+ }
1695
+ if (yes) {
1696
+ return;
1697
+ }
1698
+ if (!await io.confirm(`Move this project to ${release.version}?`)) {
1699
+ throw new UpgradeDeclinedError(release.version);
1700
+ }
1701
+ }
1702
+ async function upgrade(options) {
1703
+ const { io, fetcher, home, root, manifest, manifestFile } = options;
1704
+ const request = parseRequest2(options.argv);
1705
+ if (options.noDownload === true) {
1706
+ throw new UpgradeNoDownloadError();
1707
+ }
1708
+ if ((options.ci === true || !io.interactive) && !(request.yes && request.version !== void 0)) {
1709
+ throw new UpgradeUnattendedError();
1710
+ }
1711
+ const release = await fetchRelease({
1712
+ name: import_core10.ENGINE_PACKAGE,
1713
+ ...request.version === void 0 ? {} : { version: request.version },
1714
+ retry: UPGRADE_COMMAND,
1715
+ fetcher
1716
+ });
1717
+ const plan = (0, import_install2.planInstall)(root, release.version);
1718
+ if (manifest.engine.version === release.version && plan.satisfied) {
1719
+ io.out(`\u2713 Already on ${import_core10.ENGINE_PACKAGE} ${release.version} \u2014 nothing to move`);
1720
+ return;
1721
+ }
1722
+ const manifestText = (0, import_core10.serializeManifest)({
1723
+ ...manifest,
1724
+ engine: { package: import_core10.ENGINE_PACKAGE, version: release.version, integrity: release.integrity }
1725
+ });
1726
+ await consent(options, release, plan, request.yes);
1727
+ await installPin({
1728
+ home,
1729
+ kind: "engines",
1730
+ pin: { name: import_core10.ENGINE_PACKAGE, version: release.version, integrity: release.integrity },
1731
+ fetcher
1732
+ });
1733
+ io.out(`\u2713 ${import_core10.ENGINE_PACKAGE} ${release.version} installed`);
1734
+ if (!plan.satisfied) {
1735
+ try {
1736
+ await (options.install ?? import_install2.installWithPackageManager)(plan);
1737
+ } catch {
1738
+ throw new UpgradeInstallFailedError(plan.manager, release.version);
1739
+ }
1740
+ for (const step of plan.steps.filter((entry) => !entry.satisfied)) {
1741
+ io.out(`\u2713 ${step.manifest} depends on ${import_install2.RUNTIME_PACKAGE} ${release.version}`);
1742
+ }
1743
+ }
1744
+ (0, import_node_fs7.writeFileSync)(manifestFile, manifestText);
1745
+ io.out(`\u2713 ${import_core10.MANIFEST_PATH} pins ${import_core10.ENGINE_PACKAGE} ${release.version}`);
1746
+ const kept = pinnedElsewhere(manifest, release.version);
1747
+ if (kept.length > 0) {
1748
+ const subject = kept.length === 1 ? "keeps its own pin" : "keep their own pins";
1749
+ io.out(`\u2192 ${kept.join(", ")} ${subject} \u2014 \`penv add <package>@<version>\` moves one.`);
1750
+ }
1751
+ }
1752
+
1523
1753
  // src/launcher.ts
1524
1754
  var INSTALL = "install";
1525
1755
  var ADD = "add";
1756
+ var UPGRADE = "upgrade";
1526
1757
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["--version", "-v"]);
1527
1758
  var HELP_FLAGS = /* @__PURE__ */ new Set(["--help", "-h"]);
1528
1759
  var NO_DOWNLOAD = "--no-download";
@@ -1566,27 +1797,27 @@ function pinsOf(manifest) {
1566
1797
  ];
1567
1798
  }
1568
1799
  function reportable(error, home, argv) {
1569
- return error instanceof import_core10.UnsupportedManifestFormatError ? error.withLauncherUpdate({
1800
+ return error instanceof import_core11.UnsupportedManifestFormatError ? error.withLauncherUpdate({
1570
1801
  updateCommand: launcherUpdateCommand(home),
1571
1802
  invokedCommand: invokedCommand(argv)
1572
1803
  }) : error;
1573
1804
  }
1574
1805
  function readManifest(manifestFile, home, argv) {
1575
1806
  try {
1576
- return (0, import_core10.parseManifest)((0, import_node_fs7.readFileSync)(manifestFile, "utf8"));
1807
+ return (0, import_core11.parseManifest)((0, import_node_fs8.readFileSync)(manifestFile, "utf8"));
1577
1808
  } catch (error) {
1578
1809
  throw reportable(error, home, argv);
1579
1810
  }
1580
1811
  }
1581
1812
  function readManifestToRepair(manifestFile, home, argv) {
1582
1813
  try {
1583
- return readManifestForRepair((0, import_node_fs7.readFileSync)(manifestFile, "utf8"));
1814
+ return readManifestForRepair((0, import_node_fs8.readFileSync)(manifestFile, "utf8"));
1584
1815
  } catch (error) {
1585
1816
  throw reportable(error, home, argv);
1586
1817
  }
1587
1818
  }
1588
1819
  function report(error, io) {
1589
- if (error instanceof import_core10.PenvError && error.remedy !== void 0) {
1820
+ if (error instanceof import_core11.PenvError && error.remedy !== void 0) {
1590
1821
  const suffix = `
1591
1822
  ${error.remedy}`;
1592
1823
  const message = error.message.endsWith(suffix) ? error.message.slice(0, -suffix.length) : error.message;
@@ -1608,7 +1839,7 @@ async function launch(options) {
1608
1839
  const { argv, cwd, env, io } = options;
1609
1840
  const { noDownload, forwarded } = splitLauncherFlags(argv);
1610
1841
  const first = forwarded[0];
1611
- const home = (0, import_core10.penvHome)(env);
1842
+ const home = (0, import_core11.penvHome)(env);
1612
1843
  if (first !== void 0 && forwarded.slice(1).some((token) => HELP_FLAGS.has(token))) {
1613
1844
  if (first === INSTALL) {
1614
1845
  printInstallHelp(io);
@@ -1618,6 +1849,10 @@ async function launch(options) {
1618
1849
  printAddHelp(io);
1619
1850
  return 0;
1620
1851
  }
1852
+ if (first === UPGRADE) {
1853
+ printUpgradeHelp(io);
1854
+ return 0;
1855
+ }
1621
1856
  }
1622
1857
  const project = findProject(cwd);
1623
1858
  if (project === void 0) {
@@ -1645,6 +1880,20 @@ async function launch(options) {
1645
1880
  io.out(`penv ${manifest.engine.version}`);
1646
1881
  return 0;
1647
1882
  }
1883
+ if (first === UPGRADE) {
1884
+ await upgrade({
1885
+ argv: forwarded.slice(1),
1886
+ root: project.root,
1887
+ manifestFile: project.manifestFile,
1888
+ manifest,
1889
+ home,
1890
+ io,
1891
+ fetcher: options.fetcher,
1892
+ noDownload,
1893
+ ci: isCi(env)
1894
+ });
1895
+ return 0;
1896
+ }
1648
1897
  const enginePin = enginePinOf(manifest);
1649
1898
  const engineDir = await ensure(options, "engines", enginePin, home, noDownload);
1650
1899
  for (const pin of extensionPinsOf(manifest)) {
@@ -1663,16 +1912,16 @@ async function adopt(options, forwarded, home, cwd, noDownload) {
1663
1912
  if (root === void 0) {
1664
1913
  return 0;
1665
1914
  }
1666
- const manifestFile = (0, import_node_path7.join)(root, ...import_core10.MANIFEST_PATH.split("/"));
1667
- if ((0, import_node_fs7.existsSync)(manifestFile)) {
1915
+ const manifestFile = (0, import_node_path7.join)(root, ...import_core11.MANIFEST_PATH.split("/"));
1916
+ if ((0, import_node_fs8.existsSync)(manifestFile)) {
1668
1917
  return 0;
1669
1918
  }
1670
1919
  const pin = releaseEnginePin(options.bundledPin, engine.version);
1671
- (0, import_node_fs7.writeFileSync)(
1920
+ (0, import_node_fs8.writeFileSync)(
1672
1921
  manifestFile,
1673
- (0, import_core10.serializeManifest)({ format: import_core10.MANIFEST_FORMAT, engine: pin, extensions: {} })
1922
+ (0, import_core11.serializeManifest)({ format: import_core11.MANIFEST_FORMAT, engine: pin, extensions: {} })
1674
1923
  );
1675
- options.io.out(`\u2713 ${import_core10.MANIFEST_PATH} pins ${pin.package} ${pin.version}`);
1924
+ options.io.out(`\u2713 ${import_core11.MANIFEST_PATH} pins ${pin.package} ${pin.version}`);
1676
1925
  await ensureAdoptedEngine(
1677
1926
  options,
1678
1927
  { name: pin.package, version: pin.version, integrity: pin.integrity },
@@ -1719,12 +1968,14 @@ async function install(options, pins, home, broken) {
1719
1968
  if (state === "corrupt") {
1720
1969
  throw new PackageCorruptError(pin.name, pin.version, dir);
1721
1970
  }
1722
- if (state === "installed") {
1723
- options.io.out(`\u2713 ${pin.name} ${pin.version} already installed`);
1724
- continue;
1971
+ if (state === "absent") {
1972
+ await installPin({ home, kind, pin, fetcher: options.fetcher });
1973
+ }
1974
+ if (kind === "extensions") {
1975
+ await assertLoadable(pin.name, (0, import_core11.packageEntry)(dir), false);
1725
1976
  }
1726
- await installPin({ home, kind, pin, fetcher: options.fetcher });
1727
- options.io.out(`\u2713 ${pin.name} ${pin.version} installed`);
1977
+ const verb = state === "absent" ? "installed" : "already installed";
1978
+ options.io.out(`\u2713 ${pin.name} ${pin.version} ${verb}`);
1728
1979
  }
1729
1980
  if (broken.length > 0) {
1730
1981
  report(new ManifestEntriesUnreadableError(broken), options.io);
@@ -1760,7 +2011,7 @@ async function delegate(options, engine, forwarded, home, cwd) {
1760
2011
  command: process.execPath,
1761
2012
  args: [engine.entry, ...forwarded],
1762
2013
  cwd,
1763
- env: { ...options.env, [import_core10.PENV_HOME_VAR]: home }
2014
+ env: { ...options.env, [import_core11.PENV_HOME_VAR]: home }
1764
2015
  });
1765
2016
  if (code === 0 && isRootHelp(forwarded)) {
1766
2017
  printLauncherCommands(options.io);
@@ -1773,10 +2024,10 @@ async function delegate(options, engine, forwarded, home, cwd) {
1773
2024
  AddLocalFlagError,
1774
2025
  AddLocalInCiError,
1775
2026
  AddNoDownloadError,
1776
- AddNotInteractiveError,
1777
2027
  AddPackageNameError,
1778
2028
  AddRegistryError,
1779
2029
  AddSubjectError,
2030
+ AddTrustUnattendedError,
1780
2031
  ArchiveError,
1781
2032
  BUNDLED_ENGINE_PIN,
1782
2033
  DEFAULT_REGISTRY,
@@ -1812,7 +2063,15 @@ async function delegate(options, engine, forwarded, home, cwd) {
1812
2063
  TrustDeclinedError,
1813
2064
  TrustPublisherMissingError,
1814
2065
  TrustReasonMissingError,
2066
+ UPGRADE_COMMAND,
2067
+ UpgradeDeclinedError,
2068
+ UpgradeFlagError,
2069
+ UpgradeInstallFailedError,
2070
+ UpgradeNoDownloadError,
2071
+ UpgradeSubjectError,
2072
+ UpgradeUnattendedError,
1815
2073
  VersionUnknownError,
2074
+ YES_FLAG,
1816
2075
  add,
1817
2076
  assertReleasePin,
1818
2077
  bundledEngine,
@@ -1836,6 +2095,7 @@ async function delegate(options, engine, forwarded, home, cwd) {
1836
2095
  runLauncher,
1837
2096
  setProviderType,
1838
2097
  tarballUrl,
2098
+ upgrade,
1839
2099
  writeDeclaration
1840
2100
  });
1841
2101
  //# sourceMappingURL=index.cjs.map