@penvhq/cli 0.10.0 → 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.
@@ -0,0 +1,318 @@
1
+ // src/install.ts
2
+ import { existsSync as existsSync2, readFileSync } from "fs";
3
+ import { join as join2 } from "path";
4
+ import { PenvError as PenvError2 } from "@penvhq/core";
5
+
6
+ // src/child.ts
7
+ import { spawn } from "child_process";
8
+ import { existsSync, statSync } from "fs";
9
+ import { delimiter, isAbsolute, join, win32 } from "path";
10
+ import { PenvError } from "@penvhq/core";
11
+ var FORWARDED = ["SIGINT", "SIGTERM", "SIGHUP", "SIGBREAK"];
12
+ var startChild = (invocation) => {
13
+ const [executable, ...args] = invocation.command;
14
+ if (executable === void 0) {
15
+ throw noCommand();
16
+ }
17
+ const target = resolveTarget(executable, args, invocation.env);
18
+ const child = spawn(target.file, target.args, {
19
+ cwd: invocation.cwd,
20
+ env: invocation.env,
21
+ stdio: "inherit",
22
+ ...target.verbatim ? { windowsVerbatimArguments: true } : {}
23
+ });
24
+ const forward = /* @__PURE__ */ new Map();
25
+ for (const signal of FORWARDED) {
26
+ const handler = () => {
27
+ child.kill(signal);
28
+ };
29
+ forward.set(signal, handler);
30
+ process.on(signal, handler);
31
+ }
32
+ const release = () => {
33
+ for (const [signal, handler] of forward) {
34
+ process.off(signal, handler);
35
+ }
36
+ };
37
+ const ended = new Promise((resolve, reject) => {
38
+ child.on("error", (cause) => {
39
+ release();
40
+ reject(cannotStart(executable, cause, invocation.purpose));
41
+ });
42
+ child.on("exit", (code, signal) => {
43
+ release();
44
+ resolve({ exitCode: code ?? 1, signal });
45
+ });
46
+ });
47
+ return {
48
+ ended,
49
+ kill(signal) {
50
+ child.kill(signal);
51
+ }
52
+ };
53
+ };
54
+ function noCommand() {
55
+ return new PenvError(
56
+ "RUN_NO_COMMAND",
57
+ "`penv run` was given no command to start",
58
+ "Put the command after `--`, e.g. `penv run -- pnpm dev`."
59
+ );
60
+ }
61
+ function cannotStart(executable, cause, purpose) {
62
+ const detail = cause instanceof Error ? cause.message : String(cause);
63
+ if (purpose !== void 0) {
64
+ return new PenvError(
65
+ "PENV_COMMAND_NOT_STARTED",
66
+ `penv could not start \`${executable}\` to ${purpose}: ${detail}`,
67
+ `Check that \`${executable}\` runs on its own \u2014 penv starts it the way your shell does, so it has to be on PATH. Nothing was changed.`
68
+ );
69
+ }
70
+ return new PenvError(
71
+ "RUN_COMMAND_NOT_STARTED",
72
+ `\`${executable}\` could not be started: ${detail}`,
73
+ `Check the command after \`--\` runs on its own \u2014 \`${executable}\` has to be on PATH, exactly as it is spelled here.`
74
+ );
75
+ }
76
+ function resolveTarget(executable, args, env) {
77
+ if (process.platform !== "win32") {
78
+ return { file: executable, args, verbatim: false };
79
+ }
80
+ const resolved = findExecutable(executable, env);
81
+ if (resolved === void 0 || !/\.(cmd|bat)$/i.test(resolved)) {
82
+ return { file: resolved ?? executable, args, verbatim: false };
83
+ }
84
+ return {
85
+ file: env.ComSpec ?? "cmd.exe",
86
+ args: ["/d", "/s", "/c", `"${cmdCommandLine(resolved, args)}"`],
87
+ verbatim: true
88
+ };
89
+ }
90
+ var SHIM = /(?:^|\\)node_modules\\\.bin\\[^\\]+\.cmd$/i;
91
+ function cmdCommandLine(resolved, args) {
92
+ const command = win32.normalize(resolved);
93
+ const shim = SHIM.test(command);
94
+ return [escapeCommand(command), ...args.map((argument) => escapeArgument(argument, shim))].join(
95
+ " "
96
+ );
97
+ }
98
+ function extensions(env, platform) {
99
+ if (platform !== "win32") {
100
+ return [""];
101
+ }
102
+ const declared = env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
103
+ return [...declared.split(";").filter((extension) => extension.length > 0), ""];
104
+ }
105
+ function findExecutable(executable, env, platform = process.platform) {
106
+ const candidates = extensions(env, platform);
107
+ const isFile = (path2) => existsSync(path2) && statSync(path2).isFile();
108
+ if (executable.includes("/") || executable.includes("\\") || isAbsolute(executable)) {
109
+ return candidates.map((extension) => executable + extension).find(isFile);
110
+ }
111
+ const path = env.PATH ?? env.Path ?? "";
112
+ for (const directory of path.split(delimiter).filter((entry) => entry.length > 0)) {
113
+ const hit = candidates.map((extension) => join(directory, executable + extension)).find(isFile);
114
+ if (hit !== void 0) {
115
+ return hit;
116
+ }
117
+ }
118
+ return void 0;
119
+ }
120
+ var CMD_METACHARACTERS = /([()\][%!^"`<>&|;, *?])/g;
121
+ function escapeCommand(command) {
122
+ return command.replace(CMD_METACHARACTERS, "^$1");
123
+ }
124
+ function escapeArgument(argument, doubleEscape) {
125
+ const quoted = `"${argument.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\*)$/, "$1$1")}"`;
126
+ const escaped = quoted.replace(CMD_METACHARACTERS, "^$1");
127
+ return doubleEscape ? escaped.replace(CMD_METACHARACTERS, "^$1") : escaped;
128
+ }
129
+
130
+ // src/install.ts
131
+ var RUNTIME_PACKAGE = "@penvhq/penv";
132
+ var SCHEMA_PACKAGE = "zod";
133
+ var LOCKFILES = [
134
+ ["pnpm", "pnpm-lock.yaml"],
135
+ ["yarn", "yarn.lock"],
136
+ ["bun", "bun.lock"],
137
+ ["bun", "bun.lockb"],
138
+ ["npm", "package-lock.json"]
139
+ ];
140
+ var ADD = {
141
+ pnpm: ["pnpm", "add", "--save-exact"],
142
+ npm: ["npm", "install", "--save-exact"],
143
+ yarn: ["yarn", "add", "--exact"],
144
+ bun: ["bun", "add", "--exact"]
145
+ };
146
+ function engineVersion() {
147
+ const version = ownManifest()?.version;
148
+ if (typeof version === "string" && version.length > 0) {
149
+ return version;
150
+ }
151
+ throw new PenvError2(
152
+ "ENGINE_VERSION_UNREADABLE",
153
+ "penv could not read its own version, so it cannot say which `@penvhq/penv` this project needs",
154
+ `Reinstall penv, then run \`penv init\` again.`
155
+ );
156
+ }
157
+ function schemaPackageVersion() {
158
+ const peers = ownManifest()?.peerDependencies;
159
+ const declared = peers !== null && typeof peers === "object" && !Array.isArray(peers) ? peers[SCHEMA_PACKAGE] : void 0;
160
+ const floor = typeof declared === "string" ? declared.replace(/^[\^~>=\s]+/, "").trim() : "";
161
+ if (floor.length > 0) {
162
+ return floor;
163
+ }
164
+ throw new PenvError2(
165
+ "ENGINE_PEER_UNREADABLE",
166
+ `penv could not read its own \`${SCHEMA_PACKAGE}\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,
167
+ `Reinstall penv, then run \`penv init\` again.`
168
+ );
169
+ }
170
+ function ownManifest() {
171
+ try {
172
+ const parsed = JSON.parse(
173
+ readFileSync(new URL("../package.json", import.meta.url), "utf8")
174
+ );
175
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
176
+ } catch {
177
+ return void 0;
178
+ }
179
+ }
180
+ function detectPackageManager(root) {
181
+ for (const [manager, lockfile] of LOCKFILES) {
182
+ if (existsSync2(join2(root, lockfile))) {
183
+ return manager;
184
+ }
185
+ }
186
+ return declaredManager(root) ?? "npm";
187
+ }
188
+ function declaredManager(root) {
189
+ const declared = manifestOf(root)?.packageManager;
190
+ if (typeof declared !== "string") {
191
+ return void 0;
192
+ }
193
+ const name = declared.split("@")[0];
194
+ return name === "pnpm" || name === "npm" || name === "yarn" || name === "bun" ? name : void 0;
195
+ }
196
+ function manifestOf(root) {
197
+ const file = join2(root, "package.json");
198
+ if (!existsSync2(file)) {
199
+ return void 0;
200
+ }
201
+ try {
202
+ const manifest = JSON.parse(readFileSync(file, "utf8"));
203
+ return manifest !== null && typeof manifest === "object" && !Array.isArray(manifest) ? manifest : void 0;
204
+ } catch {
205
+ return void 0;
206
+ }
207
+ }
208
+ function declaredVersion(root, name) {
209
+ const manifest = manifestOf(root);
210
+ for (const field of ["dependencies", "devDependencies"]) {
211
+ const block = manifest?.[field];
212
+ if (block !== null && typeof block === "object" && !Array.isArray(block)) {
213
+ const version = block[name];
214
+ if (typeof version === "string") {
215
+ return version;
216
+ }
217
+ }
218
+ }
219
+ return void 0;
220
+ }
221
+ function planInstall(root, version = engineVersion()) {
222
+ const manager = detectPackageManager(root);
223
+ const lockfile = LOCKFILES.find(
224
+ ([name, file]) => name === manager && existsSync2(join2(root, file))
225
+ )?.[1];
226
+ const runtimeDeclared = declaredVersion(root, RUNTIME_PACKAGE);
227
+ const zodDeclared = declaredVersion(root, SCHEMA_PACKAGE);
228
+ const packages = [
229
+ {
230
+ name: RUNTIME_PACKAGE,
231
+ version,
232
+ ...runtimeDeclared === void 0 ? {} : { declared: runtimeDeclared },
233
+ satisfied: runtimeDeclared === version
234
+ },
235
+ {
236
+ name: SCHEMA_PACKAGE,
237
+ version: schemaPackageVersion(),
238
+ ...zodDeclared === void 0 ? {} : { declared: zodDeclared },
239
+ // Any declared zod counts: which zod a project uses is the project's
240
+ // decision, and penv is here to make sure there is one, not to move it.
241
+ satisfied: zodDeclared !== void 0
242
+ }
243
+ ];
244
+ const pending = packages.filter((entry) => !entry.satisfied);
245
+ const specs = (pending.length === 0 ? packages : pending).map(
246
+ (entry) => `${entry.name}@${entry.version}`
247
+ );
248
+ return {
249
+ root,
250
+ manager,
251
+ packages,
252
+ command: [...ADD[manager], ...specs],
253
+ ...lockfile === void 0 ? {} : { lockfile },
254
+ satisfied: pending.length === 0
255
+ };
256
+ }
257
+ function describe(entry) {
258
+ return `${entry.name} ${entry.version}`;
259
+ }
260
+ function renderInstallPlan(plan) {
261
+ if (plan.satisfied) {
262
+ return [
263
+ `package.json already has ${plan.packages.map(describe).join(" and ")} \u2014 nothing to install.`
264
+ ];
265
+ }
266
+ const pending = plan.packages.filter((entry) => !entry.satisfied);
267
+ const added = pending.filter((entry) => entry.declared === void 0);
268
+ const replaced = pending.filter((entry) => entry.declared !== void 0);
269
+ return [
270
+ "package.json",
271
+ ...added.length === 0 ? [] : [
272
+ ' + "dependencies": {',
273
+ ...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
274
+ " + }"
275
+ ],
276
+ ...replaced.flatMap((entry) => [
277
+ ` - "${entry.name}": "${entry.declared}"`,
278
+ ` + "${entry.name}": "${entry.version}"`
279
+ ]),
280
+ ...plan.lockfile === void 0 ? [] : [plan.lockfile, ...pending.map((entry) => ` + ${entry.name}@${entry.version}`)],
281
+ "",
282
+ `Run with: ${plan.command.join(" ")}`
283
+ ];
284
+ }
285
+ var installWithPackageManager = async (plan) => {
286
+ const child = startChild({
287
+ command: plan.command,
288
+ env: process.env,
289
+ cwd: plan.root,
290
+ purpose: `install ${plan.packages.map(describe).join(" and ")}`
291
+ });
292
+ const ended = await child.ended;
293
+ if (ended.exitCode !== 0 || ended.signal !== null) {
294
+ throw installFailed(plan);
295
+ }
296
+ };
297
+ function installFailed(plan) {
298
+ return new PenvError2(
299
+ "INIT_INSTALL_FAILED",
300
+ `${plan.command.join(" ")} did not finish, so penv migrated nothing`,
301
+ `Run \`${plan.command.join(" ")}\` yourself, then start this command again. Your dotenv files are exactly where they were.`
302
+ );
303
+ }
304
+
305
+ export {
306
+ startChild,
307
+ noCommand,
308
+ RUNTIME_PACKAGE,
309
+ SCHEMA_PACKAGE,
310
+ engineVersion,
311
+ schemaPackageVersion,
312
+ detectPackageManager,
313
+ planInstall,
314
+ renderInstallPlan,
315
+ installWithPackageManager,
316
+ installFailed
317
+ };
318
+ //# sourceMappingURL=chunk-JJY4RLDJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/install.ts","../src/child.ts"],"sourcesContent":["/**\n * The runtime dependencies an adopted project takes, and how they get there.\n *\n * PRD §3: an adopted project depends on `@penvhq/penv` at the engine's own\n * version — the typed `@env` surface, not a CLI distribution. It also depends on\n * zod, because the `penv.schema.ts` init scaffolds imports it: zod is a *peer* of\n * `@penvhq/penv`, and a peer is a package the project supplies. Under pnpm's\n * strict layout nothing hoists it to the project root, so an install that named\n * only `@penvhq/penv` left the very schema init had just written unable to\n * resolve `zod` — and adoption could never finish.\n *\n * Both are installed with the package manager the project already uses, and only\n * after showing the exact `package.json` and lockfile change: an install is the\n * one step of adoption that reaches outside the repository, so it is the one step\n * that is shown before it happens rather than reported after.\n *\n * The install itself is a seam. It shells out to a package manager, which the\n * tests must never do — and a fake here is not a weaker test, because what init\n * has to get right is the plan, the consent, and the refusal when the install\n * does not happen.\n *\n * Two commands write that dependency line: `penv init`, which is the engine's,\n * and `penv upgrade`, which is the launcher's. This module is published at\n * `@penvhq/cli/install` so the launcher reaches it without loading the command\n * surface — one answer to \"which package manager, which diff, which spawn\",\n * rather than a second copy on the other side of the launcher/engine split.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { PenvError } from \"@penvhq/core\";\nimport { startChild } from \"./child.js\";\n\n/** The package an adopted project depends on. The CLI engine is not one of its dependencies. */\nexport const RUNTIME_PACKAGE = \"@penvhq/penv\";\n\n/** The peer `penv.schema.ts` imports, which the project supplies because a peer is not hoisted. */\nexport const SCHEMA_PACKAGE = \"zod\";\n\nexport type PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\n/** The lockfile that names each manager, checked in this order. */\nconst LOCKFILES: readonly (readonly [PackageManager, string])[] = [\n [\"pnpm\", \"pnpm-lock.yaml\"],\n [\"yarn\", \"yarn.lock\"],\n [\"bun\", \"bun.lock\"],\n [\"bun\", \"bun.lockb\"],\n [\"npm\", \"package-lock.json\"],\n];\n\n/** How each manager is told to add one exact version. */\nconst ADD: Readonly<Record<PackageManager, readonly string[]>> = {\n pnpm: [\"pnpm\", \"add\", \"--save-exact\"],\n npm: [\"npm\", \"install\", \"--save-exact\"],\n yarn: [\"yarn\", \"add\", \"--exact\"],\n bun: [\"bun\", \"add\", \"--exact\"],\n};\n\n/** One package the adopted project needs, and what its `package.json` says today. */\nexport interface InstallPackage {\n readonly name: string;\n readonly version: string;\n /** What `package.json` already says about it, when it says anything. */\n readonly declared?: string;\n /** True when this project already has it — nothing to install for this one. */\n readonly satisfied: boolean;\n}\n\nexport interface InstallPlan {\n readonly root: string;\n readonly manager: PackageManager;\n /** Everything an adopted project needs, in the order the diff shows them. */\n readonly packages: readonly InstallPackage[];\n /** The command, argv-shaped — what runs, and what a refusal tells the user to run. */\n readonly command: readonly string[];\n /** The lockfile the manager will rewrite, when the project has one. */\n readonly lockfile?: string;\n /** True when every package is already there — nothing to install. */\n readonly satisfied: boolean;\n}\n\n/** Runs an install plan, or throws. Replaced in tests; never spawns there. */\nexport type InstallRuntime = (plan: InstallPlan) => Promise<void>;\n\n/**\n * The engine's own version, read from its manifest rather than restated in the\n * source: `@penvhq/penv` must match the engine exactly, and a constant beside\n * the version a release bumps is a second answer waiting to drift.\n */\nexport function engineVersion(): string {\n const version = ownManifest()?.version;\n if (typeof version === \"string\" && version.length > 0) {\n return version;\n }\n throw new PenvError(\n \"ENGINE_VERSION_UNREADABLE\",\n \"penv could not read its own version, so it cannot say which `@penvhq/penv` this project needs\",\n `Reinstall penv, then run \\`penv init\\` again.`,\n );\n}\n\n/**\n * The zod an adopted project installs: the floor of the peer range the engine\n * and `@penvhq/penv` both declare, which is the version penv is built and tested\n * against.\n *\n * The floor rather than the range, because the diff shown before the install has\n * to be the line that actually lands — `--save-exact` on `^4.4.3` would write\n * whatever the registry resolved that day, which is not something a reader can\n * consent to in advance.\n */\nexport function schemaPackageVersion(): string {\n const peers = ownManifest()?.peerDependencies;\n const declared =\n peers !== null && typeof peers === \"object\" && !Array.isArray(peers)\n ? (peers as Record<string, unknown>)[SCHEMA_PACKAGE]\n : undefined;\n const floor = typeof declared === \"string\" ? declared.replace(/^[\\^~>=\\s]+/, \"\").trim() : \"\";\n if (floor.length > 0) {\n return floor;\n }\n throw new PenvError(\n \"ENGINE_PEER_UNREADABLE\",\n `penv could not read its own \\`${SCHEMA_PACKAGE}\\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,\n `Reinstall penv, then run \\`penv init\\` again.`,\n );\n}\n\n/** The engine's own manifest, or `undefined` when it cannot be read. */\nfunction ownManifest(): Record<string, unknown> | undefined {\n try {\n const parsed: unknown = JSON.parse(\n readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\"),\n );\n return parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : undefined;\n } catch {\n // The callers refuse: a version penv guessed would pin a project's\n // dependency to something nobody chose.\n return undefined;\n }\n}\n\n/** The package manager this project already uses: its lockfile, then what it declares, then npm. */\nexport function detectPackageManager(root: string): PackageManager {\n for (const [manager, lockfile] of LOCKFILES) {\n if (existsSync(join(root, lockfile))) {\n return manager;\n }\n }\n return declaredManager(root) ?? \"npm\";\n}\n\n/** `\"packageManager\": \"pnpm@9.1.0\"` — corepack's field, and a project's own answer. */\nfunction declaredManager(root: string): PackageManager | undefined {\n const declared = manifestOf(root)?.packageManager;\n if (typeof declared !== \"string\") {\n return undefined;\n }\n const name = declared.split(\"@\")[0];\n return name === \"pnpm\" || name === \"npm\" || name === \"yarn\" || name === \"bun\" ? name : undefined;\n}\n\nfunction manifestOf(root: string): Record<string, unknown> | undefined {\n const file = join(root, \"package.json\");\n if (!existsSync(file)) {\n return undefined;\n }\n try {\n const manifest: unknown = JSON.parse(readFileSync(file, \"utf8\"));\n return manifest !== null && typeof manifest === \"object\" && !Array.isArray(manifest)\n ? (manifest as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** What `package.json` says about one package today, from either dependency block. */\nfunction declaredVersion(root: string, name: string): string | undefined {\n const manifest = manifestOf(root);\n for (const field of [\"dependencies\", \"devDependencies\"] as const) {\n const block: unknown = manifest?.[field];\n if (block !== null && typeof block === \"object\" && !Array.isArray(block)) {\n const version: unknown = (block as Record<string, unknown>)[name];\n if (typeof version === \"string\") {\n return version;\n }\n }\n }\n return undefined;\n}\n\nexport function planInstall(root: string, version: string = engineVersion()): InstallPlan {\n const manager = detectPackageManager(root);\n const lockfile = LOCKFILES.find(\n ([name, file]) => name === manager && existsSync(join(root, file)),\n )?.[1];\n\n const runtimeDeclared = declaredVersion(root, RUNTIME_PACKAGE);\n const zodDeclared = declaredVersion(root, SCHEMA_PACKAGE);\n const packages: InstallPackage[] = [\n {\n name: RUNTIME_PACKAGE,\n version,\n ...(runtimeDeclared === undefined ? {} : { declared: runtimeDeclared }),\n satisfied: runtimeDeclared === version,\n },\n {\n name: SCHEMA_PACKAGE,\n version: schemaPackageVersion(),\n ...(zodDeclared === undefined ? {} : { declared: zodDeclared }),\n // Any declared zod counts: which zod a project uses is the project's\n // decision, and penv is here to make sure there is one, not to move it.\n satisfied: zodDeclared !== undefined,\n },\n ];\n\n const pending = packages.filter((entry) => !entry.satisfied);\n const specs = (pending.length === 0 ? packages : pending).map(\n (entry) => `${entry.name}@${entry.version}`,\n );\n return {\n root,\n manager,\n packages,\n command: [...ADD[manager], ...specs],\n ...(lockfile === undefined ? {} : { lockfile }),\n satisfied: pending.length === 0,\n };\n}\n\nfunction describe(entry: InstallPackage): string {\n return `${entry.name} ${entry.version}`;\n}\n\n/**\n * The change, as it will appear in the diff — the whole point of showing it is\n * that the reader recognises their own file, so these are the `package.json`\n * lines that land and the lockfile that gets rewritten, not a summary of both.\n */\nexport function renderInstallPlan(plan: InstallPlan): string[] {\n if (plan.satisfied) {\n return [\n `package.json already has ${plan.packages.map(describe).join(\" and \")} — nothing to install.`,\n ];\n }\n const pending = plan.packages.filter((entry) => !entry.satisfied);\n const added = pending.filter((entry) => entry.declared === undefined);\n const replaced = pending.filter((entry) => entry.declared !== undefined);\n return [\n \"package.json\",\n ...(added.length === 0\n ? []\n : [\n ' + \"dependencies\": {',\n ...added.map((entry) => ` + \"${entry.name}\": \"${entry.version}\"`),\n \" + }\",\n ]),\n ...replaced.flatMap((entry) => [\n ` - \"${entry.name}\": \"${entry.declared}\"`,\n ` + \"${entry.name}\": \"${entry.version}\"`,\n ]),\n ...(plan.lockfile === undefined\n ? []\n : [plan.lockfile, ...pending.map((entry) => ` + ${entry.name}@${entry.version}`)]),\n \"\",\n `Run with: ${plan.command.join(\" \")}`,\n ];\n}\n\n/**\n * The real install: the project's own package manager, started the way any other\n * child is (`.cmd` shims on Windows included), with its output the user's to see.\n */\nexport const installWithPackageManager: InstallRuntime = async (plan) => {\n const child = startChild({\n command: plan.command,\n env: process.env as Record<string, string>,\n cwd: plan.root,\n purpose: `install ${plan.packages.map(describe).join(\" and \")}`,\n });\n const ended = await child.ended;\n if (ended.exitCode !== 0 || ended.signal !== null) {\n throw installFailed(plan);\n }\n};\n\nexport function installFailed(plan: InstallPlan): PenvError {\n return new PenvError(\n \"INIT_INSTALL_FAILED\",\n `${plan.command.join(\" \")} did not finish, so penv migrated nothing`,\n `Run \\`${plan.command.join(\" \")}\\` yourself, then start this command again. Your dotenv files are exactly where they were.`,\n );\n}\n","/**\n * Starting someone else's command, opaquely.\n *\n * `penv run -- <command>` starts exactly what follows `--`: the argument\n * boundaries the shell already worked out are handed to the operating system\n * untouched, stdio is the parent's, and the child's exit code and terminating\n * signal come back out. penv never parses the command, never rebuilds a command\n * line from it, never wraps it in a shell — a shell would re-split what the user\n * already split, and `penv run -- node -e \"console.log(1 > 2)\"` would redirect to\n * a file called `2`.\n *\n * Windows is the one place where \"hand it to the operating system\" needs help.\n * `pnpm`, `next` and every other node-installed tool are `.cmd` shims there, and\n * Node refuses to execute one without a shell. So a `.cmd`/`.bat` target — and\n * only that — is started through `cmd.exe /d /s /c` with\n * `windowsVerbatimArguments`, building the one command line cmd will accept and\n * escaping every argument so that cmd hands the child the same bytes penv was\n * given. Everything else spawns directly, on every platform.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { delimiter, isAbsolute, join, win32 } from \"node:path\";\nimport { PenvError } from \"@penvhq/core\";\n\n/** How a child ended. Exactly one of these is meaningful, and both are forwarded. */\nexport interface ChildResult {\n /** The child's own exit code, or 1 when a signal ended it. */\n readonly exitCode: number;\n /** The signal that ended the child, when one did. */\n readonly signal: NodeJS.Signals | null;\n}\n\nexport interface ChildInvocation {\n /** The command exactly as it followed `--`: the executable, then its arguments. */\n readonly command: readonly string[];\n readonly env: Record<string, string>;\n readonly cwd: string;\n /**\n * What penv is starting this on its own behalf to do — `init`'s dependency\n * install. Absent means the command is the user's, from after `--`, and the\n * two failures have opposite remedies: one is about what they typed, the other\n * about a program penv chose to run.\n */\n readonly purpose?: string;\n}\n\n/** A started child: how it ends, and the one thing a wrapper may do to it. */\nexport interface ChildHandle {\n /** Resolves when the child has ended, however it ended. */\n readonly ended: Promise<ChildResult>;\n /** Asks the child to stop — what `--watch` does before it starts the next one. */\n kill(signal?: NodeJS.Signals): void;\n}\n\n/** The seam `run` starts a child through — replaced in tests that assert what it was given. */\nexport type StartChild = (invocation: ChildInvocation) => ChildHandle;\n\n/** The signals a wrapper must pass through rather than absorb. */\nconst FORWARDED: readonly NodeJS.Signals[] = [\"SIGINT\", \"SIGTERM\", \"SIGHUP\", \"SIGBREAK\"];\n\nexport const startChild: StartChild = (invocation) => {\n const [executable, ...args] = invocation.command;\n if (executable === undefined) {\n throw noCommand();\n }\n\n const target = resolveTarget(executable, args, invocation.env);\n const child = spawn(target.file, target.args, {\n cwd: invocation.cwd,\n env: invocation.env,\n stdio: \"inherit\",\n ...(target.verbatim ? { windowsVerbatimArguments: true } : {}),\n });\n\n // Forwarded rather than handled: penv is a wrapper, and a Ctrl-C belongs to\n // the program the user is looking at. The child decides what to do with it,\n // and its answer comes back as the signal below.\n const forward = new Map<NodeJS.Signals, () => void>();\n for (const signal of FORWARDED) {\n const handler = (): void => {\n child.kill(signal);\n };\n forward.set(signal, handler);\n process.on(signal, handler);\n }\n const release = (): void => {\n for (const [signal, handler] of forward) {\n process.off(signal, handler);\n }\n };\n\n const ended = new Promise<ChildResult>((resolve, reject) => {\n child.on(\"error\", (cause) => {\n release();\n reject(cannotStart(executable, cause, invocation.purpose));\n });\n child.on(\"exit\", (code, signal) => {\n release();\n resolve({ exitCode: code ?? 1, signal });\n });\n });\n\n return {\n ended,\n kill(signal) {\n child.kill(signal);\n },\n };\n};\n\nexport function noCommand(): PenvError {\n return new PenvError(\n \"RUN_NO_COMMAND\",\n \"`penv run` was given no command to start\",\n \"Put the command after `--`, e.g. `penv run -- pnpm dev`.\",\n );\n}\n\nfunction cannotStart(executable: string, cause: unknown, purpose: string | undefined): PenvError {\n const detail = cause instanceof Error ? cause.message : String(cause);\n if (purpose !== undefined) {\n return new PenvError(\n \"PENV_COMMAND_NOT_STARTED\",\n `penv could not start \\`${executable}\\` to ${purpose}: ${detail}`,\n `Check that \\`${executable}\\` runs on its own — penv starts it the way your shell does, so it has to be on PATH. Nothing was changed.`,\n );\n }\n return new PenvError(\n \"RUN_COMMAND_NOT_STARTED\",\n `\\`${executable}\\` could not be started: ${detail}`,\n `Check the command after \\`--\\` runs on its own — \\`${executable}\\` has to be on PATH, exactly as it is spelled here.`,\n );\n}\n\ninterface SpawnTarget {\n readonly file: string;\n readonly args: readonly string[];\n /** True when the args are one pre-built command line rather than a list. */\n readonly verbatim: boolean;\n}\n\nfunction resolveTarget(\n executable: string,\n args: readonly string[],\n env: Readonly<Record<string, string | undefined>>,\n): SpawnTarget {\n if (process.platform !== \"win32\") {\n return { file: executable, args, verbatim: false };\n }\n const resolved = findExecutable(executable, env);\n if (resolved === undefined || !/\\.(cmd|bat)$/i.test(resolved)) {\n return { file: resolved ?? executable, args, verbatim: false };\n }\n return {\n file: env.ComSpec ?? \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", `\"${cmdCommandLine(resolved, args)}\"`],\n verbatim: true,\n };\n}\n\n/** A package-manager shim, which re-invokes cmd on its own way through. */\nconst SHIM = /(?:^|\\\\)node_modules\\\\\\.bin\\\\[^\\\\]+\\.cmd$/i;\n\n/**\n * The one command line cmd.exe is handed, escaped so the child receives the\n * bytes penv was given.\n *\n * The path is normalized first and *then* judged: `./node_modules/.bin/next.cmd`\n * and `.\\node_modules\\.bin\\next.cmd` are the same shim, and deciding on the\n * un-normalized spelling would escape a forward-slash invocation once while cmd\n * expands it twice — so an argument holding `&` would run as a command inside\n * the shim's second round. Windows' own separator, whatever this process runs\n * on, because this line is only ever read by cmd.exe.\n */\nexport function cmdCommandLine(resolved: string, args: readonly string[]): string {\n const command = win32.normalize(resolved);\n const shim = SHIM.test(command);\n return [escapeCommand(command), ...args.map((argument) => escapeArgument(argument, shim))].join(\n \" \",\n );\n}\n\n/**\n * The extensions a name is tried with, in the order the platform's own launcher\n * tries them.\n *\n * On Windows PATHEXT leads and the bare name comes last, because the bare name\n * is almost never what Windows would run: `pnpm`, `npx` and every\n * `node_modules/.bin` tool ship an extensionless POSIX shell script *beside*\n * their `.CMD` shim, in the same directory. Trying the empty extension first\n * matched that script, which is not executable by CreateProcess and is not a\n * `.cmd`, so the wrapper below was skipped and the spawn failed with ENOENT.\n * Everywhere else there are no extensions at all.\n */\nfunction extensions(\n env: Readonly<Record<string, string | undefined>>,\n platform: NodeJS.Platform,\n): string[] {\n if (platform !== \"win32\") {\n return [\"\"];\n }\n const declared = env.PATHEXT ?? \".COM;.EXE;.BAT;.CMD\";\n return [...declared.split(\";\").filter((extension) => extension.length > 0), \"\"];\n}\n\n/**\n * What the shell would have run, found the way the shell finds it: the name as\n * given if it carries a path, else each PATH directory, each with each\n * executable extension.\n *\n * `platform` is a parameter so the ordering above is testable on either kind of\n * machine — it is the whole behavior, and it differs by platform.\n */\nexport function findExecutable(\n executable: string,\n env: Readonly<Record<string, string | undefined>>,\n platform: NodeJS.Platform = process.platform,\n): string | undefined {\n const candidates = extensions(env, platform);\n const isFile = (path: string): boolean => existsSync(path) && statSync(path).isFile();\n\n if (executable.includes(\"/\") || executable.includes(\"\\\\\") || isAbsolute(executable)) {\n return candidates.map((extension) => executable + extension).find(isFile);\n }\n const path = env.PATH ?? env.Path ?? \"\";\n for (const directory of path.split(delimiter).filter((entry) => entry.length > 0)) {\n const hit = candidates.map((extension) => join(directory, executable + extension)).find(isFile);\n if (hit !== undefined) {\n return hit;\n }\n }\n return undefined;\n}\n\n/** The characters cmd.exe expands before the program ever sees them. */\nconst CMD_METACHARACTERS = /([()\\][%!^\"`<>&|;, *?])/g;\n\n/** The command's own path: cmd's metacharacters escaped, and no quotes to confuse it. */\nfunction escapeCommand(command: string): string {\n return command.replace(CMD_METACHARACTERS, \"^$1\");\n}\n\n/**\n * One argument, quoted so the child's runtime splits it exactly where penv was\n * given it, then escaped so cmd.exe passes those quotes through instead of\n * acting on them.\n */\nfunction escapeArgument(argument: string, doubleEscape: boolean): string {\n const quoted = `\"${argument.replace(/(\\\\*)\"/g, '$1$1\\\\\"').replace(/(\\\\*)$/, \"$1$1\")}\"`;\n const escaped = quoted.replace(CMD_METACHARACTERS, \"^$1\");\n return doubleEscape ? escaped.replace(CMD_METACHARACTERS, \"^$1\") : escaped;\n}\n"],"mappings":"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","names":["existsSync","join","PenvError","path","PenvError","existsSync","join"]}