vercel
Version:
The command-line interface for Vercel
1,606 lines (1,589 loc) • 72.7 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 {
getCustomEnvironments,
getInvalidSubcommand,
readStandardInput,
require_dist as require_dist3
} from "../../chunks/chunk-C5YP6KFI.js";
import {
formatTable
} from "../../chunks/chunk-2DFWEDF7.js";
import {
suggestNextCommands
} from "../../chunks/chunk-LOS7HHU3.js";
import {
formatEnvironment,
validateLsArgs
} from "../../chunks/chunk-PPIAHRII.js";
import {
validateJsonOutput
} from "../../chunks/chunk-XPKWKPWA.js";
import {
getSubcommand
} from "../../chunks/chunk-YPQSDAEW.js";
import {
getCommandAliases
} from "../../chunks/chunk-UVFXUXOZ.js";
import "../../chunks/chunk-IFATV36R.js";
import "../../chunks/chunk-JFVGRFME.js";
import "../../chunks/chunk-TZMIHH5D.js";
import "../../chunks/chunk-XVAEOG4L.js";
import "../../chunks/chunk-ZAAKSLHC.js";
import "../../chunks/chunk-CQANJIEC.js";
import "../../chunks/chunk-4PSOOFYO.js";
import {
require_execa
} from "../../chunks/chunk-N733ZD4W.js";
import {
autoInstallVercelPlugin
} from "../../chunks/chunk-ZKKIBUCU.js";
import {
help
} from "../../chunks/chunk-MMF4BVAP.js";
import {
STANDARD_ENVIRONMENTS,
addSubcommand,
envCommand,
envTargetChoices,
formatProject,
getEnvRecords,
getEnvTargetPlaceholder,
getLinkedProject,
getTeamById,
listSubcommand,
param,
parseTarget,
pull,
pullEnvRecords,
pullSubcommand,
removeSubcommand,
require_frameworks,
runSubcommand,
updateSubcommand
} from "../../chunks/chunk-X775BOSL.js";
import {
TelemetryClient,
require_dist as require_dist2
} from "../../chunks/chunk-4OEA5ILS.js";
import {
buildCommandWithYes,
buildEnvAddCommandWithPreservedArgs,
buildEnvRmCommandWithPreservedArgs,
buildEnvUpdateCommandWithPreservedArgs,
getPreservedArgsForEnvAdd,
getPreservedArgsForEnvRm,
getPreservedArgsForEnvUpdate,
outputActionRequired,
outputAgentError
} from "../../chunks/chunk-ULXHXZCZ.js";
import {
require_ms,
stamp_default
} from "../../chunks/chunk-CO5D46AG.js";
import "../../chunks/chunk-N2T234LO.js";
import "../../chunks/chunk-DKD6GTQT.js";
import {
getFlagsSpecification,
parseArguments,
printError
} from "../../chunks/chunk-4GQQJY5Y.js";
import {
getCommandName,
getCommandNamePlain,
isAPIError,
require_lib
} from "../../chunks/chunk-UGXBNJMO.js";
import "../../chunks/chunk-P4QNYOFB.js";
import {
emoji,
output_manager_default,
prependEmoji,
require_dist
} from "../../chunks/chunk-ZQKJVHXY.js";
import {
require_source
} from "../../chunks/chunk-S7KYDPEM.js";
import {
__toESM
} from "../../chunks/chunk-TZ2YI2VH.js";
// src/commands/env/add.ts
var import_chalk = __toESM(require_source(), 1);
// src/util/env/add-env-record.ts
var import_constants = __toESM(require_dist2(), 1);
async function addEnvRecord(client, projectId, upsert, type, key, value, targets, gitBranch) {
const actionWord = upsert ? "Overriding" : "Adding";
output_manager_default.debug(
`${actionWord} ${type} Environment Variable ${key} to ${targets.length} targets`
);
const target = [];
const customEnvironmentIds = [];
for (const t of targets) {
const arr = import_constants.PROJECT_ENV_TARGET.includes(t) ? target : customEnvironmentIds;
arr.push(t);
}
const body = {
type,
key,
value,
target,
customEnvironmentIds: customEnvironmentIds.length > 0 ? customEnvironmentIds : void 0,
gitBranch: gitBranch || void 0
};
const args = upsert ? `?upsert=${upsert}` : "";
const url = `/v10/projects/${projectId}/env${args}`;
await client.fetch(url, {
method: "POST",
body
});
}
// src/util/env/known-error.ts
var import_error_utils = __toESM(require_dist(), 1);
var knownErrorsCodes = /* @__PURE__ */ new Set([
"BAD_REQUEST",
"ENV_ALREADY_EXISTS",
"ENV_CONFLICT",
"EXISTING_KEY_AND_TARGET",
"FORBIDDEN",
"ID_NOT_FOUND",
"INVALID_KEY",
"INVALID_VALUE",
"KEY_INVALID_CHARACTERS",
"KEY_INVALID_LENGTH",
"KEY_RESERVED",
"RESERVED_ENV_VARIABLE",
"MAX_ENVS_EXCEEDED",
"MISSING_ID",
"MISSING_KEY",
"MISSING_TARGET",
"MISSING_VALUE",
"NOT_AUTHORIZED",
"NOT_DECRYPTABLE",
"SYSTEM_ENV_WITH_VALUE",
"TEAM_NOT_FOUND",
"TOO_MANY_IDS",
"TOO_MANY_KEYS",
"UNKNOWN_ERROR",
"VALUE_INVALID_LENGTH",
"VALUE_INVALID_TYPE"
]);
function isKnownError(error) {
const code = (0, import_error_utils.isErrnoException)(error) ? error.code : null;
if (!code)
return false;
return knownErrorsCodes.has(code.toUpperCase());
}
// src/util/env/validate-env.ts
var import_frameworks = __toESM(require_frameworks(), 1);
function getEnvValueWarnings(value) {
const warnings = [];
const normalized = value.replace(/\n$/, "");
if (/^[ \t]+/.test(normalized)) {
warnings.push({
message: "starts with whitespace",
requiresConfirmation: false
});
}
if (/[ \t]+$/.test(normalized)) {
warnings.push({
message: "ends with whitespace",
requiresConfirmation: false
});
}
if (normalized.includes("\r") || normalized.includes("\n")) {
warnings.push({
message: "contains newlines",
requiresConfirmation: false
});
}
if (value.includes("\0")) {
warnings.push({
message: "contains null characters",
requiresConfirmation: false
});
}
if (value === "") {
warnings.push({
message: "is empty",
requiresConfirmation: true
});
}
if (value.length > 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
warnings.push({
message: "includes surrounding quotes (these will be stored literally)",
requiresConfirmation: false
});
}
return warnings;
}
function formatWarnings(warnings) {
if (warnings.length === 0)
return null;
const messages = warnings.map((w) => w.message);
const startsIdx = messages.indexOf("starts with whitespace");
const endsIdx = messages.indexOf("ends with whitespace");
if (startsIdx !== -1 && endsIdx !== -1) {
messages.splice(Math.max(startsIdx, endsIdx), 1);
messages[Math.min(startsIdx, endsIdx)] = "starts and ends with whitespace";
}
if (messages.length === 1) {
return `Value ${messages[0]}`;
}
if (messages.length === 2) {
return `Value ${messages[0]} and ${messages[1]}`;
}
const last = messages.pop();
return `Value ${messages.join(", ")}, and ${last}`;
}
var PUBLIC_PREFIXES = [
...new Set(
import_frameworks.frameworkList.map((f) => f.envPrefix).filter((p) => !!p)
)
];
var SENSITIVE_PATTERN = /(?:^|_)(password|secret|private|token|key|auth|jwt|signature)(?:_|$)/i;
function hasOnlyWhitespaceWarnings(warnings) {
return warnings.length > 0 && warnings.every(
(w) => w.message === "starts with whitespace" || w.message === "ends with whitespace"
);
}
function trimValue(value) {
return value.replace(/\n$/, "").trim();
}
function normalizeStdinEnvValue(value) {
let valueWithoutTrailingNewline = value;
if (value.endsWith("\r\n")) {
valueWithoutTrailingNewline = value.slice(0, -2);
} else if (value.endsWith("\n")) {
valueWithoutTrailingNewline = value.slice(0, -1);
} else {
return {
value,
strippedTrailingNewline: false
};
}
if (valueWithoutTrailingNewline.includes("\n") || valueWithoutTrailingNewline.includes("\r")) {
return {
value,
strippedTrailingNewline: false
};
}
return {
value: valueWithoutTrailingNewline,
strippedTrailingNewline: true
};
}
function getPublicPrefix(key) {
const upperKey = key.toUpperCase();
return PUBLIC_PREFIXES.find((p) => upperKey.startsWith(p)) || null;
}
function removePublicPrefix(key) {
const prefix = getPublicPrefix(key);
if (!prefix)
return key;
return key.slice(prefix.length);
}
async function validateEnvValue(opts) {
let finalValue = opts.initialValue;
let alreadyConfirmed = false;
if (!opts.skipConfirm) {
let valueAccepted = false;
while (!valueAccepted) {
const valueWarnings = getEnvValueWarnings(finalValue);
const warningMessage = formatWarnings(valueWarnings);
if (!warningMessage) {
valueAccepted = true;
break;
}
opts.showWarning(warningMessage);
const canTrim = hasOnlyWhitespaceWarnings(valueWarnings);
const choices = canTrim ? [
{ name: "Leave as is", value: "c" },
{ name: "Re-enter", value: "r" },
{ name: "Trim whitespace", value: "t" }
] : [
{ name: "Leave as is", value: "c" },
{ name: "Re-enter", value: "r" }
];
const action = await opts.selectAction(choices);
if (action === "c") {
valueAccepted = true;
if (valueWarnings.some((w) => w.requiresConfirmation)) {
alreadyConfirmed = true;
}
} else if (action === "t") {
finalValue = trimValue(finalValue);
opts.showLog("Trimmed whitespace");
} else {
finalValue = await opts.promptForValue();
}
}
} else {
const valueWarnings = getEnvValueWarnings(finalValue);
const warningMessage = formatWarnings(valueWarnings);
if (warningMessage) {
opts.showWarning(warningMessage);
}
}
return { finalValue, alreadyConfirmed };
}
function getEnvKeyWarnings(key) {
const warnings = [];
const matchingPrefix = getPublicPrefix(key);
if (matchingPrefix) {
const sensitiveMatch = SENSITIVE_PATTERN.exec(key);
const nameWithoutPrefix = key.slice(matchingPrefix.length);
if (sensitiveMatch) {
warnings.push({
message: `The ${matchingPrefix} prefix will make ${nameWithoutPrefix} visible to anyone visiting your site`,
requiresConfirmation: true
});
} else {
warnings.push({
message: `${matchingPrefix} variables can be seen by anyone visiting your site`,
requiresConfirmation: false
});
}
}
return warnings;
}
// src/util/telemetry/commands/env/add.ts
var EnvAddTelemetryClient = class extends TelemetryClient {
trackCliArgumentName(name) {
if (name) {
this.trackCliArgument({
arg: "name",
value: this.redactedValue
});
}
}
trackCliArgumentEnvironment(environment) {
if (environment) {
this.trackCliArgument({
arg: "environment",
value: STANDARD_ENVIRONMENTS.includes(
environment
) ? environment : this.redactedValue
});
}
}
trackCliArgumentGitBranch(gitBranch) {
if (gitBranch) {
this.trackCliArgument({
arg: "git-branch",
value: this.redactedValue
});
}
}
trackCliOptionValue(value) {
if (value) {
this.trackCliOption({
option: "value",
value: this.redactedValue
});
}
}
trackCliFlagSensitive(sensitive) {
if (sensitive) {
this.trackCliFlag("sensitive");
}
}
trackCliFlagNoSensitive(noSensitive) {
if (noSensitive) {
this.trackCliFlag("no-sensitive");
}
}
trackCliFlagForce(force) {
if (force) {
this.trackCliFlag("force");
}
}
trackCliFlagGuidance(guidance) {
if (guidance) {
this.trackCliFlag("guidance");
}
}
trackCliFlagYes(yes) {
if (yes) {
this.trackCliFlag("yes");
}
}
};
// src/commands/env/add.ts
import { determineAgent } from "@vercel/detect-agent";
function resolveTypeForTarget(target, opts) {
if (target === "development") {
return "encrypted";
}
if (opts.forceEncrypted)
return "encrypted";
if (opts.forceSensitive)
return "sensitive";
if (opts.policyOn)
return "sensitive";
return "sensitive";
}
function valueForNextCommand(value) {
if (!/[\s'"\\]/.test(value))
return value;
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
}
function fillEnvAddTemplate(template, opts) {
const targetPlaceholder = getEnvTargetPlaceholder();
let out = template.replace(/<name>/g, opts.envName ?? "<name>").split(targetPlaceholder).join(opts.envTargetArg ?? targetPlaceholder).replace(/<gitbranch>/g, opts.envGitBranch ?? "<gitbranch>");
if (opts.valueFromFlag !== void 0) {
out = out.replace(/<value>/g, valueForNextCommand(opts.valueFromFlag));
} else {
out = out.replace(/<value>/g, "<value>");
}
return out;
}
async function add(client, argv) {
let parsedArgs;
const flagsSpecification = getFlagsSpecification(addSubcommand.options);
try {
parsedArgs = parseArguments(argv, flagsSpecification);
} catch (err) {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "invalid_arguments",
message: err instanceof Error ? err.message : String(err)
},
1
);
}
printError(err);
return 1;
}
const { args, flags: opts } = parsedArgs;
const stdInput = await readStandardInput(client.stdin);
const valueFromFlag = typeof opts["--value"] === "string" ? opts["--value"] : void 0;
let [envName, envTargetArg, envGitBranch] = args;
const telemetryClient = new EnvAddTelemetryClient({
opts: {
store: client.telemetryEventStore
}
});
telemetryClient.trackCliArgumentName(envName);
telemetryClient.trackCliArgumentEnvironment(envTargetArg);
telemetryClient.trackCliArgumentGitBranch(envGitBranch);
telemetryClient.trackCliOptionValue(opts["--value"]);
telemetryClient.trackCliFlagSensitive(opts["--sensitive"]);
telemetryClient.trackCliFlagNoSensitive(opts["--no-sensitive"]);
telemetryClient.trackCliFlagForce(opts["--force"]);
telemetryClient.trackCliFlagGuidance(opts["--guidance"]);
telemetryClient.trackCliFlagYes(opts["--yes"]);
if (args.length > 3) {
output_manager_default.error(
`Invalid number of arguments. Usage: ${getCommandName(
`env add <name> ${getEnvTargetPlaceholder()} <gitbranch>`
)}`
);
return 1;
}
if (stdInput && (!envName || !envTargetArg)) {
output_manager_default.error(
`Invalid number of arguments. Usage: ${getCommandName(
`env add <name> <target> <gitbranch> < <file>`
)}`
);
return 1;
}
let envTargets = [];
if (envTargetArg) {
envTargets.push(envTargetArg);
}
if (client.nonInteractive) {
const link2 = await getLinkedProject(client);
if (link2.status === "error") {
return link2.exitCode;
}
if (link2.status === "not_linked") {
const preserved = getPreservedArgsForEnvAdd(client.argv);
const linkPreserved = preserved.filter((a, i) => {
if (a === "--value")
return false;
if (a.startsWith("--value="))
return false;
if (i > 0 && preserved[i - 1] === "--value")
return false;
return true;
});
const linkArgv = [
...client.argv.slice(0, 2),
"link",
"--scope",
"<scope>",
...linkPreserved
];
let envAddRetryArgv = client.argv;
if (envTargetArg === "preview" && envGitBranch === void 0) {
const argvArgs = client.argv.slice(2);
const addIdx = argvArgs.indexOf("add");
if (addIdx !== -1) {
let pos = addIdx + 1;
let positionals = 0;
while (pos < argvArgs.length && positionals < 3 && !argvArgs[pos].startsWith("-")) {
positionals++;
pos++;
}
const insertAt = 2 + pos;
envAddRetryArgv = [
...client.argv.slice(0, insertAt),
"<gitbranch>",
...client.argv.slice(insertAt)
];
}
}
outputAgentError(
client,
{
status: "error",
reason: "not_linked",
message: `Your codebase isn't linked to a project on Vercel. Run ${getCommandNamePlain(
"link"
)} to begin. Use --yes for non-interactive; use --scope or --project to specify team or project. Then run your env add command.`,
next: [
{ command: buildCommandWithYes(linkArgv) },
{ command: buildCommandWithYes(envAddRetryArgv) }
]
},
1
);
}
if (link2.status !== "linked")
return 1;
const { project: project2 } = link2;
const org = link2.org;
client.config.currentTeam = org.type === "team" ? org.id : void 0;
const [{ envs: envs2 }, customEnvironments2] = await Promise.all([
getEnvRecords(client, project2.id, "vercel-cli:env:add"),
getCustomEnvironments(client, project2.id)
]);
const matchingEnvs2 = envs2.filter((r) => r.key === envName);
const existingTargets2 = /* @__PURE__ */ new Set();
const existingCustomEnvs2 = /* @__PURE__ */ new Set();
for (const env of matchingEnvs2) {
if (typeof env.target === "string") {
existingTargets2.add(env.target);
} else if (Array.isArray(env.target)) {
for (const target of env.target) {
existingTargets2.add(target);
}
}
if (env.customEnvironmentIds) {
for (const customEnvId of env.customEnvironmentIds) {
existingCustomEnvs2.add(customEnvId);
}
}
}
const choices2 = [
...envTargetChoices.filter((c) => !existingTargets2.has(c.value)),
...customEnvironments2.filter((c) => !existingCustomEnvs2.has(c.id)).map((c) => ({
name: c.slug,
value: c.id
}))
];
const missing = [];
if (!envName)
missing.push("missing_name");
if (valueFromFlag === void 0 && !stdInput)
missing.push("missing_value");
if (!envTargetArg && choices2.length > 0)
missing.push("missing_environment");
if (envTargetArg === "preview" && envGitBranch === void 0 && !(client.nonInteractive && args.length === 2)) {
missing.push("git_branch_required");
}
if (missing.length > 0) {
const parts = missing.map((m) => {
if (m === "missing_name")
return "variable name";
if (m === "missing_value")
return "--value or stdin";
if (m === "missing_environment")
return "environment (production, preview, or development)";
if (m === "git_branch_required")
return "third argument <gitbranch> for Preview, or omit for all Preview branches";
return m;
});
const fullTemplate = `env add <name> ${getEnvTargetPlaceholder()} <gitbranch> --value <value> --yes`;
const filledTemplate = fillEnvAddTemplate(fullTemplate, {
envName,
envTargetArg,
valueFromFlag,
envGitBranch
});
const next = [];
const onlyGitBranchMissing = missing.length === 1 && missing[0] === "git_branch_required";
if (!onlyGitBranchMissing) {
next.push({
command: buildEnvAddCommandWithPreservedArgs(
client.argv,
filledTemplate
)
});
}
if (missing.includes("git_branch_required") && envName && (valueFromFlag !== void 0 || stdInput)) {
const branchSpecific = fillEnvAddTemplate(
"env add <name> preview <gitbranch> --value <value> --yes",
{ envName, envTargetArg: "preview", valueFromFlag }
);
const branchAll = fillEnvAddTemplate(
"env add <name> preview --value <value> --yes",
{ envName, envTargetArg: "preview", valueFromFlag }
);
next.push(
{
command: buildEnvAddCommandWithPreservedArgs(
client.argv,
branchSpecific
),
when: "Add to a specific Git branch"
},
{
command: buildEnvAddCommandWithPreservedArgs(
client.argv,
branchAll
),
when: "Add to all Preview branches"
}
);
}
outputActionRequired(
client,
{
status: "action_required",
reason: "missing_requirements",
missing,
message: `Provide all required inputs for non-interactive mode: ${parts.join("; ")}. Example: ${filledTemplate}`,
next
},
1
);
}
}
if (!envName) {
envName = await client.input.text({
message: `What's the name of the variable?`,
validate: (val) => val ? true : "Name cannot be empty"
});
}
const skipConfirm = opts["--yes"] || !!stdInput || valueFromFlag !== void 0;
if (!skipConfirm) {
let keyAccepted = false;
while (!keyAccepted) {
const keyWarnings = getEnvKeyWarnings(envName);
const sensitiveWarning = keyWarnings.find((w) => w.requiresConfirmation);
if (!sensitiveWarning) {
for (const w of keyWarnings) {
output_manager_default.warn(w.message);
}
keyAccepted = true;
break;
}
if (client.nonInteractive) {
const nameWithoutPrefix2 = removePublicPrefix(envName);
outputActionRequired(client, {
status: "action_required",
reason: "env_key_sensitive",
message: `Key ${envName} may expose sensitive data (public prefix). Use --yes to keep as is, or rename to ${nameWithoutPrefix2}.`,
choices: [
{ id: "keep", name: "Leave as is (use --yes)" },
{ id: "rename", name: `Rename to ${nameWithoutPrefix2}` }
],
next: [
{
command: buildEnvAddCommandWithPreservedArgs(
client.argv,
`env add ${envName} ${getEnvTargetPlaceholder()} --value <value> --yes`
),
when: "Leave as is"
},
{
command: buildEnvAddCommandWithPreservedArgs(
client.argv,
`env add ${nameWithoutPrefix2} ${getEnvTargetPlaceholder()} --value <value> --yes`
),
when: "Rename"
}
]
});
}
for (const w of keyWarnings) {
output_manager_default.warn(w.message);
}
const nameWithoutPrefix = removePublicPrefix(envName);
const choices2 = [
{ name: "Leave as is", value: "c" },
{ name: `Rename to ${nameWithoutPrefix}`, value: "p" },
{ name: "Re-enter", value: "r" }
];
const action = await client.input.select({
message: "How to proceed?",
choices: choices2
});
if (action === "c") {
keyAccepted = true;
} else if (action === "p") {
envName = nameWithoutPrefix;
output_manager_default.log(`Renamed to ${envName}`);
} else {
envName = await client.input.text({
message: `What's the name of the variable?`,
validate: (val) => val ? true : "Name cannot be empty"
});
}
}
} else {
const keyWarnings = getEnvKeyWarnings(envName);
for (const w of keyWarnings) {
output_manager_default.warn(w.message);
}
}
const link = await getLinkedProject(client);
if (link.status === "error") {
return link.exitCode;
} else if (link.status === "not_linked") {
if (client.nonInteractive) {
const preserved = getPreservedArgsForEnvAdd(client.argv);
const linkPreserved = preserved.filter((a, i) => {
if (a === "--value")
return false;
if (a.startsWith("--value="))
return false;
if (i > 0 && preserved[i - 1] === "--value")
return false;
return true;
});
const linkArgv = [
...client.argv.slice(0, 2),
"link",
...link.status === "not_linked" ? ["--scope", "<scope>"] : [],
...linkPreserved
];
let envAddRetryArgv = client.argv;
if (envTargetArg === "preview" && envGitBranch === void 0) {
const argvArgs = client.argv.slice(2);
const addIdx = argvArgs.indexOf("add");
if (addIdx !== -1) {
let pos = addIdx + 1;
let positionals = 0;
while (pos < argvArgs.length && positionals < 3 && !argvArgs[pos].startsWith("-")) {
positionals++;
pos++;
}
const insertAt = 2 + pos;
envAddRetryArgv = [
...client.argv.slice(0, insertAt),
"<gitbranch>",
...client.argv.slice(insertAt)
];
}
}
outputAgentError(
client,
{
status: "error",
reason: "not_linked",
message: `Your codebase isn't linked to a project on Vercel. Run ${getCommandNamePlain(
"link"
)} to begin. Use --yes for non-interactive; use --scope or --project to specify team or project. Then run your env add command.`,
next: [
{ command: buildCommandWithYes(linkArgv) },
{ command: buildCommandWithYes(envAddRetryArgv) }
]
},
1
);
} else {
output_manager_default.error(
`Your codebase isn\u2019t linked to a project on Vercel. Run ${getCommandName(
"link"
)} to begin.`
);
}
return 1;
}
client.config.currentTeam = link.org.type === "team" ? link.org.id : void 0;
const { project } = link;
const [{ envs }, customEnvironments] = await Promise.all([
getEnvRecords(client, project.id, "vercel-cli:env:add"),
getCustomEnvironments(client, project.id)
]);
const matchingEnvs = envs.filter((r) => r.key === envName);
const existingTargets = /* @__PURE__ */ new Set();
const existingCustomEnvs = /* @__PURE__ */ new Set();
for (const env of matchingEnvs) {
if (typeof env.target === "string") {
existingTargets.add(env.target);
} else if (Array.isArray(env.target)) {
for (const target of env.target) {
existingTargets.add(target);
}
}
if (env.customEnvironmentIds) {
for (const customEnvId of env.customEnvironmentIds) {
existingCustomEnvs.add(customEnvId);
}
}
}
const choices = [
...envTargetChoices.filter((c) => !existingTargets.has(c.value)).map((c) => ({ name: c.name, value: c.value })),
...customEnvironments.filter((c) => !existingCustomEnvs.has(c.id)).map((c) => ({ name: c.slug, value: c.id }))
];
if (!envGitBranch && choices.length === 0 && !opts["--force"]) {
output_manager_default.error(
`The variable ${param(
envName
)} has already been added to all Environments. To remove, run ${getCommandName(
`env rm ${envName}`
)}.`
);
return 1;
}
const forceSensitive = Boolean(opts["--sensitive"]);
const forceEncrypted = Boolean(opts["--no-sensitive"]);
if (forceSensitive && forceEncrypted) {
output_manager_default.error(
`--sensitive and --no-sensitive cannot be used together. Pick one.`
);
return 1;
}
let policyOn = false;
if (link.org.type === "team") {
try {
const team = await getTeamById(client, link.org.id);
policyOn = team?.sensitiveEnvironmentVariablePolicy === "on";
} catch {
}
}
if (policyOn) {
for (const choice of choices) {
if (choice.value === "production" || choice.value === "preview") {
choice.checked = true;
}
}
}
let envValue;
if (stdInput) {
const normalizedStdinValue = normalizeStdinEnvValue(stdInput);
envValue = normalizedStdinValue.value;
if (normalizedStdinValue.strippedTrailingNewline) {
output_manager_default.log("Removed trailing newline from stdin input");
}
} else if (valueFromFlag !== void 0) {
envValue = valueFromFlag;
} else {
if (client.nonInteractive) {
outputActionRequired(client, {
status: "action_required",
reason: "missing_value",
message: "In non-interactive mode provide the value via --value or stdin. Example: vercel env add <name> <environment> --value 'value' --yes",
next: [
{
command: buildEnvAddCommandWithPreservedArgs(
client.argv,
`env add <name> ${getEnvTargetPlaceholder()} --value <value> --yes`
)
}
]
});
}
envValue = await client.input.password({
message: `What's the value of ${envName}?`,
mask: true
});
}
const { finalValue } = await validateEnvValue({
envName,
initialValue: envValue,
skipConfirm,
promptForValue: () => client.input.password({
message: `What's the value of ${envName}?`,
mask: true
}),
selectAction: (choices2) => client.input.select({ message: "How to proceed?", choices: choices2 }),
showWarning: (msg) => output_manager_default.warn(msg),
showLog: (msg) => output_manager_default.log(msg)
});
while (envTargets.length === 0) {
if (client.nonInteractive && choices.length > 0) {
outputActionRequired(client, {
status: "action_required",
reason: "missing_environment",
message: `Specify at least one environment. Add as argument or use: ${buildEnvAddCommandWithPreservedArgs(
client.argv,
`env add ${envName} <environment> --value <value> --yes`
)}`,
choices: choices.map((c) => ({
id: c.value,
name: typeof c.name === "string" ? c.name : c.value
})),
next: choices.slice(0, 5).map((c) => ({
command: buildEnvAddCommandWithPreservedArgs(
client.argv,
`env add ${envName} ${c.value} --value <value> --yes`
)
}))
});
}
envTargets = await client.input.checkbox({
message: `Add ${envName} to which Environments (select multiple)?`,
choices
});
if (envTargets.length === 0) {
output_manager_default.error("Please select at least one Environment");
}
}
if (envGitBranch === void 0 && envTargets.length === 1 && envTargets[0] === "preview") {
if (client.nonInteractive) {
outputActionRequired(
client,
{
status: "action_required",
reason: "git_branch_required",
message: `Add ${envName} to which Git branch for Preview? Pass branch as third argument, or omit for all Preview branches.`,
next: [
{
command: buildEnvAddCommandWithPreservedArgs(
client.argv,
`env add ${envName} preview <gitbranch> --value <value> --yes`
),
when: "Add to a specific Git branch"
},
{
command: buildEnvAddCommandWithPreservedArgs(
client.argv,
`env add ${envName} preview --value <value> --yes`
),
when: "Add to all Preview branches"
}
]
},
1
);
} else {
envGitBranch = await client.input.text({
message: `Add ${envName} to which Git branch? (leave empty for all Preview branches)?`
});
}
}
const hasDevelopment = envTargets.includes("development");
const hasSensitiveCapable = envTargets.some((t) => t !== "development");
if (forceSensitive && hasDevelopment) {
const msg = `--sensitive is not allowed with the Development Environment. Sensitive Environment Variables are only supported on Production and Preview.`;
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "sensitive_not_allowed_on_development",
message: msg
},
1
);
}
output_manager_default.error(msg);
return 1;
}
if (hasDevelopment && hasSensitiveCapable) {
const msg = `Development cannot be combined with other Environments because Development does not support sensitive Environment Variables. Run ${getCommandName(
"env add"
)} separately for Development.`;
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "mixed_development_and_sensitive_capable_targets",
message: msg
},
1
);
}
output_manager_default.error(msg);
return 1;
}
let finalType = resolveTypeForTarget(
hasDevelopment ? "development" : "production",
{ forceSensitive, forceEncrypted, policyOn }
);
const userWasExplicit = forceSensitive || forceEncrypted;
const canPromptForType = !client.nonInteractive && !userWasExplicit && !policyOn && hasSensitiveCapable && !skipConfirm;
if (canPromptForType) {
output_manager_default.log(
`Sensitive values cannot be retrieved later from the dashboard or CLI.`
);
const keepSensitive = await client.input.confirm(
`Make it sensitive?`,
true
);
if (!keepSensitive) {
finalType = "encrypted";
}
}
if (policyOn && hasSensitiveCapable) {
if (forceEncrypted) {
output_manager_default.warn(
`--no-sensitive is ignored: your team enforces sensitive Environment Variables for Production and Preview.`
);
finalType = "sensitive";
} else if (!userWasExplicit) {
output_manager_default.log(
`Your team requires sensitive Environment Variables for Production and Preview.`
);
}
}
const upsert = opts["--force"] ? "true" : "";
const addStamp = stamp_default();
try {
output_manager_default.spinner("Saving");
await addEnvRecord(
client,
project.id,
upsert,
finalType,
envName,
finalValue,
envTargets,
envGitBranch
);
} catch (err) {
if (client.nonInteractive && isAPIError(err)) {
const reason = err.slug || (err.serverMessage?.toLowerCase().includes("branch") ? "branch_not_found" : "api_error");
outputAgentError(
client,
{
status: "error",
reason,
message: err.serverMessage
},
1
);
}
if (isAPIError(err) && isKnownError(err)) {
output_manager_default.error(err.serverMessage);
return 1;
}
throw err;
}
output_manager_default.print(
`${prependEmoji(
`${opts["--force"] ? "Overrode" : "Added"} Environment Variable ${import_chalk.default.bold(envName)} to Project ${import_chalk.default.bold(
project.name
)} ${import_chalk.default.gray(addStamp())}`,
emoji("success")
)}
`
);
const { isAgent } = await determineAgent();
const guidanceMode = parsedArgs.flags["--guidance"] ?? isAgent;
if (guidanceMode) {
suggestNextCommands([getCommandName(`env ls`), getCommandName(`env pull`)]);
}
return 0;
}
// src/commands/env/ls.ts
var import_chalk2 = __toESM(require_source(), 1);
var import_ms = __toESM(require_ms(), 1);
// src/util/output/ellipsis.ts
function ellipsis(str, length) {
return str.length > length ? `${str.slice(0, length - 1)}\u2026` : str;
}
// src/util/env/format-environments.ts
var import_title = __toESM(require_lib(), 1);
function formatEnvironments(link, env, customEnvironments) {
const defaultTargets = (Array.isArray(env.target) ? env.target : [env.target || ""]).map((t) => {
return formatEnvironment(link.org.slug, link.project.name, {
id: t,
slug: (0, import_title.default)(t)
});
});
const customTargets = env.customEnvironmentIds ? env.customEnvironmentIds.map((id) => customEnvironments.find((e) => e.id === id)).filter(Boolean).map((e) => formatEnvironment(link.org.slug, link.project.name, e)) : [];
const targetsString = [...defaultTargets, ...customTargets].join(", ");
return env.gitBranch ? `${targetsString} (${env.gitBranch})` : targetsString;
}
// src/util/telemetry/commands/env/ls.ts
var EnvLsTelemetryClient = class extends TelemetryClient {
trackCliArgumentEnvironment(environment) {
if (environment) {
this.trackCliArgument({
arg: "environment",
value: STANDARD_ENVIRONMENTS.includes(
environment
) ? environment : this.redactedValue
});
}
}
trackCliArgumentGitBranch(gitBranch) {
if (gitBranch) {
this.trackCliArgument({
arg: "git-branch",
value: this.redactedValue
});
}
}
trackCliFlagGuidance(guidance) {
if (guidance) {
this.trackCliFlag("guidance");
}
}
};
// src/commands/env/ls.ts
import { determineAgent as determineAgent2 } from "@vercel/detect-agent";
async function ls(client, argv) {
const telemetryClient = new EnvLsTelemetryClient({
opts: {
store: client.telemetryEventStore
}
});
let parsedArgs;
const flagsSpecification = getFlagsSpecification(listSubcommand.options);
try {
parsedArgs = parseArguments(argv, flagsSpecification);
} catch (err) {
printError(err);
return 1;
}
const { args, flags } = parsedArgs;
const validationResult = validateLsArgs({
commandName: "env ls",
args,
maxArgs: 2,
exitCode: 1,
usageString: getCommandName(
`env ls ${getEnvTargetPlaceholder()} <gitbranch>`
)
});
if (validationResult !== 0) {
return validationResult;
}
const [envTarget, envGitBranch] = args;
const formatResult = validateJsonOutput(flags);
if (!formatResult.valid) {
output_manager_default.error(formatResult.error);
return 1;
}
const asJson = formatResult.jsonOutput;
telemetryClient.trackCliArgumentEnvironment(envTarget);
telemetryClient.trackCliArgumentGitBranch(envGitBranch);
telemetryClient.trackCliFlagGuidance(flags["--guidance"]);
telemetryClient.trackCliOptionFormat(flags["--format"]);
const link = await getLinkedProject(client);
if (link.status === "error") {
return link.exitCode;
} else if (link.status === "not_linked") {
output_manager_default.error(
`Your codebase isn\u2019t linked to a project on Vercel. ${client.nonInteractive ? `Run ${getCommandName("link --yes --team <team-id> --project <project-id>")} to link non-interactively.` : `Run ${getCommandName("link")} to begin.`}`
);
return 1;
}
client.config.currentTeam = link.org.type === "team" ? link.org.id : void 0;
const { project, org } = link;
const lsStamp = stamp_default();
const [envsResult, customEnvs] = await Promise.all([
getEnvRecords(client, project.id, "vercel-cli:env:ls", {
target: envTarget,
gitBranch: envGitBranch
}),
getCustomEnvironments(client, project.id)
]);
const { envs } = envsResult;
const projectSlugLink = formatProject(org.slug, project.name);
if (asJson) {
output_manager_default.stopSpinner();
const jsonOutput = {
envs: envs.map((env) => ({
key: env.key,
value: env.type === "plain" ? env.value : void 0,
type: env.type,
target: env.target,
gitBranch: env.gitBranch,
configurationId: env.configurationId,
createdAt: env.createdAt,
updatedAt: env.updatedAt
}))
};
client.stdout.write(`${JSON.stringify(jsonOutput, null, 2)}
`);
} else if (envs.length === 0) {
output_manager_default.log(
`No Environment Variables found for ${projectSlugLink} ${import_chalk2.default.gray(lsStamp())}`
);
} else {
output_manager_default.log(
`Environment Variables found for ${projectSlugLink} ${import_chalk2.default.gray(lsStamp())}`
);
client.stdout.write(`${getTable(link, envs, customEnvs)}
`);
}
if (!asJson) {
const { isAgent } = await determineAgent2();
const guidanceMode = parsedArgs.flags["--guidance"] ?? isAgent;
if (guidanceMode) {
suggestNextCommands([
getCommandName(`env add`),
getCommandName("env rm"),
getCommandName(`env pull`)
]);
}
}
return 0;
}
function getTable(link, records, customEnvironments) {
const label = records.some((env) => env.gitBranch) ? "environments (git branch)" : "environments";
return formatTable(
["name", "value", label, "created"],
["l", "l", "l", "l", "l"],
[
{
name: "",
rows: records.map((row) => getRow(link, row, customEnvironments))
}
]
);
}
function getRow(link, env, customEnvironments) {
let value;
if (env.type === "plain") {
const singleLineValue = env.value.replace(/\s/g, " ");
value = import_chalk2.default.gray(ellipsis(singleLineValue, 19));
} else if (env.type === "system") {
value = import_chalk2.default.gray.italic(env.value);
} else {
value = import_chalk2.default.gray.italic("Encrypted");
}
const now = Date.now();
return [
import_chalk2.default.bold(env.key),
value,
formatEnvironments(link, env, customEnvironments),
env.createdAt ? `${(0, import_ms.default)(now - env.createdAt)} ago` : ""
];
}
// src/commands/env/rm.ts
var import_chalk3 = __toESM(require_source(), 1);
// src/util/env/remove-env-record.ts
async function removeEnvRecord(client, projectId, env) {
output_manager_default.debug(`Removing Environment Variable ${env.key}`);
const url = `/v10/projects/${projectId}/env/${env.id}`;
await client.fetch(url, {
method: "DELETE"
});
}
// src/util/telemetry/commands/env/rm.ts
var EnvRmTelemetryClient = class extends TelemetryClient {
trackCliArgumentName(name) {
if (name) {
this.trackCliArgument({
arg: "name",
value: this.redactedValue
});
}
}
trackCliArgumentEnvironment(environment) {
if (environment) {
this.trackCliArgument({
arg: "environment",
value: STANDARD_ENVIRONMENTS.includes(
environment
) ? environment : this.redactedValue
});
}
}
trackCliArgumentGitBranch(gitBranch) {
if (gitBranch) {
this.trackCliArgument({
arg: "git-branch",
value: this.redactedValue
});
}
}
trackCliFlagYes(yes) {
if (yes) {
this.trackCliFlag("yes");
}
}
};
// src/commands/env/rm.ts
async function rm(client, argv) {
const telemetryClient = new EnvRmTelemetryClient({
opts: {
store: client.telemetryEventStore
}
});
let parsedArgs;
const flagsSpecification = getFlagsSpecification(removeSubcommand.options);
try {
parsedArgs = parseArguments(argv, flagsSpecification);
} catch (err) {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "invalid_arguments",
message: err instanceof Error ? err.message : String(err)
},
1
);
}
printError(err);
return 1;
}
const { args, flags: opts } = parsedArgs;
if (args.length > 3) {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "invalid_arguments",
message: `Invalid number of arguments. Usage: ${getCommandNamePlain(
`env rm <name> ${getEnvTargetPlaceholder()} <gitbranch>`
)}`
},
1
);
}
output_manager_default.error(
`Invalid number of arguments. Usage: ${getCommandName(
`env rm <name> ${getEnvTargetPlaceholder()} <gitbranch>`
)}`
);
return 1;
}
let [envName, envTarget, envGitBranch] = args;
telemetryClient.trackCliArgumentName(envName);
telemetryClient.trackCliArgumentEnvironment(envTarget);
telemetryClient.trackCliArgumentGitBranch(envGitBranch);
telemetryClient.trackCliFlagYes(opts["--yes"]);
const link = await getLinkedProject(client);
if (link.status === "error") {
return link.exitCode;
} else if (link.status === "not_linked") {
if (client.nonInteractive) {
const preserved = getPreservedArgsForEnvRm(client.argv).filter(
(a) => a !== "--yes" && a !== "-y"
);
const linkArgv = [
...client.argv.slice(0, 2),
"link",
"--scope",
"<scope>",
...preserved
];
outputAgentError(
client,
{
status: "error",
reason: "not_linked",
message: `Your codebase isn't linked to a project on Vercel. Run ${getCommandNamePlain(
"link"
)} to begin. Use --yes for non-interactive; use --scope or --project to specify team or project.`,
next: [
{ command: buildCommandWithYes(linkArgv) },
{ command: buildCommandWithYes(client.argv) }
]
},
1
);
} else {
output_manager_default.error(
`Your codebase isn\u2019t linked to a project on Vercel. Run ${getCommandName(
"link"
)} to begin.`
);
}
return 1;
}
client.config.currentTeam = link.org.type === "team" ? link.org.id : void 0;
const { project } = link;
if (!envName) {
if (client.nonInteractive) {
outputActionRequired(
client,
{
status: "action_required",
reason: "missing_name",
message: "Provide the variable name as an argument. Example: vercel env rm <name> --yes",
next: [
{
command: buildEnvRmCommandWithPreservedArgs(
client.argv,
`env rm <name> ${getEnvTargetPlaceholder()} --yes`
)
}
]
},
1
);
}
envName = await client.input.text({
message: "What's the name of the variable?",
validate: (val) => val ? true : "Name cannot be empty"
});
}
const [result, customEnvironments] = await Promise.all([
getEnvRecords(client, project.id, "vercel-cli:env:rm", {
target: envTarget,
gitBranch: envGitBranch
}),
getCustomEnvironments(client, project.id)
]);
let envs = result.envs.filter((env2) => env2.key === envName);
if (envs.length === 0) {
if (client.nonInteractive) {
outputAgentError(
client,
{
status: "error",
reason: "env_not_found",
message: `Environment Variable ${envName} was not found.`
},
1
);
}
output_manager_default.error(`Environment Variable was not found.
`);
return 1;
}
while (envs.length > 1) {
if (client.nonInteractive) {
outputActionRequired(
client,
{
status: "action_required",
reason: "multiple_envs",
message: `Multiple Environment Variables match ${envName}. Specify target and/or branch to remove one.`,
next: [
{
command: buildEnvRmCommandWithPreservedArgs(
client.argv,
`env rm ${envName} ${getEnvTargetPlaceholder()} --yes`
)
}
]
},
1
);
}
const id = await client.input.select({
message: `Remove ${envName} from which Environments?`,
choices: envs.map((env2) => ({
value: env2.id,
name: formatEnvironments(link, env2, customEnvironments)
}))
});
if (!id) {
output_manager_default.error("Please select at least one Environment Variable to remove");
}
envs = envs.filter((env2) => env2.id === id);
}
const env = envs[0];
const skipConfirmation = opts["--yes"];
if (!skipConfirmation) {
if (client.nonInteractive) {
outputActionRequired(
client,
{
status: "action_required",
reason: "confirmation_required",
message: `Removing Environment Variable ${env.key}. Use --yes to confirm.`,
next: [{ command: buildCommandWithYes(client.argv) }]
},
1
);
}
if (!await client.input.confirm(
`Removing Environment Variable ${param(env.key)} from ${formatEnvironments(
link,
env,
customEnvironments
)} in Project ${import_chalk3.default.bold(project.name)}. Are you sure?`,
false
)) {
output_manager_default.log("Canceled");
return 0;
}
}
const rmStamp = stamp_default();
try {
output_manager_default.spinner("Removing");
await removeEnvRecord(client, project.id, env);
} catch (err) {
if (client.nonInteractive && isAPIError(err)) {
const reason = err.slug || (err.serverMessage?.toLowerCase().includes("branch") ? "branch_not_found" : "api_error");
outputAgentError(
client,
{
status: "error",
reason,
message: err.serverMessage
},
1
);
}
if (isAPIError(err) && isKnownError(err)) {
output_manager_default.error(err.serverMessage);
return 1;
}
throw err;
}
output_manager_default.print(
`${prependEmoji(
`Removed Environment Variable ${import_chalk3.default.gray(rmStamp())}`,
emoji("success")
)}
`
);
return 0;
}
// src/commands/env/run.ts
var import_env = __toESM(require_dist3(), 1);
var import_execa = __toESM(require_execa(), 1);
function parseRunArgs(argv) {
const argvIndex = argv.indexOf("--");
const hasDoubleDash = argvIndex !== -1;
const vercelArgs = hasDoubleDash ? argv.slice(2, argvIndex) : argv.slice(2);
const userCommand = hasDoubleDash ? argv.slice(argvIndex + 1) : [];
return { vercelArgs, userCommand };
}
function needsHelpForRun(client) {
const { vercelArgs } = parseRunArgs(client.argv);
const flagsSpecification = getFlagsSpecification(runSubcommand.options);
try {
const parsedArgs = parseArguments(vercelArgs, flagsSpecification);
return Boolean(parsedArgs.flags["--help"]);
} catch {
return false;
}
}
async function run(client) {
const { vercelArgs, userCommand } = parseRunArgs(client.argv);
let parsedArgs;
const flagsSpecification = getFlagsSpecification(runSubcommand.options);
try {
parsedArgs = parseArguments(vercelArgs, flagsSpecification);
} catch (error) {
printError(error);
return 1;
}
if (userCommand.length === 0) {
output_manager_default.error(
`No command provided. Use \`--\` to separate vercel flags from your command.`
);
return 1;
}
const link = await getLinkedProject(client);
if (link.status === "error") {
return link.exitCode;
} else if (link.status === "not_linked") {
output_manager_default.error(
`Your codebase isn't linked to a project on Vercel. Run ${getCommandName(