@penvhq/launcher 0.11.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/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-A5LJQPYS.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,7 +1135,29 @@ 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
+ async function offerConfigEdit(options, name, ask) {
1131
1161
  const { io, root } = options;
1132
1162
  const configFile = findConfigFile(root);
1133
1163
  const line = `Add \`type: ${JSON.stringify(name)}\` to an environment in penv.config.ts.`;
@@ -1142,31 +1172,35 @@ async function offerConfigEdit(options, name) {
1142
1172
  io.out(line);
1143
1173
  return;
1144
1174
  }
1145
- if (entries.every((entry) => entry.type === name)) {
1175
+ const offered = entries.filter((entry) => entry.type !== name).map((entry) => entry.environment);
1176
+ if (offered.length === 0) {
1146
1177
  return;
1147
1178
  }
1148
- if (!io.interactive) {
1179
+ if (!ask) {
1149
1180
  io.out(line);
1150
1181
  return;
1151
1182
  }
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);
1183
+ const { chosen, unknown } = chooseEnvironments(
1184
+ await io.ask(`Point which of ${offered.join(", ")} at ${name} in ${shown}? all / none / names`),
1185
+ offered
1186
+ );
1187
+ if (unknown.length > 0) {
1188
+ io.out(`${shown} declares no ${unknown.join(", ")}, so nothing was repointed.`);
1189
+ io.out(line);
1190
+ return;
1191
+ }
1192
+ for (const environment of chosen) {
1193
+ const next = setProviderType(current, environment, name);
1160
1194
  if (next === void 0) {
1161
- io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${entry.environment}\` in ${shown}.`);
1195
+ io.out(`Add \`type: ${JSON.stringify(name)}\` to \`${environment}\` in ${shown}.`);
1162
1196
  continue;
1163
1197
  }
1164
1198
  current = next;
1165
1199
  writeFileSync3(configFile, current);
1166
- io.out(`\u2713 ${shown} points \`${entry.environment}\` at ${name}`);
1200
+ io.out(`\u2713 ${shown} points \`${environment}\` at ${name}`);
1167
1201
  }
1168
1202
  }
