vercel
Version:
The command-line interface for Vercel
489 lines (486 loc) • 15.5 kB
JavaScript
import { createRequire as __createRequire } from 'node:module';
import { fileURLToPath as __fileURLToPath } from 'node:url';
import { dirname as __dirname_ } from 'node:path';
const require = __createRequire(import.meta.url);
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __dirname_(__filename);
import {
LinkRequiredError,
ProjectNotFound,
isAPIError,
packageName,
stripSensitiveAuthArgs
} from "./chunk-UGXBNJMO.js";
import {
require_dist
} from "./chunk-ZQKJVHXY.js";
import {
__toESM
} from "./chunk-TZ2YI2VH.js";
// src/util/agent-output.ts
var import_error_utils = __toESM(require_dist(), 1);
function buildCommandWithYes(argv, pkgName = packageName) {
const args = stripSensitiveAuthArgs(argv.slice(2));
const hasYes = args.some((a) => a === "--yes" || a === "-y");
const out = hasYes ? args : [...args, "--yes"];
return `${pkgName} ${out.join(" ")}`.trim();
}
var GLOBAL_FLAG_NAMES = /* @__PURE__ */ new Set([
"--cwd",
"--config",
"--yes",
"-y",
"--non-interactive",
"--scope",
"--team",
"-S",
"-T"
// --token/-t are intentionally excluded and stripped via stripSensitiveAuthArgs.
]);
var BOOLEAN_GLOBAL_FLAG_NAMES = /* @__PURE__ */ new Set(["--yes", "-y", "--non-interactive"]);
function getGlobalFlagsFromArgv(argv) {
const args = stripSensitiveAuthArgs(argv.slice(2));
const out = [];
for (let i = 0; i < args.length; i++) {
const arg = args[i];
const name = arg.startsWith("--") ? arg.split("=")[0] : arg;
if (GLOBAL_FLAG_NAMES.has(name)) {
out.push(arg);
const takesSeparateValue = !BOOLEAN_GLOBAL_FLAG_NAMES.has(name) && !arg.includes("=") && i + 1 < args.length && !args[i + 1].startsWith("-");
if (takesSeparateValue) {
out.push(args[i + 1]);
i++;
}
}
}
return out;
}
function omitGlobalFlagsFromArgs(args) {
const safeArgs = stripSensitiveAuthArgs(args);
const out = [];
for (let i = 0; i < safeArgs.length; i++) {
const arg = safeArgs[i];
const name = arg.startsWith("--") ? arg.split("=")[0] : arg;
if (GLOBAL_FLAG_NAMES.has(name)) {
const skipSeparateValue = !BOOLEAN_GLOBAL_FLAG_NAMES.has(name) && !arg.includes("=") && i + 1 < safeArgs.length && !safeArgs[i + 1].startsWith("-");
if (skipSeparateValue) {
i++;
}
continue;
}
out.push(arg);
}
return out;
}
function buildIntegrationCommandTailFromArgv(argv) {
const args = argv.slice(2);
const idx = args.indexOf("integration");
if (idx === -1) {
return "integration";
}
return omitGlobalFlagsFromArgs(args.slice(idx)).join(" ");
}
function buildCommandWithGlobalFlags(argv, commandTemplate, pkgName = packageName, options) {
let preserved = getGlobalFlagsFromArgv(argv);
if (options?.excludeFlags?.length) {
const exclude = new Set(options.excludeFlags);
const out = [];
for (let i = 0; i < preserved.length; i++) {
const arg = preserved[i];
const name = arg.startsWith("--") ? arg.split("=")[0] : arg;
if (exclude.has(name)) {
if (!arg.includes("=") && i + 1 < preserved.length && !preserved[i + 1].startsWith("-")) {
i++;
}
continue;
}
out.push(arg);
}
preserved = out;
}
const base = `${pkgName} ${commandTemplate}`;
if (preserved.length === 0) {
return base;
}
if (options?.prependGlobalFlags) {
return `${pkgName} ${preserved.join(" ")} ${commandTemplate}`;
}
return `${base} ${preserved.join(" ")}`;
}
function getPreservedArgsForEnvAdd(argv) {
const args = argv.slice(2);
const addIdx = args.indexOf("add");
if (addIdx === -1 || args[addIdx - 1] !== "env")
return stripSensitiveAuthArgs(args);
let i = addIdx + 1;
let positionals = 0;
while (i < args.length && positionals < 3 && !args[i].startsWith("-")) {
positionals++;
i++;
}
return stripSensitiveAuthArgs(args.slice(i));
}
function buildEnvAddCommandWithPreservedArgs(argv, commandTemplate, pkgName = packageName) {
let preserved = getPreservedArgsForEnvAdd(argv);
if (commandTemplate.includes("--yes")) {
preserved = preserved.filter((a) => a !== "--yes" && a !== "-y");
}
if (commandTemplate.includes("--value")) {
const out = [];
for (let j = 0; j < preserved.length; j++) {
if (preserved[j] === "--value" && j + 1 < preserved.length) {
j++;
continue;
}
if (preserved[j].startsWith("--value="))
continue;
out.push(preserved[j]);
}
preserved = out;
}
const base = `${pkgName} ${commandTemplate}`;
if (preserved.length === 0)
return base;
return `${base} ${preserved.join(" ")}`;
}
function getPreservedArgsForEnvPull(argv) {
const args = argv.slice(2);
const pullIdx = args.indexOf("pull");
if (pullIdx === -1 || args[pullIdx - 1] !== "env")
return stripSensitiveAuthArgs(args);
let i = pullIdx + 1;
if (i < args.length && !args[i].startsWith("-"))
i++;
return stripSensitiveAuthArgs(args.slice(i));
}
function getPreservedArgsForEnvRm(argv) {
const args = argv.slice(2);
const rmIdx = args.indexOf("rm");
if (rmIdx === -1 || args[rmIdx - 1] !== "env")
return stripSensitiveAuthArgs(args);
let i = rmIdx + 1;
let positionals = 0;
while (i < args.length && positionals < 3 && !args[i].startsWith("-")) {
positionals++;
i++;
}
return stripSensitiveAuthArgs(args.slice(i));
}
function buildEnvRmCommandWithPreservedArgs(argv, commandTemplate, pkgName = packageName) {
let preserved = getPreservedArgsForEnvRm(argv);
if (commandTemplate.includes("--yes")) {
preserved = preserved.filter((a) => a !== "--yes" && a !== "-y");
}
const base = `${pkgName} ${commandTemplate}`;
if (preserved.length === 0)
return base;
return `${base} ${preserved.join(" ")}`;
}
function getPreservedArgsForEnvUpdate(argv) {
const args = argv.slice(2);
const updateIdx = args.indexOf("update");
if (updateIdx === -1 || args[updateIdx - 1] !== "env")
return stripSensitiveAuthArgs(args);
let i = updateIdx + 1;
let positionals = 0;
while (i < args.length && positionals < 3 && !args[i].startsWith("-")) {
positionals++;
i++;
}
return stripSensitiveAuthArgs(args.slice(i));
}
function buildEnvUpdateCommandWithPreservedArgs(argv, commandTemplate, pkgName = packageName) {
let preserved = getPreservedArgsForEnvUpdate(argv);
if (commandTemplate.includes("--yes")) {
preserved = preserved.filter((a) => a !== "--yes" && a !== "-y");
}
if (commandTemplate.includes("--value")) {
const out = [];
for (let i = 0; i < preserved.length; i++) {
if (preserved[i] === "--value" && i + 1 < preserved.length) {
i++;
continue;
}
if (preserved[i].startsWith("--value="))
continue;
out.push(preserved[i]);
}
preserved = out;
}
const base = `${pkgName} ${commandTemplate}`;
if (preserved.length === 0)
return base;
return `${base} ${preserved.join(" ")}`;
}
function buildCommandWithScope(argv, scopeSlug, pkgName = packageName) {
const args = stripSensitiveAuthArgs(argv.slice(2));
const out = [];
for (let i = 0; i < args.length; i++) {
if (args[i] === "--scope" || args[i] === "--team" || args[i] === "-S" || args[i] === "-T") {
i++;
continue;
}
if (args[i].startsWith("--scope=") || args[i].startsWith("--team=")) {
continue;
}
out.push(args[i]);
}
out.push("--scope", scopeSlug);
return `${pkgName} ${out.join(" ")}`;
}
function enrichActionRequiredWithInvokingCommand(payload, argv) {
if (!payload.choices?.length) {
return payload;
}
const next = [];
const linkArgv = [...argv.slice(0, 2), "link", ...argv.slice(3)];
for (const choice of payload.choices) {
const slug = choice.name;
next.push({
command: buildCommandWithScope(linkArgv, slug),
when: "Link first (then run any command without --scope)"
});
next.push({
command: buildCommandWithScope(argv, slug),
when: "Run this command with scope (no link)"
});
}
return { ...payload, next };
}
function outputActionRequired(client, payload, exitCode = 1) {
if (!shouldEmitNonInteractiveCommandError(client)) {
return;
}
const enriched = enrichActionRequiredWithInvokingCommand(
payload,
client.argv
);
if (!enriched.hint && enriched.next?.length) {
enriched.hint = "Run one of the commands in next[] to complete without prompting.";
}
client.stdout.write(`${JSON.stringify(enriched, null, 2)}
`);
process.exit(exitCode);
}
function argvHasNonInteractive(argv) {
if (!argv?.length) {
return false;
}
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (a === "--non-interactive") {
return argv[i + 1] !== "false";
}
if (a.startsWith("--non-interactive=")) {
return a.slice("--non-interactive=".length) !== "false";
}
}
return false;
}
function shouldEmitNonInteractiveCommandError(client) {
return client.nonInteractive || argvHasNonInteractive(client.argv ?? []);
}
function outputAgentError(client, payload, exitCode = 1) {
if (!shouldEmitNonInteractiveCommandError(client)) {
return;
}
client.stdout.write(`${JSON.stringify(payload, null, 2)}
`);
process.exit(exitCode);
}
function buildNextStepsForEdgeConfig(client) {
return [
{
command: buildCommandWithGlobalFlags(client.argv, "edge-config list"),
when: "List Edge Config stores in the current team scope"
},
{
command: buildCommandWithGlobalFlags(client.argv, "teams switch"),
when: "Switch to the team that owns the Edge Config"
},
{
command: buildCommandWithGlobalFlags(client.argv, "whoami"),
when: "Verify the current team or user scope"
}
];
}
var EDGE_CONFIG_NON_INTERACTIVE_HINT = "Edge Config commands use your current team scope. Pass --scope or run `vercel teams switch` if the store is missing.";
function buildNextStepsForProjectSubcommands(client, variant) {
const byName = variant === "access-groups" ? {
template: "project access-groups <name>",
when: "List access groups by project name (replace <name>)"
} : variant === "access-summary" ? {
template: "project access-summary <name>",
when: "Show role counts by project name (replace <name>)"
} : variant === "protection" ? {
template: "project protection <name>",
when: "Show deployment protection by project name (replace <name>)"
} : variant === "speed-insights" ? {
template: "project speed-insights <name>",
when: "Enable Speed Insights by project name (replace <name>)"
} : variant === "web-analytics" ? {
template: "project web-analytics <name>",
when: "Enable Web Analytics by project name (replace <name>)"
} : variant === "checks" ? {
template: "project checks add <name>",
when: "Create a deployment check by project name (replace <name>)"
} : {
template: "project members <name>",
when: "List members by project name (replace <name>)"
};
return [
{
command: buildCommandWithGlobalFlags(client.argv, "link"),
when: "Re-link this directory to the correct Vercel project"
},
{
command: buildCommandWithGlobalFlags(client.argv, byName.template),
when: byName.when
},
{
command: buildCommandWithGlobalFlags(client.argv, "project ls"),
when: "List projects in the current team to pick a name"
}
];
}
var PROJECT_SUBCOMMAND_ERROR_HINT = "If you use --cwd, ensure that folder is linked to the right project, or pass an explicit project name. Use --scope when the project belongs to another team.";
function resolveNonInteractiveDefaults(client, variant) {
if (variant === "edge-config") {
return {
next: buildNextStepsForEdgeConfig(client),
hint: EDGE_CONFIG_NON_INTERACTIVE_HINT
};
}
return {
next: buildNextStepsForProjectSubcommands(client, variant),
hint: PROJECT_SUBCOMMAND_ERROR_HINT
};
}
function writeAgentErrorPayloadAndExit(client, payload, exitCode, variant) {
const defaults = resolveNonInteractiveDefaults(client, variant);
const out = {
...payload,
next: payload.next ?? defaults.next,
hint: payload.hint ?? defaults.hint
};
client.stdout.write(`${JSON.stringify(out, null, 2)}
`);
process.exit(exitCode);
}
function isProjectNotFoundLike(err) {
if (err instanceof ProjectNotFound) {
return true;
}
if ((0, import_error_utils.isError)(err) && "code" in err && err.code === "PROJECT_NOT_FOUND") {
return true;
}
return false;
}
function isLinkRequiredLike(err) {
return err instanceof LinkRequiredError;
}
function normalizeApiErrorText(message) {
return message.replace(/\s*\(\d{3}\)\s*$/, "").trim();
}
function exitWithNonInteractiveError(client, err, exitCode = 1, options = {
variant: "members"
}) {
if (!shouldEmitNonInteractiveCommandError(client)) {
return;
}
const { variant } = options;
if (isLinkRequiredLike(err)) {
if (variant === "edge-config") {
writeAgentErrorPayloadAndExit(
client,
{
status: "error",
reason: "link_required",
message: err instanceof Error ? err.message : String(err),
next: buildNextStepsForEdgeConfig(client),
hint: EDGE_CONFIG_NON_INTERACTIVE_HINT
},
exitCode,
"edge-config"
);
return;
}
writeAgentErrorPayloadAndExit(
client,
{
status: "error",
reason: "link_required",
message: err instanceof Error ? err.message : String(err)
},
exitCode,
variant
);
return;
}
if (isProjectNotFoundLike(err)) {
writeAgentErrorPayloadAndExit(
client,
{
status: "error",
reason: "project_not_found",
message: err instanceof Error ? err.message : String(err)
},
exitCode,
variant
);
return;
}
if (isAPIError(err)) {
const rawMessage = err.serverMessage || err.message;
const message = normalizeApiErrorText(rawMessage);
const reason = err.status === 403 ? "forbidden" : err.status === 401 ? "not_authorized" : err.status === 404 ? variant === "edge-config" ? "not_found" : "project_not_found" : err.status === 429 ? "rate_limited" : "api_error";
writeAgentErrorPayloadAndExit(
client,
{
status: "error",
reason,
message,
...err.action && { action: err.action },
...err.resource && { resource: err.resource }
},
exitCode,
variant
);
}
writeAgentErrorPayloadAndExit(
client,
{
status: "error",
reason: "unexpected_error",
message: err instanceof Error ? err.message : String(err)
},
exitCode,
variant
);
}
function openUrlInBrowserCommand(url) {
if (process.platform === "win32")
return `start ${url}`;
if (process.platform === "darwin")
return `open '${url}'`;
return `xdg-open '${url}'`;
}
export {
buildCommandWithYes,
getGlobalFlagsFromArgv,
buildIntegrationCommandTailFromArgv,
buildCommandWithGlobalFlags,
getPreservedArgsForEnvAdd,
buildEnvAddCommandWithPreservedArgs,
getPreservedArgsForEnvPull,
getPreservedArgsForEnvRm,
buildEnvRmCommandWithPreservedArgs,
getPreservedArgsForEnvUpdate,
buildEnvUpdateCommandWithPreservedArgs,
outputActionRequired,
argvHasNonInteractive,
shouldEmitNonInteractiveCommandError,
outputAgentError,
exitWithNonInteractiveError,
openUrlInBrowserCommand
};