@penvhq/launcher 0.9.5 → 0.11.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-4YZYBC5T.js";
8
+ } from "./chunk-USYQEKPW.js";
9
9
 
10
10
  // src/bin.ts
11
11
  function readLine() {
@@ -170,12 +170,17 @@ import {
170
170
  PenvError
171
171
  } from "@penvhq/core";
172
172
  var INSTALL_COMMAND = "penv install";
173
+ var UPGRADE_COMMAND = "penv upgrade";
174
+ var YES_FLAG = "--yes";
173
175
  var MIN_PACKAGE_AGE_DAYS = 7;
174
176
  var TRUST_YOUNG_FLAG = "--trust-young";
175
177
  function addCommand(name, version, extra) {
176
178
  const spec = version === void 0 ? name : `${name}@${version}`;
177
179
  return `penv add ${spec}${extra === void 0 ? "" : ` ${extra}`}`;
178
180
  }
181
+ function addCommandFor(name, local) {
182
+ return local ? `penv add ${LOCAL_FLAG} ${name}` : addCommand(name);
183
+ }
179
184
  var NoProjectError = class extends PenvError {
180
185
  name = "NoProjectError";
181
186
  constructor(cwd) {
@@ -287,6 +292,26 @@ var AddLocalFlagError = class extends PenvError {
287
292
  );
288
293
  }
289
294
  };
295
+ var ExtensionNotImportableError = class extends PenvError {
296
+ name = "ExtensionNotImportableError";
297
+ constructor(name, entry, local) {
298
+ super(
299
+ "PENV_EXTENSION_NOT_IMPORTABLE",
300
+ entry === void 0 ? `${name} declares no entry point penv can import` : `${name} resolves to ${entry}, which penv cannot import`,
301
+ `penv imports a provider with no transform, so its entry has to be built JavaScript. Point the package's \`exports\` at its build output \u2014 \`dist/index.js\` \u2014 then run \`${addCommandFor(name, local)}\` again.`
302
+ );
303
+ }
304
+ };
305
+ var ExtensionUnloadableError = class extends PenvError {
306
+ name = "ExtensionUnloadableError";
307
+ constructor(name, entry, detail, local) {
308
+ super(
309
+ "PENV_EXTENSION_UNLOADABLE",
310
+ `${name} threw while penv imported ${entry}: ${detail}`,
311
+ `Build the package and install its dependencies, then run \`${addCommandFor(name, local)}\` again. penv adds a provider it has loaded once, or none.`
312
+ );
313
+ }
314
+ };
290
315
  var LocalExtensionUnresolvedError = class extends PenvError {
291
316
  name = "LocalExtensionUnresolvedError";
292
317
  constructor(name, root) {
@@ -329,11 +354,11 @@ var OfficialRegistryError = class extends PenvError {
329
354
  };
330
355
  var RegistryUnreadableError = class extends PenvError {
331
356
  name = "RegistryUnreadableError";
332
- constructor(name, url, detail) {
357
+ constructor(name, url, detail, retry = addCommand(name)) {
333
358
  super(
334
359
  "PENV_REGISTRY_UNREADABLE",
335
360
  `Reading ${name} from ${url} failed: ${detail}`,
336
- `Run \`${addCommand(name)}\` again when the registry is reachable.`
361
+ `Run \`${retry}\` again when the registry is reachable.`
337
362
  );
338
363
  }
339
364
  };
@@ -349,11 +374,11 @@ var PackageUnknownError = class extends PenvError {
349
374
  };
350
375
  var VersionUnknownError = class extends PenvError {
351
376
  name = "VersionUnknownError";
352
- constructor(name, version, url) {
377
+ constructor(name, version, url, retry = addCommand(name)) {
353
378
  super(
354
379
  "PENV_VERSION_UNKNOWN",
355
380
  `${url} publishes no ${version} of ${name}`,
356
- `Run \`${addCommand(name)}\` to take the version \`latest\` points at.`
381
+ `Run \`${retry}\` to take the version \`latest\` points at.`
357
382
  );
358
383
  }
359
384
  };
@@ -478,6 +503,66 @@ var EnginePinMismatchError = class extends PenvError {
478
503
  );
479
504
  }
480
505
  };
506
+ var UpgradeSubjectError = class extends PenvError {
507
+ name = "UpgradeSubjectError";
508
+ constructor() {
509
+ super(
510
+ "PENV_UPGRADE_SUBJECT",
511
+ "`penv upgrade` takes one version, or none",
512
+ `Run \`${UPGRADE_COMMAND}\` to move to whatever \`latest\` points at, or \`${UPGRADE_COMMAND} 0.9.6\` to move to an exact release.`
513
+ );
514
+ }
515
+ };
516
+ var UpgradeFlagError = class extends PenvError {
517
+ name = "UpgradeFlagError";
518
+ constructor(flag) {
519
+ super(
520
+ "PENV_UPGRADE_FLAG",
521
+ `\`${UPGRADE_COMMAND}\` does not understand \`${flag}\``,
522
+ `Run \`${UPGRADE_COMMAND} [version]\` with \`${YES_FLAG}\` \u2014 that is the one flag it takes.`
523
+ );
524
+ }
525
+ };
526
+ var UpgradeNoDownloadError = class extends PenvError {
527
+ name = "UpgradeNoDownloadError";
528
+ constructor() {
529
+ super(
530
+ "PENV_UPGRADE_NO_DOWNLOAD",
531
+ `Upgrading means reading the registry for the version and integrity to pin, and \`--no-download\` says this run does not`,
532
+ `Run \`${UPGRADE_COMMAND}\` without \`--no-download\`. Nothing was fetched or written.`
533
+ );
534
+ }
535
+ };
536
+ var UpgradeUnattendedError = class extends PenvError {
537
+ name = "UpgradeUnattendedError";
538
+ constructor() {
539
+ super(
540
+ "PENV_UPGRADE_UNATTENDED",
541
+ `Upgrading rewrites ${MANIFEST_PATH} and package.json, and this run has nobody to decide that`,
542
+ `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.`
543
+ );
544
+ }
545
+ };
546
+ var UpgradeDeclinedError = class extends PenvError {
547
+ name = "UpgradeDeclinedError";
548
+ constructor(version) {
549
+ super(
550
+ "PENV_UPGRADE_DECLINED",
551
+ `${ENGINE_PACKAGE} ${version} was not installed, so ${MANIFEST_PATH} and package.json are unchanged`,
552
+ `Run \`${UPGRADE_COMMAND} ${version}\` again when you want both of them to move.`
553
+ );
554
+ }
555
+ };
556
+ var UpgradeInstallFailedError = class extends PenvError {
557
+ name = "UpgradeInstallFailedError";
558
+ constructor(command) {
559
+ super(
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.`
563
+ );
564
+ }
565
+ };
481
566
  var EngineEntryError = class extends PenvError {
482
567
  name = "EngineEntryError";
483
568
  constructor(name, version, dir) {
@@ -593,32 +678,10 @@ function writeDeclaration(options) {
593
678
 
594
679
  // src/home.ts
595
680
  import { readFileSync as readFileSync2 } from "fs";
596
- import { homedir } from "os";
597
- import { join as join2, resolve as resolve2, sep as sep2 } from "path";
598
- import { PenvError as PenvError2 } from "@penvhq/core";
599
- var PENV_HOME_VAR = "PENV_HOME";
681
+ import { join as join2 } from "path";
600
682
  var HOME_META_FILE = "meta.json";
601
683
  var INTEGRITY_FILE = ".penv-integrity";
602
684
  var NPM_UPDATE_COMMAND = "npm install -g @penvhq/launcher";
603
- function penvHome(env) {
604
- const declared = env[PENV_HOME_VAR];
605
- if (declared !== void 0 && declared.trim() !== "") {
606
- return resolve2(declared);
607
- }
608
- return join2(homedir(), ".penv");
609
- }
610
- function packageDir(home, kind, name, version) {
611
- const bucket = resolve2(home, kind);
612
- const dir = resolve2(bucket, ...name.split("/"), version);
613
- if (!dir.startsWith(bucket + sep2)) {
614
- throw new PenvError2(
615
- "PENV_HOME_ESCAPE",
616
- `\`${name}\` at \`${version}\` resolves to ${dir}, which is outside ${bucket}`,
617
- "Name the package exactly as npm does, e.g. `@penvhq/provider-vault`."
618
- );
619
- }
620
- return dir;
621
- }
622
685
  function launcherUpdateCommand(home) {
623
686
  let meta;
624
687
  try {
@@ -723,6 +786,7 @@ import {
723
786
  writeFileSync as writeFileSync2
724
787
  } from "fs";
725
788
  import { dirname as dirname2, join as join3 } from "path";
789
+ import { packageDir } from "@penvhq/core";
726
790
  var DEFAULT_REGISTRY = "https://registry.npmjs.org";
727
791
  function tarballUrl(pin) {
728
792
  const registry = (pin.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
@@ -809,7 +873,8 @@ async function fetchRelease(query) {
809
873
  throw new RegistryUnreadableError(
810
874
  query.name,
811
875
  url,
812
- cause instanceof Error ? cause.message : String(cause)
876
+ cause instanceof Error ? cause.message : String(cause),
877
+ query.retry
813
878
  );
814
879
  }
815
880
  let parsed;
@@ -819,7 +884,8 @@ async function fetchRelease(query) {
819
884
  throw new RegistryUnreadableError(
820
885
  query.name,
821
886
  url,
822
- cause instanceof Error ? cause.message : String(cause)
887
+ cause instanceof Error ? cause.message : String(cause),
888
+ query.retry
823
889
  );
824
890
  }
825
891
  const packument = record(parsed);
@@ -835,7 +901,7 @@ async function fetchRelease(query) {
835
901
  const version = record(at(versions, asked)) === void 0 ? text3(at(tags, asked)) ?? asked : asked;
836
902
  const release = record(at(versions, version));
837
903
  if (release === void 0) {
838
- throw new VersionUnknownError(query.name, asked, url);
904
+ throw new VersionUnknownError(query.name, asked, url, query.retry);
839
905
  }
840
906
  const dist = record(at(release, "dist"));
841
907
  const integrity = text3(at(dist, "integrity"));
@@ -859,13 +925,16 @@ async function fetchRelease(query) {
859
925
  // src/add.ts
860
926
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
861
927
  import { createRequire } from "module";
862
- import { dirname as dirname3, relative, resolve as resolve3, sep as sep3 } from "path";
928
+ import { dirname as dirname3, relative, resolve as resolve2, sep as sep2 } from "path";
929
+ import { pathToFileURL } from "url";
863
930
  import {
864
931
  findConfigFile,
932
+ isImportableEntry,
865
933
  LOCAL_EXTENSIONS_PATH as LOCAL_EXTENSIONS_PATH2,
866
934
  localExtensionsFile,
867
935
  MANIFEST_PATH as MANIFEST_PATH2,
868
936
  OFFICIAL_SCOPE as OFFICIAL_SCOPE2,
937
+ packageEntry,
869
938
  parseLocalExtensions,
870
939
  serializeLocalExtensions,
871
940
  serializeManifest
@@ -1066,7 +1135,7 @@ async function offerConfigEdit(options, name) {
1066
1135
  io.out(line);
1067
1136
  return;
1068
1137
  }
1069
- const shown = relative(root, configFile).split(sep3).join("/");
1138
+ const shown = relative(root, configFile).split(sep2).join("/");
1070
1139
  let current = readFileSync4(configFile, "utf8");
1071
1140
  const entries = readProviderEntries(current);
1072
1141
  if (entries === void 0 || entries.length === 0) {
@@ -1113,7 +1182,7 @@ async function offerOnboarding(io, name, onboard) {
1113
1182
  return void 0;
1114
1183
  }
1115
1184
  function resolvePackageDir(name, root) {
1116
- const require2 = createRequire(resolve3(root, "noop.js"));
1185
+ const require2 = createRequire(resolve2(root, "noop.js"));
1117
1186
  try {
1118
1187
  return dirname3(require2.resolve(`${name}/package.json`));
1119
1188
  } catch {
@@ -1132,6 +1201,26 @@ function resolvePackageDir(name, root) {
1132
1201
  return void 0;
1133
1202
  }
1134
1203
  }
1204
+ function resolveProjectEntry(name, root) {
1205
+ let file;
1206
+ try {
1207
+ file = createRequire(resolve2(root, "noop.js")).resolve(name);
1208
+ } catch {
1209
+ return void 0;
1210
+ }
1211
+ return { file, importable: isImportableEntry(file) };
1212
+ }
1213
+ async function assertLoadable(name, entry, local) {
1214
+ if (entry === void 0 || !entry.importable) {
1215
+ throw new ExtensionNotImportableError(name, entry?.file, local);
1216
+ }
1217
+ try {
1218
+ await import(pathToFileURL(entry.file).href);
1219
+ } catch (cause) {
1220
+ const detail = cause instanceof Error ? cause.message : String(cause);
1221
+ throw new ExtensionUnloadableError(name, entry.file, detail, local);
1222
+ }
1223
+ }
1135
1224
  function recordLocalExtension(root, name) {
1136
1225
  const file = localExtensionsFile(root);
1137
1226
  let current;
@@ -1152,6 +1241,7 @@ async function addLocal(options, request) {
1152
1241
  if (dir === void 0) {
1153
1242
  throw new LocalExtensionUnresolvedError(request.name, root);
1154
1243
  }
1244
+ await assertLoadable(request.name, resolveProjectEntry(request.name, root), true);
1155
1245
  const installed = readExtensionPackage(dir);
1156
1246
  const declaration = writeDeclaration({
1157
1247
  root,
@@ -1163,7 +1253,7 @@ async function addLocal(options, request) {
1163
1253
  types: installed.types
1164
1254
  });
1165
1255
  recordLocalExtension(root, request.name);
1166
- io.out(`\u2713 ${request.name} resolves from this project \u2014 nothing is pinned`);
1256
+ io.out(`\u2713 ${request.name} resolves from this project and imports \u2014 nothing is pinned`);
1167
1257
  io.out(`\u2713 ${LOCAL_EXTENSIONS_PATH2} records it`);
1168
1258
  io.out(`\u2713 ${declaration} declares its config type`);
1169
1259
  await offerConfigEdit(options, request.name);
@@ -1208,6 +1298,7 @@ async function add(options) {
1208
1298
  },
1209
1299
  fetcher
1210
1300
  });
1301
+ await assertLoadable(release.name, packageEntry(dir), false);
1211
1302
  const installed = readExtensionPackage(dir);
1212
1303
  const declaration = writeDeclaration({
1213
1304
  root,
@@ -1272,8 +1363,8 @@ function nodeSpawner() {
1272
1363
  // src/engine.ts
1273
1364
  import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
1274
1365
  import { createRequire as createRequire2 } from "module";
1275
- import { dirname as dirname4, join as join5, resolve as resolve4, sep as sep4 } from "path";
1276
- import { ENGINE_PACKAGE as ENGINE_PACKAGE2, PenvError as PenvError3 } from "@penvhq/core";
1366
+ import { dirname as dirname4, join as join5, resolve as resolve3, sep as sep3 } from "path";
1367
+ import { ENGINE_PACKAGE as ENGINE_PACKAGE2, PenvError as PenvError2 } from "@penvhq/core";
1277
1368
  var ENGINE_BIN = "penv-engine";
1278
1369
  function readPackageManifest(dir) {
1279
1370
  try {
@@ -1299,9 +1390,9 @@ function binPath(bin) {
1299
1390
  function engineAt(dir, name, version) {
1300
1391
  const manifest = readPackageManifest(dir);
1301
1392
  const bin = manifest === void 0 ? void 0 : binPath(manifest.bin);
1302
- const root = resolve4(dir);
1303
- const entry = bin === void 0 ? void 0 : resolve4(root, bin);
1304
- if (entry === void 0 || !entry.startsWith(root + sep4) || !existsSync2(entry)) {
1393
+ const root = resolve3(dir);
1394
+ const entry = bin === void 0 ? void 0 : resolve3(root, bin);
1395
+ if (entry === void 0 || !entry.startsWith(root + sep3) || !existsSync2(entry)) {
1305
1396
  throw new EngineEntryError(name, version, dir);
1306
1397
  }
1307
1398
  return { name, version, dir, entry };
@@ -1312,7 +1403,7 @@ function bundledEngine() {
1312
1403
  try {
1313
1404
  manifestFile = require2.resolve(`${ENGINE_PACKAGE2}/package.json`);
1314
1405
  } catch {
1315
- throw new PenvError3(
1406
+ throw new PenvError2(
1316
1407
  "PENV_NO_BUNDLED_ENGINE",
1317
1408
  `This penv installation is missing ${ENGINE_PACKAGE2}, the engine it runs \`init\` with`,
1318
1409
  "Reinstall the launcher with `npm install -g @penvhq/launcher`."
@@ -1342,8 +1433,8 @@ var DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
1342
1433
  var DEV_PIN_VERSION = "0.0.0-dev";
1343
1434
  var BUNDLED_ENGINE_PIN = {
1344
1435
  package: ENGINE_PACKAGE3,
1345
- version: "0.9.5",
1346
- integrity: "sha512-cacsvynscs6G0NThpGaK1BfKy3POQSoogI/vvQNZ5sTZaftzwg4mPtUpT+i2GSc3KbYAHQu4Kq9MpYLOvSvulA=="
1436
+ version: "0.11.0",
1437
+ integrity: "sha512-Y+WKyuPsX4U8/1j0e+VMUhj0LaGrp67YEoDAWyyinrgA+c8IM4H1blqje/YK4LsQwzQDQQDF9bhWIhfmDAWoxA=="
1347
1438
  };
1348
1439
  function assertReleasePin(pin) {
1349
1440
  if (pin.integrity === DEV_PIN_INTEGRITY || pin.version === DEV_PIN_VERSION) {
@@ -1360,11 +1451,11 @@ function releaseEnginePin(pin, ranVersion) {
1360
1451
 
1361
1452
  // src/project.ts
1362
1453
  import { existsSync as existsSync3 } from "fs";
1363
- import { dirname as dirname5, join as join6, resolve as resolve5 } from "path";
1454
+ import { dirname as dirname5, join as join6, resolve as resolve4 } from "path";
1364
1455
  import { MANIFEST_PATH as MANIFEST_PATH3, stateDir } from "@penvhq/core";
1365
1456
  var MANIFEST_SEGMENTS = MANIFEST_PATH3.split("/");
1366
1457
  function findUp(cwd, matches) {
1367
- let dir = resolve5(cwd);
1458
+ let dir = resolve4(cwd);
1368
1459
  for (; ; ) {
1369
1460
  if (matches(dir)) {
1370
1461
  return dir;
@@ -1384,19 +1475,174 @@ function findAdoptedRoot(cwd) {
1384
1475
  return findUp(cwd, (dir) => existsSync3(stateDir(dir)));
1385
1476
  }
1386
1477
 
1478
+ // src/upgrade.ts
1479
+ import { writeFileSync as writeFileSync4 } from "fs";
1480
+ import {
1481
+ installWithPackageManager,
1482
+ planInstall,
1483
+ RUNTIME_PACKAGE,
1484
+ renderInstallPlan
1485
+ } from "@penvhq/cli/install";
1486
+ import { ENGINE_PACKAGE as ENGINE_PACKAGE4, MANIFEST_PATH as MANIFEST_PATH4, serializeManifest as serializeManifest2 } from "@penvhq/core";
1487
+ function parseRequest2(argv) {
1488
+ let version;
1489
+ let yes = false;
1490
+ for (const token of argv) {
1491
+ if (token === YES_FLAG) {
1492
+ yes = true;
1493
+ continue;
1494
+ }
1495
+ if (token.startsWith("-")) {
1496
+ throw new UpgradeFlagError(token);
1497
+ }
1498
+ if (version !== void 0 || token === "") {
1499
+ throw new UpgradeSubjectError();
1500
+ }
1501
+ version = token;
1502
+ }
1503
+ return { version, yes };
1504
+ }
1505
+ function renderPinChange(engine, release) {
1506
+ return [
1507
+ MANIFEST_PATH4,
1508
+ ` - "integrity": "${engine.integrity}"`,
1509
+ ` - "version": "${engine.version}"`,
1510
+ ` + "integrity": "${release.integrity}"`,
1511
+ ` + "version": "${release.version}"`
1512
+ ];
1513
+ }
1514
+ function pinnedElsewhere(manifest, version) {
1515
+ return Object.entries(manifest.extensions).filter(([, entry]) => entry.version !== version).map(([name]) => name);
1516
+ }
1517
+ async function consent(options, release, plan, yes) {
1518
+ const { io, manifest } = options;
1519
+ io.out(`${ENGINE_PACKAGE4} ${manifest.engine.version} \u2192 ${release.version}`);
1520
+ io.out("");
1521
+ for (const line of renderPinChange(manifest.engine, release)) {
1522
+ io.out(line);
1523
+ }
1524
+ for (const line of renderInstallPlan(plan)) {
1525
+ io.out(line);
1526
+ }
1527
+ if (yes) {
1528
+ return;
1529
+ }
1530
+ if (!await io.confirm(`Move this project to ${release.version}?`)) {
1531
+ throw new UpgradeDeclinedError(release.version);
1532
+ }
1533
+ }
1534
+ async function upgrade(options) {
1535
+ const { io, fetcher, home, root, manifest, manifestFile } = options;
1536
+ const request = parseRequest2(options.argv);
1537
+ if (options.noDownload === true) {
1538
+ throw new UpgradeNoDownloadError();
1539
+ }
1540
+ if ((options.ci === true || !io.interactive) && !(request.yes && request.version !== void 0)) {
1541
+ throw new UpgradeUnattendedError();
1542
+ }
1543
+ const release = await fetchRelease({
1544
+ name: ENGINE_PACKAGE4,
1545
+ ...request.version === void 0 ? {} : { version: request.version },
1546
+ retry: UPGRADE_COMMAND,
1547
+ fetcher
1548
+ });
1549
+ const plan = planInstall(root, release.version);
1550
+ if (manifest.engine.version === release.version && plan.satisfied) {
1551
+ io.out(`\u2713 Already on ${ENGINE_PACKAGE4} ${release.version} \u2014 nothing to move`);
1552
+ return;
1553
+ }
1554
+ const manifestText = serializeManifest2({
1555
+ ...manifest,
1556
+ engine: { package: ENGINE_PACKAGE4, version: release.version, integrity: release.integrity }
1557
+ });
1558
+ await consent(options, release, plan, request.yes);
1559
+ await installPin({
1560
+ home,
1561
+ kind: "engines",
1562
+ pin: { name: ENGINE_PACKAGE4, version: release.version, integrity: release.integrity },
1563
+ fetcher
1564
+ });
1565
+ io.out(`\u2713 ${ENGINE_PACKAGE4} ${release.version} installed`);
1566
+ if (!plan.satisfied) {
1567
+ try {
1568
+ await (options.install ?? installWithPackageManager)(plan);
1569
+ } catch {
1570
+ throw new UpgradeInstallFailedError(plan.command.join(" "));
1571
+ }
1572
+ io.out(`\u2713 package.json depends on ${RUNTIME_PACKAGE} ${release.version}`);
1573
+ }
1574
+ writeFileSync4(manifestFile, manifestText);
1575
+ io.out(`\u2713 ${MANIFEST_PATH4} pins ${ENGINE_PACKAGE4} ${release.version}`);
1576
+ const kept = pinnedElsewhere(manifest, release.version);
1577
+ if (kept.length > 0) {
1578
+ const subject = kept.length === 1 ? "keeps its own pin" : "keep their own pins";
1579
+ io.out(`\u2192 ${kept.join(", ")} ${subject} \u2014 \`penv add <package>@<version>\` moves one.`);
1580
+ }
1581
+ }
1582
+
1387
1583
  // src/launcher.ts
1388
- import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
1584
+ import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
1389
1585
  import { join as join7 } from "path";
1390
1586
  import {
1391
1587
  MANIFEST_FORMAT,
1392
- MANIFEST_PATH as MANIFEST_PATH4,
1393
- PenvError as PenvError4,
1588
+ MANIFEST_PATH as MANIFEST_PATH6,
1589
+ PENV_HOME_VAR,
1590
+ PenvError as PenvError3,
1394
1591
  parseManifest as parseManifest2,
1395
- serializeManifest as serializeManifest2,
1592
+ penvHome,
1593
+ serializeManifest as serializeManifest3,
1396
1594
  UnsupportedManifestFormatError
1397
1595
  } from "@penvhq/core";
1596
+
1597
+ // src/help.ts
1598
+ 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";
1600
+ function printLauncherCommands(io) {
1601
+ io.out("");
1602
+ io.out("LAUNCHER COMMANDS (penv itself, whatever engine a project pins)");
1603
+ io.out("");
1604
+ io.out(` install Installs everything ${MANIFEST_PATH5} pins`);
1605
+ io.out(" add <package> Pins a provider extension, and commits the decision");
1606
+ io.out(` add ${LOCAL_FLAG} <package> Adds the one this repository builds \u2014 nothing is pinned`);
1607
+ io.out(` upgrade [version] Moves the engine pin and ${RUNTIME_PACKAGE2} together`);
1608
+ io.out("");
1609
+ }
1610
+ function printInstallHelp(io) {
1611
+ io.out(`${INSTALL_COMMAND}`);
1612
+ io.out("");
1613
+ io.out(`Downloads, verifies and installs every version ${MANIFEST_PATH5} pins \u2014 the engine and`);
1614
+ io.out("every extension \u2014 into $PENV_HOME. The one penv command CI and production run that");
1615
+ io.out("may reach the registry.");
1616
+ }
1617
+ function printAddHelp(io) {
1618
+ io.out("penv add <package>[@<version>]");
1619
+ io.out("");
1620
+ io.out(` ${LOCAL_FLAG} Add the package this repository builds, resolved from it`);
1621
+ io.out(" --registry <url> Take the release from a private registry instead of npmjs");
1622
+ io.out(
1623
+ ` ${TRUST_YOUNG_FLAG} Add a release published less than ${MIN_PACKAGE_AGE_DAYS} days ago`
1624
+ );
1625
+ io.out("");
1626
+ io.out(`Pins the release in ${MANIFEST_PATH5} and writes its type declaration to`);
1627
+ io.out(`${EXTENSIONS_PATH3}/. Both are committed, so \`${INSTALL_COMMAND}\` gives every machine`);
1628
+ io.out(`the bytes you reviewed. \`${LOCAL_FLAG}\` pins nothing and touches no manifest.`);
1629
+ }
1630
+ function printUpgradeHelp(io) {
1631
+ io.out("penv upgrade [version]");
1632
+ io.out("");
1633
+ io.out(` ${YES_FLAG} Skip the question \u2014 unattended runs need it and a version`);
1634
+ io.out("");
1635
+ io.out(`Moves the ${ENGINE_PACKAGE5} pin in ${MANIFEST_PATH5} and this project's`);
1636
+ io.out(`${RUNTIME_PACKAGE2} dependency to the same exact version. No version takes whatever`);
1637
+ io.out("`latest` points at; the integrity is the one the registry states, never one penv");
1638
+ io.out("computed. Both files are shown before either moves, and they move together or not");
1639
+ io.out("at all. Extensions keep their own pins.");
1640
+ }
1641
+
1642
+ // src/launcher.ts
1398
1643
  var INSTALL = "install";
1399
1644
  var ADD = "add";
1645
+ var UPGRADE = "upgrade";
1400
1646
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["--version", "-v"]);
1401
1647
  var HELP_FLAGS = /* @__PURE__ */ new Set(["--help", "-h"]);
1402
1648
  var NO_DOWNLOAD = "--no-download";
@@ -1460,7 +1706,7 @@ function readManifestToRepair(manifestFile, home, argv) {
1460
1706
  }
1461
1707
  }
1462
1708
  function report(error, io) {
1463
- if (error instanceof PenvError4 && error.remedy !== void 0) {
1709
+ if (error instanceof PenvError3 && error.remedy !== void 0) {
1464
1710
  const suffix = `
1465
1711
  ${error.remedy}`;
1466
1712
  const message = error.message.endsWith(suffix) ? error.message.slice(0, -suffix.length) : error.message;
@@ -1483,6 +1729,20 @@ async function launch(options) {
1483
1729
  const { noDownload, forwarded } = splitLauncherFlags(argv);
1484
1730
  const first = forwarded[0];
1485
1731
  const home = penvHome(env);
1732
+ if (first !== void 0 && forwarded.slice(1).some((token) => HELP_FLAGS.has(token))) {
1733
+ if (first === INSTALL) {
1734
+ printInstallHelp(io);
1735
+ return 0;
1736
+ }
1737
+ if (first === ADD) {
1738
+ printAddHelp(io);
1739
+ return 0;
1740
+ }
1741
+ if (first === UPGRADE) {
1742
+ printUpgradeHelp(io);
1743
+ return 0;
1744
+ }
1745
+ }
1486
1746
  const project = findProject(cwd);
1487
1747
  if (project === void 0) {
1488
1748
  if (first !== void 0 && VERSION_FLAGS.has(first)) {
@@ -1509,6 +1769,20 @@ async function launch(options) {
1509
1769
  io.out(`penv ${manifest.engine.version}`);
1510
1770
  return 0;
1511
1771
  }
1772
+ if (first === UPGRADE) {
1773
+ await upgrade({
1774
+ argv: forwarded.slice(1),
1775
+ root: project.root,
1776
+ manifestFile: project.manifestFile,
1777
+ manifest,
1778
+ home,
1779
+ io,
1780
+ fetcher: options.fetcher,
1781
+ noDownload,
1782
+ ci: isCi(env)
1783
+ });
1784
+ return 0;
1785
+ }
1512
1786
  const enginePin = enginePinOf(manifest);
1513
1787
  const engineDir = await ensure(options, "engines", enginePin, home, noDownload);
1514
1788
  for (const pin of extensionPinsOf(manifest)) {
@@ -1527,16 +1801,16 @@ async function adopt(options, forwarded, home, cwd, noDownload) {
1527
1801
  if (root === void 0) {
1528
1802
  return 0;
1529
1803
  }
1530
- const manifestFile = join7(root, ...MANIFEST_PATH4.split("/"));
1804
+ const manifestFile = join7(root, ...MANIFEST_PATH6.split("/"));
1531
1805
  if (existsSync4(manifestFile)) {
1532
1806
  return 0;
1533
1807
  }
1534
1808
  const pin = releaseEnginePin(options.bundledPin, engine.version);
1535
- writeFileSync4(
1809
+ writeFileSync5(
1536
1810
  manifestFile,
1537
- serializeManifest2({ format: MANIFEST_FORMAT, engine: pin, extensions: {} })
1811
+ serializeManifest3({ format: MANIFEST_FORMAT, engine: pin, extensions: {} })
1538
1812
  );
1539
- options.io.out(`\u2713 ${MANIFEST_PATH4} pins ${pin.package} ${pin.version}`);
1813
+ options.io.out(`\u2713 ${MANIFEST_PATH6} pins ${pin.package} ${pin.version}`);
1540
1814
  await ensureAdoptedEngine(
1541
1815
  options,
1542
1816
  { name: pin.package, version: pin.version, integrity: pin.integrity },
@@ -1615,19 +1889,29 @@ async function addExtension(options, project, home, argv, noDownload) {
1615
1889
  const engine = engineAt(dir, enginePin.name, enginePin.version);
1616
1890
  return delegate(options, engine, onboard, home, options.cwd);
1617
1891
  }
1618
- function delegate(options, engine, forwarded, home, cwd) {
1619
- return options.spawn({
1892
+ function isRootHelp(forwarded) {
1893
+ const first = forwarded[0];
1894
+ return first === void 0 || HELP_FLAGS.has(first);
1895
+ }
1896
+ async function delegate(options, engine, forwarded, home, cwd) {
1897
+ const code = await options.spawn({
1620
1898
  command: process.execPath,
1621
1899
  args: [engine.entry, ...forwarded],
1622
1900
  cwd,
1623
1901
  env: { ...options.env, [PENV_HOME_VAR]: home }
1624
1902
  });
1903
+ if (code === 0 && isRootHelp(forwarded)) {
1904
+ printLauncherCommands(options.io);
1905
+ }
1906
+ return code;
1625
1907
  }
1626
1908
 
1627
1909
  export {
1628
1910
  readProviderEntries,
1629
1911
  setProviderType,
1630
1912
  INSTALL_COMMAND,
1913
+ UPGRADE_COMMAND,
1914
+ YES_FLAG,
1631
1915
  MIN_PACKAGE_AGE_DAYS,
1632
1916
  TRUST_YOUNG_FLAG,
1633
1917
  NoProjectError,
@@ -1642,6 +1926,8 @@ export {
1642
1926
  AddFlagError,
1643
1927
  LOCAL_FLAG,
1644
1928
  AddLocalFlagError,
1929
+ ExtensionNotImportableError,
1930
+ ExtensionUnloadableError,
1645
1931
  LocalExtensionUnresolvedError,
1646
1932
  AddLocalInCiError,
1647
1933
  AddRegistryError,
@@ -1660,17 +1946,20 @@ export {
1660
1946
  DeclarationNotSelfContainedError,
1661
1947
  EnginePinUnreleasedError,
1662
1948
  EnginePinMismatchError,
1949
+ UpgradeSubjectError,
1950
+ UpgradeFlagError,
1951
+ UpgradeNoDownloadError,
1952
+ UpgradeUnattendedError,
1953
+ UpgradeDeclinedError,
1954
+ UpgradeInstallFailedError,
1663
1955
  EngineEntryError,
1664
1956
  readExtensionPackage,
1665
1957
  renderDeclaration,
1666
1958
  declarationPath,
1667
1959
  writeDeclaration,
1668
- PENV_HOME_VAR,
1669
1960
  HOME_META_FILE,
1670
1961
  INTEGRITY_FILE,
1671
1962
  NPM_UPDATE_COMMAND,
1672
- penvHome,
1673
- packageDir,
1674
1963
  launcherUpdateCommand,
1675
1964
  integrityOf,
1676
1965
  readTarball,
@@ -1693,6 +1982,7 @@ export {
1693
1982
  releaseEnginePin,
1694
1983
  findProject,
1695
1984
  findAdoptedRoot,
1985
+ upgrade,
1696
1986
  runLauncher
1697
1987
  };
1698
- //# sourceMappingURL=chunk-4YZYBC5T.js.map
1988
+ //# sourceMappingURL=chunk-USYQEKPW.js.map