@0xrama/bnpm 0.0.1
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/README.md +83 -0
- package/dist/package.json +45 -0
- package/dist/src/cache/archive.d.ts +15 -0
- package/dist/src/cache/archive.js +163 -0
- package/dist/src/cache/archive.js.map +1 -0
- package/dist/src/cache/commands.d.ts +15 -0
- package/dist/src/cache/commands.js +168 -0
- package/dist/src/cache/commands.js.map +1 -0
- package/dist/src/cache/quarantine.d.ts +18 -0
- package/dist/src/cache/quarantine.js +104 -0
- package/dist/src/cache/quarantine.js.map +1 -0
- package/dist/src/cache/store.d.ts +10 -0
- package/dist/src/cache/store.js +155 -0
- package/dist/src/cache/store.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +4 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/diff.d.ts +34 -0
- package/dist/src/commands/diff.js +223 -0
- package/dist/src/commands/diff.js.map +1 -0
- package/dist/src/commands/doctor.d.ts +14 -0
- package/dist/src/commands/doctor.js +42 -0
- package/dist/src/commands/doctor.js.map +1 -0
- package/dist/src/commands/edit.d.ts +12 -0
- package/dist/src/commands/edit.js +100 -0
- package/dist/src/commands/edit.js.map +1 -0
- package/dist/src/commands/fund.d.ts +10 -0
- package/dist/src/commands/fund.js +32 -0
- package/dist/src/commands/fund.js.map +1 -0
- package/dist/src/commands/graph.d.ts +29 -0
- package/dist/src/commands/graph.js +90 -0
- package/dist/src/commands/graph.js.map +1 -0
- package/dist/src/commands/index.d.ts +20 -0
- package/dist/src/commands/index.js +1753 -0
- package/dist/src/commands/index.js.map +1 -0
- package/dist/src/commands/navigation.d.ts +2 -0
- package/dist/src/commands/navigation.js +47 -0
- package/dist/src/commands/navigation.js.map +1 -0
- package/dist/src/commands/outdated.d.ts +17 -0
- package/dist/src/commands/outdated.js +69 -0
- package/dist/src/commands/outdated.js.map +1 -0
- package/dist/src/commands/process.d.ts +11 -0
- package/dist/src/commands/process.js +189 -0
- package/dist/src/commands/process.js.map +1 -0
- package/dist/src/commands/prompts.d.ts +6 -0
- package/dist/src/commands/prompts.js +86 -0
- package/dist/src/commands/prompts.js.map +1 -0
- package/dist/src/commands/query.d.ts +48 -0
- package/dist/src/commands/query.js +639 -0
- package/dist/src/commands/query.js.map +1 -0
- package/dist/src/commands/rebuild.d.ts +16 -0
- package/dist/src/commands/rebuild.js +44 -0
- package/dist/src/commands/rebuild.js.map +1 -0
- package/dist/src/commands/sbom.d.ts +6 -0
- package/dist/src/commands/sbom.js +31 -0
- package/dist/src/commands/sbom.js.map +1 -0
- package/dist/src/commands/script-approvals.d.ts +22 -0
- package/dist/src/commands/script-approvals.js +128 -0
- package/dist/src/commands/script-approvals.js.map +1 -0
- package/dist/src/commands/shrinkwrap.d.ts +6 -0
- package/dist/src/commands/shrinkwrap.js +97 -0
- package/dist/src/commands/shrinkwrap.js.map +1 -0
- package/dist/src/commands/stage.d.ts +20 -0
- package/dist/src/commands/stage.js +40 -0
- package/dist/src/commands/stage.js.map +1 -0
- package/dist/src/commands/token.d.ts +2 -0
- package/dist/src/commands/token.js +47 -0
- package/dist/src/commands/token.js.map +1 -0
- package/dist/src/commands/trust.d.ts +5 -0
- package/dist/src/commands/trust.js +41 -0
- package/dist/src/commands/trust.js.map +1 -0
- package/dist/src/config/commands.d.ts +5 -0
- package/dist/src/config/commands.js +91 -0
- package/dist/src/config/commands.js.map +1 -0
- package/dist/src/config/configuration.d.ts +40 -0
- package/dist/src/config/configuration.js +157 -0
- package/dist/src/config/configuration.js.map +1 -0
- package/dist/src/config/interactive.d.ts +16 -0
- package/dist/src/config/interactive.js +19 -0
- package/dist/src/config/interactive.js.map +1 -0
- package/dist/src/config/paths.d.ts +22 -0
- package/dist/src/config/paths.js +46 -0
- package/dist/src/config/paths.js.map +1 -0
- package/dist/src/config/types.d.ts +15 -0
- package/dist/src/config/types.js +6 -0
- package/dist/src/config/types.js.map +1 -0
- package/dist/src/core/cli-parser.d.ts +102 -0
- package/dist/src/core/cli-parser.js +1148 -0
- package/dist/src/core/cli-parser.js.map +1 -0
- package/dist/src/core/cli-runner.d.ts +15 -0
- package/dist/src/core/cli-runner.js +261 -0
- package/dist/src/core/cli-runner.js.map +1 -0
- package/dist/src/core/exit-codes.d.ts +13 -0
- package/dist/src/core/exit-codes.js +13 -0
- package/dist/src/core/exit-codes.js.map +1 -0
- package/dist/src/core/output.d.ts +23 -0
- package/dist/src/core/output.js +123 -0
- package/dist/src/core/output.js.map +1 -0
- package/dist/src/installer/install.d.ts +73 -0
- package/dist/src/installer/install.js +596 -0
- package/dist/src/installer/install.js.map +1 -0
- package/dist/src/installer/mutations.d.ts +5 -0
- package/dist/src/installer/mutations.js +160 -0
- package/dist/src/installer/mutations.js.map +1 -0
- package/dist/src/linker/project-linker.d.ts +9 -0
- package/dist/src/linker/project-linker.js +223 -0
- package/dist/src/linker/project-linker.js.map +1 -0
- package/dist/src/lockfile/index.d.ts +27 -0
- package/dist/src/lockfile/index.js +221 -0
- package/dist/src/lockfile/index.js.map +1 -0
- package/dist/src/package/authoring.d.ts +31 -0
- package/dist/src/package/authoring.js +125 -0
- package/dist/src/package/authoring.js.map +1 -0
- package/dist/src/package/pack.d.ts +22 -0
- package/dist/src/package/pack.js +238 -0
- package/dist/src/package/pack.js.map +1 -0
- package/dist/src/package/provenance.d.ts +28 -0
- package/dist/src/package/provenance.js +162 -0
- package/dist/src/package/provenance.js.map +1 -0
- package/dist/src/package/publish.d.ts +28 -0
- package/dist/src/package/publish.js +189 -0
- package/dist/src/package/publish.js.map +1 -0
- package/dist/src/package/trusted-publishing.d.ts +7 -0
- package/dist/src/package/trusted-publishing.js +84 -0
- package/dist/src/package/trusted-publishing.js.map +1 -0
- package/dist/src/project/dependency-error.d.ts +3 -0
- package/dist/src/project/dependency-error.js +7 -0
- package/dist/src/project/dependency-error.js.map +1 -0
- package/dist/src/project/dev-engines.d.ts +10 -0
- package/dist/src/project/dev-engines.js +70 -0
- package/dist/src/project/dev-engines.js.map +1 -0
- package/dist/src/project/discovery.d.ts +6 -0
- package/dist/src/project/discovery.js +51 -0
- package/dist/src/project/discovery.js.map +1 -0
- package/dist/src/project/global.d.ts +3 -0
- package/dist/src/project/global.js +43 -0
- package/dist/src/project/global.js.map +1 -0
- package/dist/src/project/invalidation.d.ts +3 -0
- package/dist/src/project/invalidation.js +21 -0
- package/dist/src/project/invalidation.js.map +1 -0
- package/dist/src/project/link.d.ts +21 -0
- package/dist/src/project/link.js +152 -0
- package/dist/src/project/link.js.map +1 -0
- package/dist/src/project/manifest.d.ts +27 -0
- package/dist/src/project/manifest.js +378 -0
- package/dist/src/project/manifest.js.map +1 -0
- package/dist/src/project/pkg.d.ts +5 -0
- package/dist/src/project/pkg.js +94 -0
- package/dist/src/project/pkg.js.map +1 -0
- package/dist/src/project/recovery.d.ts +2 -0
- package/dist/src/project/recovery.js +82 -0
- package/dist/src/project/recovery.js.map +1 -0
- package/dist/src/project/workspaces.d.ts +4 -0
- package/dist/src/project/workspaces.js +59 -0
- package/dist/src/project/workspaces.js.map +1 -0
- package/dist/src/registry/account.d.ts +79 -0
- package/dist/src/registry/account.js +305 -0
- package/dist/src/registry/account.js.map +1 -0
- package/dist/src/registry/audit.d.ts +18 -0
- package/dist/src/registry/audit.js +74 -0
- package/dist/src/registry/audit.js.map +1 -0
- package/dist/src/registry/client.d.ts +19 -0
- package/dist/src/registry/client.js +102 -0
- package/dist/src/registry/client.js.map +1 -0
- package/dist/src/registry/configuration.d.ts +36 -0
- package/dist/src/registry/configuration.js +133 -0
- package/dist/src/registry/configuration.js.map +1 -0
- package/dist/src/registry/operations.d.ts +163 -0
- package/dist/src/registry/operations.js +502 -0
- package/dist/src/registry/operations.js.map +1 -0
- package/dist/src/registry/types.d.ts +30 -0
- package/dist/src/registry/types.js +2 -0
- package/dist/src/registry/types.js.map +1 -0
- package/dist/src/resolver/registry-resolver.d.ts +40 -0
- package/dist/src/resolver/registry-resolver.js +354 -0
- package/dist/src/resolver/registry-resolver.js.map +1 -0
- package/dist/src/resolver/source-provider.d.ts +17 -0
- package/dist/src/resolver/source-provider.js +336 -0
- package/dist/src/resolver/source-provider.js.map +1 -0
- package/dist/src/resolver/types.d.ts +28 -0
- package/dist/src/resolver/types.js +2 -0
- package/dist/src/resolver/types.js.map +1 -0
- package/dist/src/security/analyzer.d.ts +38 -0
- package/dist/src/security/analyzer.js +241 -0
- package/dist/src/security/analyzer.js.map +1 -0
- package/dist/src/security/audit.d.ts +19 -0
- package/dist/src/security/audit.js +61 -0
- package/dist/src/security/audit.js.map +1 -0
- package/dist/src/security/findings.d.ts +23 -0
- package/dist/src/security/findings.js +2 -0
- package/dist/src/security/findings.js.map +1 -0
- package/dist/src/security/integrity.d.ts +14 -0
- package/dist/src/security/integrity.js +40 -0
- package/dist/src/security/integrity.js.map +1 -0
- package/dist/src/security/policy.d.ts +16 -0
- package/dist/src/security/policy.js +24 -0
- package/dist/src/security/policy.js.map +1 -0
- package/dist/src/security/recent-release.d.ts +19 -0
- package/dist/src/security/recent-release.js +27 -0
- package/dist/src/security/recent-release.js.map +1 -0
- package/dist/src/security/script-runner.d.ts +13 -0
- package/dist/src/security/script-runner.js +73 -0
- package/dist/src/security/script-runner.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,1753 @@
|
|
|
1
|
+
import { ExitCode } from "../core/exit-codes.js";
|
|
2
|
+
import { installProject } from "../installer/install.js";
|
|
3
|
+
import { addDependencies, removeDependencies, updateDependencies } from "../installer/mutations.js";
|
|
4
|
+
import { execInstalled, exploreInstalled, ProcessCommandError, projectScriptNames, runProjectInstallLifecycle, runProjectScript, runProjectScriptIfPresent, runProjectScriptLifecycle } from "./process.js";
|
|
5
|
+
import { mkdir, mkdtemp, readFile, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { basename, join, relative, resolve, sep } from "node:path";
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { createBnpmPaths } from "../config/paths.js";
|
|
9
|
+
import { auditProject } from "../security/audit.js";
|
|
10
|
+
import { commandInstallPrompts, confirmCommand, readInput, readSecret } from "./prompts.js";
|
|
11
|
+
import { findOutdatedDependencies } from "./outdated.js";
|
|
12
|
+
import { findInstalledDuplicates, inspectInstalledGraph } from "./graph.js";
|
|
13
|
+
import { discoverProject } from "../project/discovery.js";
|
|
14
|
+
import { ensureGlobalProject, exposeGlobalBins } from "../project/global.js";
|
|
15
|
+
import { PackError, packPackage } from "../package/pack.js";
|
|
16
|
+
import { publishPackage } from "../package/publish.js";
|
|
17
|
+
import { loadProvenance } from "../package/provenance.js";
|
|
18
|
+
import { createRegistryToken, registryLegacyLogin, registryLogin, registryLogout, registryTokens, registryWhoami, revokeRegistryTokens } from "../registry/account.js";
|
|
19
|
+
import { spawn } from "node:child_process";
|
|
20
|
+
import { accessiblePackages, createPackageTrust, deprecatePackage, listDistTags, mutateDistTag, mutateOrganizationMember, mutatePackageOwner, mutatePackageStar, mutateRegistryProfile, mutateRegistryTeam, mutateStagedPackage, mutateTeamAccess, organizationMembers, packageAccess, packageOwners, packageTrust, pingRegistry, registryProfile, registryTeamEntries, revokePackageTrust, searchPackages, setPackageAccess, setRegistryProfile, stagedPackage, stagedPackages, starredPackages, unpublishPackage, viewPackage } from "../registry/operations.js";
|
|
21
|
+
import { getConfig, listConfig, mutateConfig } from "../config/commands.js";
|
|
22
|
+
import { changePackageVersion, initializePackage, initializeWorkspace, nextPackageVersion, readPackageVersion } from "../package/authoring.js";
|
|
23
|
+
import { rebuildPackages } from "./rebuild.js";
|
|
24
|
+
import { listFunding } from "./fund.js";
|
|
25
|
+
import { cleanCache, cleanCacheEntries, listCache, verifyCache } from "../cache/commands.js";
|
|
26
|
+
import { parseManifest } from "../project/manifest.js";
|
|
27
|
+
import { discoverWorkspacePackages } from "../project/workspaces.js";
|
|
28
|
+
import { diagnose } from "./doctor.js";
|
|
29
|
+
import { packageProperties } from "../project/pkg.js";
|
|
30
|
+
import { createSbom } from "./sbom.js";
|
|
31
|
+
import { linkPackages, registerLink, unregisterLink } from "../project/link.js";
|
|
32
|
+
import { queryInstalledPackages, QueryError } from "./query.js";
|
|
33
|
+
import { packageNavigationUrl } from "./navigation.js";
|
|
34
|
+
import { inspectScriptApprovals, mutateScriptApprovals, pruneScriptApprovals } from "./script-approvals.js";
|
|
35
|
+
import { diffPackage } from "./diff.js";
|
|
36
|
+
import { downloadStage } from "./stage.js";
|
|
37
|
+
import { shrinkwrapProject } from "./shrinkwrap.js";
|
|
38
|
+
import { editInstalledPackage } from "./edit.js";
|
|
39
|
+
import { trustConfiguration } from "./trust.js";
|
|
40
|
+
import { tokenCreationBody } from "./token.js";
|
|
41
|
+
import { checkDevEngines, DevEnginesError } from "../project/dev-engines.js";
|
|
42
|
+
import npa from "npm-package-arg";
|
|
43
|
+
export const commandNames = ["install", "install-test", "install-ci-test", "add", "remove", "update", "outdated", "list", "why", "query", "diff", "find-dupes", "bin", "prefix", "root", "run", "restart", "audit", "exec", "explore", "edit", "pack", "publish", "stage", "unpublish", "access", "owner", "token", "star", "unstar", "stars", "org", "team", "profile", "trust", "login", "logout", "whoami", "view", "search", "repo", "docs", "bugs", "dist-tag", "deprecate", "config", "init", "version", "shrinkwrap", "prune", "dedupe", "rebuild", "install-scripts", "approve-scripts", "deny-scripts", "fund", "cache", "ping", "doctor", "completion", "pkg", "sbom", "link", "unlink"];
|
|
44
|
+
function selectedRegistry(options) {
|
|
45
|
+
return options?.registry === undefined ? {} : { registry: new URL(options.registry) };
|
|
46
|
+
}
|
|
47
|
+
function installProgressReporter(context) {
|
|
48
|
+
let lastPhase;
|
|
49
|
+
let lastAt = 0;
|
|
50
|
+
let lastBucket = -1;
|
|
51
|
+
return (progress) => {
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const phaseChanged = progress.phase !== lastPhase;
|
|
54
|
+
const completed = progress.completed ?? 0;
|
|
55
|
+
const total = progress.total ?? 0;
|
|
56
|
+
const bucket = total > 0 ? Math.floor(completed * 10 / total) : 0;
|
|
57
|
+
if (progress.phase === "downloading") {
|
|
58
|
+
if (!phaseChanged && now - lastAt < 1000)
|
|
59
|
+
return;
|
|
60
|
+
const bytes = progress.bytes ?? 0;
|
|
61
|
+
const totalBytes = progress.totalBytes;
|
|
62
|
+
const amount = totalBytes === undefined ? `${(bytes / 1024 / 1024).toFixed(1)} MiB` : `${(bytes / 1024 / 1024).toFixed(1)}/${(totalBytes / 1024 / 1024).toFixed(1)} MiB`;
|
|
63
|
+
context.output.info(`Downloading ${(progress.package ?? "package").split("(")[0]} · ${amount}`, progress);
|
|
64
|
+
lastAt = now;
|
|
65
|
+
lastPhase = progress.phase;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!phaseChanged && (progress.phase === "fetching" || progress.phase === "inspecting") && bucket === lastBucket && completed !== total)
|
|
69
|
+
return;
|
|
70
|
+
if (progress.phase === "resolving")
|
|
71
|
+
context.output.info(`Resolving ${total} direct dependenc${total === 1 ? "y" : "ies"} and their transitive graph...`, progress);
|
|
72
|
+
else if (progress.phase === "resolved")
|
|
73
|
+
context.output.info(`Resolved ${total} package${total === 1 ? "" : "s"}.`, progress);
|
|
74
|
+
else if (progress.phase === "fetching")
|
|
75
|
+
context.output.info(`Fetching packages ${completed}/${total} · ${progress.cached ?? 0} cached · ${progress.downloaded ?? 0} downloaded`, progress);
|
|
76
|
+
else if (progress.phase === "inspecting")
|
|
77
|
+
context.output.info(`Security inspection ${completed}/${total} packages`, progress);
|
|
78
|
+
else if (progress.phase === "linking")
|
|
79
|
+
context.output.info(`Linking ${total} package${total === 1 ? "" : "s"} from the content-addressed store...`, progress);
|
|
80
|
+
else
|
|
81
|
+
context.output.info(`Install ready · ${progress.cached ?? 0} cached · ${progress.downloaded ?? 0} downloaded`, progress);
|
|
82
|
+
lastPhase = progress.phase;
|
|
83
|
+
lastAt = now;
|
|
84
|
+
lastBucket = bucket;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
async function commandLocation(context) {
|
|
88
|
+
if (!context.options?.globalInstall)
|
|
89
|
+
return { cwd: context.cwd };
|
|
90
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
91
|
+
await ensureGlobalProject(paths);
|
|
92
|
+
return { cwd: paths.globalRoot, globalPaths: paths };
|
|
93
|
+
}
|
|
94
|
+
async function selectedWorkspaceRoots(cwd, options) {
|
|
95
|
+
if (!options.workspaces && options.workspaceNames === undefined)
|
|
96
|
+
return [cwd];
|
|
97
|
+
const discovered = await discoverProject(cwd);
|
|
98
|
+
const root = discovered?.projectRoot ?? cwd;
|
|
99
|
+
const manifestPath = join(root, "package.json");
|
|
100
|
+
const manifest = parseManifest(await readFile(manifestPath, "utf8"), manifestPath);
|
|
101
|
+
const members = await discoverWorkspacePackages(root, manifest.workspaces);
|
|
102
|
+
const requested = new Set(options.workspaceNames ?? []);
|
|
103
|
+
const selected = [...members].filter(([name, path]) => requested.size === 0 || requested.has(name) || requested.has(relative(root, path).split(sep).join("/")) || [...requested].some((value) => path.startsWith(`${resolve(root, value)}${sep}`)));
|
|
104
|
+
if (selected.length === 0 && !options.includeWorkspaceRoot)
|
|
105
|
+
throw new ProcessCommandError(requested.size === 0 ? "No workspaces were found" : `No workspace matched: ${[...requested].join(", ")}`);
|
|
106
|
+
return [...(options.includeWorkspaceRoot ? [root] : []), ...selected.map(([, path]) => path)].filter((path, index, values) => values.indexOf(path) === index).sort();
|
|
107
|
+
}
|
|
108
|
+
async function currentPackageName(cwd) {
|
|
109
|
+
const discovered = await discoverProject(cwd);
|
|
110
|
+
if (!discovered)
|
|
111
|
+
throw new PackError("no package.json was found for the current project");
|
|
112
|
+
const path = join(discovered.importerRoot, "package.json");
|
|
113
|
+
const name = parseManifest(await readFile(path, "utf8"), path).name;
|
|
114
|
+
if (!name)
|
|
115
|
+
throw new PackError("package.json has no package name");
|
|
116
|
+
return name;
|
|
117
|
+
}
|
|
118
|
+
async function openExternal(url) {
|
|
119
|
+
const [command, args] = process.platform === "darwin"
|
|
120
|
+
? ["open", [url.href]]
|
|
121
|
+
: process.platform === "win32"
|
|
122
|
+
? ["rundll32", ["url.dll,FileProtocolHandler", url.href]]
|
|
123
|
+
: ["xdg-open", [url.href]];
|
|
124
|
+
await new Promise((resolvePromise, reject) => {
|
|
125
|
+
const child = spawn(command, args, { detached: true, stdio: "ignore" });
|
|
126
|
+
child.once("error", reject);
|
|
127
|
+
child.once("spawn", () => { child.unref(); resolvePromise(); });
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async function runAuthoringGit(cwd, args, signal) {
|
|
131
|
+
const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], windowsHide: true, env: { ...process.env, GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" } });
|
|
132
|
+
const stdout = [];
|
|
133
|
+
const stderr = [];
|
|
134
|
+
let bytes = 0;
|
|
135
|
+
const limit = 1024 * 1024;
|
|
136
|
+
const consume = (target, chunk) => { bytes += chunk.length; if (bytes <= limit)
|
|
137
|
+
target.push(chunk);
|
|
138
|
+
else
|
|
139
|
+
child.kill("SIGTERM"); };
|
|
140
|
+
child.stdout.on("data", (chunk) => consume(stdout, chunk));
|
|
141
|
+
child.stderr.on("data", (chunk) => consume(stderr, chunk));
|
|
142
|
+
const abort = () => { child.kill("SIGTERM"); };
|
|
143
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
144
|
+
try {
|
|
145
|
+
const code = await new Promise((resolvePromise, reject) => { child.once("error", reject); child.once("close", (value) => resolvePromise(value ?? 1)); }).catch((error) => {
|
|
146
|
+
throw new PackError(`cannot run Git: ${error instanceof Error ? error.message : String(error)}`);
|
|
147
|
+
});
|
|
148
|
+
if (signal.aborted)
|
|
149
|
+
throw signal.reason;
|
|
150
|
+
if (bytes > limit)
|
|
151
|
+
throw new PackError("Git output exceeded 1 MiB");
|
|
152
|
+
if (code !== 0)
|
|
153
|
+
throw new PackError(`git ${args[0] ?? "command"} failed${stderr.length ? `: ${Buffer.concat(stderr).toString("utf8").trim().slice(0, 1000)}` : ""}`);
|
|
154
|
+
return Buffer.concat(stdout).toString("utf8").trim();
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
signal.removeEventListener("abort", abort);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function gitRoot(cwd, signal) {
|
|
161
|
+
try {
|
|
162
|
+
return await runAuthoringGit(cwd, ["rev-parse", "--show-toplevel"], signal);
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
if (error instanceof PackError)
|
|
166
|
+
return undefined;
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async function writeVersionFile(path, bytes) {
|
|
171
|
+
const temporary = `${path}.${process.pid}.${randomBytes(8).toString("hex")}.restore`;
|
|
172
|
+
try {
|
|
173
|
+
await writeFile(temporary, bytes, { flag: "wx", mode: 0o644 });
|
|
174
|
+
await rename(temporary, path);
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
await rm(temporary, { force: true });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function prepareVersionLockfileUpdates(directory, version, workspaceVersions = new Map()) {
|
|
181
|
+
const updates = [];
|
|
182
|
+
for (const name of ["package-lock.json", "npm-shrinkwrap.json"]) {
|
|
183
|
+
const path = join(directory, name);
|
|
184
|
+
let original;
|
|
185
|
+
try {
|
|
186
|
+
original = await readFile(path, "utf8");
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
if (error.code === "ENOENT")
|
|
190
|
+
continue;
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
let document;
|
|
194
|
+
try {
|
|
195
|
+
const parsed = JSON.parse(original);
|
|
196
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
197
|
+
throw new Error("not an object");
|
|
198
|
+
document = parsed;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
throw new PackError(`cannot update ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
202
|
+
}
|
|
203
|
+
if (version !== undefined)
|
|
204
|
+
document.version = version;
|
|
205
|
+
if (typeof document.packages === "object" && document.packages !== null && !Array.isArray(document.packages)) {
|
|
206
|
+
const packages = document.packages;
|
|
207
|
+
const root = packages[""];
|
|
208
|
+
if (version !== undefined && typeof root === "object" && root !== null && !Array.isArray(root))
|
|
209
|
+
root.version = version;
|
|
210
|
+
for (const [workspace, workspaceVersion] of workspaceVersions) {
|
|
211
|
+
const entry = packages[workspace];
|
|
212
|
+
if (typeof entry === "object" && entry !== null && !Array.isArray(entry))
|
|
213
|
+
entry.version = workspaceVersion;
|
|
214
|
+
else
|
|
215
|
+
packages[workspace] = { version: workspaceVersion };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
updates.push({ path, original, updated: `${JSON.stringify(document, null, 2)}\n` });
|
|
219
|
+
}
|
|
220
|
+
return updates;
|
|
221
|
+
}
|
|
222
|
+
async function execEphemeral(context, specifications, binary, args) {
|
|
223
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
224
|
+
await mkdir(paths.ephemeralRoot, { recursive: true, mode: 0o700 });
|
|
225
|
+
const project = await mkdtemp(join(paths.ephemeralRoot, "exec-"));
|
|
226
|
+
const options = { ...context.options, frozenLockfile: false, offline: false, omitDev: true, noSave: true };
|
|
227
|
+
try {
|
|
228
|
+
const normalized = specifications.map((specification) => specification.startsWith("file:./") || specification.startsWith("file:../") ? `file:${relative(project, resolve(context.cwd, specification.slice("file:".length))).split(sep).join("/")}` : specification);
|
|
229
|
+
const result = await installProject({ cwd: project, ...selectedRegistry(options), specifications: normalized, commandOptions: options, signal: context.signal, prompts: commandInstallPrompts(options), onChildOutput: (stream, text, attribution) => context.output.childOutput(stream, text, attribution), onSecurityEvidence: (message, evidence) => context.output.info(message, evidence) });
|
|
230
|
+
if (result.skippedLifecyclePackages.length > 0)
|
|
231
|
+
throw new ProcessCommandError(`Ephemeral execution blocked because lifecycle scripts were skipped for ${result.skippedLifecyclePackages.join(", ")}`);
|
|
232
|
+
return await execInstalled(project, binary, args, context.signal, context.output, context.output.passthroughChildOutput === true);
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
await rm(project, { recursive: true, force: true });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const capabilityOrder = ["ai-history-read", "credential-read", "native-code", "process-spawn", "network-access", "local-write"];
|
|
239
|
+
function evidenceValue(evidence) {
|
|
240
|
+
const separator = evidence.indexOf(": ");
|
|
241
|
+
return separator < 0 ? evidence : evidence.slice(separator + 2);
|
|
242
|
+
}
|
|
243
|
+
function namedMatches(evidence, names) {
|
|
244
|
+
return names.filter(({ expression }) => evidence.some((value) => expression.test(value))).map(({ label }) => label);
|
|
245
|
+
}
|
|
246
|
+
function compactList(values, limit = 6) {
|
|
247
|
+
const unique = [...new Set(values)];
|
|
248
|
+
return unique.length <= limit ? unique.join(", ") : `${unique.slice(0, limit).join(", ")} +${unique.length - limit} more`;
|
|
249
|
+
}
|
|
250
|
+
function capabilitySummary(kind, evidence) {
|
|
251
|
+
if (kind === "ai-history-read") {
|
|
252
|
+
const products = namedMatches(evidence, [
|
|
253
|
+
{ label: "Codex", expression: /CODEX_HOME|Codex|\.codex/i }, { label: "Claude Code", expression: /Claude/i }, { label: "Cursor", expression: /Cursor/i },
|
|
254
|
+
{ label: "Gemini CLI", expression: /Gemini/i }, { label: "OpenCode", expression: /OpenCode|opencode/i }, { label: "Kimi", expression: /Kimi|\.kimi/i },
|
|
255
|
+
{ label: "Qwen", expression: /Qwen|\.qwen/i }, { label: "Cline", expression: /Cline/i }, { label: "Amp", expression: /amp\/threads/i },
|
|
256
|
+
{ label: "Zed", expression: /Zed\/threads/i }, { label: "Grok", expression: /GROK_HOME/i }, { label: "Hermes", expression: /HERMES_HOME/i },
|
|
257
|
+
{ label: "KiloCode", expression: /KiloCode/i }, { label: "JCode", expression: /JCODE_HOME/i }, { label: "Antigravity", expression: /antigravity/i },
|
|
258
|
+
]);
|
|
259
|
+
return { title: "AI chat history", summary: compactList(products), sensitive: true };
|
|
260
|
+
}
|
|
261
|
+
if (kind === "credential-read") {
|
|
262
|
+
const types = namedMatches(evidence, [
|
|
263
|
+
{ label: "API tokens", expression: /API_TOKEN|api[_-]?token/i }, { label: "macOS Keychain", expression: /Keychain/i },
|
|
264
|
+
{ label: "OAuth tokens", expression: /oauth/i }, { label: "refresh tokens", expression: /refresh/i }, { label: "session tokens", expression: /session/i },
|
|
265
|
+
{ label: "credential files", expression: /credentials|secrets|auth\.json|hosts\.yml/i },
|
|
266
|
+
]);
|
|
267
|
+
return { title: "Credentials and tokens", summary: compactList(types), sensitive: true };
|
|
268
|
+
}
|
|
269
|
+
if (kind === "network-access") {
|
|
270
|
+
const hosts = evidence.map(evidenceValue).map((value) => { try {
|
|
271
|
+
return new URL(value).hostname;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return value;
|
|
275
|
+
} }).filter((value) => value !== "127.0.0.1");
|
|
276
|
+
return { title: "Internet access", summary: compactList(hosts), sensitive: false };
|
|
277
|
+
}
|
|
278
|
+
if (kind === "local-write")
|
|
279
|
+
return { title: "Writes local files", summary: compactList(evidence.map(evidenceValue), 5), sensitive: false };
|
|
280
|
+
if (kind === "process-spawn")
|
|
281
|
+
return { title: "Starts other programs", summary: "uses subprocess APIs", sensitive: false };
|
|
282
|
+
return { title: "Native executable code", summary: compactList(evidence.map(evidenceValue), 4), sensitive: false };
|
|
283
|
+
}
|
|
284
|
+
function capabilityDisclosure(identity, binary, grouped, details) {
|
|
285
|
+
const highRisk = grouped.has("ai-history-read") || grouped.has("credential-read");
|
|
286
|
+
const lines = [
|
|
287
|
+
`Security review for ${identity} (${binary})`,
|
|
288
|
+
`${highRisk ? "HIGH REVIEW" : "REVIEW"} — ${highRisk ? "This tool may access sensitive data on your Mac." : "This tool requests access worth reviewing."}`,
|
|
289
|
+
];
|
|
290
|
+
const sections = [
|
|
291
|
+
{ title: "SENSITIVE DATA", kinds: capabilityOrder.filter((kind) => capabilitySummary(kind, []).sensitive) },
|
|
292
|
+
{ title: "SYSTEM ACTIVITY", kinds: capabilityOrder.filter((kind) => !capabilitySummary(kind, []).sensitive) },
|
|
293
|
+
];
|
|
294
|
+
for (const section of sections) {
|
|
295
|
+
const entries = section.kinds.flatMap((kind) => { const entry = grouped.get(kind); return entry ? [[kind, entry]] : []; });
|
|
296
|
+
if (entries.length === 0)
|
|
297
|
+
continue;
|
|
298
|
+
lines.push("", section.title);
|
|
299
|
+
for (const [kind, entry] of entries) {
|
|
300
|
+
const presentation = capabilitySummary(kind, [...entry.evidence]);
|
|
301
|
+
lines.push(` ${entry.severity === "warning" ? "!" : "•"} ${presentation.title}`);
|
|
302
|
+
if (presentation.summary)
|
|
303
|
+
lines.push(` ${presentation.summary}`);
|
|
304
|
+
if (details) {
|
|
305
|
+
lines.push(` Packages: ${[...entry.packages].sort().join(", ")}`);
|
|
306
|
+
lines.push(` Evidence: ${[...entry.evidence].sort().slice(0, 16).join("; ")}`);
|
|
307
|
+
lines.push(` Capability: ${kind}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
lines.push("", "Nothing has run yet. These are clues found in the package, not proof that every action will occur.");
|
|
312
|
+
if (!details)
|
|
313
|
+
lines.push("For raw package and file evidence, rerun with --details.");
|
|
314
|
+
return lines.join("\n");
|
|
315
|
+
}
|
|
316
|
+
export function initializerPackage(initializer) {
|
|
317
|
+
const scopeOnly = /^(@[a-z0-9][a-z0-9._-]*)(?:@(.+))?$/i.exec(initializer);
|
|
318
|
+
if (scopeOnly) {
|
|
319
|
+
const version = scopeOnly[2];
|
|
320
|
+
return { specifier: `${scopeOnly[1]}/create${version ? `@${version}` : ""}`, binary: "create" };
|
|
321
|
+
}
|
|
322
|
+
let parsed;
|
|
323
|
+
try {
|
|
324
|
+
parsed = npa(initializer);
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
throw new ProcessCommandError(`Invalid initializer package: ${initializer}`);
|
|
328
|
+
}
|
|
329
|
+
if (!parsed.name || !["version", "range", "tag"].includes(parsed.type))
|
|
330
|
+
throw new ProcessCommandError(`Initializer must be a registry package: ${initializer}`);
|
|
331
|
+
const slash = parsed.name.lastIndexOf("/");
|
|
332
|
+
const shortName = slash < 0 ? parsed.name : parsed.name.slice(slash + 1);
|
|
333
|
+
const target = slash < 0 ? `create-${parsed.name}` : `${parsed.name.slice(0, slash)}/create-${shortName}`;
|
|
334
|
+
const suffix = parsed.rawSpec === "*" ? "" : `@${parsed.rawSpec}`;
|
|
335
|
+
return { specifier: `${target}${suffix}`, binary: `create-${shortName}` };
|
|
336
|
+
}
|
|
337
|
+
export async function runCommand(name, context) {
|
|
338
|
+
if (["install", "install-test", "install-ci-test", "add", "remove", "update", "run", "restart"].includes(name) && !context.options?.globalInstall) {
|
|
339
|
+
try {
|
|
340
|
+
for (const warning of await checkDevEngines(context.cwd))
|
|
341
|
+
context.output.info(`devEngines warning: ${warning}`);
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
if (error instanceof DevEnginesError) {
|
|
345
|
+
context.output.error(error.message);
|
|
346
|
+
return ExitCode.policyBlocked;
|
|
347
|
+
}
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (name === "edit") {
|
|
352
|
+
try {
|
|
353
|
+
const result = await editInstalledPackage({ cwd: context.cwd, name: context.args[0] ?? "", signal: context.signal });
|
|
354
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Edited ${result.package}`, evidence: result });
|
|
355
|
+
return ExitCode.success;
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
if (error instanceof Error && (error.name === "ProcessCommandError" || error.name === "ManifestError"))
|
|
359
|
+
return ExitCode.resolutionFailure;
|
|
360
|
+
throw error;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (name === "shrinkwrap") {
|
|
364
|
+
try {
|
|
365
|
+
const result = await shrinkwrapProject(context.cwd);
|
|
366
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Created npm-shrinkwrap.json with ${result.packages} package${result.packages === 1 ? "" : "s"}`, humanMessage: result.path, evidence: result });
|
|
367
|
+
return ExitCode.success;
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
if (error instanceof Error && (error.name === "LockfileError" || error.name === "ManifestError"))
|
|
371
|
+
return ExitCode.resolutionFailure;
|
|
372
|
+
throw error;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (name === "trust") {
|
|
376
|
+
try {
|
|
377
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
378
|
+
const common = { paths, ...selectedRegistry(context.options), signal: context.signal, ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) };
|
|
379
|
+
if (context.args[0] === "list") {
|
|
380
|
+
const packageName = context.args[1] ?? await currentPackageName(context.cwd);
|
|
381
|
+
const configurations = await packageTrust({ ...common, package: packageName });
|
|
382
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${configurations.length} trust configuration${configurations.length === 1 ? "" : "s"}`, humanMessage: JSON.stringify(configurations, null, 2), evidence: { package: packageName, configurations } });
|
|
383
|
+
}
|
|
384
|
+
else if (context.args[0] === "revoke") {
|
|
385
|
+
const packageName = context.args.length === 2 ? await currentPackageName(context.cwd) : context.args[1] ?? "";
|
|
386
|
+
const id = context.args.length === 2 ? context.args[1] ?? "" : context.args[2] ?? "";
|
|
387
|
+
await revokePackageTrust({ ...common, package: packageName, id, dryRun: context.options?.dryRun === true });
|
|
388
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${context.options?.dryRun ? "Would revoke" : "Revoked"} trust configuration ${id}`, evidence: { package: packageName, id, dryRun: context.options?.dryRun === true } });
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
const provider = context.args[0];
|
|
392
|
+
const packageName = context.args[1] ?? await currentPackageName(context.cwd);
|
|
393
|
+
const configuration = trustConfiguration(provider, context.options);
|
|
394
|
+
if (context.options?.dryRun) {
|
|
395
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Would create ${provider} trust for ${packageName}`, humanMessage: JSON.stringify(configuration, null, 2), evidence: { package: packageName, configuration, dryRun: true } });
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
const confirmed = await confirmCommand(`Anyone with matching ${provider} workflow access may publish ${packageName}. Create this trusted publisher?`, context.options);
|
|
399
|
+
if (!confirmed) {
|
|
400
|
+
context.output.info("Trusted-publisher creation requires interactive confirmation or --yes");
|
|
401
|
+
return ExitCode.policyBlocked;
|
|
402
|
+
}
|
|
403
|
+
const configurations = await createPackageTrust({ ...common, package: packageName, configuration });
|
|
404
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Created ${provider} trust for ${packageName}`, humanMessage: JSON.stringify(configurations, null, 2), evidence: { package: packageName, configurations } });
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return ExitCode.success;
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
411
|
+
return ExitCode.networkFailure;
|
|
412
|
+
if (error instanceof Error && (error.name === "ManifestError" || error.name === "PackError" || error.name === "TrustError"))
|
|
413
|
+
return ExitCode.resolutionFailure;
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (name === "stage" && context.args[0] !== "publish") {
|
|
418
|
+
try {
|
|
419
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
420
|
+
const common = { paths, ...selectedRegistry(context.options), signal: context.signal, ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) };
|
|
421
|
+
const action = context.args[0];
|
|
422
|
+
if (action === "list") {
|
|
423
|
+
const items = await stagedPackages({ ...common, ...(context.args[1] === undefined ? {} : { package: context.args[1] }) });
|
|
424
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${items.length} staged package${items.length === 1 ? "" : "s"}`, humanMessage: JSON.stringify(items, null, 2), evidence: { items } });
|
|
425
|
+
}
|
|
426
|
+
else if (action === "view") {
|
|
427
|
+
const item = await stagedPackage({ ...common, id: context.args[1] ?? "" });
|
|
428
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Staged package ${context.args[1]}`, humanMessage: JSON.stringify(item, null, 2), evidence: item });
|
|
429
|
+
}
|
|
430
|
+
else if (action === "download") {
|
|
431
|
+
const item = await downloadStage({ ...common, cwd: context.cwd, id: context.args[1] ?? "" });
|
|
432
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Downloaded ${item.package}@${item.version}`, humanMessage: item.filename, evidence: item });
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
await mutateStagedPackage({ ...common, id: context.args[1] ?? "", action: action === "approve" ? "approve" : "reject" });
|
|
436
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${action === "approve" ? "Approved" : "Rejected"} staged package ${context.args[1]}`, evidence: { action, id: context.args[1] } });
|
|
437
|
+
}
|
|
438
|
+
return ExitCode.success;
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
442
|
+
return ExitCode.networkFailure;
|
|
443
|
+
if (error instanceof Error && error.name === "ArchiveError")
|
|
444
|
+
return ExitCode.integrityFailure;
|
|
445
|
+
if (error instanceof Error && error.name === "PackError")
|
|
446
|
+
return ExitCode.resolutionFailure;
|
|
447
|
+
throw error;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (name === "profile") {
|
|
451
|
+
try {
|
|
452
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
453
|
+
const common = { paths, ...selectedRegistry(context.options), signal: context.signal, ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) };
|
|
454
|
+
const action = context.args[0];
|
|
455
|
+
if (action === "get") {
|
|
456
|
+
const profile = await registryProfile(common);
|
|
457
|
+
const key = context.args[1];
|
|
458
|
+
const output = key === undefined ? profile : { [key]: profile[key] };
|
|
459
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: "Registry profile", humanMessage: JSON.stringify(output, null, 2), evidence: output });
|
|
460
|
+
}
|
|
461
|
+
else if (action === "set" && context.args[1] !== "password") {
|
|
462
|
+
const key = context.args[1] ?? "";
|
|
463
|
+
const profile = await setRegistryProfile({ ...common, key, value: context.args[2] ?? "" });
|
|
464
|
+
const output = { [key]: profile[key] };
|
|
465
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: "Registry profile", humanMessage: JSON.stringify(output, null, 2), evidence: output });
|
|
466
|
+
}
|
|
467
|
+
else if (action === "set") {
|
|
468
|
+
const current = await readSecret("Current registry password: ", context.signal);
|
|
469
|
+
const next = await readSecret("New registry password: ", context.signal);
|
|
470
|
+
const confirmation = await readSecret("Confirm new registry password: ", context.signal);
|
|
471
|
+
if (!current || !next || next !== confirmation) {
|
|
472
|
+
context.output.info("Password change cancelled or new passwords did not match");
|
|
473
|
+
return ExitCode.policyBlocked;
|
|
474
|
+
}
|
|
475
|
+
await mutateRegistryProfile({ ...common, change: { password: { old: current, new: next } }, preserveWritable: true });
|
|
476
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: "Changed registry password" });
|
|
477
|
+
}
|
|
478
|
+
else if (["enable-2fa", "enable-tfa", "enable2fa", "enabletfa"].includes(action ?? "")) {
|
|
479
|
+
const mode = context.args[1] ?? "auth-and-writes";
|
|
480
|
+
const password = await readSecret("Registry password: ", context.signal);
|
|
481
|
+
if (!password) {
|
|
482
|
+
context.output.info("Enabling 2FA requires a masked password prompt");
|
|
483
|
+
return ExitCode.policyBlocked;
|
|
484
|
+
}
|
|
485
|
+
const challenge = await mutateRegistryProfile({ ...common, change: { tfa: { password, mode } } });
|
|
486
|
+
if (typeof challenge.tfa === "object" && challenge.tfa !== null) {
|
|
487
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Two-factor authentication set to ${mode}` });
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
let setup;
|
|
491
|
+
try {
|
|
492
|
+
setup = new URL(typeof challenge.tfa === "string" ? challenge.tfa : "");
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
throw new PackError("registry returned an invalid two-factor setup challenge");
|
|
496
|
+
}
|
|
497
|
+
if (setup.protocol !== "otpauth:" || setup.searchParams.get("secret") === null)
|
|
498
|
+
throw new PackError("registry returned an invalid two-factor setup challenge");
|
|
499
|
+
const otp = await readSecret(`Authenticator setup URI: ${setup.href}\nOne-time code: `, context.signal);
|
|
500
|
+
if (!otp || !/^\d{6,10}$/.test(otp)) {
|
|
501
|
+
context.output.info("Two-factor setup requires a valid one-time code");
|
|
502
|
+
return ExitCode.policyBlocked;
|
|
503
|
+
}
|
|
504
|
+
const finalized = await mutateRegistryProfile({ ...common, change: { tfa: [otp] } });
|
|
505
|
+
if (!Array.isArray(finalized.tfa) || finalized.tfa.some((code) => typeof code !== "string"))
|
|
506
|
+
throw new PackError("registry returned invalid two-factor recovery codes");
|
|
507
|
+
const recoveryCodes = finalized.tfa;
|
|
508
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: "Enabled two-factor authentication; store the recovery codes securely", humanMessage: recoveryCodes.join("\n"), evidence: { recoveryCodes } });
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
const password = await readSecret("Registry password: ", context.signal);
|
|
513
|
+
if (!password) {
|
|
514
|
+
context.output.info("Disabling 2FA requires a masked password prompt");
|
|
515
|
+
return ExitCode.policyBlocked;
|
|
516
|
+
}
|
|
517
|
+
await mutateRegistryProfile({ ...common, change: { tfa: { password, mode: "disable" } } });
|
|
518
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: "Disabled two-factor authentication" });
|
|
519
|
+
}
|
|
520
|
+
return ExitCode.success;
|
|
521
|
+
}
|
|
522
|
+
catch (error) {
|
|
523
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
524
|
+
return ExitCode.networkFailure;
|
|
525
|
+
if (error instanceof Error && error.name === "PackError")
|
|
526
|
+
return ExitCode.resolutionFailure;
|
|
527
|
+
throw error;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (name === "org" || name === "team") {
|
|
531
|
+
try {
|
|
532
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
533
|
+
const common = { paths, ...selectedRegistry(context.options), signal: context.signal, ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) };
|
|
534
|
+
if (name === "org") {
|
|
535
|
+
const [action, organization, user, role] = context.args;
|
|
536
|
+
if (action === "ls" || action === "list") {
|
|
537
|
+
const members = await organizationMembers({ ...common, organization: organization ?? "" });
|
|
538
|
+
const selected = user === undefined ? members : members[user] === undefined ? {} : { [user]: members[user] };
|
|
539
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${Object.keys(selected).length} organization member${Object.keys(selected).length === 1 ? "" : "s"}`, humanMessage: Object.entries(selected).map(([name, value]) => `${name} - ${value}`).join("\n"), evidence: { organization, members: selected } });
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
await mutateOrganizationMember({ ...common, organization: organization ?? "", user: user ?? "", ...(action === "set" || action === "add" ? { role: (role ?? "developer") } : {}) });
|
|
543
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${action === "rm" || action === "remove" ? "Removed" : "Updated"} organization member`, evidence: { action, organization, user, role: role ?? "developer" } });
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
const [action, entity, user] = context.args;
|
|
548
|
+
if (action === "ls" || action === "list") {
|
|
549
|
+
const entries = await registryTeamEntries({ ...common, entity: entity ?? "" });
|
|
550
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${entries.length} team entr${entries.length === 1 ? "y" : "ies"}`, humanMessage: entries.join("\n"), evidence: { entity, entries } });
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
const mutation = action === "rm" || action === "remove" ? "remove" : action;
|
|
554
|
+
await mutateRegistryTeam({ ...common, action: mutation, entity: entity ?? "", ...(user === undefined ? {} : { user }) });
|
|
555
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${mutation} team operation completed`, evidence: { action: mutation, entity, user } });
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return ExitCode.success;
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
562
|
+
return ExitCode.networkFailure;
|
|
563
|
+
throw error;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (name === "star" || name === "unstar" || name === "stars") {
|
|
567
|
+
try {
|
|
568
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
569
|
+
const registry = selectedRegistry(context.options).registry;
|
|
570
|
+
const common = { paths, ...(registry === undefined ? {} : { registry }), signal: context.signal };
|
|
571
|
+
const user = context.args[0] && name === "stars" ? context.args[0] : await registryWhoami(common);
|
|
572
|
+
if (name === "stars") {
|
|
573
|
+
const packages = await starredPackages({ ...common, user });
|
|
574
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${packages.length} favorite package${packages.length === 1 ? "" : "s"}`, humanMessage: packages.join("\n") || "No favorite packages", evidence: { user, packages } });
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
for (const packageName of context.args)
|
|
578
|
+
await mutatePackageStar({ ...common, package: packageName, user, starred: name === "star", ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) });
|
|
579
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${name === "star" ? "Starred" : "Unstarred"} ${context.args.length} package${context.args.length === 1 ? "" : "s"}`, evidence: { user, packages: context.args, starred: name === "star" } });
|
|
580
|
+
}
|
|
581
|
+
return ExitCode.success;
|
|
582
|
+
}
|
|
583
|
+
catch (error) {
|
|
584
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
585
|
+
return ExitCode.networkFailure;
|
|
586
|
+
throw error;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (name === "completion") {
|
|
590
|
+
const commands = [...commandNames].sort().join(" ");
|
|
591
|
+
const script = `# bash/zsh completion for bnpm\n_bnpm_complete() { COMPREPLY=( $(compgen -W '${commands}' -- \"${'$'}{COMP_WORDS[COMP_CWORD]}\") ); }\ncomplete -F _bnpm_complete bnpm bnpmx`;
|
|
592
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: "Shell completion", humanMessage: script, evidence: { script } });
|
|
593
|
+
return ExitCode.success;
|
|
594
|
+
}
|
|
595
|
+
if (name === "find-dupes") {
|
|
596
|
+
try {
|
|
597
|
+
const duplicates = await findInstalledDuplicates(context.cwd);
|
|
598
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${duplicates.length} duplicated package name${duplicates.length === 1 ? "" : "s"}`, humanMessage: duplicates.map((entry) => `${entry.name}: ${entry.versions.join(", ")}`).join("\n") || "No duplicate packages", evidence: { duplicates } });
|
|
599
|
+
return ExitCode.success;
|
|
600
|
+
}
|
|
601
|
+
catch (error) {
|
|
602
|
+
if (error instanceof Error && error.name === "LockfileError")
|
|
603
|
+
return ExitCode.resolutionFailure;
|
|
604
|
+
throw error;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (name === "token") {
|
|
608
|
+
try {
|
|
609
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
610
|
+
const registry = selectedRegistry(context.options).registry;
|
|
611
|
+
if (context.args[0] === "create") {
|
|
612
|
+
const password = await readSecret("Registry password: ", context.signal);
|
|
613
|
+
if (!password) {
|
|
614
|
+
context.output.info("Token creation requires a masked interactive password prompt");
|
|
615
|
+
return ExitCode.policyBlocked;
|
|
616
|
+
}
|
|
617
|
+
const body = tokenCreationBody(context.options);
|
|
618
|
+
const token = await createRegistryToken({ paths, password, body, ...(registry === undefined ? {} : { registry }), signal: context.signal, ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) });
|
|
619
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: "Created authentication token", humanMessage: token.token, evidence: token });
|
|
620
|
+
}
|
|
621
|
+
else if (context.args[0] === "list" || context.args[0] === "ls") {
|
|
622
|
+
const tokens = await registryTokens({ paths, ...(registry === undefined ? {} : { registry }), signal: context.signal });
|
|
623
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${tokens.length} authentication token${tokens.length === 1 ? "" : "s"}`, humanMessage: tokens.map((token) => `${token.id}${token.name ? ` ${token.name}` : ""}${token.readonly ? " read-only" : ""}`).join("\n") || "No authentication tokens", evidence: { tokens } });
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
const ids = await revokeRegistryTokens({ paths, ...(registry === undefined ? {} : { registry }), ids: context.args.slice(1), signal: context.signal, ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) });
|
|
627
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Revoked ${ids.length} authentication token${ids.length === 1 ? "" : "s"}`, evidence: { ids } });
|
|
628
|
+
}
|
|
629
|
+
return ExitCode.success;
|
|
630
|
+
}
|
|
631
|
+
catch (error) {
|
|
632
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
633
|
+
return ExitCode.networkFailure;
|
|
634
|
+
throw error;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (name === "diff") {
|
|
638
|
+
try {
|
|
639
|
+
const explicitSpecs = context.options?.diffSpecs;
|
|
640
|
+
const legacySpec = explicitSpecs === undefined ? context.args[0] : undefined;
|
|
641
|
+
const filters = explicitSpecs === undefined ? context.args.slice(legacySpec === undefined ? 0 : 1) : context.args;
|
|
642
|
+
const result = await diffPackage({ cwd: context.cwd, ...(explicitSpecs === undefined ? legacySpec === undefined ? {} : { spec: legacySpec } : { specs: explicitSpecs }), ...(filters.length === 0 ? {} : { filters }), ...(context.options?.tag === undefined ? {} : { tag: context.options.tag }), render: { ...(context.options?.diffNameOnly ? { nameOnly: true } : {}), ...(context.options?.diffUnified === undefined ? {} : { unified: context.options.diffUnified }), ...(context.options?.diffIgnoreAllSpace ? { ignoreAllSpace: true } : {}), ...(context.options?.diffNoPrefix ? { noPrefix: true } : {}), ...(context.options?.diffSrcPrefix === undefined ? {} : { srcPrefix: context.options.diffSrcPrefix }), ...(context.options?.diffDstPrefix === undefined ? {} : { dstPrefix: context.options.diffDstPrefix }), ...(context.options?.diffText ? { text: true } : {}) }, ...selectedRegistry(context.options), signal: context.signal });
|
|
643
|
+
const human = result.text || "No package differences";
|
|
644
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${result.added.length + result.removed.length + result.changed.length} package file difference${result.added.length + result.removed.length + result.changed.length === 1 ? "" : "s"}`, humanMessage: human, evidence: result });
|
|
645
|
+
return ExitCode.success;
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
649
|
+
return ExitCode.networkFailure;
|
|
650
|
+
if (error instanceof Error && (error.name === "PackError" || error.name === "ArchiveError" || error.name === "IntegrityError"))
|
|
651
|
+
return ExitCode.integrityFailure;
|
|
652
|
+
throw error;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (name === "approve-scripts" || name === "deny-scripts") {
|
|
656
|
+
try {
|
|
657
|
+
const action = name === "approve-scripts" ? "approve" : "deny";
|
|
658
|
+
const packages = await mutateScriptApprovals({ cwd: context.cwd, names: context.args, action });
|
|
659
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${action === "approve" ? "Approved" : "Denied"} scripts for ${packages.length} locked package${packages.length === 1 ? "" : "s"}`, evidence: { action, packages } });
|
|
660
|
+
return ExitCode.success;
|
|
661
|
+
}
|
|
662
|
+
catch (error) {
|
|
663
|
+
if (error instanceof Error && error.name === "LockfileError")
|
|
664
|
+
return ExitCode.resolutionFailure;
|
|
665
|
+
throw error;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (name === "install-scripts") {
|
|
669
|
+
try {
|
|
670
|
+
const action = context.args[0] ?? "";
|
|
671
|
+
if (action === "ls") {
|
|
672
|
+
const status = await inspectScriptApprovals({ cwd: context.cwd });
|
|
673
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${status.pending.length} package${status.pending.length === 1 ? "" : "s"} await script review`, humanMessage: status.pending.join("\n") || "No install scripts await review", evidence: status });
|
|
674
|
+
}
|
|
675
|
+
else if (action === "prune") {
|
|
676
|
+
const packages = await pruneScriptApprovals({ cwd: context.cwd, ...(context.options?.dryRun ? { dryRun: true } : {}) });
|
|
677
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${context.options?.dryRun ? "Would prune" : "Pruned"} ${packages.length} stale script approval${packages.length === 1 ? "" : "s"}`, humanMessage: packages.join("\n"), evidence: { packages, dryRun: context.options?.dryRun === true } });
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
const packages = await mutateScriptApprovals({ cwd: context.cwd, names: context.args.slice(1), action: action });
|
|
681
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${action === "approve" ? "Approved" : "Denied"} scripts for ${packages.length} locked package${packages.length === 1 ? "" : "s"}`, evidence: { action, packages } });
|
|
682
|
+
}
|
|
683
|
+
return ExitCode.success;
|
|
684
|
+
}
|
|
685
|
+
catch (error) {
|
|
686
|
+
if (error instanceof Error && error.name === "LockfileError")
|
|
687
|
+
return ExitCode.resolutionFailure;
|
|
688
|
+
throw error;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
if (name === "explore") {
|
|
692
|
+
try {
|
|
693
|
+
const discovered = await discoverProject(context.cwd);
|
|
694
|
+
const root = discovered?.importerRoot ?? context.cwd;
|
|
695
|
+
const code = await exploreInstalled(root, context.args[0] ?? "", context.args.slice(1), context.signal, context.output);
|
|
696
|
+
return code === 0 ? ExitCode.success : ExitCode.installIncomplete;
|
|
697
|
+
}
|
|
698
|
+
catch (error) {
|
|
699
|
+
if (error instanceof ProcessCommandError || (error instanceof Error && error.name === "ManifestError"))
|
|
700
|
+
return ExitCode.resolutionFailure;
|
|
701
|
+
throw error;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (name === "repo" || name === "docs" || name === "bugs") {
|
|
705
|
+
try {
|
|
706
|
+
let packageName = context.args[0];
|
|
707
|
+
if (!packageName) {
|
|
708
|
+
const discovered = await discoverProject(context.cwd);
|
|
709
|
+
const root = discovered?.importerRoot ?? context.cwd;
|
|
710
|
+
const path = join(root, "package.json");
|
|
711
|
+
packageName = parseManifest(await readFile(path, "utf8"), path).name;
|
|
712
|
+
}
|
|
713
|
+
if (!packageName)
|
|
714
|
+
throw new PackError("package.json has no package name");
|
|
715
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
716
|
+
const metadata = await viewPackage({ paths, ...selectedRegistry(context.options), spec: packageName, signal: context.signal });
|
|
717
|
+
const url = packageNavigationUrl(metadata, name, packageName);
|
|
718
|
+
await openExternal(url);
|
|
719
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Opened ${url.href}`, humanMessage: url.href, evidence: { package: packageName, url: url.href, kind: name } });
|
|
720
|
+
return ExitCode.success;
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
724
|
+
return ExitCode.networkFailure;
|
|
725
|
+
if (error instanceof Error && (error.name === "ManifestError" || error.name === "PackError"))
|
|
726
|
+
return ExitCode.resolutionFailure;
|
|
727
|
+
throw error;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (name === "access" || name === "owner") {
|
|
731
|
+
try {
|
|
732
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
733
|
+
const common = { paths, ...selectedRegistry(context.options), signal: context.signal, ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) };
|
|
734
|
+
if (name === "owner") {
|
|
735
|
+
const [action, first, second] = context.args;
|
|
736
|
+
const packageName = (action === "ls" || action === "list" ? first : second) ?? await currentPackageName(context.cwd);
|
|
737
|
+
const owners = action === "ls" || action === "list" ? await packageOwners({ ...common, package: packageName }) : await mutatePackageOwner({ ...common, action: action === "add" ? "add" : "remove", user: first ?? "", package: packageName });
|
|
738
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${owners.length} package owner${owners.length === 1 ? "" : "s"}`, humanMessage: owners.map((owner) => `${owner.name} <${owner.email}>`).join("\n") || "no owner found", evidence: { owners } });
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
const [action, subcommand, first, second] = context.args;
|
|
742
|
+
if ((action === "list" || action === "ls") && subcommand === "packages") {
|
|
743
|
+
const owner = first ?? await registryWhoami(common);
|
|
744
|
+
const result = await accessiblePackages({ ...common, owner });
|
|
745
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${Object.keys(result).length} accessible package${Object.keys(result).length === 1 ? "" : "s"}`, humanMessage: JSON.stringify(result, null, 2), evidence: result });
|
|
746
|
+
}
|
|
747
|
+
else if (action === "get" || action === "list" || action === "ls") {
|
|
748
|
+
const packageName = first ?? await currentPackageName(context.cwd);
|
|
749
|
+
const result = await packageAccess({ ...common, package: packageName, action: action === "get" ? "status" : "collaborators" });
|
|
750
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: "Package access", humanMessage: JSON.stringify(result, null, 2), evidence: result });
|
|
751
|
+
}
|
|
752
|
+
else if (action === "set") {
|
|
753
|
+
const [setting, value] = (subcommand ?? "").split("=");
|
|
754
|
+
const packageName = first ?? await currentPackageName(context.cwd);
|
|
755
|
+
await setPackageAccess({ ...common, package: packageName, ...(setting === "status" ? { access: value } : { mfa: value }) });
|
|
756
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Set package ${setting} to ${value}`, evidence: { package: packageName, setting, value } });
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
const packageName = (action === "grant" ? second : first) ?? await currentPackageName(context.cwd);
|
|
760
|
+
const team = action === "grant" ? first ?? "" : subcommand ?? "";
|
|
761
|
+
await mutateTeamAccess({ ...common, package: packageName, team, ...(action === "grant" ? { permission: subcommand } : {}) });
|
|
762
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${action === "grant" ? "Granted" : "Revoked"} team access`, evidence: { action, package: packageName, team } });
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return ExitCode.success;
|
|
766
|
+
}
|
|
767
|
+
catch (error) {
|
|
768
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
769
|
+
return ExitCode.networkFailure;
|
|
770
|
+
if (error instanceof Error && (error.name === "ManifestError" || error.name === "PackError"))
|
|
771
|
+
return ExitCode.resolutionFailure;
|
|
772
|
+
throw error;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (name === "unpublish") {
|
|
776
|
+
try {
|
|
777
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
778
|
+
const result = await unpublishPackage({ paths, ...selectedRegistry(context.options), package: context.args[0] ?? "", signal: context.signal, force: context.options?.force === true, dryRun: context.options?.dryRun === true, ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) });
|
|
779
|
+
const identity = `${result.name}${result.version ? `@${result.version}` : ""}`;
|
|
780
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${context.options?.dryRun ? "Would unpublish" : "Unpublished"} ${identity}`, humanMessage: `- ${identity}`, evidence: result });
|
|
781
|
+
return ExitCode.success;
|
|
782
|
+
}
|
|
783
|
+
catch (error) {
|
|
784
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
785
|
+
return ExitCode.networkFailure;
|
|
786
|
+
throw error;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (name === "query") {
|
|
790
|
+
try {
|
|
791
|
+
const location = await commandLocation(context);
|
|
792
|
+
const packages = await queryInstalledPackages({ cwd: location.cwd, selector: context.args[0] ?? "", ...(location.globalPaths === undefined ? {} : { paths: location.globalPaths }), ...(context.options?.workspaces ? { allWorkspaces: true } : {}), ...selectedRegistry(context.options), signal: context.signal });
|
|
793
|
+
if (context.options?.expectResultCount !== undefined && packages.length !== context.options.expectResultCount)
|
|
794
|
+
throw new QueryError(`expected ${context.options.expectResultCount} result${context.options.expectResultCount === 1 ? "" : "s"}, found ${packages.length}`);
|
|
795
|
+
if (context.options?.expectResults === true && packages.length === 0)
|
|
796
|
+
throw new QueryError("expected at least one result, found none");
|
|
797
|
+
if (context.options?.expectResults === false && packages.length > 0)
|
|
798
|
+
throw new QueryError(`expected no results, found ${packages.length}`);
|
|
799
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${packages.length} matching package${packages.length === 1 ? "" : "s"}`, humanMessage: JSON.stringify(packages, null, 2), evidence: { packages } });
|
|
800
|
+
return ExitCode.success;
|
|
801
|
+
}
|
|
802
|
+
catch (error) {
|
|
803
|
+
if (error instanceof QueryError || (error instanceof Error && error.name === "LockfileError"))
|
|
804
|
+
return ExitCode.resolutionFailure;
|
|
805
|
+
throw error;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (name === "restart") {
|
|
809
|
+
try {
|
|
810
|
+
const scripts = await projectScriptNames(context.cwd);
|
|
811
|
+
const approvals = new Set(context.options?.allowDangerous ?? []);
|
|
812
|
+
if (scripts.has("stop")) {
|
|
813
|
+
const code = await runProjectScriptLifecycle(context.cwd, "stop", [], context.signal, context.output, approvals);
|
|
814
|
+
if (code !== 0)
|
|
815
|
+
return ExitCode.installIncomplete;
|
|
816
|
+
}
|
|
817
|
+
const target = scripts.has("restart") ? "restart" : scripts.has("start") ? "start" : undefined;
|
|
818
|
+
if (!target)
|
|
819
|
+
throw new ProcessCommandError("Unknown project script: restart");
|
|
820
|
+
const code = await runProjectScriptLifecycle(context.cwd, target, [], context.signal, context.output, approvals);
|
|
821
|
+
return code === 0 ? ExitCode.success : ExitCode.installIncomplete;
|
|
822
|
+
}
|
|
823
|
+
catch (error) {
|
|
824
|
+
if (error instanceof ProcessCommandError || (error instanceof Error && error.name === "ManifestError"))
|
|
825
|
+
return ExitCode.resolutionFailure;
|
|
826
|
+
throw error;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (name === "link" || name === "unlink") {
|
|
830
|
+
try {
|
|
831
|
+
const result = name === "link"
|
|
832
|
+
? context.args.length === 0 ? [await registerLink({ cwd: context.cwd })] : await linkPackages({ cwd: context.cwd, names: context.args })
|
|
833
|
+
: await unregisterLink({ cwd: context.cwd, names: context.args });
|
|
834
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${name === "link" ? "Linked" : "Unlinked"} ${result.length} package${result.length === 1 ? "" : "s"}`, evidence: { packages: result } });
|
|
835
|
+
return ExitCode.success;
|
|
836
|
+
}
|
|
837
|
+
catch (error) {
|
|
838
|
+
if (error instanceof Error && (error.name === "ManifestError" || error.code === "ENOENT"))
|
|
839
|
+
return ExitCode.resolutionFailure;
|
|
840
|
+
throw error;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (name === "pkg") {
|
|
844
|
+
try {
|
|
845
|
+
const action = context.args[0];
|
|
846
|
+
const value = await packageProperties({ directory: context.cwd, action, operands: context.args.slice(1) });
|
|
847
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: action === "get" ? "Package properties" : `Package properties ${action === "set" ? "updated" : "deleted"}`, humanMessage: JSON.stringify(value, null, 2), evidence: value });
|
|
848
|
+
return ExitCode.success;
|
|
849
|
+
}
|
|
850
|
+
catch (error) {
|
|
851
|
+
if (error instanceof Error && error.name === "ManifestError")
|
|
852
|
+
return ExitCode.resolutionFailure;
|
|
853
|
+
throw error;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
if (name === "sbom") {
|
|
857
|
+
try {
|
|
858
|
+
const document = await createSbom({ cwd: context.cwd, format: context.options?.sbomFormat ?? "cyclonedx" });
|
|
859
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${context.options?.sbomFormat ?? "cyclonedx"} SBOM`, humanMessage: JSON.stringify(document, null, 2), evidence: document });
|
|
860
|
+
return ExitCode.success;
|
|
861
|
+
}
|
|
862
|
+
catch (error) {
|
|
863
|
+
if (error instanceof Error && error.name === "LockfileError")
|
|
864
|
+
return ExitCode.resolutionFailure;
|
|
865
|
+
throw error;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
if (name === "doctor") {
|
|
869
|
+
const checks = await diagnose({ cwd: context.cwd, ...selectedRegistry(context.options), signal: context.signal });
|
|
870
|
+
const failed = checks.filter((check) => !check.ok);
|
|
871
|
+
const human = checks.map((check) => `${check.ok ? "ok" : "not ok"} - ${check.name}: ${check.detail}`).join("\n");
|
|
872
|
+
const exitCode = failed.length === 0 ? ExitCode.success : failed.some((check) => check.name === "cache") ? ExitCode.integrityFailure : ExitCode.networkFailure;
|
|
873
|
+
context.output.result({ status: failed.length === 0 ? "success" : "failure", category: failed.length === 0 ? "success" : exitCode === ExitCode.integrityFailure ? "integrity" : "network", exitCode, summary: failed.length === 0 ? "All diagnostics passed" : `${failed.length} diagnostics failed`, humanMessage: human, evidence: { checks } });
|
|
874
|
+
return exitCode;
|
|
875
|
+
}
|
|
876
|
+
if (name === "ping") {
|
|
877
|
+
try {
|
|
878
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
879
|
+
const result = await pingRegistry({ paths, ...selectedRegistry(context.options), signal: context.signal });
|
|
880
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `PONG ${result.registry}`, humanMessage: `PONG ${result.registry}`, evidence: result });
|
|
881
|
+
return ExitCode.success;
|
|
882
|
+
}
|
|
883
|
+
catch (error) {
|
|
884
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError" || error.name === "TypeError"))
|
|
885
|
+
return ExitCode.networkFailure;
|
|
886
|
+
throw error;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if (name === "cache") {
|
|
890
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
891
|
+
if (context.args[0] === "clean") {
|
|
892
|
+
const filter = context.args[1];
|
|
893
|
+
if (filter !== undefined) {
|
|
894
|
+
const removed = await cleanCacheEntries(paths, filter);
|
|
895
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Removed ${removed.length} cache entr${removed.length === 1 ? "y" : "ies"}`, evidence: { removed } });
|
|
896
|
+
}
|
|
897
|
+
else {
|
|
898
|
+
const removed = await cleanCache(paths);
|
|
899
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Cleaned ${removed.length} cache director${removed.length === 1 ? "y" : "ies"}`, evidence: { removed } });
|
|
900
|
+
}
|
|
901
|
+
return ExitCode.success;
|
|
902
|
+
}
|
|
903
|
+
if (["ls", "list", "info"].includes(context.args[0] ?? "")) {
|
|
904
|
+
const entries = await listCache(paths, context.args[1]);
|
|
905
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${entries.length} cache entr${entries.length === 1 ? "y" : "ies"}`, humanMessage: entries.map((entry) => `${entry.id} ${entry.integrity}`).join("\n") || "Cache is empty", evidence: { entries } });
|
|
906
|
+
return ExitCode.success;
|
|
907
|
+
}
|
|
908
|
+
if (context.args[0] === "add") {
|
|
909
|
+
await mkdir(paths.ephemeralRoot, { recursive: true, mode: 0o700 });
|
|
910
|
+
const temporary = await mkdtemp(join(paths.ephemeralRoot, "cache-add-"));
|
|
911
|
+
try {
|
|
912
|
+
const cacheOptions = { json: context.options?.json ?? false, ...(context.options?.registry === undefined ? {} : { registry: context.options.registry }), allowRecent: context.options?.allowRecent ?? [], allowDangerous: context.options?.allowDangerous ?? [], frozenLockfile: false, offline: false, omitDev: true, saveExact: false, noSave: true, ignoreScripts: true };
|
|
913
|
+
const requested = context.args[1] ?? "";
|
|
914
|
+
const specification = requested.startsWith("file:./") || requested.startsWith("file:../") ? `file:${relative(temporary, resolve(context.cwd, requested.slice("file:".length))).split(sep).join("/")}` : requested;
|
|
915
|
+
const result = await installProject({ cwd: temporary, ...selectedRegistry(context.options), specifications: [specification], commandOptions: cacheOptions, signal: context.signal, prompts: commandInstallPrompts(cacheOptions), onSecurityEvidence: (message, evidence) => context.output.info(message, evidence) });
|
|
916
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Cached ${result.graph.packages.size} package${result.graph.packages.size === 1 ? "" : "s"}`, evidence: { packages: [...result.graph.packages.keys()].sort() } });
|
|
917
|
+
return ExitCode.success;
|
|
918
|
+
}
|
|
919
|
+
finally {
|
|
920
|
+
await rm(temporary, { recursive: true, force: true });
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
const result = await verifyCache(paths);
|
|
924
|
+
context.output.result({ status: result.corrupt.length === 0 ? "success" : "failure", category: result.corrupt.length === 0 ? "success" : "integrity", exitCode: result.corrupt.length === 0 ? ExitCode.success : ExitCode.integrityFailure, summary: `${result.valid}/${result.entries} cache entries verified`, evidence: result });
|
|
925
|
+
return result.corrupt.length === 0 ? ExitCode.success : ExitCode.integrityFailure;
|
|
926
|
+
}
|
|
927
|
+
if (name === "fund") {
|
|
928
|
+
try {
|
|
929
|
+
const entries = await listFunding({ cwd: context.cwd });
|
|
930
|
+
const human = entries.map((entry) => `${entry.package}${entry.type ? ` (${entry.type})` : ""}: ${entry.url}`).join("\n") || "No funding information found";
|
|
931
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${entries.length} funding link${entries.length === 1 ? "" : "s"}`, humanMessage: human, evidence: { entries } });
|
|
932
|
+
return ExitCode.success;
|
|
933
|
+
}
|
|
934
|
+
catch (error) {
|
|
935
|
+
if (error instanceof Error && error.name === "LockfileError")
|
|
936
|
+
return ExitCode.resolutionFailure;
|
|
937
|
+
throw error;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (name === "rebuild") {
|
|
941
|
+
try {
|
|
942
|
+
const result = await rebuildPackages({ cwd: context.cwd, names: context.args, allowedDangerous: new Set(context.options?.allowDangerous ?? []), ...(context.options?.ignoreScripts ? { ignoreScripts: true } : {}), signal: context.signal, onOutput: (stream, text, attribution) => context.output.childOutput(stream, text, attribution) });
|
|
943
|
+
context.output.info(`Rebuilt ${result.rebuilt.length} lifecycle stage${result.rebuilt.length === 1 ? "" : "s"}`, result);
|
|
944
|
+
if (result.skipped.length > 0) {
|
|
945
|
+
context.output.error(`Skipped ${result.skipped.length} unapproved lifecycle stage${result.skipped.length === 1 ? "" : "s"}`);
|
|
946
|
+
return ExitCode.installIncomplete;
|
|
947
|
+
}
|
|
948
|
+
return ExitCode.success;
|
|
949
|
+
}
|
|
950
|
+
catch (error) {
|
|
951
|
+
if (error instanceof Error && (error.name === "PolicyError" || error.name === "LockfileError"))
|
|
952
|
+
return ExitCode.policyBlocked;
|
|
953
|
+
if (error instanceof Error && error.name === "ResolutionError")
|
|
954
|
+
return ExitCode.resolutionFailure;
|
|
955
|
+
if (error instanceof Error && error.name === "ScriptExecutionError")
|
|
956
|
+
return ExitCode.installIncomplete;
|
|
957
|
+
throw error;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
if (name === "init") {
|
|
961
|
+
try {
|
|
962
|
+
if (context.options?.workspaceNames !== undefined) {
|
|
963
|
+
if (context.options.workspaceNames.length !== 1)
|
|
964
|
+
throw new PackError("init requires exactly one --workspace path");
|
|
965
|
+
const prepared = await initializeWorkspace({ root: context.cwd, workspace: context.options.workspaceNames[0] ?? "", createManifest: context.args.length === 0 });
|
|
966
|
+
if (context.args.length > 0) {
|
|
967
|
+
const initializer = initializerPackage(context.args[0] ?? "");
|
|
968
|
+
const code = await execEphemeral({ ...context, cwd: prepared.directory }, [initializer.specifier], initializer.binary, context.args.slice(1));
|
|
969
|
+
return code === 0 ? ExitCode.success : ExitCode.installIncomplete;
|
|
970
|
+
}
|
|
971
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Created workspace ${prepared.workspace}`, humanMessage: join(prepared.directory, "package.json"), evidence: prepared });
|
|
972
|
+
return ExitCode.success;
|
|
973
|
+
}
|
|
974
|
+
if (context.args.length > 0) {
|
|
975
|
+
const initializer = initializerPackage(context.args[0] ?? "");
|
|
976
|
+
const code = await execEphemeral(context, [initializer.specifier], initializer.binary, context.args.slice(1));
|
|
977
|
+
return code === 0 ? ExitCode.success : ExitCode.installIncomplete;
|
|
978
|
+
}
|
|
979
|
+
if (!context.options?.yes)
|
|
980
|
+
context.output.info("Using safe defaults; pass --yes to suppress this notice.");
|
|
981
|
+
const manifest = await initializePackage({ directory: context.cwd });
|
|
982
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Created ${join(context.cwd, "package.json")}`, evidence: manifest });
|
|
983
|
+
return ExitCode.success;
|
|
984
|
+
}
|
|
985
|
+
catch (error) {
|
|
986
|
+
if ((error instanceof Error && error.name === "PackError") || error instanceof ProcessCommandError)
|
|
987
|
+
return ExitCode.resolutionFailure;
|
|
988
|
+
throw error;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (name === "version") {
|
|
992
|
+
try {
|
|
993
|
+
if (context.options?.workspaces || context.options?.workspaceNames !== undefined) {
|
|
994
|
+
const roots = await selectedWorkspaceRoots(context.cwd, context.options);
|
|
995
|
+
const versions = await Promise.all(roots.map(async (root) => ({ root, ...(await readPackageVersion(root)), original: await readFile(join(root, "package.json"), "utf8") })));
|
|
996
|
+
if (context.args.length === 0) {
|
|
997
|
+
const evidence = Object.fromEntries(versions.map((entry) => [entry.name, entry.version]));
|
|
998
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${versions.length} package version${versions.length === 1 ? "" : "s"}`, humanMessage: versions.map((entry) => `${entry.name}\nv${entry.version}`).join("\n"), evidence });
|
|
999
|
+
return ExitCode.success;
|
|
1000
|
+
}
|
|
1001
|
+
let requested = context.args[0] ?? "";
|
|
1002
|
+
if (requested === "from-git") {
|
|
1003
|
+
const repository = await gitRoot(context.cwd, context.signal);
|
|
1004
|
+
if (!repository)
|
|
1005
|
+
throw new PackError("from-git requires a Git repository with a version tag");
|
|
1006
|
+
const tag = await runAuthoringGit(repository, ["describe", "--tags", "--abbrev=0"], context.signal);
|
|
1007
|
+
requested = tag.startsWith("v") ? tag.slice(1) : tag;
|
|
1008
|
+
}
|
|
1009
|
+
const planned = versions.map((entry) => ({ ...entry, next: nextPackageVersion(entry.version, requested, { ...(context.options?.preid === undefined ? {} : { preid: context.options.preid }), ...(context.options?.allowSameVersion ? { allowSame: true } : {}) }) }));
|
|
1010
|
+
const discovered = await discoverProject(context.cwd);
|
|
1011
|
+
const projectRoot = discovered?.projectRoot ?? context.cwd;
|
|
1012
|
+
const rootEntry = planned.find((entry) => resolve(entry.root) === resolve(projectRoot));
|
|
1013
|
+
const workspaceVersions = new Map(planned.filter((entry) => entry !== rootEntry).map((entry) => [relative(projectRoot, entry.root).split(sep).join("/"), entry.next]));
|
|
1014
|
+
const lockfileUpdates = context.options.noSave ? [] : await prepareVersionLockfileUpdates(projectRoot, rootEntry?.next, workspaceVersions);
|
|
1015
|
+
const approvals = new Set(context.options.allowDangerous ?? []);
|
|
1016
|
+
try {
|
|
1017
|
+
for (const entry of planned) {
|
|
1018
|
+
const environment = { npm_old_version: entry.version, npm_new_version: entry.next };
|
|
1019
|
+
if (!context.options.ignoreScripts)
|
|
1020
|
+
await runProjectScriptIfPresent(entry.root, "preversion", context.signal, context.output, approvals, environment);
|
|
1021
|
+
await changePackageVersion(entry.root, entry.next, { allowSame: true });
|
|
1022
|
+
if (!context.options.ignoreScripts)
|
|
1023
|
+
await runProjectScriptIfPresent(entry.root, "version", context.signal, context.output, approvals, environment);
|
|
1024
|
+
}
|
|
1025
|
+
for (const update of lockfileUpdates)
|
|
1026
|
+
await writeVersionFile(update.path, update.updated);
|
|
1027
|
+
}
|
|
1028
|
+
catch (error) {
|
|
1029
|
+
for (const entry of planned)
|
|
1030
|
+
await writeVersionFile(join(entry.root, "package.json"), entry.original);
|
|
1031
|
+
for (const update of lockfileUpdates)
|
|
1032
|
+
await writeVersionFile(update.path, update.original);
|
|
1033
|
+
throw error;
|
|
1034
|
+
}
|
|
1035
|
+
if (!context.options.ignoreScripts)
|
|
1036
|
+
for (const entry of planned)
|
|
1037
|
+
await runProjectScriptIfPresent(entry.root, "postversion", context.signal, context.output, approvals, { npm_old_version: entry.version, npm_new_version: entry.next });
|
|
1038
|
+
const evidence = Object.fromEntries(planned.map((entry) => [entry.name, { previous: entry.version, version: entry.next }]));
|
|
1039
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Versioned ${planned.length} package${planned.length === 1 ? "" : "s"}`, humanMessage: planned.map((entry) => `${entry.name}\nv${entry.next}`).join("\n"), evidence });
|
|
1040
|
+
return ExitCode.success;
|
|
1041
|
+
}
|
|
1042
|
+
if (context.args.length === 0) {
|
|
1043
|
+
const current = await readPackageVersion(context.cwd);
|
|
1044
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: current.version, humanMessage: current.version, evidence: { name: current.name, version: current.version, node: process.version } });
|
|
1045
|
+
return ExitCode.success;
|
|
1046
|
+
}
|
|
1047
|
+
const approvals = new Set(context.options?.allowDangerous ?? []);
|
|
1048
|
+
const current = await readPackageVersion(context.cwd);
|
|
1049
|
+
const detectedRepository = await gitRoot(context.cwd, context.signal);
|
|
1050
|
+
const repository = context.options?.gitTagVersion === false ? undefined : detectedRepository;
|
|
1051
|
+
if (repository && !context.options?.force && await runAuthoringGit(repository, ["status", "--porcelain"], context.signal))
|
|
1052
|
+
throw new PackError("Git working directory is not clean; use --force only after reviewing the changes");
|
|
1053
|
+
let requested = context.args[0] ?? "";
|
|
1054
|
+
if (requested === "from-git") {
|
|
1055
|
+
if (!detectedRepository)
|
|
1056
|
+
throw new PackError("from-git requires a Git repository with a version tag");
|
|
1057
|
+
const tag = await runAuthoringGit(detectedRepository, ["describe", "--tags", "--abbrev=0"], context.signal);
|
|
1058
|
+
requested = tag.startsWith("v") ? tag.slice(1) : tag;
|
|
1059
|
+
}
|
|
1060
|
+
const next = nextPackageVersion(current.version, requested, { ...(context.options?.preid === undefined ? {} : { preid: context.options.preid }), ...(context.options?.allowSameVersion ? { allowSame: true } : {}) });
|
|
1061
|
+
const versionEnvironment = { npm_old_version: current.version, npm_new_version: next };
|
|
1062
|
+
const manifestPath = join(context.cwd, "package.json");
|
|
1063
|
+
const original = await readFile(manifestPath, "utf8");
|
|
1064
|
+
const lockfileUpdates = context.options?.noSave ? [] : await prepareVersionLockfileUpdates(context.cwd, next);
|
|
1065
|
+
let previousHead;
|
|
1066
|
+
let committed = false;
|
|
1067
|
+
let changed;
|
|
1068
|
+
try {
|
|
1069
|
+
if (!context.options?.ignoreScripts)
|
|
1070
|
+
await runProjectScriptIfPresent(context.cwd, "preversion", context.signal, context.output, approvals, versionEnvironment);
|
|
1071
|
+
changed = await changePackageVersion(context.cwd, next, { allowSame: true });
|
|
1072
|
+
for (const update of lockfileUpdates)
|
|
1073
|
+
await writeVersionFile(update.path, update.updated);
|
|
1074
|
+
if (!context.options?.ignoreScripts)
|
|
1075
|
+
await runProjectScriptIfPresent(context.cwd, "version", context.signal, context.output, approvals, versionEnvironment);
|
|
1076
|
+
if (repository) {
|
|
1077
|
+
previousHead = await runAuthoringGit(repository, ["rev-parse", "HEAD"], context.signal);
|
|
1078
|
+
const changedPaths = [manifestPath, ...lockfileUpdates.map((update) => update.path)];
|
|
1079
|
+
const packagePaths = await Promise.all(changedPaths.map(async (path) => relative(repository, await realpath(path))));
|
|
1080
|
+
if (packagePaths.some((path) => !path || path.startsWith(`..${sep}`) || path === ".."))
|
|
1081
|
+
throw new PackError("version metadata is outside the Git repository");
|
|
1082
|
+
const tag = `v${next}`;
|
|
1083
|
+
try {
|
|
1084
|
+
await runAuthoringGit(repository, ["rev-parse", "--verify", `refs/tags/${tag}`], context.signal);
|
|
1085
|
+
throw new PackError(`Git tag ${tag} already exists`);
|
|
1086
|
+
}
|
|
1087
|
+
catch (error) {
|
|
1088
|
+
if (!(error instanceof PackError) || error.message === `Git tag ${tag} already exists`)
|
|
1089
|
+
throw error;
|
|
1090
|
+
}
|
|
1091
|
+
await runAuthoringGit(repository, ["add", "--", ...packagePaths], context.signal);
|
|
1092
|
+
const message = (context.options?.versionMessage ?? "v%s").replaceAll("%s", next);
|
|
1093
|
+
const commitArgs = ["commit", ...(context.options?.commitHooks === false ? ["--no-verify"] : []), "-m", message];
|
|
1094
|
+
await runAuthoringGit(repository, commitArgs, context.signal);
|
|
1095
|
+
committed = true;
|
|
1096
|
+
await runAuthoringGit(repository, context.options?.signGitTag ? ["tag", "-s", "-m", tag, tag] : ["tag", tag], context.signal);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
catch (error) {
|
|
1100
|
+
if (committed && previousHead && repository)
|
|
1101
|
+
await runAuthoringGit(repository, ["reset", "--mixed", previousHead], context.signal).catch(() => undefined);
|
|
1102
|
+
await writeVersionFile(manifestPath, original);
|
|
1103
|
+
for (const update of lockfileUpdates)
|
|
1104
|
+
await writeVersionFile(update.path, update.original);
|
|
1105
|
+
throw error;
|
|
1106
|
+
}
|
|
1107
|
+
if (!context.options?.ignoreScripts)
|
|
1108
|
+
await runProjectScriptIfPresent(context.cwd, "postversion", context.signal, context.output, approvals, versionEnvironment);
|
|
1109
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: changed.version, humanMessage: `v${changed.version}`, evidence: changed });
|
|
1110
|
+
return ExitCode.success;
|
|
1111
|
+
}
|
|
1112
|
+
catch (error) {
|
|
1113
|
+
if (error instanceof Error && error.name === "PackError")
|
|
1114
|
+
return ExitCode.resolutionFailure;
|
|
1115
|
+
if (error instanceof ProcessCommandError)
|
|
1116
|
+
return ExitCode.installIncomplete;
|
|
1117
|
+
throw error;
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
if (name === "config") {
|
|
1121
|
+
try {
|
|
1122
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
1123
|
+
const action = context.args[0];
|
|
1124
|
+
if (action === "list") {
|
|
1125
|
+
const values = await listConfig(paths);
|
|
1126
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: "Configuration", humanMessage: Object.entries(values).map(([entry, value]) => `${entry}=${value}`).join("\n"), evidence: values });
|
|
1127
|
+
}
|
|
1128
|
+
else if (action === "get") {
|
|
1129
|
+
const value = await getConfig(paths, context.args[1] ?? "");
|
|
1130
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: String(value), humanMessage: String(value), evidence: { key: context.args[1], value } });
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
await mutateConfig(paths, action === "set" ? "set" : "delete", context.args[1] ?? "", context.args[2]);
|
|
1134
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${action === "set" ? "Set" : "Deleted"} ${context.args[1]}`, evidence: { action, key: context.args[1] } });
|
|
1135
|
+
}
|
|
1136
|
+
return ExitCode.success;
|
|
1137
|
+
}
|
|
1138
|
+
catch (error) {
|
|
1139
|
+
if (error instanceof Error && error.name === "ConfigError")
|
|
1140
|
+
return ExitCode.policyBlocked;
|
|
1141
|
+
throw error;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
if (name === "view" || name === "search" || name === "dist-tag" || name === "deprecate") {
|
|
1145
|
+
try {
|
|
1146
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
1147
|
+
const registry = selectedRegistry(context.options).registry;
|
|
1148
|
+
const common = { paths, ...(registry === undefined ? {} : { registry }), signal: context.signal };
|
|
1149
|
+
if (name === "view") {
|
|
1150
|
+
const result = await viewPackage({ ...common, spec: context.args[0] ?? "" });
|
|
1151
|
+
const identity = `${String(result.name ?? context.args[0])}@${String(result.version ?? "unknown")}`;
|
|
1152
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: identity, humanMessage: `${identity}${typeof result.description === "string" ? `\n${result.description}` : ""}`, evidence: result });
|
|
1153
|
+
}
|
|
1154
|
+
else if (name === "search") {
|
|
1155
|
+
const results = await searchPackages({ ...common, terms: context.args });
|
|
1156
|
+
const lines = results.map((result) => `${String(result.name ?? "unknown")}@${String(result.version ?? "unknown")}${typeof result.description === "string" ? ` - ${result.description}` : ""}`);
|
|
1157
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${results.length} package${results.length === 1 ? "" : "s"} found`, humanMessage: lines.join("\n") || "No matches found", evidence: { results } });
|
|
1158
|
+
}
|
|
1159
|
+
else if (name === "dist-tag") {
|
|
1160
|
+
const action = context.args[0];
|
|
1161
|
+
if (action === "ls" || action === "list") {
|
|
1162
|
+
const tags = await listDistTags({ ...common, package: context.args[1] ?? "" });
|
|
1163
|
+
const lines = Object.entries(tags).sort(([left], [right]) => left.localeCompare(right)).map(([tag, version]) => `${tag}: ${version}`);
|
|
1164
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${Object.keys(tags).length} distribution tags`, humanMessage: lines.join("\n"), evidence: { tags } });
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
const mutation = action === "add" ? "add" : "remove";
|
|
1168
|
+
const tag = context.args[2] ?? "latest";
|
|
1169
|
+
await mutateDistTag({ ...common, action: mutation, package: context.args[1] ?? "", tag, ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }) });
|
|
1170
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${mutation === "add" ? "Set" : "Removed"} tag ${tag}`, humanMessage: `${mutation === "add" ? "+" : "-"}${tag}`, evidence: { action: mutation, package: context.args[1], tag } });
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
else {
|
|
1174
|
+
const versions = await deprecatePackage({ ...common, package: context.args[0] ?? "", message: context.args[1] ?? "", ...(context.options?.otp === undefined ? {} : { otp: context.options.otp }), dryRun: context.options?.dryRun === true });
|
|
1175
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${context.options?.dryRun ? "Would deprecate" : "Deprecated"} ${versions.length} version${versions.length === 1 ? "" : "s"}`, evidence: { versions, message: context.args[1], dryRun: context.options?.dryRun === true } });
|
|
1176
|
+
}
|
|
1177
|
+
return ExitCode.success;
|
|
1178
|
+
}
|
|
1179
|
+
catch (error) {
|
|
1180
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
1181
|
+
return ExitCode.networkFailure;
|
|
1182
|
+
throw error;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
if (name === "login" || name === "logout" || name === "whoami") {
|
|
1186
|
+
try {
|
|
1187
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
1188
|
+
const registry = selectedRegistry(context.options).registry;
|
|
1189
|
+
if (name === "whoami") {
|
|
1190
|
+
const username = await registryWhoami({ paths, ...(registry === undefined ? {} : { registry }), signal: context.signal });
|
|
1191
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: username, humanMessage: username, evidence: { username } });
|
|
1192
|
+
}
|
|
1193
|
+
else if (name === "logout") {
|
|
1194
|
+
const result = await registryLogout({ paths, ...(registry === undefined ? {} : { registry }), signal: context.signal });
|
|
1195
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Logged out of ${result}`, humanMessage: `Logged out of ${result}`, evidence: { registry: result } });
|
|
1196
|
+
}
|
|
1197
|
+
else {
|
|
1198
|
+
if (context.options?.authType === "legacy") {
|
|
1199
|
+
const username = await readInput("Registry username: ", context.options.json);
|
|
1200
|
+
const password = await readSecret("Registry password: ", context.signal);
|
|
1201
|
+
const email = context.options.accountCreate ? await readInput("Email address: ", context.options.json) : undefined;
|
|
1202
|
+
if (!username || !password || (context.options.accountCreate && !email)) {
|
|
1203
|
+
context.output.info("Legacy authentication requires interactive username, password, and adduser email inputs");
|
|
1204
|
+
return ExitCode.policyBlocked;
|
|
1205
|
+
}
|
|
1206
|
+
const result = await registryLegacyLogin({ paths, username, password, ...(email === undefined ? {} : { email }), create: context.options.accountCreate === true, ...(registry === undefined ? {} : { registry }), signal: context.signal, ...(context.options.otp === undefined ? {} : { otp: context.options.otp }) });
|
|
1207
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${result.created ? "Created" : "Authenticated"} registry user ${result.username}`, evidence: result });
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
const result = await registryLogin({ paths, ...(registry === undefined ? {} : { registry }), signal: context.signal, open: openExternal, announce: (url) => context.output.info(`Log in at ${url.href}`) });
|
|
1211
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Logged in to ${result.registry}`, humanMessage: `Logged in to ${result.registry}`, evidence: result });
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
return ExitCode.success;
|
|
1215
|
+
}
|
|
1216
|
+
catch (error) {
|
|
1217
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "ConfigError"))
|
|
1218
|
+
return ExitCode.networkFailure;
|
|
1219
|
+
throw error;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (name === "pack") {
|
|
1223
|
+
try {
|
|
1224
|
+
const packageRoot = resolve(context.cwd, context.args[0] ?? ".");
|
|
1225
|
+
const approvals = new Set(context.options?.allowDangerous ?? []);
|
|
1226
|
+
if (!context.options?.ignoreScripts)
|
|
1227
|
+
await runProjectScriptIfPresent(packageRoot, "prepack", context.signal, context.output, approvals);
|
|
1228
|
+
if (!context.options?.ignoreScripts)
|
|
1229
|
+
await runProjectScriptIfPresent(packageRoot, "prepare", context.signal, context.output, approvals);
|
|
1230
|
+
const artifact = await packPackage(packageRoot);
|
|
1231
|
+
if (!context.options?.ignoreScripts)
|
|
1232
|
+
await runProjectScriptIfPresent(packageRoot, "postpack", context.signal, context.output, approvals);
|
|
1233
|
+
const destination = resolve(context.cwd, context.options?.packDestination ?? ".", artifact.filename);
|
|
1234
|
+
if (!context.options?.dryRun) {
|
|
1235
|
+
const temporary = `${destination}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`;
|
|
1236
|
+
try {
|
|
1237
|
+
await writeFile(temporary, artifact.tarball, { flag: "wx", mode: 0o644 });
|
|
1238
|
+
await rename(temporary, destination);
|
|
1239
|
+
}
|
|
1240
|
+
catch (error) {
|
|
1241
|
+
await rm(temporary, { force: true });
|
|
1242
|
+
throw error;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
const evidence = {
|
|
1246
|
+
id: artifact.id,
|
|
1247
|
+
name: artifact.name,
|
|
1248
|
+
version: artifact.version,
|
|
1249
|
+
filename: artifact.filename,
|
|
1250
|
+
path: destination,
|
|
1251
|
+
size: artifact.size,
|
|
1252
|
+
unpackedSize: artifact.unpackedSize,
|
|
1253
|
+
shasum: artifact.shasum,
|
|
1254
|
+
integrity: artifact.integrity,
|
|
1255
|
+
files: artifact.files,
|
|
1256
|
+
dryRun: context.options?.dryRun === true,
|
|
1257
|
+
};
|
|
1258
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: artifact.filename, humanMessage: artifact.filename, evidence });
|
|
1259
|
+
return ExitCode.success;
|
|
1260
|
+
}
|
|
1261
|
+
catch (error) {
|
|
1262
|
+
if (error instanceof Error && error.name === "PackError")
|
|
1263
|
+
return ExitCode.resolutionFailure;
|
|
1264
|
+
if (error instanceof ProcessCommandError)
|
|
1265
|
+
return ExitCode.installIncomplete;
|
|
1266
|
+
throw error;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
if (name === "publish" || name === "stage") {
|
|
1270
|
+
try {
|
|
1271
|
+
const staged = name === "stage";
|
|
1272
|
+
const packageRoot = resolve(context.cwd, context.args[staged ? 1 : 0] ?? ".");
|
|
1273
|
+
const approvals = new Set(context.options?.allowDangerous ?? []);
|
|
1274
|
+
if (!context.options?.ignoreScripts)
|
|
1275
|
+
await runProjectScriptIfPresent(packageRoot, "prepublishOnly", context.signal, context.output, approvals);
|
|
1276
|
+
if (!context.options?.ignoreScripts)
|
|
1277
|
+
await runProjectScriptIfPresent(packageRoot, "prepack", context.signal, context.output, approvals);
|
|
1278
|
+
if (!context.options?.ignoreScripts)
|
|
1279
|
+
await runProjectScriptIfPresent(packageRoot, "prepare", context.signal, context.output, approvals);
|
|
1280
|
+
const artifact = await packPackage(packageRoot);
|
|
1281
|
+
if (!context.options?.ignoreScripts)
|
|
1282
|
+
await runProjectScriptIfPresent(packageRoot, "postpack", context.signal, context.output, approvals);
|
|
1283
|
+
if (artifact.manifest.private === true)
|
|
1284
|
+
throw new PackError("refusing to publish a package marked private");
|
|
1285
|
+
const evidence = {
|
|
1286
|
+
id: artifact.id,
|
|
1287
|
+
name: artifact.name,
|
|
1288
|
+
version: artifact.version,
|
|
1289
|
+
filename: artifact.filename,
|
|
1290
|
+
size: artifact.size,
|
|
1291
|
+
unpackedSize: artifact.unpackedSize,
|
|
1292
|
+
shasum: artifact.shasum,
|
|
1293
|
+
integrity: artifact.integrity,
|
|
1294
|
+
files: artifact.files,
|
|
1295
|
+
};
|
|
1296
|
+
if (context.options?.dryRun) {
|
|
1297
|
+
if (!context.options.ignoreScripts)
|
|
1298
|
+
await runProjectScriptIfPresent(packageRoot, "publish", context.signal, context.output, approvals);
|
|
1299
|
+
if (!context.options.ignoreScripts)
|
|
1300
|
+
await runProjectScriptIfPresent(packageRoot, "postpublish", context.signal, context.output, approvals);
|
|
1301
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Dry run: ${artifact.id}`, humanMessage: `Dry run: ${artifact.id}`, evidence: { ...evidence, dryRun: true } });
|
|
1302
|
+
return ExitCode.success;
|
|
1303
|
+
}
|
|
1304
|
+
const result = await publishPackage({
|
|
1305
|
+
artifact,
|
|
1306
|
+
paths: createBnpmPaths({ cwd: packageRoot }),
|
|
1307
|
+
...selectedRegistry(context.options),
|
|
1308
|
+
...(context.options?.tag === undefined ? {} : { tag: context.options.tag }),
|
|
1309
|
+
...(context.options?.access === undefined ? {} : { access: context.options.access }),
|
|
1310
|
+
...(context.options?.otp === undefined ? {} : { otp: context.options.otp }),
|
|
1311
|
+
...(context.options?.provenanceFile === undefined ? {} : { provenance: await loadProvenance(artifact, resolve(packageRoot, context.options.provenanceFile)) }),
|
|
1312
|
+
...(context.options?.provenance === true ? { generateProvenance: true } : {}),
|
|
1313
|
+
signal: context.signal,
|
|
1314
|
+
...(staged ? { stage: true } : {}),
|
|
1315
|
+
});
|
|
1316
|
+
if (!context.options?.ignoreScripts)
|
|
1317
|
+
await runProjectScriptIfPresent(packageRoot, "publish", context.signal, context.output, approvals);
|
|
1318
|
+
if (!context.options?.ignoreScripts)
|
|
1319
|
+
await runProjectScriptIfPresent(packageRoot, "postpublish", context.signal, context.output, approvals);
|
|
1320
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `${staged ? "Staged" : "Published"} ${artifact.id}`, humanMessage: `${staged ? "Staged" : "Published"} ${artifact.id}${result.stageId ? ` (${result.stageId})` : ""}`, evidence: { ...evidence, ...result, dryRun: false } });
|
|
1321
|
+
return ExitCode.success;
|
|
1322
|
+
}
|
|
1323
|
+
catch (error) {
|
|
1324
|
+
if (error instanceof Error && error.name === "PackError")
|
|
1325
|
+
return ExitCode.resolutionFailure;
|
|
1326
|
+
if (error instanceof ProcessCommandError)
|
|
1327
|
+
return ExitCode.installIncomplete;
|
|
1328
|
+
if (error instanceof Error && error.name === "ConfigError")
|
|
1329
|
+
return ExitCode.policyBlocked;
|
|
1330
|
+
if (error instanceof Error && error.name === "ProvenanceError")
|
|
1331
|
+
return ExitCode.policyBlocked;
|
|
1332
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "TypeError"))
|
|
1333
|
+
return ExitCode.networkFailure;
|
|
1334
|
+
throw error;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
if (name === "bin" || name === "prefix" || name === "root") {
|
|
1338
|
+
const paths = createBnpmPaths({ cwd: context.cwd });
|
|
1339
|
+
const discovered = await discoverProject(context.cwd);
|
|
1340
|
+
const localPrefix = discovered?.importerRoot ?? context.cwd;
|
|
1341
|
+
const value = context.options?.globalInstall
|
|
1342
|
+
? name === "bin" ? paths.globalBin : name === "root" ? join(paths.globalRoot, "node_modules") : paths.globalRoot
|
|
1343
|
+
: name === "bin" ? join(localPrefix, "node_modules", ".bin") : name === "root" ? join(localPrefix, "node_modules") : localPrefix;
|
|
1344
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: value, humanMessage: value, evidence: { path: value, global: context.options?.globalInstall === true } });
|
|
1345
|
+
return ExitCode.success;
|
|
1346
|
+
}
|
|
1347
|
+
if (name === "install" || name === "install-test" || name === "install-ci-test" || name === "add" || name === "remove" || name === "update" || name === "prune" || name === "dedupe") {
|
|
1348
|
+
try {
|
|
1349
|
+
if (!context.options)
|
|
1350
|
+
throw new Error("Command options are unavailable");
|
|
1351
|
+
const location = await commandLocation(context);
|
|
1352
|
+
const roots = location.globalPaths ? [location.cwd] : await selectedWorkspaceRoots(location.cwd, context.options);
|
|
1353
|
+
for (const cwd of roots) {
|
|
1354
|
+
const securityEvents = [];
|
|
1355
|
+
const collectSecurityEvidence = (message, evidence) => { securityEvents.push({ message, evidence }); };
|
|
1356
|
+
const reportProgress = installProgressReporter(context);
|
|
1357
|
+
const result = name === "add" || (name === "install" && context.args.length > 0)
|
|
1358
|
+
? await addDependencies(cwd, context.args, context.options, {
|
|
1359
|
+
signal: context.signal,
|
|
1360
|
+
...selectedRegistry(context.options),
|
|
1361
|
+
prompts: commandInstallPrompts(context.options),
|
|
1362
|
+
onChildOutput: (stream, text, attribution) => context.output.childOutput(stream, text, attribution),
|
|
1363
|
+
onSecurityEvidence: collectSecurityEvidence,
|
|
1364
|
+
onProgress: reportProgress,
|
|
1365
|
+
})
|
|
1366
|
+
: name === "remove"
|
|
1367
|
+
? await removeDependencies(cwd, context.args, context.options, {
|
|
1368
|
+
signal: context.signal,
|
|
1369
|
+
...selectedRegistry(context.options),
|
|
1370
|
+
prompts: commandInstallPrompts(context.options),
|
|
1371
|
+
onChildOutput: (stream, text, attribution) => context.output.childOutput(stream, text, attribution),
|
|
1372
|
+
onSecurityEvidence: collectSecurityEvidence,
|
|
1373
|
+
onProgress: reportProgress,
|
|
1374
|
+
})
|
|
1375
|
+
: name === "update"
|
|
1376
|
+
? await updateDependencies(cwd, context.args, context.options, {
|
|
1377
|
+
signal: context.signal,
|
|
1378
|
+
...selectedRegistry(context.options),
|
|
1379
|
+
prompts: commandInstallPrompts(context.options),
|
|
1380
|
+
onChildOutput: (stream, text, attribution) => context.output.childOutput(stream, text, attribution),
|
|
1381
|
+
onSecurityEvidence: collectSecurityEvidence,
|
|
1382
|
+
onProgress: reportProgress,
|
|
1383
|
+
})
|
|
1384
|
+
: await installProject({
|
|
1385
|
+
cwd,
|
|
1386
|
+
...selectedRegistry(context.options),
|
|
1387
|
+
specifications: context.args,
|
|
1388
|
+
commandOptions: context.options,
|
|
1389
|
+
signal: context.signal,
|
|
1390
|
+
prompts: commandInstallPrompts(context.options),
|
|
1391
|
+
onChildOutput: (stream, text, attribution) => context.output.childOutput(stream, text, attribution),
|
|
1392
|
+
onSecurityEvidence: collectSecurityEvidence,
|
|
1393
|
+
onProgress: reportProgress,
|
|
1394
|
+
...((name === "prune" || name === "dedupe") ? { forceRelink: true } : {}),
|
|
1395
|
+
...(name === "dedupe" ? { forceResolution: true } : {}),
|
|
1396
|
+
});
|
|
1397
|
+
if (context.options.json || context.options.details)
|
|
1398
|
+
for (const event of securityEvents.sort((left, right) => left.message.localeCompare(right.message)))
|
|
1399
|
+
context.output.info(event.message, event.evidence);
|
|
1400
|
+
if (!context.options.json) {
|
|
1401
|
+
const findings = [...result.analyses.values()].flatMap((analyzed) => analyzed.analysis.findings);
|
|
1402
|
+
const lifecycles = [...result.analyses.values()].flatMap((analyzed) => analyzed.lifecycles);
|
|
1403
|
+
const limited = findings.filter((finding) => finding.ruleId === "BNPM-SEC-009").length;
|
|
1404
|
+
const warnings = findings.length - limited;
|
|
1405
|
+
context.output.info(`Security review: ${result.analyses.size} package${result.analyses.size === 1 ? "" : "s"} inspected · ${warnings} finding${warnings === 1 ? "" : "s"} · ${lifecycles.length} install script${lifecycles.length === 1 ? "" : "s"}${limited > 0 ? ` · ${limited} bounded scan${limited === 1 ? "" : "s"}` : ""}${context.options.details ? "" : " (use --details for evidence)"}`);
|
|
1406
|
+
}
|
|
1407
|
+
if (location.globalPaths && !context.options.packageLockOnly && !context.options.dryRun)
|
|
1408
|
+
await exposeGlobalBins(location.globalPaths);
|
|
1409
|
+
const action = context.options.dryRun ? "Would install" : context.options.packageLockOnly ? "Resolved" : "Installed";
|
|
1410
|
+
context.output.info(`${action} ${result.graph.packages.size} package${result.graph.packages.size === 1 ? "" : "s"}${context.options.packageLockOnly ? " into the lockfile only" : ""}`);
|
|
1411
|
+
if (result.skippedLifecyclePackages.length > 0) {
|
|
1412
|
+
context.output.error(`Skipped unapproved lifecycle scripts for ${result.skippedLifecyclePackages.join(", ")}`);
|
|
1413
|
+
return ExitCode.installIncomplete;
|
|
1414
|
+
}
|
|
1415
|
+
if (!context.options.dryRun && !context.options.packageLockOnly && !context.options.ignoreScripts && !location.globalPaths && ["install", "install-test", "install-ci-test", "add", "remove", "update"].includes(name)) {
|
|
1416
|
+
const code = await runProjectInstallLifecycle(cwd, context.signal, context.output, new Set(context.options.allowDangerous));
|
|
1417
|
+
if (code !== 0)
|
|
1418
|
+
return ExitCode.installIncomplete;
|
|
1419
|
+
}
|
|
1420
|
+
if (name === "install-test" || name === "install-ci-test") {
|
|
1421
|
+
const code = await runProjectScriptLifecycle(cwd, "test", [], context.signal, context.output, new Set(context.options.allowDangerous), !context.options.ignoreScripts);
|
|
1422
|
+
if (code !== 0)
|
|
1423
|
+
return ExitCode.installIncomplete;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return ExitCode.success;
|
|
1427
|
+
}
|
|
1428
|
+
catch (error) {
|
|
1429
|
+
const name = error instanceof Error ? error.name : "";
|
|
1430
|
+
const report = () => context.output.error(error instanceof Error ? error.message : String(error));
|
|
1431
|
+
if (name === "PolicyError" || name === "LockfileError" || name === "ConfigError") {
|
|
1432
|
+
report();
|
|
1433
|
+
return ExitCode.policyBlocked;
|
|
1434
|
+
}
|
|
1435
|
+
if (name === "IntegrityError" || name === "ArchiveError") {
|
|
1436
|
+
report();
|
|
1437
|
+
return ExitCode.integrityFailure;
|
|
1438
|
+
}
|
|
1439
|
+
if (name === "RegistryError" || name === "TypeError") {
|
|
1440
|
+
report();
|
|
1441
|
+
return ExitCode.networkFailure;
|
|
1442
|
+
}
|
|
1443
|
+
if (name === "ResolutionError" || name === "ManifestError" || name === "DependencyError") {
|
|
1444
|
+
report();
|
|
1445
|
+
return ExitCode.resolutionFailure;
|
|
1446
|
+
}
|
|
1447
|
+
if (name === "ScriptExecutionError") {
|
|
1448
|
+
report();
|
|
1449
|
+
return ExitCode.installIncomplete;
|
|
1450
|
+
}
|
|
1451
|
+
if (error instanceof ProcessCommandError) {
|
|
1452
|
+
report();
|
|
1453
|
+
return ExitCode.resolutionFailure;
|
|
1454
|
+
}
|
|
1455
|
+
throw error;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
if (name === "outdated") {
|
|
1459
|
+
try {
|
|
1460
|
+
const location = await commandLocation(context);
|
|
1461
|
+
const entries = await findOutdatedDependencies({ cwd: location.cwd, names: context.args, signal: context.signal, ...selectedRegistry(context.options) });
|
|
1462
|
+
if (entries.length === 0)
|
|
1463
|
+
context.output.info("All dependencies are current");
|
|
1464
|
+
for (const entry of entries) {
|
|
1465
|
+
context.output.info(`${entry.name}: ${entry.current ?? "missing"} -> ${entry.wanted} (latest ${entry.latest})`, entry);
|
|
1466
|
+
}
|
|
1467
|
+
return ExitCode.success;
|
|
1468
|
+
}
|
|
1469
|
+
catch (error) {
|
|
1470
|
+
if (error instanceof Error && (error.name === "RegistryError" || error.name === "TypeError"))
|
|
1471
|
+
return ExitCode.networkFailure;
|
|
1472
|
+
if (error instanceof Error && error.name === "ConfigError")
|
|
1473
|
+
return ExitCode.policyBlocked;
|
|
1474
|
+
if (error instanceof Error && (error.name === "ManifestError" || error.name === "DependencyError"))
|
|
1475
|
+
return ExitCode.resolutionFailure;
|
|
1476
|
+
throw error;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (name === "list" || name === "why") {
|
|
1480
|
+
try {
|
|
1481
|
+
const location = await commandLocation(context);
|
|
1482
|
+
const report = await inspectInstalledGraph(location.cwd, name === "list" ? { names: context.args } : { why: context.args[0] ?? "" });
|
|
1483
|
+
if (name === "list") {
|
|
1484
|
+
context.output.info(report.human, { roots: report.roots });
|
|
1485
|
+
}
|
|
1486
|
+
else {
|
|
1487
|
+
context.output.info(report.human, { package: context.args[0], paths: report.paths });
|
|
1488
|
+
}
|
|
1489
|
+
return ExitCode.success;
|
|
1490
|
+
}
|
|
1491
|
+
catch (error) {
|
|
1492
|
+
if (error instanceof Error && error.name === "LockfileError")
|
|
1493
|
+
return ExitCode.resolutionFailure;
|
|
1494
|
+
throw error;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
if (name === "run" || name === "exec") {
|
|
1498
|
+
try {
|
|
1499
|
+
const location = await commandLocation(context);
|
|
1500
|
+
const [target, ...args] = context.args;
|
|
1501
|
+
if (!target) {
|
|
1502
|
+
if (name === "exec")
|
|
1503
|
+
return ExitCode.usage;
|
|
1504
|
+
const scripts = [...await projectScriptNames(location.cwd)].sort();
|
|
1505
|
+
context.output.info(scripts.join("\n") || "No scripts defined", { scripts });
|
|
1506
|
+
return ExitCode.success;
|
|
1507
|
+
}
|
|
1508
|
+
if (name === "run" && (context.options?.workspaces || context.options?.workspaceNames !== undefined)) {
|
|
1509
|
+
const discovered = await discoverProject(location.cwd);
|
|
1510
|
+
const root = discovered?.projectRoot ?? location.cwd;
|
|
1511
|
+
const manifestPath = join(root, "package.json");
|
|
1512
|
+
const manifest = parseManifest(await readFile(manifestPath, "utf8"), manifestPath);
|
|
1513
|
+
const members = await discoverWorkspacePackages(root, manifest.workspaces);
|
|
1514
|
+
const requested = new Set(context.options.workspaceNames ?? []);
|
|
1515
|
+
const selected = [...members].filter(([workspace, path]) => requested.size === 0 || requested.has(workspace) || requested.has(relative(root, path).split(sep).join("/")) || [...requested].some((value) => path.startsWith(`${resolve(root, value)}${sep}`)));
|
|
1516
|
+
if (selected.length === 0)
|
|
1517
|
+
throw new ProcessCommandError(requested.size === 0 ? "No workspaces were found" : `No workspace matched: ${[...requested].join(", ")}`);
|
|
1518
|
+
if (context.options.includeWorkspaceRoot) {
|
|
1519
|
+
context.output.info(`Running ${target} in workspace root`);
|
|
1520
|
+
if (context.options.ifPresent && !(await projectScriptNames(root)).has(target)) {
|
|
1521
|
+
context.output.info(`Skipping missing script ${target} in workspace root`);
|
|
1522
|
+
}
|
|
1523
|
+
else {
|
|
1524
|
+
const code = await runProjectScriptLifecycle(root, target, args, context.signal, context.output, new Set(context.options.allowDangerous), !context.options.ignoreScripts);
|
|
1525
|
+
if (code !== 0)
|
|
1526
|
+
return ExitCode.installIncomplete;
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
for (const [workspace, path] of selected.sort(([left], [right]) => left.localeCompare(right))) {
|
|
1530
|
+
if (context.options.ifPresent && !(await projectScriptNames(path)).has(target)) {
|
|
1531
|
+
context.output.info(`Skipping missing script ${target} in ${workspace}`);
|
|
1532
|
+
continue;
|
|
1533
|
+
}
|
|
1534
|
+
context.output.info(`Running ${target} in ${workspace}`);
|
|
1535
|
+
const code = await runProjectScriptLifecycle(path, target, args, context.signal, context.output, new Set(context.options.allowDangerous), !context.options.ignoreScripts);
|
|
1536
|
+
if (code !== 0)
|
|
1537
|
+
return ExitCode.installIncomplete;
|
|
1538
|
+
}
|
|
1539
|
+
return ExitCode.success;
|
|
1540
|
+
}
|
|
1541
|
+
if (name === "run" && context.options?.ifPresent && !(await projectScriptNames(location.cwd)).has(target))
|
|
1542
|
+
return ExitCode.success;
|
|
1543
|
+
const code = name === "run"
|
|
1544
|
+
? await runProjectScriptLifecycle(location.cwd, target, args, context.signal, context.output, new Set(context.options?.allowDangerous ?? []), !context.options?.ignoreScripts)
|
|
1545
|
+
: context.options?.execPackages !== undefined
|
|
1546
|
+
? await execEphemeral(context, context.options.execPackages, target, args)
|
|
1547
|
+
: await execInstalled(location.cwd, target, args, context.signal, context.output, context.output.passthroughChildOutput === true);
|
|
1548
|
+
return code === 0 ? ExitCode.success : ExitCode.installIncomplete;
|
|
1549
|
+
}
|
|
1550
|
+
catch (error) {
|
|
1551
|
+
if (error instanceof ProcessCommandError) {
|
|
1552
|
+
context.output.error(error.message);
|
|
1553
|
+
return ExitCode.resolutionFailure;
|
|
1554
|
+
}
|
|
1555
|
+
throw error;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
if (name === "audit") {
|
|
1559
|
+
try {
|
|
1560
|
+
const location = await commandLocation(context);
|
|
1561
|
+
const projectRoot = (await discoverProject(location.cwd))?.projectRoot ?? location.cwd;
|
|
1562
|
+
let result = await auditProject({ paths: createBnpmPaths({ cwd: projectRoot }), signal: context.signal, ...selectedRegistry(context.options) });
|
|
1563
|
+
const initialAdvisories = result.advisories.length;
|
|
1564
|
+
if (context.args[0] === "fix" && initialAdvisories > 0) {
|
|
1565
|
+
if (context.options?.dryRun) {
|
|
1566
|
+
context.output.result({ status: "success", category: "success", exitCode: ExitCode.success, summary: `Would re-resolve ${initialAdvisories} advisor${initialAdvisories === 1 ? "y" : "ies"} within declared dependency ranges`, evidence: { advisories: result.advisories, dryRun: true } });
|
|
1567
|
+
return ExitCode.success;
|
|
1568
|
+
}
|
|
1569
|
+
const commandOptions = context.options;
|
|
1570
|
+
const fixed = await installProject({ cwd: location.cwd, commandOptions, forceResolution: true, signal: context.signal, ...selectedRegistry(context.options), prompts: commandInstallPrompts(commandOptions), onChildOutput: (stream, text, attribution) => context.output.childOutput(stream, text, attribution), onSecurityEvidence: (message, evidence) => context.output.info(message, evidence) });
|
|
1571
|
+
if (fixed.skippedLifecyclePackages.length > 0) {
|
|
1572
|
+
context.output.error(`Skipped unapproved lifecycle scripts for ${fixed.skippedLifecyclePackages.join(", ")}`);
|
|
1573
|
+
return ExitCode.installIncomplete;
|
|
1574
|
+
}
|
|
1575
|
+
result = await auditProject({ paths: createBnpmPaths({ cwd: projectRoot }), signal: context.signal, ...selectedRegistry(context.options) });
|
|
1576
|
+
context.output.info(`Audit fix removed ${initialAdvisories - result.advisories.length} of ${initialAdvisories} advisories without changing declared ranges`, { before: initialAdvisories, after: result.advisories.length });
|
|
1577
|
+
}
|
|
1578
|
+
let blocked = false;
|
|
1579
|
+
for (const [id, analyzed] of result.packages) {
|
|
1580
|
+
if (analyzed.analysis.findings.length > 0)
|
|
1581
|
+
context.output.info(`Local security findings for ${id}`, analyzed.analysis.findings);
|
|
1582
|
+
if (analyzed.analysis.findings.some((finding) => finding.severity === "dangerous"))
|
|
1583
|
+
blocked = true;
|
|
1584
|
+
}
|
|
1585
|
+
for (const advisory of result.advisories) {
|
|
1586
|
+
context.output.info(`Registry advisory for ${advisory.packageName}: ${advisory.title}`, advisory);
|
|
1587
|
+
if (advisory.severity === "high" || advisory.severity === "critical")
|
|
1588
|
+
blocked = true;
|
|
1589
|
+
}
|
|
1590
|
+
context.output.info(`Audit completed at ${result.analyzedAt}`, { packageCount: result.packages.size, advisoryCount: result.advisories.length });
|
|
1591
|
+
return blocked ? ExitCode.policyBlocked : ExitCode.success;
|
|
1592
|
+
}
|
|
1593
|
+
catch (error) {
|
|
1594
|
+
if (error instanceof Error && error.name === "RegistryError")
|
|
1595
|
+
return ExitCode.networkFailure;
|
|
1596
|
+
if (error instanceof Error && error.name === "LockfileError")
|
|
1597
|
+
return ExitCode.resolutionFailure;
|
|
1598
|
+
if (error instanceof Error && error.name === "ConfigError")
|
|
1599
|
+
return ExitCode.policyBlocked;
|
|
1600
|
+
throw error;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return ExitCode.usage;
|
|
1604
|
+
}
|
|
1605
|
+
export async function runBnpmxCommand(context, global, specification, targetArgs) {
|
|
1606
|
+
if (specification === "check") {
|
|
1607
|
+
if (targetArgs.length > 0) {
|
|
1608
|
+
context.output.error("bnpmx check does not accept package arguments");
|
|
1609
|
+
return ExitCode.usage;
|
|
1610
|
+
}
|
|
1611
|
+
try {
|
|
1612
|
+
const discovered = await discoverProject(context.cwd);
|
|
1613
|
+
const projectRoot = discovered?.projectRoot ?? context.cwd;
|
|
1614
|
+
const result = await auditProject({ paths: createBnpmPaths({ cwd: projectRoot }), ...selectedRegistry(global), signal: context.signal });
|
|
1615
|
+
const directIdentities = new Set([...result.graph.roots.values(), ...[...(result.graph.importers?.values() ?? [])].flatMap((roots) => [...roots.values()])].map((id) => { const pkg = result.graph.packages.get(id); return pkg ? `${pkg.name}@${pkg.version}` : id; }));
|
|
1616
|
+
const capabilities = [...result.packages.values()].flatMap((analyzed) => analyzed.capabilities ?? []);
|
|
1617
|
+
const grouped = new Map();
|
|
1618
|
+
for (const capability of capabilities) {
|
|
1619
|
+
let entry = grouped.get(capability.kind);
|
|
1620
|
+
if (!entry) {
|
|
1621
|
+
entry = { behavior: capability.behavior, severity: capability.severity, packages: new Set(), evidence: new Set() };
|
|
1622
|
+
grouped.set(capability.kind, entry);
|
|
1623
|
+
}
|
|
1624
|
+
entry.packages.add(`${capability.packageName}@${capability.packageVersion}`);
|
|
1625
|
+
for (const evidence of capability.evidence)
|
|
1626
|
+
entry.evidence.add(evidence);
|
|
1627
|
+
}
|
|
1628
|
+
const findings = [...result.packages.values()].flatMap((analyzed) => analyzed.analysis.findings);
|
|
1629
|
+
const lifecycles = [...result.packages.values()].flatMap((analyzed) => analyzed.lifecycles);
|
|
1630
|
+
const direct = directIdentities.size;
|
|
1631
|
+
const transitive = Math.max(0, result.graph.packages.size - direct);
|
|
1632
|
+
const lines = ["Project security check", `${result.graph.packages.size} packages · ${direct} direct · ${transitive} transitive`];
|
|
1633
|
+
if (grouped.size > 0)
|
|
1634
|
+
lines.push("", "RUNTIME CAPABILITIES");
|
|
1635
|
+
for (const kind of capabilityOrder) {
|
|
1636
|
+
const entry = grouped.get(kind);
|
|
1637
|
+
if (!entry)
|
|
1638
|
+
continue;
|
|
1639
|
+
const presentation = capabilitySummary(kind, [...entry.evidence]);
|
|
1640
|
+
const identities = [...entry.packages];
|
|
1641
|
+
const directCount = identities.filter((identity) => directIdentities.has(identity)).length;
|
|
1642
|
+
lines.push(` ${entry.severity === "warning" ? "!" : "•"} ${presentation.title} · ${identities.length} package${identities.length === 1 ? "" : "s"} (${directCount} direct, ${identities.length - directCount} transitive)`);
|
|
1643
|
+
if (presentation.summary)
|
|
1644
|
+
lines.push(` ${presentation.summary}`);
|
|
1645
|
+
if (global.details)
|
|
1646
|
+
lines.push(` Packages: ${identities.sort().join(", ")}`, ` Evidence: ${[...entry.evidence].sort().slice(0, 24).join("; ")}`);
|
|
1647
|
+
}
|
|
1648
|
+
lines.push("", "INSTALL-TIME REVIEW", ` ${lifecycles.length > 0 ? "!" : "✓"} ${lifecycles.length} lifecycle script${lifecycles.length === 1 ? "" : "s"}`, ` ${findings.length > 0 ? "!" : "✓"} ${findings.length} static finding${findings.length === 1 ? "" : "s"}`, ` ${result.advisories.length > 0 ? "!" : "✓"} ${result.advisories.length} registry advisor${result.advisories.length === 1 ? "y" : "ies"}`);
|
|
1649
|
+
if (global.details && findings.length > 0)
|
|
1650
|
+
for (const finding of findings.sort((left, right) => left.packageName.localeCompare(right.packageName) || left.ruleId.localeCompare(right.ruleId)))
|
|
1651
|
+
lines.push(` ${finding.severity.toUpperCase()} ${finding.packageName}@${finding.packageVersion} ${finding.ruleId}: ${finding.behavior} (${finding.location?.path ?? "package"})`);
|
|
1652
|
+
lines.push("", `Scanned ${result.packages.size}/${result.graph.packages.size} locked packages. No package code was executed.`);
|
|
1653
|
+
if (!global.details)
|
|
1654
|
+
lines.push("Use bnpmx --details check for package and file evidence.");
|
|
1655
|
+
context.output.info(lines.join("\n"), { graph: { packages: result.graph.packages.size, direct, transitive }, capabilities, findings, lifecycles, advisories: result.advisories });
|
|
1656
|
+
return findings.some((finding) => finding.severity === "dangerous") || result.advisories.some((advisory) => advisory.severity === "high" || advisory.severity === "critical") ? ExitCode.policyBlocked : ExitCode.success;
|
|
1657
|
+
}
|
|
1658
|
+
catch (error) {
|
|
1659
|
+
context.output.error(error instanceof Error ? error.message : String(error));
|
|
1660
|
+
const name = error instanceof Error ? error.name : "";
|
|
1661
|
+
return name === "LockfileError" ? ExitCode.resolutionFailure : name === "RegistryError" ? ExitCode.networkFailure : ExitCode.internalError;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
const base = createBnpmPaths({ cwd: context.cwd }).ephemeralRoot;
|
|
1665
|
+
await mkdir(base, { recursive: true, mode: 0o700 });
|
|
1666
|
+
const project = await mkdtemp(join(base, "run-"));
|
|
1667
|
+
const options = {
|
|
1668
|
+
...global,
|
|
1669
|
+
frozenLockfile: false,
|
|
1670
|
+
offline: false,
|
|
1671
|
+
omitDev: true,
|
|
1672
|
+
saveExact: false,
|
|
1673
|
+
noSave: true,
|
|
1674
|
+
};
|
|
1675
|
+
try {
|
|
1676
|
+
const result = await installProject({
|
|
1677
|
+
cwd: project,
|
|
1678
|
+
...selectedRegistry(global),
|
|
1679
|
+
specifications: [specification],
|
|
1680
|
+
commandOptions: options,
|
|
1681
|
+
signal: context.signal,
|
|
1682
|
+
prompts: commandInstallPrompts(options),
|
|
1683
|
+
onChildOutput: (stream, text, attribution) => context.output.childOutput(stream, text, attribution),
|
|
1684
|
+
onSecurityEvidence: (message, evidence) => context.output.info(message, evidence),
|
|
1685
|
+
});
|
|
1686
|
+
if (result.skippedLifecyclePackages.length > 0) {
|
|
1687
|
+
context.output.error(`bnpmx will not execute because lifecycle scripts were skipped for ${result.skippedLifecyclePackages.join(", ")}`);
|
|
1688
|
+
return ExitCode.installIncomplete;
|
|
1689
|
+
}
|
|
1690
|
+
const root = [...result.graph.roots][0];
|
|
1691
|
+
if (!root)
|
|
1692
|
+
return ExitCode.usage;
|
|
1693
|
+
const pkg = result.graph.packages.get(root[1]);
|
|
1694
|
+
if (!pkg)
|
|
1695
|
+
return ExitCode.internalError;
|
|
1696
|
+
const bin = pkg.manifest.bin;
|
|
1697
|
+
const preferred = basename(root[0]);
|
|
1698
|
+
const binary = typeof bin === "string"
|
|
1699
|
+
? basename(pkg.name)
|
|
1700
|
+
: bin?.[preferred] !== undefined
|
|
1701
|
+
? preferred
|
|
1702
|
+
: bin?.[basename(pkg.name)] !== undefined
|
|
1703
|
+
? basename(pkg.name)
|
|
1704
|
+
: Object.keys(bin ?? {}).sort()[0];
|
|
1705
|
+
if (!binary) {
|
|
1706
|
+
context.output.error(`${pkg.id} does not expose an executable`);
|
|
1707
|
+
return ExitCode.usage;
|
|
1708
|
+
}
|
|
1709
|
+
const capabilities = [...result.analyses.values()].flatMap((analyzed) => analyzed.capabilities ?? []);
|
|
1710
|
+
if (capabilities.length > 0) {
|
|
1711
|
+
const grouped = new Map();
|
|
1712
|
+
for (const capability of capabilities) {
|
|
1713
|
+
let entry = grouped.get(capability.kind);
|
|
1714
|
+
if (!entry) {
|
|
1715
|
+
entry = { behavior: capability.behavior, severity: capability.severity, packages: new Set(), evidence: new Set() };
|
|
1716
|
+
grouped.set(capability.kind, entry);
|
|
1717
|
+
}
|
|
1718
|
+
entry.packages.add(`${capability.packageName}@${capability.packageVersion}`);
|
|
1719
|
+
for (const evidence of capability.evidence)
|
|
1720
|
+
entry.evidence.add(evidence);
|
|
1721
|
+
}
|
|
1722
|
+
const identity = `${pkg.name}@${pkg.version}`;
|
|
1723
|
+
const explicitlyAllowed = global.allowDangerous.includes(identity);
|
|
1724
|
+
context.output.info(capabilityDisclosure(identity, binary, grouped, global.details === true), capabilities);
|
|
1725
|
+
if (!explicitlyAllowed && !await confirmCommand(`Allow ${identity} to run?`, { json: global.json, yes: false })) {
|
|
1726
|
+
context.output.error(`Execution cancelled before ${binary} started. Review the disclosure or pass --allow-dangerous=${identity} for this exact version.`);
|
|
1727
|
+
return ExitCode.policyBlocked;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
const code = await execInstalled(project, binary, targetArgs, context.signal, context.output, context.output.passthroughChildOutput === true);
|
|
1731
|
+
return code === 0 ? ExitCode.success : ExitCode.installIncomplete;
|
|
1732
|
+
}
|
|
1733
|
+
catch (error) {
|
|
1734
|
+
const name = error instanceof Error ? error.name : "";
|
|
1735
|
+
if (name === "PolicyError" || name === "LockfileError" || name === "ConfigError")
|
|
1736
|
+
return ExitCode.policyBlocked;
|
|
1737
|
+
if (name === "IntegrityError" || name === "ArchiveError")
|
|
1738
|
+
return ExitCode.integrityFailure;
|
|
1739
|
+
if (name === "RegistryError" || name === "TypeError" || name === "TimeoutError")
|
|
1740
|
+
return ExitCode.networkFailure;
|
|
1741
|
+
if (name === "ResolutionError" || name === "ManifestError" || name === "DependencyError")
|
|
1742
|
+
return ExitCode.resolutionFailure;
|
|
1743
|
+
if (name === "ScriptExecutionError")
|
|
1744
|
+
return ExitCode.installIncomplete;
|
|
1745
|
+
if (error instanceof ProcessCommandError)
|
|
1746
|
+
return ExitCode.resolutionFailure;
|
|
1747
|
+
throw error;
|
|
1748
|
+
}
|
|
1749
|
+
finally {
|
|
1750
|
+
await rm(project, { recursive: true, force: true });
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
//# sourceMappingURL=index.js.map
|