@penvhq/launcher 0.9.4 → 0.10.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-Q27F7QYT.js";
8
+ } from "./chunk-BNUZ52VN.js";
9
9
 
10
10
  // src/bin.ts
11
11
  function readLine() {
@@ -164,6 +164,7 @@ function setProviderType(source, environment, type) {
164
164
  import {
165
165
  ENGINE_PACKAGE,
166
166
  EXTENSIONS_PATH,
167
+ LOCAL_EXTENSIONS_PATH,
167
168
  MANIFEST_PATH,
168
169
  OFFICIAL_SCOPE,
169
170
  PenvError
@@ -175,6 +176,9 @@ function addCommand(name, version, extra) {
175
176
  const spec = version === void 0 ? name : `${name}@${version}`;
176
177
  return `penv add ${spec}${extra === void 0 ? "" : ` ${extra}`}`;
177
178
  }
179
+ function addCommandFor(name, local) {
180
+ return local ? `penv add ${LOCAL_FLAG} ${name}` : addCommand(name);
181
+ }
178
182
  var NoProjectError = class extends PenvError {
179
183
  name = "NoProjectError";
180
184
  constructor(cwd) {
@@ -271,7 +275,58 @@ var AddFlagError = class extends PenvError {
271
275
  super(
272
276
  "PENV_ADD_FLAG",
273
277
  `\`penv add\` does not understand \`${flag}\``,
274
- `Run \`penv add <package>\` with \`${TRUST_YOUNG_FLAG}\` or \`--registry <url>\` \u2014 those are the two it takes.`
278
+ `Run \`penv add <package>\` with \`${TRUST_YOUNG_FLAG}\`, \`--registry <url>\`, or \`${LOCAL_FLAG}\` \u2014 those are the three it takes.`
279
+ );
280
+ }
281
+ };
282
+ var LOCAL_FLAG = "--local";
283
+ var AddLocalFlagError = class extends PenvError {
284
+ name = "AddLocalFlagError";
285
+ constructor(subject) {
286
+ super(
287
+ "PENV_ADD_LOCAL_FLAG",
288
+ `\`penv add ${LOCAL_FLAG}\` cannot take ${subject}`,
289
+ `${LOCAL_FLAG} adds the package this project already builds, so there is no release to name and nothing to pin. Publish it and run \`penv add <package>\` to pin one.`
290
+ );
291
+ }
292
+ };
293
+ var ExtensionNotImportableError = class extends PenvError {
294
+ name = "ExtensionNotImportableError";
295
+ constructor(name, entry, local) {
296
+ super(
297
+ "PENV_EXTENSION_NOT_IMPORTABLE",
298
+ entry === void 0 ? `${name} declares no entry point penv can import` : `${name} resolves to ${entry}, which penv cannot import`,
299
+ `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.`
300
+ );
301
+ }
302
+ };
303
+ var ExtensionUnloadableError = class extends PenvError {
304
+ name = "ExtensionUnloadableError";
305
+ constructor(name, entry, detail, local) {
306
+ super(
307
+ "PENV_EXTENSION_UNLOADABLE",
308
+ `${name} threw while penv imported ${entry}: ${detail}`,
309
+ `Build the package and install its dependencies, then run \`${addCommandFor(name, local)}\` again. penv adds a provider it has loaded once, or none.`
310
+ );
311
+ }
312
+ };
313
+ var LocalExtensionUnresolvedError = class extends PenvError {
314
+ name = "LocalExtensionUnresolvedError";
315
+ constructor(name, root) {
316
+ super(
317
+ "PENV_LOCAL_EXTENSION_UNRESOLVED",
318
+ `${name} does not resolve from ${root}`,
319
+ `${LOCAL_FLAG} records that penv should read this package out of the project, so the project has to have it \u2014 add it as a dependency of the root (a workspace link is one), then run this again.`
320
+ );
321
+ }
322
+ };
323
+ var AddLocalInCiError = class extends PenvError {
324
+ name = "AddLocalInCiError";
325
+ constructor(name) {
326
+ super(
327
+ "PENV_ADD_LOCAL_IN_CI",
328
+ `Adding ${name} writes ${LOCAL_EXTENSIONS_PATH}, and CI does not decide what a project develops`,
329
+ `Run \`penv add ${LOCAL_FLAG} ${name}\` from a terminal and commit what it writes.`
275
330
  );
276
331
  }
277
332
  };
@@ -474,14 +529,20 @@ function readExtensionPackage(dir) {
474
529
  try {
475
530
  parsed = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
476
531
  } catch {
477
- return { types: void 0, onboard: void 0 };
532
+ return { name: void 0, version: void 0, types: void 0, onboard: void 0 };
478
533
  }
479
534
  const penv = field(parsed, "penv");
480
- return { types: text(field(penv, "types")), onboard: text(field(penv, "onboard")) };
535
+ return {
536
+ name: text(field(parsed, "name")),
537
+ version: text(field(parsed, "version")),
538
+ types: text(field(penv, "types")),
539
+ onboard: text(field(penv, "onboard"))
540
+ };
481
541
  }
482
542
  function header(subject) {
483
- const provenance = subject.attested ? "npm records a provenance attestation for it" : "npm records no provenance attestation for it";
484
- return `// Written by \`penv add ${subject.name}\`, and committed.
543
+ const local = subject.local === true;
544
+ const provenance = local ? "resolved from this project, not from a published release" : subject.attested ? "npm records a provenance attestation for it" : "npm records no provenance attestation for it";
545
+ return `// Written by \`penv add ${local ? "--local " : ""}${subject.name}\`, and committed.
485
546
  // ${subject.name} ${subject.version} \u2014 ${provenance}.
486
547
  //
487
548
  // Types only: this declares the shape of the provider's \`penv.config.ts\`
@@ -555,32 +616,10 @@ function writeDeclaration(options) {
555
616
 
556
617
  // src/home.ts
557
618
  import { readFileSync as readFileSync2 } from "fs";
558
- import { homedir } from "os";
559
- import { join as join2, resolve as resolve2, sep as sep2 } from "path";
560
- import { PenvError as PenvError2 } from "@penvhq/core";
561
- var PENV_HOME_VAR = "PENV_HOME";
619
+ import { join as join2 } from "path";
562
620
  var HOME_META_FILE = "meta.json";
563
621
  var INTEGRITY_FILE = ".penv-integrity";
564
622
  var NPM_UPDATE_COMMAND = "npm install -g @penvhq/launcher";
565
- function penvHome(env) {
566
- const declared = env[PENV_HOME_VAR];
567
- if (declared !== void 0 && declared.trim() !== "") {
568
- return resolve2(declared);
569
- }
570
- return join2(homedir(), ".penv");
571
- }
572
- function packageDir(home, kind, name, version) {
573
- const bucket = resolve2(home, kind);
574
- const dir = resolve2(bucket, ...name.split("/"), version);
575
- if (!dir.startsWith(bucket + sep2)) {
576
- throw new PenvError2(
577
- "PENV_HOME_ESCAPE",
578
- `\`${name}\` at \`${version}\` resolves to ${dir}, which is outside ${bucket}`,
579
- "Name the package exactly as npm does, e.g. `@penvhq/provider-vault`."
580
- );
581
- }
582
- return dir;
583
- }
584
623
  function launcherUpdateCommand(home) {
585
624
  let meta;
586
625
  try {
@@ -685,6 +724,7 @@ import {
685
724
  writeFileSync as writeFileSync2
686
725
  } from "fs";
687
726
  import { dirname as dirname2, join as join3 } from "path";
727
+ import { packageDir } from "@penvhq/core";
688
728
  var DEFAULT_REGISTRY = "https://registry.npmjs.org";
689
729
  function tarballUrl(pin) {
690
730
  const registry = (pin.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
@@ -819,9 +859,22 @@ async function fetchRelease(query) {
819
859
  }
820
860
 
821
861
  // src/add.ts
822
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
823
- import { relative, sep as sep3 } from "path";
824
- import { findConfigFile, MANIFEST_PATH as MANIFEST_PATH2, OFFICIAL_SCOPE as OFFICIAL_SCOPE2, serializeManifest } from "@penvhq/core";
862
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
863
+ import { createRequire } from "module";
864
+ import { dirname as dirname3, relative, resolve as resolve2, sep as sep2 } from "path";
865
+ import { pathToFileURL } from "url";
866
+ import {
867
+ findConfigFile,
868
+ isImportableEntry,
869
+ LOCAL_EXTENSIONS_PATH as LOCAL_EXTENSIONS_PATH2,
870
+ localExtensionsFile,
871
+ MANIFEST_PATH as MANIFEST_PATH2,
872
+ OFFICIAL_SCOPE as OFFICIAL_SCOPE2,
873
+ packageEntry,
874
+ parseLocalExtensions,
875
+ serializeLocalExtensions,
876
+ serializeManifest
877
+ } from "@penvhq/core";
825
878
 
826
879
  // src/repair.ts
827
880
  import { parseManifest } from "@penvhq/core";
@@ -885,8 +938,13 @@ function parseRequest(argv) {
885
938
  let spec;
886
939
  let registry;
887
940
  let trustYoung = false;
941
+ let local = false;
888
942
  for (let index = 0; index < argv.length; index += 1) {
889
943
  const token = argv[index] ?? "";
944
+ if (token === LOCAL_FLAG) {
945
+ local = true;
946
+ continue;
947
+ }
890
948
  if (token === TRUST_YOUNG_FLAG) {
891
949
  trustYoung = true;
892
950
  continue;
@@ -915,7 +973,18 @@ function parseRequest(argv) {
915
973
  if (!PACKAGE_NAME.test(name) || version === "") {
916
974
  throw new AddPackageNameError(spec);
917
975
  }
918
- return { name, version, registry, trustYoung };
976
+ if (local) {
977
+ if (version !== void 0) {
978
+ throw new AddLocalFlagError("a version");
979
+ }
980
+ if (registry !== void 0) {
981
+ throw new AddLocalFlagError(`\`${REGISTRY_FLAG}\``);
982
+ }
983
+ if (trustYoung) {
984
+ throw new AddLocalFlagError(`\`${TRUST_YOUNG_FLAG}\``);
985
+ }
986
+ }
987
+ return { name, version, registry, trustYoung, local };
919
988
  }
920
989
  function tierOf(request) {
921
990
  if (request.name.startsWith(OFFICIAL_SCOPE2)) {
@@ -1002,7 +1071,7 @@ async function offerConfigEdit(options, name) {
1002
1071
  io.out(line);
1003
1072
  return;
1004
1073
  }
1005
- const shown = relative(root, configFile).split(sep3).join("/");
1074
+ const shown = relative(root, configFile).split(sep2).join("/");
1006
1075
  let current = readFileSync4(configFile, "utf8");
1007
1076
  const entries = readProviderEntries(current);
1008
1077
  if (entries === void 0 || entries.length === 0) {
@@ -1048,9 +1117,90 @@ async function offerOnboarding(io, name, onboard) {
1048
1117
  io.out(`Run \`${command}\` to finish setting ${name} up.`);
1049
1118
  return void 0;
1050
1119
  }
1120
+ function resolvePackageDir(name, root) {
1121
+ const require2 = createRequire(resolve2(root, "noop.js"));
1122
+ try {
1123
+ return dirname3(require2.resolve(`${name}/package.json`));
1124
+ } catch {
1125
+ let dir;
1126
+ try {
1127
+ dir = dirname3(require2.resolve(name));
1128
+ } catch {
1129
+ return void 0;
1130
+ }
1131
+ for (let current = dir, parent = dirname3(dir); current !== parent; parent = dirname3(current)) {
1132
+ if (readExtensionPackage(current).name === name) {
1133
+ return current;
1134
+ }
1135
+ current = parent;
1136
+ }
1137
+ return void 0;
1138
+ }
1139
+ }
1140
+ function resolveProjectEntry(name, root) {
1141
+ let file;
1142
+ try {
1143
+ file = createRequire(resolve2(root, "noop.js")).resolve(name);
1144
+ } catch {
1145
+ return void 0;
1146
+ }
1147
+ return { file, importable: isImportableEntry(file) };
1148
+ }
1149
+ async function assertLoadable(name, entry, local) {
1150
+ if (entry === void 0 || !entry.importable) {
1151
+ throw new ExtensionNotImportableError(name, entry?.file, local);
1152
+ }
1153
+ try {
1154
+ await import(pathToFileURL(entry.file).href);
1155
+ } catch (cause) {
1156
+ const detail = cause instanceof Error ? cause.message : String(cause);
1157
+ throw new ExtensionUnloadableError(name, entry.file, detail, local);
1158
+ }
1159
+ }
1160
+ function recordLocalExtension(root, name) {
1161
+ const file = localExtensionsFile(root);
1162
+ let current;
1163
+ try {
1164
+ current = readFileSync4(file, "utf8");
1165
+ } catch {
1166
+ current = "[]";
1167
+ }
1168
+ mkdirSync3(dirname3(file), { recursive: true });
1169
+ writeFileSync3(file, serializeLocalExtensions([...parseLocalExtensions(current), name]));
1170
+ }
1171
+ async function addLocal(options, request) {
1172
+ const { io, root } = options;
1173
+ if (options.ci === true) {
1174
+ throw new AddLocalInCiError(request.name);
1175
+ }
1176
+ const dir = resolvePackageDir(request.name, root);
1177
+ if (dir === void 0) {
1178
+ throw new LocalExtensionUnresolvedError(request.name, root);
1179
+ }
1180
+ await assertLoadable(request.name, resolveProjectEntry(request.name, root), true);
1181
+ const installed = readExtensionPackage(dir);
1182
+ const declaration = writeDeclaration({
1183
+ root,
1184
+ dir,
1185
+ name: request.name,
1186
+ version: installed.version ?? "unpublished",
1187
+ attested: false,
1188
+ local: true,
1189
+ types: installed.types
1190
+ });
1191
+ recordLocalExtension(root, request.name);
1192
+ io.out(`\u2713 ${request.name} resolves from this project and imports \u2014 nothing is pinned`);
1193
+ io.out(`\u2713 ${LOCAL_EXTENSIONS_PATH2} records it`);
1194
+ io.out(`\u2713 ${declaration} declares its config type`);
1195
+ await offerConfigEdit(options, request.name);
1196
+ return { onboard: await offerOnboarding(io, request.name, installed.onboard) };
1197
+ }
1051
1198
  async function add(options) {
1052
1199
  const { io, fetcher, home, root, manifestFile } = options;
1053
1200
  const request = parseRequest(options.argv);
1201
+ if (request.local) {
1202
+ return addLocal(options, request);
1203
+ }
1054
1204
  const tier = tierOf(request);
1055
1205
  const now = (options.now ?? (() => /* @__PURE__ */ new Date()))();
1056
1206
  if (options.noDownload === true) {
@@ -1084,6 +1234,7 @@ async function add(options) {
1084
1234
  },
1085
1235
  fetcher
1086
1236
  });
1237
+ await assertLoadable(release.name, packageEntry(dir), false);
1087
1238
  const installed = readExtensionPackage(dir);
1088
1239
  const declaration = writeDeclaration({
1089
1240
  root,
@@ -1147,13 +1298,13 @@ function nodeSpawner() {
1147
1298
 
1148
1299
  // src/engine.ts
1149
1300
  import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
1150
- import { createRequire } from "module";
1151
- import { dirname as dirname3, join as join4, resolve as resolve3, sep as sep4 } from "path";
1152
- import { ENGINE_PACKAGE as ENGINE_PACKAGE2, PenvError as PenvError3 } from "@penvhq/core";
1301
+ import { createRequire as createRequire2 } from "module";
1302
+ import { dirname as dirname4, join as join5, resolve as resolve3, sep as sep3 } from "path";
1303
+ import { ENGINE_PACKAGE as ENGINE_PACKAGE2, PenvError as PenvError2 } from "@penvhq/core";
1153
1304
  var ENGINE_BIN = "penv-engine";
1154
1305
  function readPackageManifest(dir) {
1155
1306
  try {
1156
- return JSON.parse(readFileSync5(join4(dir, "package.json"), "utf8"));
1307
+ return JSON.parse(readFileSync5(join5(dir, "package.json"), "utf8"));
1157
1308
  } catch {
1158
1309
  return void 0;
1159
1310
  }
@@ -1177,24 +1328,24 @@ function engineAt(dir, name, version) {
1177
1328
  const bin = manifest === void 0 ? void 0 : binPath(manifest.bin);
1178
1329
  const root = resolve3(dir);
1179
1330
  const entry = bin === void 0 ? void 0 : resolve3(root, bin);
1180
- if (entry === void 0 || !entry.startsWith(root + sep4) || !existsSync2(entry)) {
1331
+ if (entry === void 0 || !entry.startsWith(root + sep3) || !existsSync2(entry)) {
1181
1332
  throw new EngineEntryError(name, version, dir);
1182
1333
  }
1183
1334
  return { name, version, dir, entry };
1184
1335
  }
1185
1336
  function bundledEngine() {
1186
- const require2 = createRequire(import.meta.url);
1337
+ const require2 = createRequire2(import.meta.url);
1187
1338
  let manifestFile;
1188
1339
  try {
1189
1340
  manifestFile = require2.resolve(`${ENGINE_PACKAGE2}/package.json`);
1190
1341
  } catch {
1191
- throw new PenvError3(
1342
+ throw new PenvError2(
1192
1343
  "PENV_NO_BUNDLED_ENGINE",
1193
1344
  `This penv installation is missing ${ENGINE_PACKAGE2}, the engine it runs \`init\` with`,
1194
1345
  "Reinstall the launcher with `npm install -g @penvhq/launcher`."
1195
1346
  );
1196
1347
  }
1197
- const dir = dirname3(manifestFile);
1348
+ const dir = dirname4(manifestFile);
1198
1349
  const version = readPackageManifest(dir)?.version;
1199
1350
  return engineAt(dir, ENGINE_PACKAGE2, typeof version === "string" ? version : "unknown");
1200
1351
  }
@@ -1218,8 +1369,8 @@ var DEV_PIN_INTEGRITY = "sha512-development-build-not-a-published-release";
1218
1369
  var DEV_PIN_VERSION = "0.0.0-dev";
1219
1370
  var BUNDLED_ENGINE_PIN = {
1220
1371
  package: ENGINE_PACKAGE3,
1221
- version: "0.9.4",
1222
- integrity: "sha512-XnG+KlEAZyUaaKUfANPiyhY1dfYZ9KgnkF2R+5A0OskRgfYPSpBRN0i1UPtq8RnwMJaPxfSNDNdqfiiESz6kRw=="
1372
+ version: "0.10.0",
1373
+ integrity: "sha512-b+U0c3xWbk1ILtMfPjJW8x5XBxSLAaw/IAOLMBmGvT2uuYv41JPED8PrenEj4s3ZJY5BxpWU9mQcJHEBouaw/Q=="
1223
1374
  };
1224
1375
  function assertReleasePin(pin) {
1225
1376
  if (pin.integrity === DEV_PIN_INTEGRITY || pin.version === DEV_PIN_VERSION) {
@@ -1236,7 +1387,7 @@ function releaseEnginePin(pin, ranVersion) {
1236
1387
 
1237
1388
  // src/project.ts
1238
1389
  import { existsSync as existsSync3 } from "fs";
1239
- import { dirname as dirname4, join as join5, resolve as resolve4 } from "path";
1390
+ import { dirname as dirname5, join as join6, resolve as resolve4 } from "path";
1240
1391
  import { MANIFEST_PATH as MANIFEST_PATH3, stateDir } from "@penvhq/core";
1241
1392
  var MANIFEST_SEGMENTS = MANIFEST_PATH3.split("/");
1242
1393
  function findUp(cwd, matches) {
@@ -1245,7 +1396,7 @@ function findUp(cwd, matches) {
1245
1396
  if (matches(dir)) {
1246
1397
  return dir;
1247
1398
  }
1248
- const parent = dirname4(dir);
1399
+ const parent = dirname5(dir);
1249
1400
  if (parent === dir) {
1250
1401
  return void 0;
1251
1402
  }
@@ -1253,8 +1404,8 @@ function findUp(cwd, matches) {
1253
1404
  }
1254
1405
  }
1255
1406
  function findProject(cwd) {
1256
- const root = findUp(cwd, (dir) => existsSync3(join5(dir, ...MANIFEST_SEGMENTS)));
1257
- return root === void 0 ? void 0 : { root, manifestFile: join5(root, ...MANIFEST_SEGMENTS) };
1407
+ const root = findUp(cwd, (dir) => existsSync3(join6(dir, ...MANIFEST_SEGMENTS)));
1408
+ return root === void 0 ? void 0 : { root, manifestFile: join6(root, ...MANIFEST_SEGMENTS) };
1258
1409
  }
1259
1410
  function findAdoptedRoot(cwd) {
1260
1411
  return findUp(cwd, (dir) => existsSync3(stateDir(dir)));
@@ -1262,15 +1413,51 @@ function findAdoptedRoot(cwd) {
1262
1413
 
1263
1414
  // src/launcher.ts
1264
1415
  import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
1265
- import { join as join6 } from "path";
1416
+ import { join as join7 } from "path";
1266
1417
  import {
1267
1418
  MANIFEST_FORMAT,
1268
- MANIFEST_PATH as MANIFEST_PATH4,
1269
- PenvError as PenvError4,
1419
+ MANIFEST_PATH as MANIFEST_PATH5,
1420
+ PENV_HOME_VAR,
1421
+ PenvError as PenvError3,
1270
1422
  parseManifest as parseManifest2,
1423
+ penvHome,
1271
1424
  serializeManifest as serializeManifest2,
1272
1425
  UnsupportedManifestFormatError
1273
1426
  } from "@penvhq/core";
1427
+
1428
+ // src/help.ts
1429
+ import { EXTENSIONS_PATH as EXTENSIONS_PATH3, MANIFEST_PATH as MANIFEST_PATH4 } from "@penvhq/core";
1430
+ function printLauncherCommands(io) {
1431
+ io.out("");
1432
+ io.out("LAUNCHER COMMANDS (penv itself, whatever engine a project pins)");
1433
+ io.out("");
1434
+ io.out(` install Installs everything ${MANIFEST_PATH4} pins`);
1435
+ io.out(" add <package> Pins a provider extension, and commits the decision");
1436
+ io.out(` add ${LOCAL_FLAG} <package> Adds the one this repository builds \u2014 nothing is pinned`);
1437
+ io.out("");
1438
+ }
1439
+ function printInstallHelp(io) {
1440
+ io.out(`${INSTALL_COMMAND}`);
1441
+ io.out("");
1442
+ io.out(`Downloads, verifies and installs every version ${MANIFEST_PATH4} pins \u2014 the engine and`);
1443
+ io.out("every extension \u2014 into $PENV_HOME. The one penv command CI and production run that");
1444
+ io.out("may reach the registry.");
1445
+ }
1446
+ function printAddHelp(io) {
1447
+ io.out("penv add <package>[@<version>]");
1448
+ io.out("");
1449
+ io.out(` ${LOCAL_FLAG} Add the package this repository builds, resolved from it`);
1450
+ io.out(" --registry <url> Take the release from a private registry instead of npmjs");
1451
+ io.out(
1452
+ ` ${TRUST_YOUNG_FLAG} Add a release published less than ${MIN_PACKAGE_AGE_DAYS} days ago`
1453
+ );
1454
+ io.out("");
1455
+ io.out(`Pins the release in ${MANIFEST_PATH4} and writes its type declaration to`);
1456
+ io.out(`${EXTENSIONS_PATH3}/. Both are committed, so \`${INSTALL_COMMAND}\` gives every machine`);
1457
+ io.out(`the bytes you reviewed. \`${LOCAL_FLAG}\` pins nothing and touches no manifest.`);
1458
+ }
1459
+
1460
+ // src/launcher.ts
1274
1461
  var INSTALL = "install";
1275
1462
  var ADD = "add";
1276
1463
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["--version", "-v"]);
@@ -1336,7 +1523,7 @@ function readManifestToRepair(manifestFile, home, argv) {
1336
1523
  }
1337
1524
  }
1338
1525
  function report(error, io) {
1339
- if (error instanceof PenvError4 && error.remedy !== void 0) {
1526
+ if (error instanceof PenvError3 && error.remedy !== void 0) {
1340
1527
  const suffix = `
1341
1528
  ${error.remedy}`;
1342
1529
  const message = error.message.endsWith(suffix) ? error.message.slice(0, -suffix.length) : error.message;
@@ -1359,6 +1546,16 @@ async function launch(options) {
1359
1546
  const { noDownload, forwarded } = splitLauncherFlags(argv);
1360
1547
  const first = forwarded[0];
1361
1548
  const home = penvHome(env);
1549
+ if (first !== void 0 && forwarded.slice(1).some((token) => HELP_FLAGS.has(token))) {
1550
+ if (first === INSTALL) {
1551
+ printInstallHelp(io);
1552
+ return 0;
1553
+ }
1554
+ if (first === ADD) {
1555
+ printAddHelp(io);
1556
+ return 0;
1557
+ }
1558
+ }
1362
1559
  const project = findProject(cwd);
1363
1560
  if (project === void 0) {
1364
1561
  if (first !== void 0 && VERSION_FLAGS.has(first)) {
@@ -1403,7 +1600,7 @@ async function adopt(options, forwarded, home, cwd, noDownload) {
1403
1600
  if (root === void 0) {
1404
1601
  return 0;
1405
1602
  }
1406
- const manifestFile = join6(root, ...MANIFEST_PATH4.split("/"));
1603
+ const manifestFile = join7(root, ...MANIFEST_PATH5.split("/"));
1407
1604
  if (existsSync4(manifestFile)) {
1408
1605
  return 0;
1409
1606
  }
@@ -1412,7 +1609,7 @@ async function adopt(options, forwarded, home, cwd, noDownload) {
1412
1609
  manifestFile,
1413
1610
  serializeManifest2({ format: MANIFEST_FORMAT, engine: pin, extensions: {} })
1414
1611
  );
1415
- options.io.out(`\u2713 ${MANIFEST_PATH4} pins ${pin.package} ${pin.version}`);
1612
+ options.io.out(`\u2713 ${MANIFEST_PATH5} pins ${pin.package} ${pin.version}`);
1416
1613
  await ensureAdoptedEngine(
1417
1614
  options,
1418
1615
  { name: pin.package, version: pin.version, integrity: pin.integrity },
@@ -1491,13 +1688,21 @@ async function addExtension(options, project, home, argv, noDownload) {
1491
1688
  const engine = engineAt(dir, enginePin.name, enginePin.version);
1492
1689
  return delegate(options, engine, onboard, home, options.cwd);
1493
1690
  }
1494
- function delegate(options, engine, forwarded, home, cwd) {
1495
- return options.spawn({
1691
+ function isRootHelp(forwarded) {
1692
+ const first = forwarded[0];
1693
+ return first === void 0 || HELP_FLAGS.has(first);
1694
+ }
1695
+ async function delegate(options, engine, forwarded, home, cwd) {
1696
+ const code = await options.spawn({
1496
1697
  command: process.execPath,
1497
1698
  args: [engine.entry, ...forwarded],
1498
1699
  cwd,
1499
1700
  env: { ...options.env, [PENV_HOME_VAR]: home }
1500
1701
  });
1702
+ if (code === 0 && isRootHelp(forwarded)) {
1703
+ printLauncherCommands(options.io);
1704
+ }
1705
+ return code;
1501
1706
  }
1502
1707
 
1503
1708
  export {
@@ -1516,6 +1721,12 @@ export {
1516
1721
  AddSubjectError,
1517
1722
  AddPackageNameError,
1518
1723
  AddFlagError,
1724
+ LOCAL_FLAG,
1725
+ AddLocalFlagError,
1726
+ ExtensionNotImportableError,
1727
+ ExtensionUnloadableError,
1728
+ LocalExtensionUnresolvedError,
1729
+ AddLocalInCiError,
1519
1730
  AddRegistryError,
1520
1731
  OfficialRegistryError,
1521
1732
  RegistryUnreadableError,
@@ -1537,12 +1748,9 @@ export {
1537
1748
  renderDeclaration,
1538
1749
  declarationPath,
1539
1750
  writeDeclaration,
1540
- PENV_HOME_VAR,
1541
1751
  HOME_META_FILE,
1542
1752
  INTEGRITY_FILE,
1543
1753
  NPM_UPDATE_COMMAND,
1544
- penvHome,
1545
- packageDir,
1546
1754
  launcherUpdateCommand,
1547
1755
  integrityOf,
1548
1756
  readTarball,
@@ -1567,4 +1775,4 @@ export {
1567
1775
  findAdoptedRoot,
1568
1776
  runLauncher
1569
1777
  };
1570
- //# sourceMappingURL=chunk-Q27F7QYT.js.map
1778
+ //# sourceMappingURL=chunk-BNUZ52VN.js.map