1169
- async function offerOnboarding(io, name, onboard) {
1203
+ async function offerOnboarding(io, name, onboard, ask) {
1170
1204
  if (onboard === void 0) {
1171
1205
  return void 0;
1172
1206
  }
@@ -1175,7 +1209,7 @@ async function offerOnboarding(io, name, onboard) {
1175
1209
  return void 0;
1176
1210
  }
1177
1211
  const command = `penv ${args.join(" ")}`;
1178
- if (io.interactive && await io.confirm(`Run \`${command}\` now?`)) {
1212
+ if (ask && await io.confirm(`Run \`${command}\` now?`)) {
1179
1213
  return args;
1180
1214
  }
1181
1215
  io.out(`Run \`${command}\` to finish setting ${name} up.`);
@@ -1256,8 +1290,9 @@ async function addLocal(options, request) {
1256
1290
  io.out(`\u2713 ${request.name} resolves from this project and imports \u2014 nothing is pinned`);
1257
1291
  io.out(`\u2713 ${LOCAL_EXTENSIONS_PATH2} records it`);
1258
1292
  io.out(`\u2713 ${declaration} declares its config type`);
1259
- await offerConfigEdit(options, request.name);
1260
- return { onboard: await offerOnboarding(io, request.name, installed.onboard) };
1293
+ const ask = io.interactive && !request.yes;
1294
+ await offerConfigEdit(options, request.name, ask);
1295
+ return { onboard: await offerOnboarding(io, request.name, installed.onboard, ask) };
1261
1296
  }
1262
1297
  async function add(options) {
1263
1298
  const { io, fetcher, home, root, manifestFile } = options;
@@ -1267,11 +1302,13 @@ async function add(options) {
1267
1302
  }
1268
1303
  const tier = tierOf(request);
1269
1304
  const now = (options.now ?? (() => /* @__PURE__ */ new Date()))();
1305
+ const ask = io.interactive && !request.yes;
1270
1306
  if (options.noDownload === true) {
1271
1307
  throw new AddNoDownloadError(request.name);
1272
1308
  }
1273
- if (options.ci === true || !io.interactive) {
1274
- throw new AddNotInteractiveError(request.name);
1309
+ const ceremony = ceremonyFor(tier);
1310
+ if (ceremony !== void 0 && (options.ci === true || !ask)) {
1311
+ throw new AddTrustUnattendedError(request.name, ceremony);
1275
1312
  }
1276
1313
  const release = await fetchRelease({
1277
1314
  name: request.name,
@@ -1313,8 +1350,8 @@ async function add(options) {
1313
1350
  io.out(`\u2713 ${release.name} ${release.version} installed \u2014 ${provenance}`);
1314
1351
  io.out(`\u2713 ${MANIFEST_PATH2} pins it`);
1315
1352
  io.out(`\u2713 ${declaration} declares its config type`);
1316
- await offerConfigEdit(options, release.name);
1317
- return { onboard: await offerOnboarding(io, release.name, installed.onboard) };
1353
+ await offerConfigEdit(options, release.name, ask);
1354
+ return { onboard: await offerOnboarding(io, release.name, installed.onboard, ask) };
1318
1355
  }
1319
1356
 
1320
1357
  // src/delegate.ts
@@ -1363,12 +1400,12 @@ function nodeSpawner() {
1363
1400
  // src/engine.ts
1364
1401
  import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
1365
1402
  import { createRequire as createRequire2 } from "module";
1366
- import { dirname as dirname4, join as join5, resolve as resolve3, sep as sep3 } from "path";
1403
+ import { dirname as dirname4, join as join4, resolve as resolve3, sep as sep3 } from "path";
1367
1404
  import { ENGINE_PACKAGE as ENGINE_PACKAGE2, PenvError as PenvError2 } from "@penvhq/core";
1368
1405
  var ENGINE_BIN = "penv-engine";
1369
1406
  function readPackageManifest(dir) {
1370
1407
  try {
1371
- return JSON.parse(readFileSync5(join5(dir, "package.json"), "utf8"));
1408
+ return JSON.parse(readFileSync5(join4(dir, "package.json"), "utf8"));
1372
1409
  } catch {
1373
1410
  return void 0;
1374
1411
  }
@@ -1433,8 +1470,8 @@ var DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
1433
1470
  var DEV_PIN_VERSION = "0.0.0-dev";
1434
1471
  var BUNDLED_ENGINE_PIN = {
1435
1472
  package: ENGINE_PACKAGE3,
1436
- version: "0.11.0",
1437
- integrity: "sha512-Y+WKyuPsX4U8/1j0e+VMUhj0LaGrp67YEoDAWyyinrgA+c8IM4H1blqje/YK4LsQwzQDQQDF9bhWIhfmDAWoxA=="
1473
+ version: "0.12.0",
1474
+ integrity: "sha512-9HUylEIbFq0BGTOW54Vxhkzv8AvuB7H3bImTFP5J+dfglkR7udP2KUsr8b0I2mf45YMTQO5xsYpI5+nhLERGtA=="
1438
1475
  };
1439
1476
  function assertReleasePin(pin) {
1440
1477
  if (pin.integrity === DEV_PIN_INTEGRITY || pin.version === DEV_PIN_VERSION) {
@@ -1451,7 +1488,7 @@ function releaseEnginePin(pin, ranVersion) {
1451
1488
 
1452
1489
  // src/project.ts
1453
1490
  import { existsSync as existsSync3 } from "fs";
1454
- import { dirname as dirname5, join as join6, resolve as resolve4 } from "path";
1491
+ import { dirname as dirname5, join as join5, resolve as resolve4 } from "path";
1455
1492
  import { MANIFEST_PATH as MANIFEST_PATH3, stateDir } from "@penvhq/core";
1456
1493
  var MANIFEST_SEGMENTS = MANIFEST_PATH3.split("/");
1457
1494
  function findUp(cwd, matches) {
@@ -1468,8 +1505,8 @@ function findUp(cwd, matches) {
1468
1505
  }
1469
1506
  }
1470
1507
  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) };
1508
+ const root = findUp(cwd, (dir) => existsSync3(join5(dir, ...MANIFEST_SEGMENTS)));
1509
+ return root === void 0 ? void 0 : { root, manifestFile: join5(root, ...MANIFEST_SEGMENTS) };
1473
1510
  }
1474
1511
  function findAdoptedRoot(cwd) {
1475
1512
  return findUp(cwd, (dir) => existsSync3(stateDir(dir)));
@@ -1567,9 +1604,11 @@ async function upgrade(options) {
1567
1604
  try {
1568
1605
  await (options.install ?? installWithPackageManager)(plan);
1569
1606
  } catch {
1570
- throw new UpgradeInstallFailedError(plan.command.join(" "));
1607
+ throw new UpgradeInstallFailedError(plan.manager, release.version);
1608
+ }
1609
+ for (const step of plan.steps.filter((entry) => !entry.satisfied)) {
1610
+ io.out(`\u2713 ${step.manifest} depends on ${RUNTIME_PACKAGE} ${release.version}`);
1571
1611
  }
1572
- io.out(`\u2713 package.json depends on ${RUNTIME_PACKAGE} ${release.version}`);
1573
1612
  }
1574
1613
  writeFileSync4(manifestFile, manifestText);
1575
1614
  io.out(`\u2713 ${MANIFEST_PATH4} pins ${ENGINE_PACKAGE4} ${release.version}`);
@@ -1582,12 +1621,13 @@ async function upgrade(options) {
1582
1621
 
1583
1622
  // src/launcher.ts
1584
1623
  import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
1585
- import { join as join7 } from "path";
1624
+ import { join as join6 } from "path";
1586
1625
  import {
1587
1626
  MANIFEST_FORMAT,
1588
1627
  MANIFEST_PATH as MANIFEST_PATH6,
1589
1628
  PENV_HOME_VAR,
1590
1629
  PenvError as PenvError3,
1630
+ packageEntry as packageEntry2,
1591
1631
  parseManifest as parseManifest2,
1592
1632
  penvHome,
1593
1633
  serializeManifest as serializeManifest3,
@@ -1596,7 +1636,7 @@ import {
1596
1636
 
1597
1637
  // src/help.ts
1598
1638
  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";
1639
+ 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
1640
  function printLauncherCommands(io) {
1601
1641
  io.out("");
1602
1642
  io.out("LAUNCHER COMMANDS (penv itself, whatever engine a project pins)");
@@ -1622,10 +1662,15 @@ function printAddHelp(io) {
1622
1662
  io.out(
1623
1663
  ` ${TRUST_YOUNG_FLAG} Add a release published less than ${MIN_PACKAGE_AGE_DAYS} days ago`
1624
1664
  );
1665
+ io.out(` ${YES_FLAG} Ask nothing \u2014 the config and onboarding offers print instead`);
1625
1666
  io.out("");
1626
1667
  io.out(`Pins the release in ${MANIFEST_PATH5} and writes its type declaration to`);
1627
1668
  io.out(`${EXTENSIONS_PATH3}/. Both are committed, so \`${INSTALL_COMMAND}\` gives every machine`);
1628
1669
  io.out(`the bytes you reviewed. \`${LOCAL_FLAG}\` pins nothing and touches no manifest.`);
1670
+ io.out("");
1671
+ io.out(`An \`${OFFICIAL_SCOPE3}*\` package asks nothing, so it adds unattended. Anything else`);
1672
+ io.out("records who publishes it and why you trust it, which needs a person at a terminal \u2014");
1673
+ io.out(`\`${YES_FLAG}\` cannot write that line for you.`);
1629
1674
  }
1630
1675
  function printUpgradeHelp(io) {
1631
1676
  io.out("penv upgrade [version]");
@@ -1801,7 +1846,7 @@ async function adopt(options, forwarded, home, cwd, noDownload) {
1801
1846
  if (root === void 0) {
1802
1847
  return 0;
1803
1848
  }
1804
- const manifestFile = join7(root, ...MANIFEST_PATH6.split("/"));
1849
+ const manifestFile = join6(root, ...MANIFEST_PATH6.split("/"));
1805
1850
  if (existsSync4(manifestFile)) {
1806
1851
  return 0;
1807
1852
  }
@@ -1857,12 +1902,14 @@ async function install(options, pins, home, broken) {
1857
1902
  if (state === "corrupt") {
1858
1903
  throw new PackageCorruptError(pin.name, pin.version, dir);
1859
1904
  }
1860
- if (state === "installed") {
1861
- options.io.out(`\u2713 ${pin.name} ${pin.version} already installed`);
1862
- continue;
1905
+ if (state === "absent") {
1906
+ await installPin({ home, kind, pin, fetcher: options.fetcher });
1907
+ }
1908
+ if (kind === "extensions") {
1909
+ await assertLoadable(pin.name, packageEntry2(dir), false);
1863
1910
  }
1864
- await installPin({ home, kind, pin, fetcher: options.fetcher });
1865
- options.io.out(`\u2713 ${pin.name} ${pin.version} installed`);
1911
+ const verb = state === "absent" ? "installed" : "already installed";
1912
+ options.io.out(`\u2713 ${pin.name} ${pin.version} ${verb}`);
1866
1913
  }
1867
1914
  if (broken.length > 0) {
1868
1915
  report(new ManifestEntriesUnreadableError(broken), options.io);
@@ -1938,7 +1985,7 @@ export {
1938
1985
  ReleaseIncompleteError,
1939
1986
  PackageTooYoungError,
1940
1987
  AddNoDownloadError,
1941
- AddNotInteractiveError,
1988
+ AddTrustUnattendedError,
1942
1989
  TrustDeclinedError,
1943
1990
  TrustPublisherMissingError,
1944
1991
  TrustReasonMissingError,
@@ -1985,4 +2032,4 @@ export {
1985
2032
  upgrade,
1986
2033
  runLauncher
1987
2034
  };
1988
- //# sourceMappingURL=chunk-USYQEKPW.js.map
2035
+ //# sourceMappingURL=chunk-A5LJQPYS.js.map