@penvhq/launcher 0.11.0 → 0.13.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/bin.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  httpFetcher,
6
6
  nodeSpawner,
7
7
  runLauncher
8
- } from "./chunk-USYQEKPW.js";
8
+ } from "./chunk-QOQVOYJC.js";
9
9
 
10
10
  // src/bin.ts
11
11
  function readLine() {
@@ -277,7 +277,7 @@ var AddFlagError = class extends PenvError {
277
277
  super(
278
278
  "PENV_ADD_FLAG",
279
279
  `\`penv add\` does not understand \`${flag}\``,
280
- `Run \`penv add <package>\` with \`${TRUST_YOUNG_FLAG}\`, \`--registry <url>\`, or \`${LOCAL_FLAG}\` \u2014 those are the three it takes.`
280
+ `Run \`penv add <package>\` with \`${TRUST_YOUNG_FLAG}\`, \`--registry <url>\`, \`${LOCAL_FLAG}\`, or \`${YES_FLAG}\` \u2014 those are the four it takes.`
281
281
  );
282
282
  }
283
283
  };
@@ -423,13 +423,13 @@ var AddNoDownloadError = class extends PenvError {
423
423
  );
424
424
  }
425
425
  };
426
- var AddNotInteractiveError = class extends PenvError {
427
- name = "AddNotInteractiveError";
428
- constructor(name) {
426
+ var AddTrustUnattendedError = class extends PenvError {
427
+ name = "AddTrustUnattendedError";
428
+ constructor(name, ceremony) {
429
429
  super(
430
- "PENV_ADD_NOT_INTERACTIVE",
431
- `Adding ${name} rewrites ${MANIFEST_PATH}, and this run has nobody to decide that`,
432
- `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.`
430
+ "PENV_ADD_TRUST_UNATTENDED",
431
+ `Adding ${name} records ${ceremony}, and this run has nobody to write it`,
432
+ `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.`
433
433
  );
434
434
  }
435
435
  };
@@ -555,11 +555,11 @@ var UpgradeDeclinedError = class extends PenvError {
555
555
  };
556
556
  var UpgradeInstallFailedError = class extends PenvError {
557
557
  name = "UpgradeInstallFailedError";
558
- constructor(command) {
558
+ constructor(manager, version) {
559
559
  super(
560
560
  "PENV_UPGRADE_INSTALL_FAILED",
561
- `${command} did not finish, so ${MANIFEST_PATH} still pins the engine it pinned before`,
562
- `Run \`${command}\` yourself, then run \`${UPGRADE_COMMAND}\` again \u2014 the pin and the dependency move together or not at all.`
561
+ `${manager} did not finish moving this project to ${ENGINE_PACKAGE} ${version}, so ${MANIFEST_PATH} still pins the engine it pinned before`,
562
+ `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.`
563
563
  );
564
564
  }
565
565
  };
@@ -1003,12 +1003,17 @@ function parseRequest(argv) {
1003
1003
  let registry;
1004
1004
  let trustYoung = false;
1005
1005
  let local = false;
1006
+ let yes = false;
1006
1007
  for (let index = 0; index < argv.length; index += 1) {
1007
1008
  const token = argv[index] ?? "";
1008
1009
  if (token === LOCAL_FLAG) {
1009
1010
  local = true;
1010
1011
  continue;
1011
1012
  }
1013
+ if (token === YES_FLAG) {
1014
+ yes = true;
1015
+ continue;
1016
+ }
1012
1017
  if (token === TRUST_YOUNG_FLAG) {
1013
1018
  trustYoung = true;
1014
1019
  continue;
@@ -1048,7 +1053,10 @@ function parseRequest(argv) {
1048
1053
  throw new AddLocalFlagError(`\`${TRUST_YOUNG_FLAG}\``);
1049
1054
  }
1050
1055
  }
1051
- return { name, version, registry, trustYoung, local };
1056
+ return { name, version, registry, trustYoung, local, yes };
1057
+ }
1058
+ function ceremonyFor(tier) {
1059
+ return tier === "official" ? void 0 : "who publishes it and why you trust it";
1052
1060
  }
1053
1061
  function tierOf(request) {
1054
1062
  if (request.name.startsWith(OFFICIAL_SCOPE2)) {
@@ -1127,46 +1135,84 @@ function recordExtension(manifestFile, name, entry) {
1127
1135
  };
1128
1136
  return serializeManifest(next);
1129
1137
  }
1130
- async function offerConfigEdit(options, name) {
1138
+ var NONE = /* @__PURE__ */ new Set(["", "none", "no", "n"]);
1139
+ var ALL = /* @__PURE__ */ new Set(["all", "a", "yes", "y"]);
1140
+ function chooseEnvironments(answer, offered) {
1141
+ const normalized = answer.trim().toLowerCase();
1142
+ if (NONE.has(normalized)) {
1143
+ return { chosen: [], unknown: [] };
1144
+ }
1145
+ if (ALL.has(normalized)) {
1146
+ return { chosen: offered, unknown: [] };
1147
+ }
1148
+ const chosen = [];
1149
+ const unknown = [];
1150
+ for (const token of normalized.split(/[\s,]+/).filter((part) => part !== "")) {
1151
+ const match = offered.find((environment) => environment.toLowerCase() === token);
1152
+ if (match === void 0) {
1153
+ unknown.push(token);
1154
+ } else {
1155
+ chosen.push(match);
1156
+ }
1157
+ }
1158
+ return { chosen, unknown };
1159
+ }
1160
+ function quoted(environments) {
1161
+ return environments.map((environment) => `\`${environment}\``).join(", ");
1162
+ }
1163
+ function configAdvice(name, pointing, unpointed) {
1164
+ const add2 = `Add \`type: ${JSON.stringify(name)}\``;
1165
+ if (pointing.length === 0) {
1166
+ return `${add2} to an environment in penv.config.ts.`;
1167
+ }
1168
+ return `${quoted(pointing)} already ${pointing.length === 1 ? "points" : "point"} at ${name}. ${add2} to ${quoted(unpointed)} in penv.config.ts if ${unpointed.length === 1 ? "it" : "they"} should too.`;
1169
+ }
1170
+ async function offerConfigEdit(options, name, ask) {
1131
1171
  const { io, root } = options;
1132
1172
  const configFile = findConfigFile(root);
1133
- const line = `Add \`type: ${JSON.stringify(name)}\` to an environment in penv.config.ts.`;
1173
+ const blind = `Add \`type: ${JSON.stringify(name)}\` to an environment in penv.config.ts.`;
1134
1174
  if (configFile === void 0) {
1135
- io.out(line);
1175
+ io.out(blind);
1136
1176
  return;
1137
1177
  }
1138
1178
  const shown = relative(root, configFile).split(sep2).join("/");
1139
1179
  let current = readFileSync4(configFile, "utf8");
1140
1180
  const entries = readProviderEntries(current);
1141
1181
  if (entries === void 0 || entries.length === 0) {
1142
- io.out(line);
1182
+ io.out(blind);
1143
1183
  return;
1144
1184
  }
1145
- if (entries.every((entry) => entry.type === name)) {
1185
+ const pointing = entries.filter((entry) => entry.type === name).map((entry) => entry.environment);
1186
+ const offered = entries.filter((entry) => entry.type !== name).map((entry) => entry.environment);
1187
+ if (offered.length === 0) {
1146
1188
  return;
1147
1189
  }
1148
- if (!io.interactive) {
1149
- io.out(line);
1190
+ const advice = configAdvice(name, pointing, offered);
1191
+ if (!ask) {
1192
+ io.out(advice);
1150
1193
  return;
1151
1194
  }
1152
- for (const entry of entries) {
1153
- if (entry.type === name) {
1154
- continue;
1155
- }
1156
- if (!await io.confirm(`Point \`${entry.environment}\` at ${name} in ${shown}?`)) {
1157
- continue;
1158
- }
1159
- const next = setProviderType(current, entry.environment, name);
1195
+ const { chosen, unknown } = chooseEnvironments(
1196
+ await io.ask(`Point which of ${offered.join(", ")} at ${name} in ${shown}? all / none / names`),
1197
+ offered
1198
+ );
1199
+ if (unknown.length > 0) {
1200
+ io.out(`${shown} declares no ${unknown.join(", ")}, so nothing was repointed.`);
1201
+ io.out(advice);
1202
+ return;
1203
+ }
1204
+ for (const environment of chosen) {
1205
+ const next = setProviderType(current, environment, name);
1160
1206
  if (next === void 0) {
1161
- io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${entry.environment}\` in ${shown}.`);
1207
+ io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${environment}\` in ${shown}.`);
1162
1208
  continue;
1163
1209
  }
1164
1210
  current = next;
1165
1211
  writeFileSync3(configFile, current);
1166
- io.out(`\u2713 ${shown} points \`${entry.environment}\` at ${name}`);
1212
+ io.out(`\u2713 ${shown} points \`${environment}\` at ${name}`);
1167
1213
  }
1168
1214
  }
1169
- async function offerOnboarding(io, name, onboard) {
1215
+ async function offerOnboarding(io, name, onboard, ask) {
1170
1216
  if (onboard === void 0) {
1171
1217
  return void 0;
1172
1218
  }
@@ -1175,7 +1221,7 @@ async function offerOnboarding(io, name, onboard) {
1175
1221
  return void 0;
1176
1222
  }
1177
1223
  const command = `penv ${args.join(" ")}`;
1178
- if (io.interactive && await io.confirm(`Run \`${command}\` now?`)) {
1224
+ if (ask && await io.confirm(`Run \`${command}\` now?`)) {
1179
1225
  return args;
1180
1226
  }
1181
1227
  io.out(`Run \`${command}\` to finish setting ${name} up.`);
@@ -1256,8 +1302,9 @@ async function addLocal(options, request) {
1256
1302
  io.out(`\u2713 ${request.name} resolves from this project and imports \u2014 nothing is pinned`);
1257
1303
  io.out(`\u2713 ${LOCAL_EXTENSIONS_PATH2} records it`);
1258
1304
  io.out(`\u2713 ${declaration} declares its config type`);
1259
- await offerConfigEdit(options, request.name);
1260
- return { onboard: await offerOnboarding(io, request.name, installed.onboard) };
1305
+ const ask = io.interactive && !request.yes;
1306
+ await offerConfigEdit(options, request.name, ask);
1307
+ return { onboard: await offerOnboarding(io, request.name, installed.onboard, ask) };
1261
1308
  }
1262
1309
  async function add(options) {
1263
1310
  const { io, fetcher, home, root, manifestFile } = options;
@@ -1267,11 +1314,13 @@ async function add(options) {
1267
1314
  }
1268
1315
  const tier = tierOf(request);
1269
1316
  const now = (options.now ?? (() => /* @__PURE__ */ new Date()))();
1317
+ const ask = io.interactive && !request.yes;
1270
1318
  if (options.noDownload === true) {
1271
1319
  throw new AddNoDownloadError(request.name);
1272
1320
  }
1273
- if (options.ci === true || !io.interactive) {
1274
- throw new AddNotInteractiveError(request.name);
1321
+ const ceremony = ceremonyFor(tier);
1322
+ if (ceremony !== void 0 && (options.ci === true || !ask)) {
1323
+ throw new AddTrustUnattendedError(request.name, ceremony);
1275
1324
  }
1276
1325
  const release = await fetchRelease({
1277
1326
  name: request.name,
@@ -1313,8 +1362,8 @@ async function add(options) {
1313
1362
  io.out(`\u2713 ${release.name} ${release.version} installed \u2014 ${provenance}`);
1314
1363
  io.out(`\u2713 ${MANIFEST_PATH2} pins it`);
1315
1364
  io.out(`\u2713 ${declaration} declares its config type`);
1316
- await offerConfigEdit(options, release.name);
1317
- return { onboard: await offerOnboarding(io, release.name, installed.onboard) };
1365
+ await offerConfigEdit(options, release.name, ask);
1366
+ return { onboard: await offerOnboarding(io, release.name, installed.onboard, ask) };
1318
1367
  }
1319
1368
 
1320
1369
  // src/delegate.ts
@@ -1363,12 +1412,12 @@ function nodeSpawner() {
1363
1412
  // src/engine.ts
1364
1413
  import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
1365
1414
  import { createRequire as createRequire2 } from "module";
1366
- import { dirname as dirname4, join as join5, resolve as resolve3, sep as sep3 } from "path";
1415
+ import { dirname as dirname4, join as join4, resolve as resolve3, sep as sep3 } from "path";
1367
1416
  import { ENGINE_PACKAGE as ENGINE_PACKAGE2, PenvError as PenvError2 } from "@penvhq/core";
1368
1417
  var ENGINE_BIN = "penv-engine";
1369
1418
  function readPackageManifest(dir) {
1370
1419
  try {
1371
- return JSON.parse(readFileSync5(join5(dir, "package.json"), "utf8"));
1420
+ return JSON.parse(readFileSync5(join4(dir, "package.json"), "utf8"));
1372
1421
  } catch {
1373
1422
  return void 0;
1374
1423
  }
@@ -1433,8 +1482,8 @@ var DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
1433
1482
  var DEV_PIN_VERSION = "0.0.0-dev";
1434
1483
  var BUNDLED_ENGINE_PIN = {
1435
1484
  package: ENGINE_PACKAGE3,
1436
- version: "0.11.0",
1437
- integrity: "sha512-Y+WKyuPsX4U8/1j0e+VMUhj0LaGrp67YEoDAWyyinrgA+c8IM4H1blqje/YK4LsQwzQDQQDF9bhWIhfmDAWoxA=="
1485
+ version: "0.13.0",
1486
+ integrity: "sha512-gZ2AlzxmQ4c83OvWSRrBdVMAQdwcGqAoENSv4ZNyZuer0fpIEqiT/xUl2b1+t5JSW8EZto5JpilmBSfiNoWFWA=="
1438
1487
  };
1439
1488
  function assertReleasePin(pin) {
1440
1489
  if (pin.integrity === DEV_PIN_INTEGRITY || pin.version === DEV_PIN_VERSION) {
@@ -1451,7 +1500,7 @@ function releaseEnginePin(pin, ranVersion) {
1451
1500
 
1452
1501
  // src/project.ts
1453
1502
  import { existsSync as existsSync3 } from "fs";
1454
- import { dirname as dirname5, join as join6, resolve as resolve4 } from "path";
1503
+ import { dirname as dirname5, join as join5, resolve as resolve4 } from "path";
1455
1504
  import { MANIFEST_PATH as MANIFEST_PATH3, stateDir } from "@penvhq/core";
1456
1505
  var MANIFEST_SEGMENTS = MANIFEST_PATH3.split("/");
1457
1506
  function findUp(cwd, matches) {
@@ -1468,8 +1517,8 @@ function findUp(cwd, matches) {
1468
1517
  }
1469
1518
  }
1470
1519
  function findProject(cwd) {
1471
- const root = findUp(cwd, (dir) => existsSync3(join6(dir, ...MANIFEST_SEGMENTS)));
1472
- return root === void 0 ? void 0 : { root, manifestFile: join6(root, ...MANIFEST_SEGMENTS) };
1520
+ const root = findUp(cwd, (dir) => existsSync3(join5(dir, ...MANIFEST_SEGMENTS)));
1521
+ return root === void 0 ? void 0 : { root, manifestFile: join5(root, ...MANIFEST_SEGMENTS) };
1473
1522
  }
1474
1523
  function findAdoptedRoot(cwd) {
1475
1524
  return findUp(cwd, (dir) => existsSync3(stateDir(dir)));
@@ -1567,9 +1616,14 @@ async function upgrade(options) {
1567
1616
  try {
1568
1617
  await (options.install ?? installWithPackageManager)(plan);
1569
1618
  } catch {
1570
- throw new UpgradeInstallFailedError(plan.command.join(" "));
1619
+ throw new UpgradeInstallFailedError(plan.manager, release.version);
1620
+ }
1621
+ const moved = plan.steps.filter(
1622
+ (entry) => !entry.satisfied && entry.packages.some((one) => one.name === RUNTIME_PACKAGE)
1623
+ );
1624
+ for (const step of moved) {
1625
+ io.out(`\u2713 ${step.manifest} depends on ${RUNTIME_PACKAGE} ${release.version}`);
1571
1626
  }
1572
- io.out(`\u2713 package.json depends on ${RUNTIME_PACKAGE} ${release.version}`);
1573
1627
  }
1574
1628
  writeFileSync4(manifestFile, manifestText);
1575
1629
  io.out(`\u2713 ${MANIFEST_PATH4} pins ${ENGINE_PACKAGE4} ${release.version}`);
@@ -1582,12 +1636,13 @@ async function upgrade(options) {
1582
1636
 
1583
1637
  // src/launcher.ts
1584
1638
  import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
1585
- import { join as join7 } from "path";
1639
+ import { join as join6 } from "path";
1586
1640
  import {
1587
1641
  MANIFEST_FORMAT,
1588
1642
  MANIFEST_PATH as MANIFEST_PATH6,
1589
1643
  PENV_HOME_VAR,
1590
1644
  PenvError as PenvError3,
1645
+ packageEntry as packageEntry2,
1591
1646
  parseManifest as parseManifest2,
1592
1647
  penvHome,
1593
1648
  serializeManifest as serializeManifest3,
@@ -1596,7 +1651,7 @@ import {
1596
1651
 
1597
1652
  // src/help.ts
1598
1653
  import { RUNTIME_PACKAGE as RUNTIME_PACKAGE2 } from "@penvhq/cli/install";
1599
- import { ENGINE_PACKAGE as ENGINE_PACKAGE5, EXTENSIONS_PATH as EXTENSIONS_PATH3, MANIFEST_PATH as MANIFEST_PATH5 } from "@penvhq/core";
1654
+ import { ENGINE_PACKAGE as ENGINE_PACKAGE5, EXTENSIONS_PATH as EXTENSIONS_PATH3, MANIFEST_PATH as MANIFEST_PATH5, OFFICIAL_SCOPE as OFFICIAL_SCOPE3 } from "@penvhq/core";
1600
1655
  function printLauncherCommands(io) {
1601
1656
  io.out("");
1602
1657
  io.out("LAUNCHER COMMANDS (penv itself, whatever engine a project pins)");
@@ -1622,10 +1677,15 @@ function printAddHelp(io) {
1622
1677
  io.out(
1623
1678
  ` ${TRUST_YOUNG_FLAG} Add a release published less than ${MIN_PACKAGE_AGE_DAYS} days ago`
1624
1679
  );
1680
+ io.out(` ${YES_FLAG} Ask nothing \u2014 the config and onboarding offers print instead`);
1625
1681
  io.out("");
1626
1682
  io.out(`Pins the release in ${MANIFEST_PATH5} and writes its type declaration to`);
1627
1683
  io.out(`${EXTENSIONS_PATH3}/. Both are committed, so \`${INSTALL_COMMAND}\` gives every machine`);
1628
1684
  io.out(`the bytes you reviewed. \`${LOCAL_FLAG}\` pins nothing and touches no manifest.`);
1685
+ io.out("");
1686
+ io.out(`An \`${OFFICIAL_SCOPE3}*\` package asks nothing, so it adds unattended. Anything else`);
1687
+ io.out("records who publishes it and why you trust it, which needs a person at a terminal \u2014");
1688
+ io.out(`\`${YES_FLAG}\` cannot write that line for you.`);
1629
1689
  }
1630
1690
  function printUpgradeHelp(io) {
1631
1691
  io.out("penv upgrade [version]");
@@ -1801,7 +1861,7 @@ async function adopt(options, forwarded, home, cwd, noDownload) {
1801
1861
  if (root === void 0) {
1802
1862
  return 0;
1803
1863
  }
1804
- const manifestFile = join7(root, ...MANIFEST_PATH6.split("/"));
1864
+ const manifestFile = join6(root, ...MANIFEST_PATH6.split("/"));
1805
1865
  if (existsSync4(manifestFile)) {
1806
1866
  return 0;
1807
1867
  }
@@ -1857,12 +1917,14 @@ async function install(options, pins, home, broken) {
1857
1917
  if (state === "corrupt") {
1858
1918
  throw new PackageCorruptError(pin.name, pin.version, dir);
1859
1919
  }
1860
- if (state === "installed") {
1861
- options.io.out(`\u2713 ${pin.name} ${pin.version} already installed`);
1862
- continue;
1920
+ if (state === "absent") {
1921
+ await installPin({ home, kind, pin, fetcher: options.fetcher });
1922
+ }
1923
+ if (kind === "extensions") {
1924
+ await assertLoadable(pin.name, packageEntry2(dir), false);
1863
1925
  }
1864
- await installPin({ home, kind, pin, fetcher: options.fetcher });
1865
- options.io.out(`\u2713 ${pin.name} ${pin.version} installed`);
1926
+ const verb = state === "absent" ? "installed" : "already installed";
1927
+ options.io.out(`\u2713 ${pin.name} ${pin.version} ${verb}`);
1866
1928
  }
1867
1929
  if (broken.length > 0) {
1868
1930
  report(new ManifestEntriesUnreadableError(broken), options.io);
@@ -1938,7 +2000,7 @@ export {
1938
2000
  ReleaseIncompleteError,
1939
2001
  PackageTooYoungError,
1940
2002
  AddNoDownloadError,
1941
- AddNotInteractiveError,
2003
+ AddTrustUnattendedError,
1942
2004
  TrustDeclinedError,
1943
2005
  TrustPublisherMissingError,
1944
2006
  TrustReasonMissingError,
@@ -1985,4 +2047,4 @@ export {
1985
2047
  upgrade,
1986
2048
  runLauncher
1987
2049
  };
1988
- //# sourceMappingURL=chunk-USYQEKPW.js.map
2050
+ //# sourceMappingURL=chunk-QOQVOYJC.js.map