vercel 59.5.0 → 59.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/chunks/{add-Z5FM6TFI.js → add-7O4TM33A.js} +12 -12
  2. package/dist/chunks/{add-MH3WV6PV.js → add-UT6ZPA4F.js} +11 -11
  3. package/dist/chunks/{add-W3FFMTX4.js → add-UVMFKSSY.js} +11 -11
  4. package/dist/chunks/{build-VYYFBJL7.js → build-SKKT3DIB.js} +11 -11
  5. package/dist/chunks/{chunk-LLM3ZNXJ.js → chunk-2EH6SZRG.js} +2 -2
  6. package/dist/chunks/{chunk-UUVMIXT4.js → chunk-2T3IZZTJ.js} +4 -4
  7. package/dist/chunks/{chunk-RZ5NP6HN.js → chunk-2YRAWYGE.js} +1 -1
  8. package/dist/chunks/{chunk-TDXNNIZ6.js → chunk-3PA7LXYM.js} +2 -2
  9. package/dist/chunks/{chunk-KQRVZWJU.js → chunk-3T7WSWZH.js} +1 -1
  10. package/dist/chunks/{chunk-R6IGDGX3.js → chunk-5XECIWME.js} +1 -1
  11. package/dist/chunks/{chunk-PBHBX6UL.js → chunk-63OX2DER.js} +3 -3
  12. package/dist/chunks/{chunk-V4YLY44R.js → chunk-67IIZBBY.js} +2 -2
  13. package/dist/chunks/{chunk-AWSLWAOC.js → chunk-6JPVTPTU.js} +3 -3
  14. package/dist/chunks/{chunk-HT2XWSAJ.js → chunk-7AEEQOD4.js} +2 -29
  15. package/dist/chunks/{chunk-QG4PCM45.js → chunk-7HE63HCR.js} +102 -15
  16. package/dist/chunks/{chunk-FA3MO56Q.js → chunk-7IDPPWDN.js} +5 -5
  17. package/dist/chunks/{chunk-MRMNMFMD.js → chunk-7KSICC3A.js} +1 -1
  18. package/dist/chunks/{chunk-XBN2O34P.js → chunk-7ST552E3.js} +17 -4
  19. package/dist/chunks/{chunk-TAZ5ZAKG.js → chunk-AFKB6VMO.js} +2 -2
  20. package/dist/chunks/{chunk-CV4S27SS.js → chunk-ASBP32T6.js} +1 -1
  21. package/dist/chunks/{chunk-CQJRLNTX.js → chunk-AYLY3ZVL.js} +2 -2
  22. package/dist/chunks/{chunk-AWCID36T.js → chunk-BMKU5KEL.js} +1 -1
  23. package/dist/chunks/{chunk-E6LFKMI2.js → chunk-BQG777JE.js} +794 -124
  24. package/dist/chunks/{chunk-VWYNSJXI.js → chunk-CBHLLOGZ.js} +8 -8
  25. package/dist/chunks/{chunk-CSCSGSQG.js → chunk-CMRZ4KI4.js} +5 -5
  26. package/dist/chunks/{chunk-TIWAL2YU.js → chunk-DG6IMLCF.js} +6 -6
  27. package/dist/chunks/{chunk-YIHOS3ZF.js → chunk-FRAXI2WT.js} +1 -1
  28. package/dist/chunks/{chunk-7XX4ZEOV.js → chunk-FWKSJYDV.js} +1 -1
  29. package/dist/chunks/{chunk-OHER4DGX.js → chunk-FXD67VN5.js} +25 -9
  30. package/dist/chunks/{chunk-GUWPUX2Y.js → chunk-GK3RE3AC.js} +4 -4
  31. package/dist/chunks/{chunk-JNU3DTEV.js → chunk-IFNCPWH5.js} +9 -9
  32. package/dist/chunks/{chunk-PIMIKY4X.js → chunk-IJ4X426J.js} +1 -1
  33. package/dist/chunks/{chunk-NSU2BTRS.js → chunk-KD2N44NG.js} +4 -4
  34. package/dist/chunks/{chunk-TVXCXIRO.js → chunk-L3UXB2CW.js} +1 -1
  35. package/dist/chunks/{chunk-JJQP3PYM.js → chunk-MHCGKUI7.js} +5 -5
  36. package/dist/chunks/{chunk-47VQ6H2S.js → chunk-MUMITVZP.js} +2 -2
  37. package/dist/chunks/{chunk-L7CEMAJG.js → chunk-NGGLYKNU.js} +27 -5
  38. package/dist/chunks/{chunk-ZKUHCL6O.js → chunk-NJCM5JJN.js} +5 -5
  39. package/dist/chunks/{chunk-5LX2JDZP.js → chunk-O75LSD3C.js} +1 -1
  40. package/dist/chunks/{chunk-OX7KI3LF.js → chunk-QFAS4OVW.js} +18 -0
  41. package/dist/chunks/{chunk-4JTF6RD7.js → chunk-SSBYESJ5.js} +4 -4
  42. package/dist/chunks/{chunk-G75NFPIT.js → chunk-WNAKOXGK.js} +1 -1
  43. package/dist/chunks/{chunk-PPUWG2UB.js → chunk-WVPMR7XV.js} +58 -25
  44. package/dist/chunks/{chunk-CYNB6LL4.js → chunk-XNFHNTS2.js} +1 -1
  45. package/dist/chunks/{chunk-M3OSDZPY.js → chunk-XSYZDMUP.js} +16 -7
  46. package/dist/chunks/{chunk-VLPLCJZC.js → chunk-ZSFEACZU.js} +1 -1
  47. package/dist/chunks/{clear-TNGRDA7D.js → clear-X7M4WGBC.js} +11 -11
  48. package/dist/chunks/{compile-vercel-config-ERLXKKWW.js → compile-vercel-config-FBOZBWJC.js} +7 -7
  49. package/dist/chunks/{config-7EDKML5Q.js → config-KGW667XC.js} +9 -9
  50. package/dist/chunks/{config-H22EE765.js → config-RYPSCAR7.js} +11 -11
  51. package/dist/chunks/{delete-LORYIZHY.js → delete-OXYXXPHR.js} +8 -8
  52. package/dist/chunks/{delete-Q4S5F2AI.js → delete-VK6FTDBI.js} +9 -9
  53. package/dist/chunks/{disable-HKWCBDJC.js → disable-XRJTKNX4.js} +8 -8
  54. package/dist/chunks/{discard-UCCRF2JQ.js → discard-NAPSPR6Y.js} +8 -8
  55. package/dist/chunks/{edit-AWL35PYQ.js → edit-34GDLPQG.js} +10 -10
  56. package/dist/chunks/{edit-6IM6VRBR.js → edit-WLG4GLU3.js} +10 -10
  57. package/dist/chunks/{enable-GN3D3L3V.js → enable-IDKKVXKB.js} +8 -8
  58. package/dist/chunks/{exec-CNBOV577.js → exec-XH4K34JM.js} +3 -3
  59. package/dist/chunks/{export-HHRVFALV.js → export-MPMU3FYY.js} +8 -8
  60. package/dist/chunks/{image-4DCU4VPP.js → image-RZ2A4FWP.js} +6 -6
  61. package/dist/chunks/{import-builders-DVG3HOTF.js → import-builders-GZPE5N44.js} +9 -9
  62. package/dist/chunks/{inspect-B3OWUFLH.js → inspect-56S2DKPO.js} +11 -11
  63. package/dist/chunks/{inspect-VPME6UC7.js → inspect-EVDBNPP4.js} +11 -11
  64. package/dist/chunks/{inspect-TGOJDKIT.js → inspect-KGEMHV2T.js} +9 -9
  65. package/dist/chunks/{inspect-VLGKNHJW.js → inspect-OOV2ORKD.js} +11 -11
  66. package/dist/chunks/{inspect-TUW67CFZ.js → inspect-X2Y5FDKR.js} +10 -10
  67. package/dist/chunks/{list-K552ITMK.js → list-FWLSZYMO.js} +9 -9
  68. package/dist/chunks/{list-2DPKDHRV.js → list-H357CF7M.js} +10 -10
  69. package/dist/chunks/{list-TK2JO7BK.js → list-TWSOAQRB.js} +8 -8
  70. package/dist/chunks/{login-HRWEKW5P.js → login-54OPNB5E.js} +21 -12
  71. package/dist/chunks/{ls-JSV2WZVF.js → ls-6JHFSJBJ.js} +11 -11
  72. package/dist/chunks/{ls-O2WYF5UL.js → ls-DU3S2GBJ.js} +11 -11
  73. package/dist/chunks/{ls-IOXGXEY4.js → ls-VDFNIR5H.js} +10 -10
  74. package/dist/chunks/{ls-M5TYGFP5.js → ls-WFMTVJOO.js} +11 -11
  75. package/dist/chunks/{ls-YIF3KLF3.js → ls-WGIRTAVM.js} +11 -11
  76. package/dist/chunks/{ls-TM6NTZEB.js → ls-ZMR5RMU4.js} +9 -9
  77. package/dist/chunks/{open-FWAVEWGU.js → open-RCSCNKHF.js} +9 -9
  78. package/dist/chunks/{permissions-DPFYL47Z.js → permissions-XNN37R4N.js} +7 -7
  79. package/dist/chunks/{publish-63CGFKQR.js → publish-PLXEACYY.js} +8 -8
  80. package/dist/chunks/{push-T5ZTBW3P.js → push-EFYSB52Q.js} +11 -11
  81. package/dist/chunks/{query-B6O3EMEV.js → query-EKTXDOL7.js} +11 -11
  82. package/dist/chunks/{reorder-ANV4NMKH.js → reorder-OSA7D2RG.js} +8 -8
  83. package/dist/chunks/{reply-C73KGOCG.js → reply-6M2IYKLK.js} +10 -10
  84. package/dist/chunks/{resolve-IHSIVPEE.js → resolve-MCX5STKN.js} +9 -9
  85. package/dist/chunks/{restore-GSUNXP2O.js → restore-52OEP3NL.js} +8 -8
  86. package/dist/chunks/{rm-M7IJC2WZ.js → rm-2VAJFK25.js} +11 -11
  87. package/dist/chunks/{rm-RV5SQFQA.js → rm-4BJN2LJX.js} +11 -13
  88. package/dist/chunks/{rm-4O74PS4Q.js → rm-IYEDZOMN.js} +11 -11
  89. package/dist/chunks/{rm-PRINF4OU.js → rm-QW35WHSK.js} +11 -11
  90. package/dist/chunks/{rm-YDTK5ESZ.js → rm-TXV4GB4L.js} +10 -10
  91. package/dist/chunks/{routes-SZI2G4SE.js → routes-2OLIBRYX.js} +3 -3
  92. package/dist/chunks/{rule-inspect-ZZC53FLP.js → rule-inspect-WOBAN3Y2.js} +10 -10
  93. package/dist/chunks/{rules-SAOSSGFI.js → rules-CUJRAZYO.js} +10 -10
  94. package/dist/chunks/{schema-23ZFAKDU.js → schema-45SJNY5L.js} +4 -4
  95. package/dist/chunks/{schema-2Q3QRMRO.js → schema-VCGEJAOG.js} +9 -9
  96. package/dist/chunks/{set-L47ZCZBJ.js → set-2QSXMDVF.js} +9 -9
  97. package/dist/chunks/{tags-2TWLM77N.js → tags-NHX2VQ7E.js} +5 -5
  98. package/dist/chunks/{types-G3PEUIJH.js → types-JGANJ5EQ.js} +3 -3
  99. package/dist/chunks/{update-SZH2VBD6.js → update-EOHXTT4N.js} +12 -12
  100. package/dist/commands/build/builder-worker.cjs +281 -0
  101. package/dist/commands/build/index.js +798 -236
  102. package/dist/commands/deploy/index.js +103 -31
  103. package/dist/commands/dev/index.js +983 -176
  104. package/dist/commands/env/index.js +1167 -542
  105. package/dist/commands/link/index.js +10 -10
  106. package/dist/commands/list/index.js +10 -10
  107. package/dist/commands-bulk.js +2002 -962
  108. package/dist/index.js +16 -16
  109. package/dist/version.mjs +1 -1
  110. package/package.json +48 -48
  111. package/dist/chunks/chunk-HGRWPPYB.js +0 -18
@@ -9,7 +9,7 @@ import {
9
9
  getCustomEnvironments,
10
10
  readStandardInput,
11
11
  require_dist as require_dist3
12
- } from "../../chunks/chunk-VLPLCJZC.js";
12
+ } from "../../chunks/chunk-ZSFEACZU.js";
13
13
  import {
14
14
  getInvalidSubcommand
15
15
  } from "../../chunks/chunk-VGIMO3ZK.js";
@@ -21,14 +21,15 @@ import {
21
21
  } from "../../chunks/chunk-OQ3C5VXZ.js";
22
22
  import "../../chunks/chunk-XRWOTCDO.js";
23
23
  import {
24
+ isGuidanceEnabled,
24
25
  suggestNextCommands
25
- } from "../../chunks/chunk-XBN2O34P.js";
26
+ } from "../../chunks/chunk-7ST552E3.js";
26
27
  import {
27
28
  formatEnvironment
28
- } from "../../chunks/chunk-V4YLY44R.js";
29
+ } from "../../chunks/chunk-67IIZBBY.js";
29
30
  import {
30
31
  validateLsArgs
31
- } from "../../chunks/chunk-5LX2JDZP.js";
32
+ } from "../../chunks/chunk-O75LSD3C.js";
32
33
  import {
33
34
  validateJsonOutput
34
35
  } from "../../chunks/chunk-KXDWXXJH.js";
@@ -37,7 +38,7 @@ import {
37
38
  } from "../../chunks/chunk-YPQSDAEW.js";
38
39
  import {
39
40
  getCommandAliases
40
- } from "../../chunks/chunk-QG4PCM45.js";
41
+ } from "../../chunks/chunk-7HE63HCR.js";
41
42
  import "../../chunks/chunk-7Q45OKWR.js";
42
43
  import "../../chunks/chunk-GY5I4AYD.js";
43
44
  import "../../chunks/chunk-CJV7J7B5.js";
@@ -52,33 +53,53 @@ import "../../chunks/chunk-DWIC7MRV.js";
52
53
  import "../../chunks/chunk-XM3LOQIX.js";
53
54
  import "../../chunks/chunk-XDGOOW3K.js";
54
55
  import "../../chunks/chunk-VP5Y3SZG.js";
55
- import {
56
- require_execa
57
- } from "../../chunks/chunk-R6IGDGX3.js";
58
56
  import {
59
57
  autoInstallVercelPlugin
60
- } from "../../chunks/chunk-HT2XWSAJ.js";
58
+ } from "../../chunks/chunk-7AEEQOD4.js";
59
+ import {
60
+ require_execa
61
+ } from "../../chunks/chunk-5XECIWME.js";
61
62
  import {
62
63
  stamp_default
63
64
  } from "../../chunks/chunk-64IF634X.js";
64
65
  import "../../chunks/chunk-VXYGCOKL.js";
65
- import "../../chunks/chunk-7XX4ZEOV.js";
66
+ import "../../chunks/chunk-FWKSJYDV.js";
66
67
  import {
67
68
  help
68
- } from "../../chunks/chunk-RZ5NP6HN.js";
69
+ } from "../../chunks/chunk-2YRAWYGE.js";
69
70
  import {
71
+ ENV_VISIBILITY_DEPRECATION_MESSAGE,
72
+ SENSITIVE_ENV_VALUE_PLACEHOLDER,
70
73
  STANDARD_ENVIRONMENTS,
71
74
  formatProject,
75
+ formatVisibilityLabel,
76
+ getApiPublicPrefix,
77
+ getEnvKeyWarnings,
72
78
  getEnvRecords,
79
+ getFrameworkPublicPrefix,
80
+ getLocalSecretFallbackMessage,
81
+ getLocalSvelteKitPublicPrefixes,
82
+ getPublicPrefix,
83
+ getPublicPrefixSecretVisibilityError,
73
84
  getTeamById,
85
+ getUnavailableSecretValuesMessage,
86
+ isFlagsSecretNeedingSplit,
87
+ isSecretEnvVar,
88
+ looksLikeSecret,
89
+ looksLikeSecretValue,
90
+ normalizeStdinEnvValue,
74
91
  param,
75
92
  parseTarget,
76
93
  printAlignedLabel,
77
94
  pull,
78
95
  pullEnvRecords,
79
- require_frameworks,
80
- resolveProjectContext
81
- } from "../../chunks/chunk-E6LFKMI2.js";
96
+ removePublicPrefix,
97
+ resolveEnvVarTypeOption,
98
+ resolveEnvVarVisibility,
99
+ resolveProjectContext,
100
+ shouldConfirmRotationBeforeDelete,
101
+ validateEnvValue
102
+ } from "../../chunks/chunk-BQG777JE.js";
82
103
  import {
83
104
  addSubcommand,
84
105
  envCommand,
@@ -91,11 +112,11 @@ import {
91
112
  removeSubcommand,
92
113
  runSubcommand,
93
114
  updateSubcommand
94
- } from "../../chunks/chunk-OHER4DGX.js";
115
+ } from "../../chunks/chunk-FXD67VN5.js";
95
116
  import {
96
117
  TelemetryClient,
97
118
  require_dist as require_dist2
98
- } from "../../chunks/chunk-CYNB6LL4.js";
119
+ } from "../../chunks/chunk-XNFHNTS2.js";
99
120
  import {
100
121
  buildCommandWithYes,
101
122
  buildEnvAddCommandWithPreservedArgs,
@@ -105,21 +126,24 @@ import {
105
126
  getPreservedArgsForEnvRm,
106
127
  getPreservedArgsForEnvUpdate,
107
128
  outputActionRequired,
108
- outputAgentError
109
- } from "../../chunks/chunk-L7CEMAJG.js";
129
+ outputAgentError,
130
+ quoteArg,
131
+ redactEnvValueArgs,
132
+ withGlobalFlags
133
+ } from "../../chunks/chunk-NGGLYKNU.js";
110
134
  import {
111
135
  require_ms
112
136
  } from "../../chunks/chunk-GGP5R3FU.js";
113
137
  import {
114
138
  printError
115
- } from "../../chunks/chunk-CQJRLNTX.js";
139
+ } from "../../chunks/chunk-AYLY3ZVL.js";
116
140
  import {
117
141
  parseArguments
118
142
  } from "../../chunks/chunk-57RHXXXG.js";
119
143
  import "../../chunks/chunk-RKDCNQ4S.js";
120
144
  import {
121
145
  isAPIError
122
- } from "../../chunks/chunk-AWCID36T.js";
146
+ } from "../../chunks/chunk-BMKU5KEL.js";
123
147
  import {
124
148
  getCommandName,
125
149
  getCommandNamePlain,
@@ -134,7 +158,7 @@ import {
134
158
  output_manager_default,
135
159
  prependEmoji,
136
160
  require_dist
137
- } from "../../chunks/chunk-OX7KI3LF.js";
161
+ } from "../../chunks/chunk-QFAS4OVW.js";
138
162
  import {
139
163
  require_source
140
164
  } from "../../chunks/chunk-S7KYDPEM.js";
@@ -212,180 +236,6 @@ function isKnownError(error) {
212
236
  return knownErrorsCodes.has(code.toUpperCase());
213
237
  }
214
238
 
215
- // src/util/env/validate-env.ts
216
- var import_frameworks = __toESM(require_frameworks(), 1);
217
- function getEnvValueWarnings(value) {
218
- const warnings = [];
219
- const normalized = value.replace(/\n$/, "");
220
- if (/^[ \t]+/.test(normalized)) {
221
- warnings.push({
222
- message: "starts with whitespace",
223
- requiresConfirmation: false
224
- });
225
- }
226
- if (/[ \t]+$/.test(normalized)) {
227
- warnings.push({
228
- message: "ends with whitespace",
229
- requiresConfirmation: false
230
- });
231
- }
232
- if (normalized.includes("\r") || normalized.includes("\n")) {
233
- warnings.push({
234
- message: "contains newlines",
235
- requiresConfirmation: false
236
- });
237
- }
238
- if (value.includes("\0")) {
239
- warnings.push({
240
- message: "contains null characters",
241
- requiresConfirmation: false
242
- });
243
- }
244
- if (value === "") {
245
- warnings.push({
246
- message: "is empty",
247
- requiresConfirmation: true
248
- });
249
- }
250
- if (value.length > 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
251
- warnings.push({
252
- message: "includes surrounding quotes (these will be stored literally)",
253
- requiresConfirmation: false
254
- });
255
- }
256
- return warnings;
257
- }
258
- function formatWarnings(warnings) {
259
- if (warnings.length === 0)
260
- return null;
261
- const messages = warnings.map((w) => w.message);
262
- const startsIdx = messages.indexOf("starts with whitespace");
263
- const endsIdx = messages.indexOf("ends with whitespace");
264
- if (startsIdx !== -1 && endsIdx !== -1) {
265
- messages.splice(Math.max(startsIdx, endsIdx), 1);
266
- messages[Math.min(startsIdx, endsIdx)] = "starts and ends with whitespace";
267
- }
268
- if (messages.length === 1) {
269
- return `Value ${messages[0]}`;
270
- }
271
- if (messages.length === 2) {
272
- return `Value ${messages[0]} and ${messages[1]}`;
273
- }
274
- const last = messages.pop();
275
- return `Value ${messages.join(", ")}, and ${last}`;
276
- }
277
- var PUBLIC_PREFIXES = [
278
- ...new Set(
279
- import_frameworks.frameworkList.map((f) => f.envPrefix).filter((p) => !!p)
280
- )
281
- ];
282
- var SENSITIVE_PATTERN = /(?:^|_)(password|secret|private|token|key|auth|jwt|signature)(?:_|$)/i;
283
- function hasOnlyWhitespaceWarnings(warnings) {
284
- return warnings.length > 0 && warnings.every(
285
- (w) => w.message === "starts with whitespace" || w.message === "ends with whitespace"
286
- );
287
- }
288
- function trimValue(value) {
289
- return value.replace(/\n$/, "").trim();
290
- }
291
- function normalizeStdinEnvValue(value) {
292
- let valueWithoutTrailingNewline = value;
293
- if (value.endsWith("\r\n")) {
294
- valueWithoutTrailingNewline = value.slice(0, -2);
295
- } else if (value.endsWith("\n")) {
296
- valueWithoutTrailingNewline = value.slice(0, -1);
297
- } else {
298
- return {
299
- value,
300
- strippedTrailingNewline: false
301
- };
302
- }
303
- if (valueWithoutTrailingNewline.includes("\n") || valueWithoutTrailingNewline.includes("\r")) {
304
- return {
305
- value,
306
- strippedTrailingNewline: false
307
- };
308
- }
309
- return {
310
- value: valueWithoutTrailingNewline,
311
- strippedTrailingNewline: true
312
- };
313
- }
314
- function getPublicPrefix(key) {
315
- const upperKey = key.toUpperCase();
316
- return PUBLIC_PREFIXES.find((p) => upperKey.startsWith(p)) || null;
317
- }
318
- function removePublicPrefix(key) {
319
- const prefix = getPublicPrefix(key);
320
- if (!prefix)
321
- return key;
322
- return key.slice(prefix.length);
323
- }
324
- async function validateEnvValue(opts) {
325
- let finalValue = opts.initialValue;
326
- let alreadyConfirmed = false;
327
- if (!opts.skipConfirm) {
328
- let valueAccepted = false;
329
- while (!valueAccepted) {
330
- const valueWarnings = getEnvValueWarnings(finalValue);
331
- const warningMessage = formatWarnings(valueWarnings);
332
- if (!warningMessage) {
333
- valueAccepted = true;
334
- break;
335
- }
336
- opts.showWarning(warningMessage);
337
- const canTrim = hasOnlyWhitespaceWarnings(valueWarnings);
338
- const choices = canTrim ? [
339
- { name: "Leave as is", value: "c" },
340
- { name: "Re-enter", value: "r" },
341
- { name: "Trim whitespace", value: "t" }
342
- ] : [
343
- { name: "Leave as is", value: "c" },
344
- { name: "Re-enter", value: "r" }
345
- ];
346
- const action = await opts.selectAction(choices);
347
- if (action === "c") {
348
- valueAccepted = true;
349
- if (valueWarnings.some((w) => w.requiresConfirmation)) {
350
- alreadyConfirmed = true;
351
- }
352
- } else if (action === "t") {
353
- finalValue = trimValue(finalValue);
354
- opts.showLog("Trimmed whitespace");
355
- } else {
356
- finalValue = await opts.promptForValue();
357
- }
358
- }
359
- } else {
360
- const valueWarnings = getEnvValueWarnings(finalValue);
361
- const warningMessage = formatWarnings(valueWarnings);
362
- if (warningMessage) {
363
- opts.showWarning(warningMessage);
364
- }
365
- }
366
- return { finalValue, alreadyConfirmed };
367
- }
368
- function getEnvKeyWarnings(key) {
369
- const warnings = [];
370
- const matchingPrefix = getPublicPrefix(key);
371
- if (matchingPrefix) {
372
- const sensitiveMatch = SENSITIVE_PATTERN.exec(key);
373
- const nameWithoutPrefix = key.slice(matchingPrefix.length);
374
- if (sensitiveMatch) {
375
- warnings.push({
376
- message: `The ${matchingPrefix} prefix will make ${nameWithoutPrefix} visible to anyone visiting your site`,
377
- requiresConfirmation: true
378
- });
379
- } else {
380
- warnings.push({
381
- message: `${matchingPrefix} variables can be seen by anyone visiting your site`,
382
- requiresConfirmation: false
383
- });
384
- }
385
- }
386
- return warnings;
387
- }
388
-
389
239
  // src/util/telemetry/commands/env/add.ts
390
240
  var EnvAddTelemetryClient = class extends TelemetryClient {
391
241
  trackCliArgumentName(name) {
@@ -438,6 +288,15 @@ var EnvAddTelemetryClient = class extends TelemetryClient {
438
288
  });
439
289
  }
440
290
  }
291
+ trackCliOptionType(type) {
292
+ if (type) {
293
+ const validTypes = ["config", "secret"];
294
+ this.trackCliOption({
295
+ option: "type",
296
+ value: validTypes.includes(type) ? type : this.redactedValue
297
+ });
298
+ }
299
+ }
441
300
  trackCliFlagSensitive(sensitive) {
442
301
  if (sensitive) {
443
302
  this.trackCliFlag("sensitive");
@@ -468,100 +327,42 @@ var EnvAddTelemetryClient = class extends TelemetryClient {
468
327
  // src/commands/env/add.ts
469
328
  import { determineAgent } from "@vercel/detect-agent";
470
329
 
471
- // src/util/env/env-var-config-secret-ui.ts
472
- function formatVisibilityLabel(visibility, type) {
473
- const resolved = visibility ?? visibilityFromEnvType(type);
474
- if (resolved === "config") {
475
- return "Config";
476
- }
477
- if (resolved === "secret") {
478
- return "Secret";
479
- }
480
- return void 0;
330
+ // src/util/env/secret-storage-guidance.ts
331
+ function includesDevelopment(targets) {
332
+ return targets.length === 0 || targets.includes("development");
481
333
  }
482
- function visibilityFromEnvType(type) {
483
- if (type === "sensitive") {
484
- return "secret";
334
+ function getProductionSecretPolicyErrorKind(message) {
335
+ if (/production secrets?.*different value|different value.*production secrets?/i.test(
336
+ message
337
+ )) {
338
+ return "different-values";
485
339
  }
486
- if (type === "plain" || type === "encrypted") {
487
- return "config";
340
+ if (/production secrets?.*(?:own environment group|separat)|(?:own environment group|separat).*production secrets?/i.test(
341
+ message
342
+ )) {
343
+ return "separate-environments";
488
344
  }
489
- return void 0;
345
+ return null;
490
346
  }
491
- function hasNonDevelopmentTarget(envTargets) {
492
- return envTargets.some((target) => target !== "development");
347
+ function getProductionSecretPolicyRecovery(kind) {
348
+ return kind === "different-values" ? "Use different Secret values for Production and non-Production." : "Create separate Production and non-Production variables with different values.";
493
349
  }
494
- function getPublicPrefixSecretVisibilityError(key, options) {
495
- const publicPrefix = getPublicPrefix(key);
496
- if (!publicPrefix) {
497
- return null;
498
- }
499
- const wouldBeSecret = options.visibility === "secret" || options.type === "sensitive";
500
- if (!wouldBeSecret) {
501
- return null;
502
- }
503
- const prefixLabel = publicPrefix.replace(/_$/, "");
504
- if (hasNonDevelopmentTarget(options.envTargets)) {
505
- return `Environment variables with a public framework prefix (${prefixLabel}) cannot use secret visibility on Production or Preview. Target Development only, rename to remove the public prefix, or use \`--visibility config\` with \`--no-sensitive\` on Development.`;
506
- }
507
- return `Environment variables with a public framework prefix (${prefixLabel}) cannot use secret visibility. Rename to remove the public prefix or use \`--visibility config\` with \`--no-sensitive\`.`;
350
+ function getSecretStorageChoice(targets, recommendedReason) {
351
+ const availability = includesDevelopment(targets) ? "hidden in the dashboard; Development values can be pulled" : "hidden in the dashboard and unavailable to pulls";
352
+ const recommendation = recommendedReason ? `; recommended ${recommendedReason}` : "";
353
+ return `Secret (${availability}${recommendation})`;
508
354
  }
509
- function shouldOmitInferredVisibility(key, envTargets, teamSensitivePolicyOn) {
510
- if (!getPublicPrefix(key)) {
511
- return false;
512
- }
513
- if (!hasNonDevelopmentTarget(envTargets)) {
514
- return false;
355
+ function getDefaultSecretStorageGuidance(targets) {
356
+ if (includesDevelopment(targets)) {
357
+ return "Stored as Secret by default. This value is hidden in the dashboard; Development values can be pulled. Use `--type config` for other values you need to read later.";
515
358
  }
516
- return teamSensitivePolicyOn;
517
- }
518
- function resolveEnvVarVisibility(options) {
519
- if (options.explicitVisibility !== void 0) {
520
- if (options.explicitVisibility !== "config" && options.explicitVisibility !== "secret") {
521
- return {
522
- error: "The `--visibility` flag must be either `config` or `secret`."
523
- };
524
- }
525
- const publicPrefixError2 = getPublicPrefixSecretVisibilityError(
526
- options.key,
527
- {
528
- visibility: options.explicitVisibility,
529
- type: options.type,
530
- envTargets: options.envTargets
531
- }
532
- );
533
- if (publicPrefixError2) {
534
- return { error: publicPrefixError2 };
535
- }
536
- return { visibility: options.explicitVisibility };
537
- }
538
- const inferred = visibilityFromEnvType(options.type);
539
- if (inferred === void 0) {
540
- return {};
541
- }
542
- if (shouldOmitInferredVisibility(
543
- options.key,
544
- options.envTargets,
545
- options.teamSensitivePolicyOn
546
- )) {
547
- return {};
548
- }
549
- const publicPrefixError = getPublicPrefixSecretVisibilityError(options.key, {
550
- visibility: inferred,
551
- type: options.type,
552
- envTargets: options.envTargets
553
- });
554
- if (publicPrefixError) {
555
- return { error: publicPrefixError };
556
- }
557
- return { visibility: inferred };
359
+ return "Stored as Secret by default. This value is hidden in the dashboard and unavailable to pulls; use `--type config` for values you need to read later.";
558
360
  }
559
361
 
560
362
  // src/commands/env/add.ts
561
- var SENSITIVE_VALUE_HINT = "Sensitive values cannot be read later";
562
- var SENSITIVE_SECRET_PROMPT = `Store as sensitive? ${import_chalk.default.dim(
563
- SENSITIVE_VALUE_HINT
564
- )}`;
363
+ function looksLikeCredentialName(key, publicPrefix = getPublicPrefix(key)) {
364
+ return looksLikeSecret(publicPrefix ? key.slice(publicPrefix.length) : key);
365
+ }
565
366
  function filterEnvChoicesForSensitivity(choices, opts) {
566
367
  if (opts.policyOn) {
567
368
  return choices.filter((c) => c.value === "development");
@@ -592,19 +393,13 @@ function resolveFinalType(envTargets, isSensitive, opts) {
592
393
  }
593
394
  function fillEnvAddTemplate(template, opts) {
594
395
  const targetPlaceholder = getEnvTargetPlaceholder();
595
- return template.replace(/<name>/g, opts.envName ?? "<name>").split(targetPlaceholder).join(opts.envTargetArg ?? targetPlaceholder).replace(/<git-branch>/g, opts.envGitBranch ?? "<git-branch>").replace(/<gitbranch>/g, opts.envGitBranch ?? "<gitbranch>").replace(/<value>/g, "<value>");
596
- }
597
- function redactEnvValueArgs(argv) {
598
- const redacted = [...argv];
599
- for (let i = 0; i < redacted.length; i++) {
600
- if (redacted[i] === "--value" && i + 1 < redacted.length) {
601
- redacted[i + 1] = '"<value>"';
602
- i++;
603
- } else if (redacted[i].startsWith("--value=")) {
604
- redacted[i] = '--value="<value>"';
605
- }
606
- }
607
- return redacted;
396
+ return template.replace(/<name>/g, opts.envName ? quoteArg(opts.envName) : "<name>").split(targetPlaceholder).join(opts.envTargetArg ? quoteArg(opts.envTargetArg) : targetPlaceholder).replace(
397
+ /<git-branch>/g,
398
+ opts.envGitBranch ? quoteArg(opts.envGitBranch) : "<git-branch>"
399
+ ).replace(
400
+ /<gitbranch>/g,
401
+ opts.envGitBranch ? quoteArg(opts.envGitBranch) : "<gitbranch>"
402
+ ).replace(/<value>/g, "<value>");
608
403
  }
609
404
  function multiTargetSuggestion(argv, envName, targets, addNoSensitive) {
610
405
  const flag = addNoSensitive ? " --no-sensitive" : "";
@@ -637,9 +432,6 @@ function formatEnvironmentTarget(target, customEnvironments) {
637
432
  function formatEnvironmentTargets(envTargets, customEnvironments) {
638
433
  return envTargets.map((target) => formatEnvironmentTarget(target, customEnvironments)).join(", ");
639
434
  }
640
- function typeLabel(type) {
641
- return type === "sensitive" ? "Sensitive" : "Non-sensitive";
642
- }
643
435
  function printEnvAddResult(link, envName, envTargets, envGitBranch, customEnvironments, finalType, force, visibility) {
644
436
  output_manager_default.print("\n");
645
437
  printAlignedLabel(force ? "Overrode" : "Added", envName, { gutter: "\u2713" });
@@ -651,10 +443,9 @@ function printEnvAddResult(link, envName, envTargets, envGitBranch, customEnviro
651
443
  if (envGitBranch) {
652
444
  printAlignedLabel("Branch", envGitBranch);
653
445
  }
654
- printAlignedLabel("Type", typeLabel(finalType));
655
446
  const visibilityLabel = formatVisibilityLabel(visibility, finalType);
656
447
  if (visibilityLabel) {
657
- printAlignedLabel("Visibility", visibilityLabel);
448
+ printAlignedLabel("Type", visibilityLabel);
658
449
  }
659
450
  }
660
451
  function printEnvAddWarning(message) {
@@ -667,6 +458,13 @@ function promptEnvValue(client, opts) {
667
458
  ...opts.isSensitive ? { transformer: (value) => "*".repeat(value.length) } : {}
668
459
  });
669
460
  }
461
+ function buildHumanEnvAddCommand(argv, commandTemplate) {
462
+ const globalFlags = getGlobalFlagsFromArgs(argv.slice(2), {
463
+ preserveProject: true
464
+ }).filter((flag) => !flag.startsWith("--non-interactive"));
465
+ const suffix = globalFlags.length > 0 ? ` ${globalFlags.map(quoteArg).join(" ")}` : "";
466
+ return getCommandNamePlain(`${commandTemplate}${suffix}`);
467
+ }
670
468
  async function add(client, argv) {
671
469
  let parsedArgs;
672
470
  const flagsSpecification = getFlagsSpecification(addSubcommand.options);
@@ -694,6 +492,14 @@ async function add(client, argv) {
694
492
  const positionalGitBranch = args[2];
695
493
  const optionGitBranch = typeof opts["--git-branch"] === "string" ? opts["--git-branch"] : void 0;
696
494
  let envGitBranch = optionGitBranch ?? positionalGitBranch;
495
+ const forceSensitive = Boolean(opts["--sensitive"]);
496
+ const forceEncrypted = Boolean(opts["--no-sensitive"]);
497
+ const typeOption = resolveEnvVarTypeOption({
498
+ type: typeof opts["--type"] === "string" ? opts["--type"] : void 0,
499
+ visibility: typeof opts["--visibility"] === "string" ? opts["--visibility"] : void 0
500
+ });
501
+ let explicitType = typeOption.explicitVisibility;
502
+ let typeSource = explicitType || forceSensitive || forceEncrypted ? "argv" : void 0;
697
503
  const telemetryClient = new EnvAddTelemetryClient({
698
504
  opts: {
699
505
  store: client.telemetryEventStore
@@ -711,10 +517,47 @@ async function add(client, argv) {
711
517
  telemetryClient.trackCliFlagForce(opts["--force"]);
712
518
  telemetryClient.trackCliFlagGuidance(opts["--guidance"]);
713
519
  telemetryClient.trackCliFlagYes(opts["--yes"]);
520
+ telemetryClient.trackCliOptionType(
521
+ typeof opts["--type"] === "string" ? opts["--type"] : void 0
522
+ );
714
523
  telemetryClient.trackCliOptionVisibility(
715
524
  typeof opts["--visibility"] === "string" ? opts["--visibility"] : void 0
716
525
  );
717
526
  telemetryClient.trackCliOptionProject(opts["--project"]);
527
+ if (typeOption.error) {
528
+ if (client.nonInteractive) {
529
+ outputAgentError(
530
+ client,
531
+ {
532
+ status: "error",
533
+ reason: typeOption.errorReason ?? "invalid_type",
534
+ message: typeOption.error
535
+ },
536
+ 1
537
+ );
538
+ }
539
+ output_manager_default.fatal(typeOption.error);
540
+ return 1;
541
+ }
542
+ if (typeOption.usedDeprecatedVisibility) {
543
+ printEnvAddWarning(ENV_VISIBILITY_DEPRECATION_MESSAGE);
544
+ }
545
+ const typeConflictMessage = forceSensitive ? forceEncrypted ? "`--sensitive` and `--no-sensitive` cannot be used together. Pick one." : explicitType === "config" ? "`--type config` cannot be used with `--sensitive`. Pick one." : null : forceEncrypted && explicitType === "secret" ? "`--type secret` cannot be used with `--no-sensitive`. Pick one." : null;
546
+ if (typeConflictMessage) {
547
+ if (client.nonInteractive) {
548
+ outputAgentError(
549
+ client,
550
+ {
551
+ status: "error",
552
+ reason: "conflicting_type_flags",
553
+ message: typeConflictMessage
554
+ },
555
+ 1
556
+ );
557
+ }
558
+ output_manager_default.fatal(typeConflictMessage);
559
+ return 1;
560
+ }
718
561
  if (positionalGitBranch && optionGitBranch) {
719
562
  output_manager_default.error(
720
563
  "Git branch was provided twice. Use either `--git-branch <git-branch>` or the legacy third argument."
@@ -738,6 +581,7 @@ async function add(client, argv) {
738
581
  return 1;
739
582
  }
740
583
  let envTargets = envTargetArg ? parseEnvTargetArg(envTargetArg) : [];
584
+ let resolvedLink;
741
585
  if (client.nonInteractive) {
742
586
  const link2 = await resolveProjectContext({
743
587
  client,
@@ -788,9 +632,9 @@ async function add(client, argv) {
788
632
  {
789
633
  status: "error",
790
634
  reason: "not_linked",
791
- message: `Your codebase isn't linked to a project on Vercel. Run ${getCommandNamePlain(
635
+ message: `Your codebase isn't linked to a project on Vercel. Run \`${getCommandNamePlain(
792
636
  "link"
793
- )} to begin. Use --yes for non-interactive; use --scope or --project to specify team or project. Then run your env add command.`,
637
+ )}\` to begin. Use \`--yes\` for non-interactive mode; use \`--scope\` or \`--project\` to specify a team or project. Then run your \`env add\` command.`,
794
638
  next: [
795
639
  { command: buildCommandWithYes(linkArgv) },
796
640
  { command: buildCommandWithYes(envAddRetryArgv) }
@@ -801,6 +645,7 @@ async function add(client, argv) {
801
645
  }
802
646
  if (link2.status !== "linked")
803
647
  return 1;
648
+ resolvedLink = link2;
804
649
  const { project: project2 } = link2;
805
650
  const org = link2.org;
806
651
  client.config.currentTeam = org.type === "team" ? org.id : void 0;
@@ -922,7 +767,7 @@ async function add(client, argv) {
922
767
  status: "action_required",
923
768
  reason: "missing_requirements",
924
769
  missing,
925
- message: `Provide all required inputs for non-interactive mode: ${parts.join("; ")}. Example: ${filledTemplate}`,
770
+ message: `Provide all required inputs for non-interactive mode: ${parts.join("; ")}. Example: \`${filledTemplate}\``,
926
771
  next
927
772
  },
928
773
  1
@@ -935,147 +780,246 @@ async function add(client, argv) {
935
780
  validate: (val) => val ? true : "Name cannot be empty"
936
781
  });
937
782
  }
938
- const skipConfirm = opts["--yes"] || !!stdInput || valueFromFlag !== void 0;
939
- if (!skipConfirm) {
940
- let keyAccepted = false;
941
- while (!keyAccepted) {
942
- const keyWarnings = getEnvKeyWarnings(envName);
943
- const sensitiveWarning = keyWarnings.find((w) => w.requiresConfirmation);
944
- if (!sensitiveWarning) {
945
- for (const w of keyWarnings) {
946
- printEnvAddWarning(w.message);
947
- }
948
- keyAccepted = true;
949
- break;
783
+ if (!resolvedLink) {
784
+ const link2 = await resolveProjectContext({
785
+ client,
786
+ projectNameOrId: opts["--project"]
787
+ });
788
+ if (link2.status === "error") {
789
+ return link2.exitCode;
790
+ } else if (link2.status === "not_linked") {
791
+ if (client.nonInteractive) {
792
+ const preserved = getPreservedArgsForEnvAdd(client.argv);
793
+ const linkPreserved = preserved.filter((a, i) => {
794
+ if (a === "--value")
795
+ return false;
796
+ if (a.startsWith("--value="))
797
+ return false;
798
+ if (i > 0 && preserved[i - 1] === "--value")
799
+ return false;
800
+ return true;
801
+ });
802
+ const linkArgv = [
803
+ ...client.argv.slice(0, 2),
804
+ "link",
805
+ "--scope",
806
+ "<scope>",
807
+ ...linkPreserved
808
+ ];
809
+ outputAgentError(
810
+ client,
811
+ {
812
+ status: "error",
813
+ reason: "not_linked",
814
+ message: `Your codebase isn't linked to a project on Vercel. Run ${getCommandNamePlain(
815
+ "link"
816
+ )} to begin. Use --yes for non-interactive; use --scope or --project to specify team or project. Then run your env add command.`,
817
+ next: [
818
+ { command: buildCommandWithYes(linkArgv) },
819
+ { command: buildCommandWithYes(redactEnvValueArgs(client.argv)) }
820
+ ]
821
+ },
822
+ 1
823
+ );
824
+ } else {
825
+ output_manager_default.error(
826
+ `Your codebase isn\u2019t linked to a project on Vercel. Run ${getCommandName(
827
+ "link"
828
+ )} to begin.`
829
+ );
950
830
  }
831
+ return 1;
832
+ }
833
+ resolvedLink = link2;
834
+ }
835
+ const link = resolvedLink;
836
+ client.config.currentTeam = link.org.type === "team" ? link.org.id : void 0;
837
+ const { project } = link;
838
+ if (explicitType === "secret" || forceSensitive) {
839
+ const publicPrefixError = getPublicPrefixSecretVisibilityError(envName, {
840
+ visibility: explicitType === "secret" ? "secret" : void 0,
841
+ type: "sensitive",
842
+ context: "add"
843
+ });
844
+ if (publicPrefixError) {
845
+ if (client.nonInteractive) {
846
+ outputAgentError(
847
+ client,
848
+ {
849
+ status: "error",
850
+ reason: "invalid_type",
851
+ message: publicPrefixError
852
+ },
853
+ 1
854
+ );
855
+ }
856
+ output_manager_default.fatal(publicPrefixError);
857
+ return 1;
858
+ }
859
+ }
860
+ let keyAccepted = false;
861
+ while (!keyAccepted) {
862
+ const apiPublicPrefix2 = getApiPublicPrefix(envName);
863
+ const frameworkPublicPrefix = getFrameworkPublicPrefix(
864
+ project.framework,
865
+ envName
866
+ );
867
+ const globallyKnownPrefix = getPublicPrefix(envName);
868
+ const deferFrameworkSecretError = frameworkPublicPrefix !== null && (explicitType === "secret" || forceSensitive);
869
+ const keyWarnings = deferFrameworkSecretError || globallyKnownPrefix !== null && apiPublicPrefix2 === null && frameworkPublicPrefix === null ? [] : getEnvKeyWarnings(envName);
870
+ const sensitiveWarning = keyWarnings.find((w) => w.requiresConfirmation);
871
+ if (!sensitiveWarning) {
872
+ for (const warning of keyWarnings) {
873
+ printEnvAddWarning(warning.message);
874
+ }
875
+ keyAccepted = true;
876
+ break;
877
+ }
878
+ const nameWithoutPrefix = removePublicPrefix(envName);
879
+ const publicPrefix = getPublicPrefix(envName);
880
+ if (typeSource === "argv") {
881
+ for (const warning of keyWarnings) {
882
+ printEnvAddWarning(warning.message);
883
+ }
884
+ keyAccepted = true;
885
+ break;
886
+ }
887
+ const privateCommand = buildEnvAddCommandWithPreservedArgs(
888
+ client.argv,
889
+ `env add ${nameWithoutPrefix} ${envTargetArg ?? getEnvTargetPlaceholder()} --type secret --value "<value>" --yes`
890
+ );
891
+ const publicCommand = buildEnvAddCommandWithPreservedArgs(
892
+ client.argv,
893
+ `env add ${envName} ${envTargetArg ?? getEnvTargetPlaceholder()} --type config --value "<value>" --yes`
894
+ );
895
+ const humanTargetArg = envTargetArg ? ` ${envTargetArg}` : "";
896
+ const privateHumanCommand = buildHumanEnvAddCommand(
897
+ client.argv,
898
+ `env add ${nameWithoutPrefix}${humanTargetArg} --type secret`
899
+ );
900
+ const publicHumanCommand = buildHumanEnvAddCommand(
901
+ client.argv,
902
+ `env add ${envName}${humanTargetArg} --type config`
903
+ );
904
+ const message = `\`${envName}\` looks like a credential, and \`${publicPrefix}\` exposes its value to anyone visiting your site. Choose explicitly: rename to \`${nameWithoutPrefix}\` with \`--type secret\` to keep it private, or keep the name with \`--type config\` to expose it.`;
905
+ if (client.nonInteractive || opts["--yes"] || !!stdInput) {
951
906
  if (client.nonInteractive) {
952
- const nameWithoutPrefix2 = removePublicPrefix(envName);
953
907
  outputActionRequired(client, {
954
908
  status: "action_required",
955
- reason: "env_key_sensitive",
956
- message: `Key ${envName} may expose sensitive data (public prefix). Use --yes to keep as is, or rename to ${nameWithoutPrefix2}.`,
909
+ reason: "public_prefix_requires_type",
910
+ message,
957
911
  choices: [
958
- { id: "keep", name: "Leave as is (use --yes)" },
959
- { id: "rename", name: `Rename to ${nameWithoutPrefix2}` }
912
+ {
913
+ id: "private",
914
+ name: `Keep private: rename to ${nameWithoutPrefix} and use Secret`
915
+ },
916
+ {
917
+ id: "public",
918
+ name: `Expose publicly: keep ${envName} as Config`
919
+ }
960
920
  ],
961
921
  next: [
962
922
  {
963
- command: buildEnvAddCommandWithPreservedArgs(
964
- client.argv,
965
- `env add ${envName} ${getEnvTargetPlaceholder()} --value "<value>" --yes`
966
- ),
967
- when: "Leave as is"
923
+ command: privateCommand,
924
+ when: "Keep it private; replace <value> before running"
968
925
  },
969
926
  {
970
- command: buildEnvAddCommandWithPreservedArgs(
971
- client.argv,
972
- `env add ${nameWithoutPrefix2} ${getEnvTargetPlaceholder()} --value "<value>" --yes`
973
- ),
974
- when: "Rename"
927
+ command: publicCommand,
928
+ when: "Expose it publicly; replace <value> before running"
975
929
  }
976
930
  ]
977
931
  });
978
932
  }
979
- for (const w of keyWarnings) {
980
- printEnvAddWarning(w.message);
981
- }
982
- const nameWithoutPrefix = removePublicPrefix(envName);
983
- const choices2 = [
984
- { name: `Keep ${envName}`, value: "c" },
985
- { name: `Rename to ${nameWithoutPrefix}`, value: "p" },
986
- { name: "Re-enter name", value: "r" }
987
- ];
988
- const action = await client.input.select({
989
- message: "Variable name?",
990
- choices: choices2
991
- });
992
- if (action === "c") {
993
- keyAccepted = true;
994
- } else if (action === "p") {
995
- envName = nameWithoutPrefix;
996
- output_manager_default.log(`Renamed to ${envName}`);
997
- } else {
998
- envName = await client.input.text({
999
- message: `Name?`,
1000
- validate: (val) => val ? true : "Name cannot be empty"
1001
- });
1002
- }
933
+ output_manager_default.fatal(message);
934
+ output_manager_default.print(` Keep it private:
935
+ ${privateHumanCommand}
936
+ `);
937
+ output_manager_default.print(` Expose it publicly:
938
+ ${publicHumanCommand}
939
+ `);
940
+ return 1;
1003
941
  }
1004
- } else {
1005
- const keyWarnings = getEnvKeyWarnings(envName);
1006
- for (const w of keyWarnings) {
1007
- printEnvAddWarning(w.message);
942
+ for (const warning of keyWarnings) {
943
+ printEnvAddWarning(warning.message);
944
+ }
945
+ const action = await client.input.select({
946
+ message: "How should this variable be stored?",
947
+ choices: [
948
+ {
949
+ name: `Keep private: rename to ${nameWithoutPrefix} and use Secret`,
950
+ value: "p"
951
+ },
952
+ {
953
+ name: `Expose to anyone visiting your site: keep ${envName} as Config`,
954
+ value: "c"
955
+ },
956
+ { name: "Enter a different name", value: "r" }
957
+ ]
958
+ });
959
+ if (action === "p") {
960
+ envName = nameWithoutPrefix;
961
+ explicitType = "secret";
962
+ typeSource = "prompt";
963
+ output_manager_default.log(`Renamed to ${envName}`);
964
+ } else if (action === "c") {
965
+ explicitType = "config";
966
+ typeSource = "prompt";
967
+ keyAccepted = true;
968
+ } else {
969
+ envName = await client.input.text({
970
+ message: `Name?`,
971
+ validate: (val) => val ? true : "Name cannot be empty"
972
+ });
1008
973
  }
1009
974
  }
1010
- const link = await resolveProjectContext({
1011
- client,
1012
- projectNameOrId: opts["--project"]
1013
- });
1014
- if (link.status === "error") {
1015
- return link.exitCode;
1016
- } else if (link.status === "not_linked") {
975
+ let customSveltePublicPrefix;
976
+ const apiPublicPrefix = getApiPublicPrefix(envName);
977
+ const matchingFrameworkPublicPrefix = getFrameworkPublicPrefix(
978
+ project.framework,
979
+ envName
980
+ );
981
+ if (matchingFrameworkPublicPrefix !== null && (explicitType === "secret" || forceSensitive)) {
982
+ const message = `\`${matchingFrameworkPublicPrefix}\` exposes this value to anyone visiting your site, so \`${envName}\` cannot be kept private as a Secret. Rename the variable without the framework's public prefix to keep it private, or use \`--type config\` only if the value is safe to expose.`;
1017
983
  if (client.nonInteractive) {
1018
- const preserved = getPreservedArgsForEnvAdd(client.argv);
1019
- const linkPreserved = preserved.filter((a, i) => {
1020
- if (a === "--value")
1021
- return false;
1022
- if (a.startsWith("--value="))
1023
- return false;
1024
- if (i > 0 && preserved[i - 1] === "--value")
1025
- return false;
1026
- return true;
1027
- });
1028
- const linkArgv = [
1029
- ...client.argv.slice(0, 2),
1030
- "link",
1031
- ...link.status === "not_linked" ? ["--scope", "<scope>"] : [],
1032
- ...linkPreserved
1033
- ];
1034
- let envAddRetryArgv = redactEnvValueArgs(client.argv);
1035
- if (envTargetArg === "preview" && envGitBranch === void 0) {
1036
- const argvArgs = client.argv.slice(2);
1037
- const addIdx = argvArgs.indexOf("add");
1038
- if (addIdx !== -1) {
1039
- let pos = addIdx + 1;
1040
- let positionals = 0;
1041
- while (pos < argvArgs.length && positionals < 3 && !argvArgs[pos].startsWith("-")) {
1042
- positionals++;
1043
- pos++;
1044
- }
1045
- const insertAt = 2 + pos;
1046
- envAddRetryArgv = redactEnvValueArgs([
1047
- ...client.argv.slice(0, insertAt),
1048
- "<gitbranch>",
1049
- ...client.argv.slice(insertAt)
1050
- ]);
1051
- }
1052
- }
1053
984
  outputAgentError(
1054
985
  client,
1055
- {
1056
- status: "error",
1057
- reason: "not_linked",
1058
- message: `Your codebase isn't linked to a project on Vercel. Run ${getCommandNamePlain(
1059
- "link"
1060
- )} to begin. Use --yes for non-interactive; use --scope or --project to specify team or project. Then run your env add command.`,
1061
- next: [
1062
- { command: buildCommandWithYes(linkArgv) },
1063
- { command: buildCommandWithYes(envAddRetryArgv) }
1064
- ]
1065
- },
986
+ { status: "error", reason: "invalid_type", message },
1066
987
  1
1067
988
  );
1068
- } else {
1069
- output_manager_default.error(
1070
- `Your codebase isn\u2019t linked to a project on Vercel. Run ${getCommandName(
1071
- "link"
1072
- )} to begin.`
989
+ }
990
+ output_manager_default.fatal(message);
991
+ return 1;
992
+ }
993
+ if (project.framework === "sveltekit" || project.framework === "sveltekit-1" || project.framework === "sveltekit-2") {
994
+ const localPublicPrefixes = await getLocalSvelteKitPublicPrefixes(
995
+ link.repoRoot ?? client.cwd,
996
+ project.rootDirectory
997
+ );
998
+ const matchingLocalPrefix = localPublicPrefixes?.find(
999
+ (prefix) => envName.startsWith(prefix)
1000
+ );
1001
+ if (matchingLocalPrefix !== void 0 && apiPublicPrefix === null && matchingFrameworkPublicPrefix === null) {
1002
+ customSveltePublicPrefix = matchingLocalPrefix;
1003
+ }
1004
+ }
1005
+ if (customSveltePublicPrefix !== void 0 && (explicitType === "secret" || forceSensitive)) {
1006
+ const message = customSveltePublicPrefix === "" ? "This SvelteKit project uses an empty `publicPrefix`, so every Environment Variable is exposed to the browser and cannot be kept private as a Secret. Change the SvelteKit `publicPrefix`, or use `--type config` only if the value is safe to expose." : `\`${customSveltePublicPrefix}\` exposes this value to anyone visiting your site, so \`${envName}\` cannot be kept private as a Secret. Rename the variable without the configured public prefix, or use \`--type config\` only if the value is safe to expose.`;
1007
+ if (client.nonInteractive) {
1008
+ outputAgentError(
1009
+ client,
1010
+ { status: "error", reason: "invalid_type", message },
1011
+ 1
1073
1012
  );
1074
1013
  }
1014
+ output_manager_default.fatal(message);
1075
1015
  return 1;
1076
1016
  }
1077
- client.config.currentTeam = link.org.type === "team" ? link.org.id : void 0;
1078
- const { project } = link;
1017
+ if (customSveltePublicPrefix !== void 0) {
1018
+ printEnvAddWarning(
1019
+ customSveltePublicPrefix === "" ? "This SvelteKit project uses an empty `publicPrefix`, so every Environment Variable is exposed to the browser." : `\`${customSveltePublicPrefix}\` variables are exposed to the browser by this SvelteKit project.`
1020
+ );
1021
+ }
1022
+ const skipConfirm = opts["--yes"] || !!stdInput || valueFromFlag !== void 0;
1079
1023
  const [{ envs }, customEnvironments] = await Promise.all([
1080
1024
  getEnvRecords(client, project.id, "vercel-cli:env:add"),
1081
1025
  getCustomEnvironments(client, project.id)
@@ -1163,50 +1107,54 @@ async function add(client, argv) {
1163
1107
  );
1164
1108
  return 1;
1165
1109
  }
1166
- const forceSensitive = Boolean(opts["--sensitive"]);
1167
- const forceEncrypted = Boolean(opts["--no-sensitive"]);
1168
- if (forceSensitive && forceEncrypted) {
1169
- output_manager_default.error(
1170
- `--sensitive and --no-sensitive cannot be used together. Pick one.`
1171
- );
1172
- return 1;
1173
- }
1174
- const explicitVisibility = typeof opts["--visibility"] === "string" ? opts["--visibility"] : void 0;
1175
- if (explicitVisibility === "secret" && forceEncrypted) {
1176
- output_manager_default.error(
1177
- "`--visibility secret` cannot be used with `--no-sensitive`. Pick one."
1178
- );
1179
- return 1;
1180
- }
1181
- if (explicitVisibility === "config" && forceSensitive) {
1182
- output_manager_default.error(
1183
- "`--visibility config` cannot be used with `--sensitive`. Pick one."
1184
- );
1185
- return 1;
1186
- }
1187
1110
  const policyOn = false;
1188
1111
  let teamSensitivePolicyOn = false;
1112
+ let disjunctiveProductionSecretPolicyOn = false;
1189
1113
  if (link.org.type === "team") {
1190
1114
  try {
1191
1115
  const team = await getTeamById(client, link.org.id);
1192
1116
  teamSensitivePolicyOn = team?.sensitiveEnvironmentVariablePolicy === "on";
1117
+ disjunctiveProductionSecretPolicyOn = team?.disjunctiveProductionSecretPolicy === "on";
1193
1118
  } catch {
1194
1119
  }
1195
1120
  }
1121
+ const userWasExplicit = forceSensitive || forceEncrypted || !!explicitType;
1122
+ const skipSensitivePrompt = userWasExplicit || client.nonInteractive || skipConfirm;
1196
1123
  const isDevelopmentOnlyTarget = envTargets.length === 1 && envTargets[0] === "development";
1197
- const userWasExplicit = forceSensitive || forceEncrypted;
1198
- const skipSensitivePrompt = userWasExplicit || client.nonInteractive || skipConfirm || isDevelopmentOnlyTarget;
1199
1124
  let isSensitive;
1200
1125
  if (forceSensitive) {
1201
1126
  isSensitive = true;
1202
1127
  } else if (forceEncrypted) {
1203
1128
  isSensitive = false;
1129
+ } else if (explicitType) {
1130
+ isSensitive = explicitType === "secret";
1131
+ } else if (apiPublicPrefix || matchingFrameworkPublicPrefix || customSveltePublicPrefix !== void 0) {
1132
+ isSensitive = false;
1133
+ explicitType = "config";
1134
+ typeSource = "inferred";
1204
1135
  } else if (isDevelopmentOnlyTarget) {
1205
1136
  isSensitive = false;
1137
+ typeSource = "inferred";
1206
1138
  } else if (skipSensitivePrompt) {
1207
1139
  isSensitive = true;
1140
+ typeSource = "default";
1208
1141
  } else {
1209
- isSensitive = await client.input.confirm(SENSITIVE_SECRET_PROMPT, true);
1142
+ const selectedType = await client.input.select({
1143
+ message: "Environment Variable type?",
1144
+ choices: [
1145
+ {
1146
+ name: getSecretStorageChoice(
1147
+ envTargets,
1148
+ looksLikeCredentialName(envName) ? "because this name looks like a credential" : void 0
1149
+ ),
1150
+ value: "secret"
1151
+ },
1152
+ { name: "Config (can be revealed after saving)", value: "config" }
1153
+ ]
1154
+ });
1155
+ isSensitive = selectedType === "secret";
1156
+ explicitType = selectedType;
1157
+ typeSource = "prompt";
1210
1158
  }
1211
1159
  if (envTargets.length > 0) {
1212
1160
  const compatibilityError = getTargetCompatibilityError(
@@ -1286,7 +1234,7 @@ async function add(client, argv) {
1286
1234
  }
1287
1235
  envValue = await promptEnvValue(client, { isSensitive });
1288
1236
  }
1289
- const { finalValue } = await validateEnvValue({
1237
+ let { finalValue } = await validateEnvValue({
1290
1238
  envName,
1291
1239
  initialValue: envValue,
1292
1240
  skipConfirm,
@@ -1407,13 +1355,189 @@ async function add(client, argv) {
1407
1355
  finalType = "sensitive";
1408
1356
  }
1409
1357
  }
1358
+ while (finalType === "encrypted" && (looksLikeCredentialName(envName, customSveltePublicPrefix) || looksLikeSecretValue(finalValue))) {
1359
+ printEnvAddWarning(
1360
+ "This name or value looks like a credential. Config values can be revealed after saving."
1361
+ );
1362
+ const publicPrefix = getApiPublicPrefix(envName) ?? getFrameworkPublicPrefix(project.framework, envName) ?? customSveltePublicPrefix;
1363
+ if (publicPrefix !== void 0 && typeSource === "inferred") {
1364
+ const privateName = envName.slice(publicPrefix.length);
1365
+ const privateCommand = buildEnvAddCommandWithPreservedArgs(
1366
+ client.argv,
1367
+ `env add ${privateName} ${envTargets.join(
1368
+ ","
1369
+ )} --type secret --value "<value>" --yes`
1370
+ );
1371
+ const publicCommand = buildEnvAddCommandWithPreservedArgs(
1372
+ client.argv,
1373
+ `env add ${envName} ${envTargets.join(
1374
+ ","
1375
+ )} --type config --value "<value>" --yes`
1376
+ );
1377
+ const privateHumanCommand = buildHumanEnvAddCommand(
1378
+ client.argv,
1379
+ `env add ${privateName} ${envTargets.join(",")} --type secret`
1380
+ );
1381
+ const publicHumanCommand = buildHumanEnvAddCommand(
1382
+ client.argv,
1383
+ `env add ${envName} ${envTargets.join(",")} --type config`
1384
+ );
1385
+ const message = publicPrefix === "" ? `\`${envName}\` looks like a credential, and this SvelteKit project exposes every Environment Variable to the browser. Change the SvelteKit \`publicPrefix\` to keep it private, or use \`--type config\` only if the value is safe to expose.` : `\`${envName}\` looks like a credential, and \`${publicPrefix}\` exposes its value to anyone visiting your site. Choose explicitly: rename to \`${privateName}\` with \`--type secret\` to keep it private, or keep the name with \`--type config\` to expose it.`;
1386
+ if (client.nonInteractive) {
1387
+ outputActionRequired(client, {
1388
+ status: "action_required",
1389
+ reason: "public_prefix_requires_type",
1390
+ message,
1391
+ next: [
1392
+ ...publicPrefix === "" ? [] : [
1393
+ {
1394
+ command: privateCommand,
1395
+ when: "Keep it private; replace <value> before running"
1396
+ }
1397
+ ],
1398
+ {
1399
+ command: publicCommand,
1400
+ when: "Expose it publicly; replace <value> before running"
1401
+ }
1402
+ ]
1403
+ });
1404
+ }
1405
+ if (opts["--yes"]) {
1406
+ output_manager_default.fatal(message);
1407
+ if (publicPrefix !== "") {
1408
+ output_manager_default.print(` Keep it private:
1409
+ ${privateHumanCommand}
1410
+ `);
1411
+ }
1412
+ output_manager_default.print(` Expose it publicly:
1413
+ ${publicHumanCommand}
1414
+ `);
1415
+ return 1;
1416
+ }
1417
+ const selectedAction = await client.input.select({
1418
+ message: "How should this variable be stored?",
1419
+ choices: [
1420
+ ...publicPrefix === "" ? [] : [
1421
+ {
1422
+ name: `Keep private: rename to ${privateName} and use Secret`,
1423
+ value: "private"
1424
+ }
1425
+ ],
1426
+ {
1427
+ name: `Expose to anyone visiting your site: keep ${envName} as Config`,
1428
+ value: "public"
1429
+ },
1430
+ { name: "Enter a different value", value: "reenter" }
1431
+ ]
1432
+ });
1433
+ if (selectedAction === "private") {
1434
+ envName = privateName;
1435
+ finalType = "sensitive";
1436
+ explicitType = "secret";
1437
+ typeSource = "prompt";
1438
+ output_manager_default.log(`Renamed to ${envName}`);
1439
+ break;
1440
+ }
1441
+ if (selectedAction === "reenter") {
1442
+ const reenteredValue = await promptEnvValue(client, {
1443
+ isSensitive: false
1444
+ });
1445
+ const validated = await validateEnvValue({
1446
+ envName,
1447
+ initialValue: reenteredValue,
1448
+ skipConfirm: false,
1449
+ promptForValue: () => promptEnvValue(client, { isSensitive: false }),
1450
+ selectAction: (choices2) => client.input.select({ message: "Value?", choices: choices2 }),
1451
+ showWarning: (msg) => printEnvAddWarning(msg),
1452
+ showLog: (msg) => output_manager_default.log(msg)
1453
+ });
1454
+ finalValue = validated.finalValue;
1455
+ continue;
1456
+ }
1457
+ } else if (typeSource !== "argv" && typeSource !== "prompt" && !opts["--yes"] && !client.nonInteractive) {
1458
+ const selectedType = await client.input.select({
1459
+ message: "Store this value as?",
1460
+ choices: [
1461
+ {
1462
+ name: getSecretStorageChoice(envTargets, "for this value"),
1463
+ value: "secret"
1464
+ },
1465
+ {
1466
+ name: "Config (can be revealed after saving)",
1467
+ value: "config"
1468
+ }
1469
+ ]
1470
+ });
1471
+ if (selectedType === "secret") {
1472
+ finalType = "sensitive";
1473
+ explicitType = "secret";
1474
+ typeSource = "prompt";
1475
+ }
1476
+ }
1477
+ break;
1478
+ }
1479
+ if (finalType === "sensitive" && customSveltePublicPrefix !== void 0 && envName.startsWith(customSveltePublicPrefix)) {
1480
+ printEnvAddWarning(
1481
+ "This SvelteKit project exposes this variable to the browser; the Secret type does not prevent that. Rename the variable without the configured public prefix to keep it private."
1482
+ );
1483
+ }
1484
+ if (isFlagsSecretNeedingSplit({
1485
+ key: envName,
1486
+ type: finalType,
1487
+ targets: envTargets.filter(isValidEnvTarget),
1488
+ customEnvironmentIds: envTargets.filter(
1489
+ (target) => !isValidEnvTarget(target)
1490
+ )
1491
+ })) {
1492
+ printEnvAddWarning(
1493
+ "FLAGS_SECRET should use a separate value for each environment so Development overrides cannot affect Preview or Production."
1494
+ );
1495
+ }
1496
+ const hasProduction = envTargets.includes("production");
1497
+ const hasNonProduction = envTargets.some((target) => target !== "production");
1498
+ if (finalType === "sensitive" && disjunctiveProductionSecretPolicyOn && hasProduction && hasNonProduction) {
1499
+ const message = "Your team requires Production and non-Production Secrets to be stored separately with different values. Run one command for Production and another for the remaining environments.";
1500
+ if (client.nonInteractive) {
1501
+ outputAgentError(
1502
+ client,
1503
+ {
1504
+ status: "error",
1505
+ reason: "production_secret_must_be_separate",
1506
+ message,
1507
+ next: [
1508
+ {
1509
+ command: buildEnvAddCommandWithPreservedArgs(
1510
+ client.argv,
1511
+ `env add ${envName} production --type secret --value "<production-value>" --yes`
1512
+ ),
1513
+ when: "Set the Production Secret"
1514
+ },
1515
+ {
1516
+ command: buildEnvAddCommandWithPreservedArgs(
1517
+ client.argv,
1518
+ `env add ${envName} ${envTargets.filter((target) => target !== "production").join(
1519
+ ","
1520
+ )} --type secret --value "<non-production-value>" --yes`
1521
+ ),
1522
+ when: "Set the non-Production Secret"
1523
+ }
1524
+ ]
1525
+ },
1526
+ 1
1527
+ );
1528
+ }
1529
+ output_manager_default.fatal(message);
1530
+ return 1;
1531
+ }
1410
1532
  const upsert = opts["--force"] ? "true" : "";
1411
1533
  const { visibility, error: visibilityError } = resolveEnvVarVisibility({
1412
- explicitVisibility,
1534
+ explicitVisibility: explicitType,
1535
+ explicitOptionSource: typeOption.source,
1413
1536
  type: finalType,
1414
1537
  key: envName,
1415
1538
  envTargets,
1416
- teamSensitivePolicyOn
1539
+ teamSensitivePolicyOn,
1540
+ context: "add"
1417
1541
  });
1418
1542
  if (visibilityError) {
1419
1543
  if (client.nonInteractive) {
@@ -1421,13 +1545,13 @@ async function add(client, argv) {
1421
1545
  client,
1422
1546
  {
1423
1547
  status: "error",
1424
- reason: "invalid_visibility",
1548
+ reason: "invalid_type",
1425
1549
  message: visibilityError
1426
1550
  },
1427
1551
  1
1428
1552
  );
1429
1553
  }
1430
- output_manager_default.error(visibilityError);
1554
+ output_manager_default.fatal(visibilityError);
1431
1555
  return 1;
1432
1556
  }
1433
1557
  try {
@@ -1445,13 +1569,18 @@ async function add(client, argv) {
1445
1569
  );
1446
1570
  } catch (err) {
1447
1571
  if (client.nonInteractive && isAPIError(err)) {
1448
- const reason = err.slug || (err.serverMessage?.toLowerCase().includes("branch") ? "branch_not_found" : "api_error");
1572
+ const productionSecretPolicyError = getProductionSecretPolicyErrorKind(
1573
+ err.serverMessage
1574
+ );
1575
+ const reason = productionSecretPolicyError ? productionSecretPolicyError === "different-values" ? "production_secret_requires_different_value" : "production_secret_must_be_separate" : err.slug || (err.serverMessage?.toLowerCase().includes("branch") ? "branch_not_found" : "api_error");
1449
1576
  outputAgentError(
1450
1577
  client,
1451
1578
  {
1452
1579
  status: "error",
1453
1580
  reason,
1454
- message: err.serverMessage
1581
+ message: productionSecretPolicyError ? `${err.serverMessage} ${getProductionSecretPolicyRecovery(
1582
+ productionSecretPolicyError
1583
+ )}` : err.serverMessage
1455
1584
  },
1456
1585
  1
1457
1586
  );
@@ -1472,13 +1601,30 @@ async function add(client, argv) {
1472
1601
  Boolean(opts["--force"]),
1473
1602
  visibility
1474
1603
  );
1475
- const { isAgent } = await determineAgent();
1476
- const guidanceMode = parsedArgs.flags["--guidance"] ?? isAgent;
1604
+ if (typeSource === "default") {
1605
+ output_manager_default.print(
1606
+ ` ${import_chalk.default.dim(getDefaultSecretStorageGuidance(envTargets))}
1607
+ `
1608
+ );
1609
+ }
1610
+ const { isAgent } = await determineAgent();
1611
+ const guidanceMode = isGuidanceEnabled(
1612
+ client,
1613
+ parsedArgs.flags["--guidance"],
1614
+ isAgent
1615
+ );
1477
1616
  if (guidanceMode) {
1478
- const projectFlag = opts["--project"] ? ` --project ${opts["--project"]}` : "";
1479
1617
  suggestNextCommands([
1480
- getCommandName(`env ls${projectFlag}`),
1481
- getCommandName(`env pull${projectFlag}`)
1618
+ {
1619
+ description: "List Environment Variables",
1620
+ command: withGlobalFlags(client, "env ls", { preserveProject: true })
1621
+ },
1622
+ {
1623
+ description: "Pull Development Environment Variables into .env.local",
1624
+ command: withGlobalFlags(client, "env pull", {
1625
+ preserveProject: true
1626
+ })
1627
+ }
1482
1628
  ]);
1483
1629
  }
1484
1630
  return 0;
@@ -1606,7 +1752,7 @@ async function ls(client, argv) {
1606
1752
  const jsonOutput = {
1607
1753
  envs: envs.map((env) => ({
1608
1754
  key: env.key,
1609
- value: isSensitiveEnvironmentVariable(env) ? void 0 : env.value,
1755
+ value: isSecretEnvVar(env) ? void 0 : env.value,
1610
1756
  type: env.type,
1611
1757
  visibility: env.visibility,
1612
1758
  target: env.target,
@@ -1631,13 +1777,31 @@ async function ls(client, argv) {
1631
1777
  }
1632
1778
  if (!asJson) {
1633
1779
  const { isAgent } = await determineAgent2();
1634
- const guidanceMode = parsedArgs.flags["--guidance"] ?? isAgent;
1780
+ const guidanceMode = isGuidanceEnabled(
1781
+ client,
1782
+ parsedArgs.flags["--guidance"],
1783
+ isAgent
1784
+ );
1635
1785
  if (guidanceMode) {
1636
- const projectFlag = projectName ? ` --project ${projectName}` : "";
1637
1786
  suggestNextCommands([
1638
- getCommandName(`env add${projectFlag}`),
1639
- getCommandName(`env rm${projectFlag}`),
1640
- getCommandName(`env pull${projectFlag}`)
1787
+ {
1788
+ description: "Add an Environment Variable",
1789
+ command: withGlobalFlags(client, "env add", {
1790
+ preserveProject: true
1791
+ })
1792
+ },
1793
+ {
1794
+ description: "Remove an Environment Variable",
1795
+ command: withGlobalFlags(client, "env rm", {
1796
+ preserveProject: true
1797
+ })
1798
+ },
1799
+ {
1800
+ description: "Pull Development Environment Variables into .env.local",
1801
+ command: withGlobalFlags(client, "env pull", {
1802
+ preserveProject: true
1803
+ })
1804
+ }
1641
1805
  ]);
1642
1806
  }
1643
1807
  }
@@ -1658,7 +1822,7 @@ function getTable(link, records, customEnvironments) {
1658
1822
  }
1659
1823
  function getRow(link, env, customEnvironments) {
1660
1824
  let value;
1661
- if (isSensitiveEnvironmentVariable(env)) {
1825
+ if (isSecretEnvVar(env)) {
1662
1826
  value = import_chalk2.default.gray.italic("Hidden");
1663
1827
  } else if (env.type === "system") {
1664
1828
  value = import_chalk2.default.gray.italic(env.value);
@@ -1675,14 +1839,14 @@ function getRow(link, env, customEnvironments) {
1675
1839
  env.createdAt ? `${(0, import_ms.default)(now - env.createdAt)} ago` : ""
1676
1840
  ];
1677
1841
  }
1678
- function isSensitiveEnvironmentVariable(env) {
1679
- return env.type === "sensitive" || env.visibility === "secret";
1680
- }
1681
1842
  function getEnvironmentVariableTypeLabel(env) {
1682
- if (isSensitiveEnvironmentVariable(env)) {
1683
- return "Sensitive";
1843
+ if (isSecretEnvVar(env)) {
1844
+ return "Secret";
1845
+ }
1846
+ if (env.type === "system") {
1847
+ return "System";
1684
1848
  }
1685
- return env.type === "system" ? "System" : "Non-sensitive";
1849
+ return "Config";
1686
1850
  }
1687
1851
 
1688
1852
  // src/commands/env/rm.ts
@@ -1733,6 +1897,10 @@ var EnvRmTelemetryClient = class extends TelemetryClient {
1733
1897
  };
1734
1898
 
1735
1899
  // src/commands/env/rm.ts
1900
+ function printEnvRmWarning(message) {
1901
+ output_manager_default.print(`${import_chalk3.default.yellow("!")} ${message}
1902
+ `);
1903
+ }
1736
1904
  async function rm(client, argv) {
1737
1905
  const telemetryClient = new EnvRmTelemetryClient({
1738
1906
  opts: {
@@ -1766,9 +1934,9 @@ async function rm(client, argv) {
1766
1934
  {
1767
1935
  status: "error",
1768
1936
  reason: "invalid_arguments",
1769
- message: `Invalid number of arguments. Usage: ${getCommandNamePlain(
1937
+ message: `Invalid number of arguments. Usage: \`${getCommandNamePlain(
1770
1938
  `env rm <name> ${getEnvTargetPlaceholder()} <gitbranch>`
1771
- )}`
1939
+ )}\``
1772
1940
  },
1773
1941
  1
1774
1942
  );
@@ -1809,9 +1977,9 @@ async function rm(client, argv) {
1809
1977
  {
1810
1978
  status: "error",
1811
1979
  reason: "not_linked",
1812
- message: `Your codebase isn't linked to a project on Vercel. Run ${getCommandNamePlain(
1980
+ message: `Your codebase isn't linked to a project on Vercel. Run \`${getCommandNamePlain(
1813
1981
  "link"
1814
- )} to begin. Use --yes for non-interactive; use --scope or --project to specify team or project.`,
1982
+ )}\` to begin. Use \`--yes\` for non-interactive; use \`--scope\` or \`--project\` to specify team or project.`,
1815
1983
  next: [
1816
1984
  { command: buildCommandWithYes(linkArgv) },
1817
1985
  { command: buildCommandWithYes(client.argv) }
@@ -1837,7 +2005,7 @@ async function rm(client, argv) {
1837
2005
  {
1838
2006
  status: "action_required",
1839
2007
  reason: "missing_name",
1840
- message: "Provide the variable name as an argument. Example: vercel env rm <name> --yes",
2008
+ message: "Provide the variable name as an argument. Example: `vercel env rm <name> --yes`",
1841
2009
  next: [
1842
2010
  {
1843
2011
  command: buildEnvRmCommandWithPreservedArgs(
@@ -1912,6 +2080,15 @@ async function rm(client, argv) {
1912
2080
  envs = envs.filter((env2) => env2.id === id);
1913
2081
  }
1914
2082
  const env = envs[0];
2083
+ const shouldWarnAboutRotation = shouldConfirmRotationBeforeDelete({
2084
+ key: env.key,
2085
+ type: env.type,
2086
+ hasPublicPrefix: getPublicPrefix(env.key) !== null
2087
+ });
2088
+ const rotationWarning = "Removing this variable from Vercel does not revoke the credential. Rotate or disable it at its provider.";
2089
+ if (shouldWarnAboutRotation) {
2090
+ printEnvRmWarning(rotationWarning);
2091
+ }
1915
2092
  const skipConfirmation = opts["--yes"];
1916
2093
  if (!skipConfirmation) {
1917
2094
  if (client.nonInteractive) {
@@ -1920,18 +2097,18 @@ async function rm(client, argv) {
1920
2097
  {
1921
2098
  status: "action_required",
1922
2099
  reason: "confirmation_required",
1923
- message: `Removing Environment Variable ${env.key}. Use --yes to confirm.`,
2100
+ message: `Removing Environment Variable ${env.key}. ${shouldWarnAboutRotation ? `${rotationWarning} ` : ""}Use --yes to confirm.`,
1924
2101
  next: [{ command: buildCommandWithYes(client.argv) }]
1925
2102
  },
1926
2103
  1
1927
2104
  );
1928
2105
  }
1929
2106
  if (!await client.input.confirm(
1930
- `Removing Environment Variable ${param(env.key)} from ${formatEnvironments(
2107
+ `Remove ${param(env.key)} from ${formatEnvironments(
1931
2108
  link,
1932
2109
  env,
1933
2110
  customEnvironments
1934
- )} in Project ${import_chalk3.default.bold(project.name)}. Are you sure?`,
2111
+ )} in Project ${import_chalk3.default.bold(project.name)}?`,
1935
2112
  false
1936
2113
  )) {
1937
2114
  output_manager_default.log("Canceled");
@@ -1973,7 +2150,21 @@ async function rm(client, argv) {
1973
2150
 
1974
2151
  // src/commands/env/run.ts
1975
2152
  var import_env = __toESM(require_dist3(), 1);
2153
+ var import_chalk4 = __toESM(require_source(), 1);
1976
2154
  var import_execa = __toESM(require_execa(), 1);
2155
+ function printEnvRunWarning(message) {
2156
+ output_manager_default.print(`${import_chalk4.default.yellow("!")} ${message}
2157
+ `);
2158
+ }
2159
+ function omitUnavailableSecretPlaceholders(localEnv, secretKeys) {
2160
+ const safeLocalEnv = { ...localEnv };
2161
+ for (const key of secretKeys) {
2162
+ if (safeLocalEnv[key] === SENSITIVE_ENV_VALUE_PLACEHOLDER) {
2163
+ delete safeLocalEnv[key];
2164
+ }
2165
+ }
2166
+ return safeLocalEnv;
2167
+ }
1977
2168
  function parseRunArgs(argv) {
1978
2169
  const argvIndex = argv.indexOf("--");
1979
2170
  const hasDoubleDash = argvIndex !== -1;
@@ -2002,6 +2193,16 @@ async function run(client, telemetry) {
2002
2193
  return 1;
2003
2194
  }
2004
2195
  if (userCommand.length === 0) {
2196
+ outputActionRequired(
2197
+ client,
2198
+ {
2199
+ status: "action_required",
2200
+ reason: "missing_command",
2201
+ message: "No command provided. Use `--` to separate Vercel flags from your command.",
2202
+ next: [{ command: getCommandNamePlain("env run -- <command>") }]
2203
+ },
2204
+ 1
2205
+ );
2005
2206
  output_manager_default.error(
2006
2207
  `No command provided. Use \`--\` to separate vercel flags from your command.`
2007
2208
  );
@@ -2016,6 +2217,16 @@ async function run(client, telemetry) {
2016
2217
  if (link.status === "error") {
2017
2218
  return link.exitCode;
2018
2219
  } else if (link.status === "not_linked") {
2220
+ outputActionRequired(
2221
+ client,
2222
+ {
2223
+ status: "action_required",
2224
+ reason: "not_linked",
2225
+ message: "Your codebase isn't linked to a project on Vercel. Run `vercel link`, then retry this command.",
2226
+ next: [{ command: getCommandNamePlain("link") }]
2227
+ },
2228
+ 1
2229
+ );
2019
2230
  output_manager_default.error(
2020
2231
  `Your codebase isn't linked to a project on Vercel. Run ${getCommandName(
2021
2232
  "link"
@@ -2049,6 +2260,45 @@ async function run(client, telemetry) {
2049
2260
  } catch (err) {
2050
2261
  output_manager_default.debug(`Failed to load local env files: ${err}`);
2051
2262
  }
2263
+ const localPlaceholderKeys = Object.entries(localEnv).filter(([, value]) => value === SENSITIVE_ENV_VALUE_PLACEHOLDER).map(([key]) => key);
2264
+ const safeLocalEnv = omitUnavailableSecretPlaceholders(
2265
+ localEnv,
2266
+ localPlaceholderKeys
2267
+ );
2268
+ const processPlaceholderKeys = Object.keys(records.env).filter(
2269
+ (key) => process.env[key] === SENSITIVE_ENV_VALUE_PLACEHOLDER
2270
+ );
2271
+ const safeProcessEnv = omitUnavailableSecretPlaceholders(
2272
+ process.env,
2273
+ processPlaceholderKeys
2274
+ );
2275
+ const unavailableCandidateKeys = new Set(
2276
+ Object.entries(records.env).filter(
2277
+ ([key, value]) => !value && !safeLocalEnv[key] && !safeProcessEnv[key]
2278
+ ).map(([key]) => key)
2279
+ );
2280
+ if (unavailableCandidateKeys.size > 0) {
2281
+ try {
2282
+ const { envs } = await getEnvRecords(
2283
+ client,
2284
+ link.project.id,
2285
+ "vercel-cli:env:run",
2286
+ { target: environment, gitBranch }
2287
+ );
2288
+ const unavailableSecretKeys = envs.filter(
2289
+ (env) => isSecretEnvVar(env) && unavailableCandidateKeys.has(env.key)
2290
+ );
2291
+ if (unavailableSecretKeys.length > 0) {
2292
+ printEnvRunWarning(
2293
+ `${getUnavailableSecretValuesMessage(
2294
+ environment,
2295
+ unavailableSecretKeys.length
2296
+ )}${getLocalSecretFallbackMessage(unavailableSecretKeys.length)}`
2297
+ );
2298
+ }
2299
+ } catch {
2300
+ }
2301
+ }
2052
2302
  try {
2053
2303
  const result = await (0, import_execa.default)(userCommand[0], userCommand.slice(1), {
2054
2304
  cwd: client.cwd,
@@ -2056,8 +2306,8 @@ async function run(client, telemetry) {
2056
2306
  reject: false,
2057
2307
  env: {
2058
2308
  ...records.env,
2059
- ...localEnv,
2060
- ...process.env
2309
+ ...safeLocalEnv,
2310
+ ...safeProcessEnv
2061
2311
  }
2062
2312
  });
2063
2313
  if (result instanceof Error && typeof result.exitCode !== "number") {
@@ -2072,10 +2322,10 @@ async function run(client, telemetry) {
2072
2322
  }
2073
2323
 
2074
2324
  // src/commands/env/update.ts
2075
- var import_chalk4 = __toESM(require_source(), 1);
2325
+ var import_chalk5 = __toESM(require_source(), 1);
2076
2326
 
2077
2327
  // src/util/env/update-env-record.ts
2078
- var import_constants2 = __toESM(require_dist2(), 1);
2328
+ var import_constants3 = __toESM(require_dist2(), 1);
2079
2329
  async function updateEnvRecord(client, projectId, envId, type, key, value, targets, gitBranch, visibility) {
2080
2330
  output_manager_default.debug(
2081
2331
  `Updating ${type} Environment Variable ${key} in ${targets.length} targets`
@@ -2083,7 +2333,7 @@ async function updateEnvRecord(client, projectId, envId, type, key, value, targe
2083
2333
  const target = [];
2084
2334
  const customEnvironmentIds = [];
2085
2335
  for (const t of targets) {
2086
- const arr = import_constants2.PROJECT_ENV_TARGET.includes(t) ? target : customEnvironmentIds;
2336
+ const arr = import_constants3.PROJECT_ENV_TARGET.includes(t) ? target : customEnvironmentIds;
2087
2337
  arr.push(t);
2088
2338
  }
2089
2339
  const body = {
@@ -2159,9 +2409,51 @@ var EnvUpdateTelemetryClient = class extends TelemetryClient {
2159
2409
  });
2160
2410
  }
2161
2411
  }
2412
+ trackCliOptionType(type) {
2413
+ if (type) {
2414
+ const validTypes = ["config", "secret"];
2415
+ this.trackCliOption({
2416
+ option: "type",
2417
+ value: validTypes.includes(type) ? type : this.redactedValue
2418
+ });
2419
+ }
2420
+ }
2162
2421
  };
2163
2422
 
2164
2423
  // src/commands/env/update.ts
2424
+ function looksLikeCredentialName2(key, publicPrefix = getPublicPrefix(key)) {
2425
+ return looksLikeSecret(publicPrefix ? key.slice(publicPrefix.length) : key);
2426
+ }
2427
+ function printEnvUpdateWarning(message) {
2428
+ output_manager_default.print(`${import_chalk5.default.yellow("!")} ${message}
2429
+ `);
2430
+ }
2431
+ function omitEnvValueArgs(argv) {
2432
+ const out = [];
2433
+ for (let i = 0; i < argv.length; i++) {
2434
+ if (argv[i] === "--value") {
2435
+ i++;
2436
+ continue;
2437
+ }
2438
+ if (argv[i].startsWith("--value="))
2439
+ continue;
2440
+ out.push(argv[i]);
2441
+ }
2442
+ return out;
2443
+ }
2444
+ function formatTargetArg(targets, customEnvironments) {
2445
+ return targets.map(
2446
+ (target) => customEnvironments.find(
2447
+ (environment) => environment.id === target || environment.slug === target
2448
+ )?.slug ?? target
2449
+ ).join(",");
2450
+ }
2451
+ function promptEnvUpdateValue(client, envName, isSecret) {
2452
+ return client.input.text({
2453
+ message: `What's the new value of ${envName}?`,
2454
+ ...isSecret ? { transformer: (value) => "*".repeat(value.length) } : {}
2455
+ });
2456
+ }
2165
2457
  async function update(client, argv) {
2166
2458
  let parsedArgs;
2167
2459
  const flagsSpecification = getFlagsSpecification(updateSubcommand.options);
@@ -2186,6 +2478,12 @@ async function update(client, argv) {
2186
2478
  const valueFromFlag = typeof opts["--value"] === "string" ? opts["--value"] : void 0;
2187
2479
  const stdInput = await readStandardInput(client.stdin);
2188
2480
  let [envName, envTargetArg, envGitBranch] = args;
2481
+ const typeOption = resolveEnvVarTypeOption({
2482
+ type: typeof opts["--type"] === "string" ? opts["--type"] : void 0,
2483
+ visibility: typeof opts["--visibility"] === "string" ? opts["--visibility"] : void 0
2484
+ });
2485
+ let explicitType = typeOption.explicitVisibility;
2486
+ const typeWasExplicit = explicitType !== void 0 || Boolean(opts["--sensitive"]);
2189
2487
  const telemetryClient = new EnvUpdateTelemetryClient({
2190
2488
  opts: {
2191
2489
  store: client.telemetryEventStore
@@ -2196,10 +2494,49 @@ async function update(client, argv) {
2196
2494
  telemetryClient.trackCliArgumentGitBranch(envGitBranch);
2197
2495
  telemetryClient.trackCliFlagSensitive(opts["--sensitive"]);
2198
2496
  telemetryClient.trackCliFlagYes(opts["--yes"]);
2199
- telemetryClient.trackCliOptionValue(valueFromFlag);
2497
+ telemetryClient.trackCliOptionValue(
2498
+ valueFromFlag === void 0 ? void 0 : "<redacted>"
2499
+ );
2500
+ telemetryClient.trackCliOptionType(
2501
+ typeof opts["--type"] === "string" ? opts["--type"] : void 0
2502
+ );
2200
2503
  telemetryClient.trackCliOptionVisibility(
2201
2504
  typeof opts["--visibility"] === "string" ? opts["--visibility"] : void 0
2202
2505
  );
2506
+ if (typeOption.error) {
2507
+ if (client.nonInteractive) {
2508
+ outputAgentError(
2509
+ client,
2510
+ {
2511
+ status: "error",
2512
+ reason: typeOption.errorReason ?? "invalid_type",
2513
+ message: typeOption.error
2514
+ },
2515
+ 1
2516
+ );
2517
+ }
2518
+ output_manager_default.fatal(typeOption.error);
2519
+ return 1;
2520
+ }
2521
+ if (typeOption.usedDeprecatedVisibility) {
2522
+ printEnvUpdateWarning(ENV_VISIBILITY_DEPRECATION_MESSAGE);
2523
+ }
2524
+ if (explicitType === "config" && opts["--sensitive"]) {
2525
+ const message = "`--type config` cannot be used with `--sensitive`. Pick one.";
2526
+ if (client.nonInteractive) {
2527
+ outputAgentError(
2528
+ client,
2529
+ {
2530
+ status: "error",
2531
+ reason: "conflicting_type_flags",
2532
+ message
2533
+ },
2534
+ 1
2535
+ );
2536
+ }
2537
+ output_manager_default.fatal(message);
2538
+ return 1;
2539
+ }
2203
2540
  if (args.length > 3) {
2204
2541
  if (client.nonInteractive) {
2205
2542
  outputAgentError(
@@ -2207,9 +2544,9 @@ async function update(client, argv) {
2207
2544
  {
2208
2545
  status: "error",
2209
2546
  reason: "invalid_arguments",
2210
- message: `Invalid number of arguments. Usage: ${getCommandNamePlain(
2547
+ message: `Invalid number of arguments. Usage: \`${getCommandNamePlain(
2211
2548
  `env update <name> ${getEnvTargetPlaceholder()} <gitbranch>`
2212
- )}`
2549
+ )}\``
2213
2550
  },
2214
2551
  1
2215
2552
  );
@@ -2301,9 +2638,9 @@ async function update(client, argv) {
2301
2638
  return link.exitCode;
2302
2639
  } else if (link.status === "not_linked") {
2303
2640
  if (client.nonInteractive) {
2304
- const preserved = getPreservedArgsForEnvUpdate(client.argv).filter(
2305
- (a) => a !== "--yes" && a !== "-y"
2306
- );
2641
+ const preserved = omitEnvValueArgs(
2642
+ getPreservedArgsForEnvUpdate(client.argv)
2643
+ ).filter((a) => a !== "--yes" && a !== "-y");
2307
2644
  const linkArgv = [
2308
2645
  ...client.argv.slice(0, 2),
2309
2646
  "link",
@@ -2321,7 +2658,9 @@ async function update(client, argv) {
2321
2658
  )} to begin. Use --yes for non-interactive; use --scope or --project to specify team or project.`,
2322
2659
  next: [
2323
2660
  { command: buildCommandWithYes(linkArgv) },
2324
- { command: buildCommandWithYes(client.argv) }
2661
+ {
2662
+ command: buildCommandWithYes(redactEnvValueArgs(client.argv))
2663
+ }
2325
2664
  ]
2326
2665
  },
2327
2666
  1
@@ -2404,7 +2743,7 @@ async function update(client, argv) {
2404
2743
  {
2405
2744
  command: buildEnvUpdateCommandWithPreservedArgs(
2406
2745
  client.argv,
2407
- `env update ${envName} ${getEnvTargetPlaceholder()} <gitbranch>`
2746
+ `env update ${envName} ${getEnvTargetPlaceholder()} <gitbranch> --value "<value>" --yes`
2408
2747
  )
2409
2748
  }
2410
2749
  ]
@@ -2439,7 +2778,7 @@ async function update(client, argv) {
2439
2778
  {
2440
2779
  command: buildEnvUpdateCommandWithPreservedArgs(
2441
2780
  client.argv,
2442
- `env update ${envName} ${getEnvTargetPlaceholder()} <gitbranch>`
2781
+ `env update ${envName} ${getEnvTargetPlaceholder()} <gitbranch> --value "<value>" --yes`
2443
2782
  )
2444
2783
  }
2445
2784
  ]
@@ -2460,15 +2799,178 @@ async function update(client, argv) {
2460
2799
  });
2461
2800
  selectedEnv = matchingEnvs[selectedIndex];
2462
2801
  }
2802
+ let customSveltePublicPrefix;
2803
+ const apiPublicPrefix = getApiPublicPrefix(envName);
2804
+ const frameworkPublicPrefix = getFrameworkPublicPrefix(
2805
+ project.framework,
2806
+ envName
2807
+ );
2808
+ if (project.framework === "sveltekit" || project.framework === "sveltekit-1" || project.framework === "sveltekit-2") {
2809
+ const localPublicPrefixes = await getLocalSvelteKitPublicPrefixes(
2810
+ link.repoRoot ?? client.cwd,
2811
+ project.rootDirectory
2812
+ );
2813
+ const matchingLocalPrefix = localPublicPrefixes?.find(
2814
+ (prefix) => envName.startsWith(prefix)
2815
+ );
2816
+ if (matchingLocalPrefix !== void 0 && apiPublicPrefix === null && frameworkPublicPrefix === null) {
2817
+ customSveltePublicPrefix = matchingLocalPrefix;
2818
+ }
2819
+ }
2820
+ const targets = Array.isArray(selectedEnv.target) ? selectedEnv.target : [selectedEnv.target].filter(
2821
+ (r) => Boolean(r)
2822
+ );
2823
+ const allTargets = [...targets, ...selectedEnv.customEnvironmentIds || []];
2824
+ const selectedEnvIsSecret = isSecretEnvVar(selectedEnv);
2825
+ if (explicitType === "config" && selectedEnvIsSecret) {
2826
+ const removeTargetArg = envTargetArg || targets[0] || allTargets[0] || getEnvTargetPlaceholder();
2827
+ const addTargetArg = formatTargetArg(allTargets, customEnvironments) || removeTargetArg;
2828
+ const removeBranchArg = selectedEnv.gitBranch ? ` ${quoteArg(selectedEnv.gitBranch)}` : "";
2829
+ const addBranchArg = selectedEnv.gitBranch ? ` --git-branch ${quoteArg(selectedEnv.gitBranch)}` : "";
2830
+ const globalFlags = getGlobalFlagsFromArgs(client.argv.slice(2), {
2831
+ preserveProject: true
2832
+ });
2833
+ const humanGlobalFlags = globalFlags.filter(
2834
+ (flag) => !flag.startsWith("--non-interactive")
2835
+ );
2836
+ const globalSuffix = globalFlags.length > 0 ? ` ${globalFlags.map(quoteArg).join(" ")}` : "";
2837
+ const humanGlobalSuffix = humanGlobalFlags.length > 0 ? ` ${humanGlobalFlags.map(quoteArg).join(" ")}` : "";
2838
+ const removeCommand = getCommandNamePlain(
2839
+ `env rm ${envName} ${removeTargetArg}${removeBranchArg} --yes${globalSuffix}`
2840
+ );
2841
+ const addCommand = getCommandNamePlain(
2842
+ `env add ${envName} ${addTargetArg}${addBranchArg} --type config --value "<value>" --yes${globalSuffix}`
2843
+ );
2844
+ const removeHumanCommand = getCommandNamePlain(
2845
+ `env rm ${envName} ${removeTargetArg}${removeBranchArg}${humanGlobalSuffix}`
2846
+ );
2847
+ const addHumanCommand = getCommandNamePlain(
2848
+ `env add ${envName} ${addTargetArg}${addBranchArg} --type config${humanGlobalSuffix}`
2849
+ );
2850
+ const message = "A Secret cannot be changed to Config. To store this value as Config, remove the variable and add it again with `--type config`.";
2851
+ if (client.nonInteractive) {
2852
+ outputAgentError(
2853
+ client,
2854
+ {
2855
+ status: "error",
2856
+ reason: "secret_cannot_become_config",
2857
+ message,
2858
+ next: [
2859
+ {
2860
+ command: removeCommand,
2861
+ when: "Remove the Secret from all of its environments"
2862
+ },
2863
+ {
2864
+ command: addCommand,
2865
+ when: "Add it again as Config; replace <value> before running"
2866
+ }
2867
+ ]
2868
+ },
2869
+ 1
2870
+ );
2871
+ }
2872
+ output_manager_default.fatal(
2873
+ `A Secret cannot be changed to Config. Remove the variable, then add it again as Config. ${param(envName)} will be unavailable to new builds between these commands.`
2874
+ );
2875
+ output_manager_default.print(` Remove:
2876
+ ${removeHumanCommand}
2877
+ `);
2878
+ output_manager_default.print(
2879
+ ` Add as Config (prompts for the value):
2880
+ ${addHumanCommand}
2881
+ `
2882
+ );
2883
+ return 1;
2884
+ }
2885
+ if (explicitType === "secret" || opts["--sensitive"]) {
2886
+ const knownPublicPrefix = apiPublicPrefix ?? frameworkPublicPrefix;
2887
+ const publicPrefix = knownPublicPrefix ?? customSveltePublicPrefix;
2888
+ const apiPublicPrefixError = apiPublicPrefix ? getPublicPrefixSecretVisibilityError(envName, {
2889
+ visibility: explicitType === "secret" ? "secret" : void 0,
2890
+ type: "sensitive",
2891
+ context: "update"
2892
+ }) : null;
2893
+ const publicPrefixError = apiPublicPrefixError ? apiPublicPrefixError : knownPublicPrefix ? `\`${knownPublicPrefix}\` exposes this value to anyone visiting your site, so \`${envName}\` cannot be kept private as a Secret. Add a new Secret without the public prefix, then remove this Config.` : customSveltePublicPrefix === "" ? "This SvelteKit project uses an empty `publicPrefix`, so every Environment Variable is exposed to the browser and cannot be kept private as a Secret. Change the SvelteKit `publicPrefix`, or keep this variable as Config only if the value is safe to expose." : customSveltePublicPrefix !== void 0 ? `\`${customSveltePublicPrefix}\` exposes this value to anyone visiting your site, so \`${envName}\` cannot be kept private as a Secret. Add a new Secret without the configured public prefix, then remove this Config.` : null;
2894
+ if (publicPrefixError) {
2895
+ const privateName = publicPrefix === void 0 ? envName : envName.slice(publicPrefix.length);
2896
+ const removeTargetArg = envTargetArg || targets[0] || allTargets[0] || getEnvTargetPlaceholder();
2897
+ const addTargetArg = formatTargetArg(allTargets, customEnvironments) || removeTargetArg;
2898
+ const removeBranchArg = selectedEnv.gitBranch ? ` ${quoteArg(selectedEnv.gitBranch)}` : "";
2899
+ const addBranchArg = selectedEnv.gitBranch ? ` --git-branch ${quoteArg(selectedEnv.gitBranch)}` : "";
2900
+ const globalFlags = getGlobalFlagsFromArgs(client.argv.slice(2), {
2901
+ preserveProject: true
2902
+ });
2903
+ const humanGlobalFlags = globalFlags.filter(
2904
+ (flag) => !flag.startsWith("--non-interactive")
2905
+ );
2906
+ const globalSuffix = globalFlags.length > 0 ? ` ${globalFlags.map(quoteArg).join(" ")}` : "";
2907
+ const humanGlobalSuffix = humanGlobalFlags.length > 0 ? ` ${humanGlobalFlags.map(quoteArg).join(" ")}` : "";
2908
+ const addCommand = publicPrefix === "" ? void 0 : getCommandNamePlain(
2909
+ `env add ${privateName} ${addTargetArg}${addBranchArg} --type secret --value "<value>" --yes${globalSuffix}`
2910
+ );
2911
+ const removeCommand = getCommandNamePlain(
2912
+ `env rm ${envName} ${removeTargetArg}${removeBranchArg} --yes${globalSuffix}`
2913
+ );
2914
+ const addHumanCommand = publicPrefix === "" ? void 0 : getCommandNamePlain(
2915
+ `env add ${privateName} ${addTargetArg}${addBranchArg} --type secret${humanGlobalSuffix}`
2916
+ );
2917
+ const removeHumanCommand = getCommandNamePlain(
2918
+ `env rm ${envName} ${removeTargetArg}${removeBranchArg}${humanGlobalSuffix}`
2919
+ );
2920
+ if (client.nonInteractive) {
2921
+ outputAgentError(
2922
+ client,
2923
+ {
2924
+ status: "error",
2925
+ reason: "invalid_type",
2926
+ message: publicPrefixError,
2927
+ next: addCommand ? [
2928
+ {
2929
+ command: addCommand,
2930
+ when: "Add the private Secret; replace <value> before running"
2931
+ },
2932
+ {
2933
+ command: removeCommand,
2934
+ when: "Remove the public Config after the Secret is available"
2935
+ }
2936
+ ] : []
2937
+ },
2938
+ 1
2939
+ );
2940
+ }
2941
+ output_manager_default.fatal(publicPrefixError);
2942
+ if (addHumanCommand) {
2943
+ output_manager_default.print(
2944
+ ` Add the private Secret (prompts for the value):
2945
+ ${addHumanCommand}
2946
+ `
2947
+ );
2948
+ output_manager_default.print(
2949
+ ` Remove the public Config:
2950
+ ${removeHumanCommand}
2951
+ `
2952
+ );
2953
+ }
2954
+ return 1;
2955
+ }
2956
+ }
2957
+ if (customSveltePublicPrefix !== void 0) {
2958
+ printEnvUpdateWarning(
2959
+ customSveltePublicPrefix === "" ? "This SvelteKit project uses an empty `publicPrefix`, so every Environment Variable is exposed to the browser." : `\`${customSveltePublicPrefix}\` variables are exposed to the browser by this SvelteKit project.`
2960
+ );
2961
+ }
2463
2962
  let teamSensitivePolicyOn = false;
2963
+ let disjunctiveProductionSecretPolicyOn = false;
2464
2964
  if (link.org.type === "team") {
2465
2965
  try {
2466
2966
  const team = await getTeamById(client, link.org.id);
2467
2967
  teamSensitivePolicyOn = team?.sensitiveEnvironmentVariablePolicy === "on";
2968
+ disjunctiveProductionSecretPolicyOn = team?.disjunctiveProductionSecretPolicy === "on";
2468
2969
  } catch {
2469
2970
  }
2470
2971
  }
2471
2972
  let envValue;
2973
+ const shouldMaskValue = Boolean(opts["--sensitive"]) || explicitType === "secret" || explicitType === void 0 && selectedEnvIsSecret;
2472
2974
  if (stdInput) {
2473
2975
  const normalizedStdinValue = normalizeStdinEnvValue(stdInput);
2474
2976
  envValue = normalizedStdinValue.value;
@@ -2486,7 +2988,7 @@ async function update(client, argv) {
2486
2988
  {
2487
2989
  status: "action_required",
2488
2990
  reason: "missing_value",
2489
- message: "In non-interactive mode provide the new value via --value or stdin. Example: vercel env update <name> <environment> --value 'value' --yes",
2991
+ message: 'In non-interactive mode, provide the new value with `--value` or stdin. Example: `vercel env update <name> <environment> --value "<value>" --yes`',
2490
2992
  next: [
2491
2993
  {
2492
2994
  command: buildEnvUpdateCommandWithPreservedArgs(
@@ -2499,85 +3001,205 @@ async function update(client, argv) {
2499
3001
  1
2500
3002
  );
2501
3003
  }
2502
- envValue = await client.input.text({
2503
- message: `What's the new value of ${envName}?`
2504
- });
3004
+ envValue = await promptEnvUpdateValue(client, envName, shouldMaskValue);
2505
3005
  }
2506
3006
  const skipConfirm = opts["--yes"] || !!stdInput || valueFromFlag !== void 0;
2507
- const { finalValue, alreadyConfirmed } = await validateEnvValue({
3007
+ const { finalValue } = await validateEnvValue({
2508
3008
  envName,
2509
3009
  initialValue: envValue,
2510
3010
  skipConfirm,
2511
- promptForValue: () => client.input.text({ message: `What's the new value of ${envName}?` }),
3011
+ promptForValue: () => promptEnvUpdateValue(client, envName, shouldMaskValue),
2512
3012
  selectAction: (choices) => client.input.select({ message: "How to proceed?", choices }),
2513
- showWarning: (msg) => output_manager_default.warn(msg),
3013
+ showWarning: printEnvUpdateWarning,
2514
3014
  showLog: (msg) => output_manager_default.log(msg)
2515
3015
  });
2516
- if (!opts["--yes"] && !alreadyConfirmed) {
3016
+ let type = opts["--sensitive"] || explicitType === "secret" ? "sensitive" : explicitType === "config" ? "encrypted" : selectedEnv.type;
3017
+ const isFinalSecret = () => type === "sensitive" || explicitType === void 0 && selectedEnvIsSecret;
3018
+ if (!isFinalSecret() && (type === "plain" || type === "encrypted") && (looksLikeCredentialName2(envName, customSveltePublicPrefix) || looksLikeSecretValue(finalValue))) {
3019
+ printEnvUpdateWarning(
3020
+ "This name or value looks like a credential. Config values can be revealed after saving."
3021
+ );
3022
+ const publicPrefix = apiPublicPrefix ?? frameworkPublicPrefix ?? customSveltePublicPrefix;
3023
+ if (publicPrefix !== null && publicPrefix !== void 0) {
3024
+ printEnvUpdateWarning(
3025
+ publicPrefix === "" ? "This SvelteKit project exposes every Environment Variable to the browser. Keep Config only if the value is safe to expose." : `\`${publicPrefix}\` exposes this value to anyone visiting your site. Keep Config only if the value is safe to expose.`
3026
+ );
3027
+ if (!typeWasExplicit && (opts["--yes"] || client.nonInteractive)) {
3028
+ const message = publicPrefix === "" ? "This SvelteKit project exposes every Environment Variable to the browser. To keep this value private, change the configured public prefix before storing it as Secret. If the value is safe to expose, rerun with `--type config`." : `\`${publicPrefix}\` exposes \`${envName}\` to anyone visiting your site. To keep this value private, add \`${envName.slice(
3029
+ publicPrefix.length
3030
+ )}\` as Secret, then remove \`${envName}\`. If the value is safe to expose, rerun with \`--type config\`.`;
3031
+ if (client.nonInteractive) {
3032
+ outputAgentError(
3033
+ client,
3034
+ {
3035
+ status: "error",
3036
+ reason: "unsafe_public_config",
3037
+ message
3038
+ },
3039
+ 1
3040
+ );
3041
+ }
3042
+ output_manager_default.fatal(message);
3043
+ return 1;
3044
+ }
3045
+ if (publicPrefix !== "" && !typeWasExplicit && !opts["--yes"] && !client.nonInteractive) {
3046
+ const privateName = envName.slice(publicPrefix.length);
3047
+ const removeTargetArg = envTargetArg || targets[0] || allTargets[0] || getEnvTargetPlaceholder();
3048
+ const addTargetArg = formatTargetArg(allTargets, customEnvironments) || removeTargetArg;
3049
+ const removeBranchArg = selectedEnv.gitBranch ? ` ${quoteArg(selectedEnv.gitBranch)}` : "";
3050
+ const addBranchArg = selectedEnv.gitBranch ? ` --git-branch ${quoteArg(selectedEnv.gitBranch)}` : "";
3051
+ const humanGlobalFlags = getGlobalFlagsFromArgs(client.argv.slice(2), {
3052
+ preserveProject: true
3053
+ }).filter((flag) => !flag.startsWith("--non-interactive"));
3054
+ const humanGlobalSuffix = humanGlobalFlags.length > 0 ? ` ${humanGlobalFlags.map(quoteArg).join(" ")}` : "";
3055
+ const addPrivateCommand = getCommandNamePlain(
3056
+ `env add ${privateName} ${addTargetArg}${addBranchArg} --type secret${humanGlobalSuffix}`
3057
+ );
3058
+ const removePublicCommand = getCommandNamePlain(
3059
+ `env rm ${envName} ${removeTargetArg}${removeBranchArg}${humanGlobalSuffix}`
3060
+ );
3061
+ const selectedAction = await client.input.select({
3062
+ message: "How should this variable be stored?",
3063
+ choices: [
3064
+ {
3065
+ name: `Keep private: add ${privateName} as Secret, then remove ${envName}`,
3066
+ value: "private"
3067
+ },
3068
+ {
3069
+ name: `Expose to anyone visiting your site: keep ${envName} as Config`,
3070
+ value: "config"
3071
+ }
3072
+ ]
3073
+ });
3074
+ if (selectedAction === "private") {
3075
+ output_manager_default.print(" Add the private Secret (prompts for the value):\n");
3076
+ output_manager_default.print(` ${addPrivateCommand}
3077
+ `);
3078
+ output_manager_default.print(" Remove the public Config:\n");
3079
+ output_manager_default.print(` ${removePublicCommand}
3080
+ `);
3081
+ return 1;
3082
+ }
3083
+ }
3084
+ } else if (!typeWasExplicit && !opts["--yes"] && !client.nonInteractive) {
3085
+ const selectedType = await client.input.select({
3086
+ message: "Store this value as?",
3087
+ choices: [
3088
+ {
3089
+ name: getSecretStorageChoice(targets, "for this value"),
3090
+ value: "secret"
3091
+ },
3092
+ {
3093
+ name: "Config (can be revealed after saving)",
3094
+ value: "config"
3095
+ }
3096
+ ]
3097
+ });
3098
+ if (selectedType === "secret") {
3099
+ type = "sensitive";
3100
+ explicitType = "secret";
3101
+ }
3102
+ } else if (!typeWasExplicit) {
3103
+ printEnvUpdateWarning("Re-run with `--type secret` to protect it.");
3104
+ }
3105
+ }
3106
+ if (isFinalSecret() && !selectedEnvIsSecret) {
3107
+ printEnvUpdateWarning(
3108
+ "The previous value was readable as Config and may have been exposed. If this variable still holds the same credential, rotate it at its provider and update this variable again."
3109
+ );
3110
+ }
3111
+ const finalPublicPrefix = apiPublicPrefix ?? frameworkPublicPrefix ?? customSveltePublicPrefix;
3112
+ if (isFinalSecret() && finalPublicPrefix !== null && finalPublicPrefix !== void 0) {
3113
+ printEnvUpdateWarning(
3114
+ finalPublicPrefix === "" ? "This SvelteKit project exposes every Environment Variable to the browser; the Secret type does not prevent that. Change the configured public prefix to keep values private." : `\`${finalPublicPrefix}\` exposes this variable to the browser; the Secret type does not prevent that. Rename the variable without the public prefix to keep it private.`
3115
+ );
3116
+ }
3117
+ if (isFinalSecret() && disjunctiveProductionSecretPolicyOn && targets.includes("production") && allTargets.some((target) => target !== "production")) {
3118
+ const message = "Your team requires Production and non-Production Secrets to be stored separately with different values. Create separate variables with different values before converting this one.";
2517
3119
  if (client.nonInteractive) {
2518
- outputActionRequired(
3120
+ outputAgentError(
2519
3121
  client,
2520
3122
  {
2521
- status: "action_required",
2522
- reason: "confirmation_required",
2523
- message: `Updating Environment Variable ${envName}. Use --yes to confirm.`,
2524
- next: [{ command: buildCommandWithYes(client.argv) }]
3123
+ status: "error",
3124
+ reason: "production_secret_must_be_separate",
3125
+ message
2525
3126
  },
2526
3127
  1
2527
3128
  );
2528
3129
  }
2529
- const currentTargets = formatEnvironments(
2530
- link,
2531
- selectedEnv,
2532
- customEnvironments
2533
- );
2534
- const confirmed = await client.input.confirm(
2535
- `Updating Environment Variable ${param(envName)} in ${currentTargets} in Project ${import_chalk4.default.bold(project.name)}. Are you sure?`,
2536
- false
2537
- );
2538
- if (!confirmed) {
2539
- output_manager_default.log("Canceled");
2540
- return 0;
2541
- }
2542
- }
2543
- const type = opts["--sensitive"] ? "sensitive" : selectedEnv.type;
2544
- const explicitVisibility = typeof opts["--visibility"] === "string" ? opts["--visibility"] : void 0;
2545
- if (explicitVisibility === "config" && opts["--sensitive"]) {
2546
- output_manager_default.error(
2547
- "`--visibility config` cannot be used with `--sensitive`. Pick one."
2548
- );
3130
+ output_manager_default.fatal(message);
2549
3131
  return 1;
2550
3132
  }
2551
- const targets = Array.isArray(selectedEnv.target) ? selectedEnv.target : [selectedEnv.target].filter(
2552
- (r) => Boolean(r)
2553
- );
2554
- const allTargets = [...targets, ...selectedEnv.customEnvironmentIds || []];
2555
- const { visibility, error: visibilityError } = resolveEnvVarVisibility({
2556
- explicitVisibility,
3133
+ const preserveExistingVisibility = explicitType === void 0 && selectedEnv.visibility === "secret" && type !== "sensitive";
3134
+ const { visibility: resolvedVisibility, error: visibilityError } = resolveEnvVarVisibility({
3135
+ explicitVisibility: typeOption.source ? explicitType : void 0,
3136
+ explicitOptionSource: typeOption.source,
2557
3137
  type,
2558
3138
  key: envName,
2559
3139
  envTargets: allTargets,
2560
- teamSensitivePolicyOn
3140
+ teamSensitivePolicyOn,
3141
+ context: "update"
2561
3142
  });
3143
+ const visibility = preserveExistingVisibility ? void 0 : resolvedVisibility;
3144
+ const displayVisibility = preserveExistingVisibility ? selectedEnv.visibility : visibility;
2562
3145
  if (visibilityError) {
2563
3146
  if (client.nonInteractive) {
2564
3147
  outputAgentError(
2565
3148
  client,
2566
3149
  {
2567
3150
  status: "error",
2568
- reason: "invalid_visibility",
3151
+ reason: "invalid_type",
2569
3152
  message: visibilityError
2570
3153
  },
2571
3154
  1
2572
3155
  );
2573
3156
  }
2574
- output_manager_default.error(visibilityError);
3157
+ output_manager_default.fatal(visibilityError);
2575
3158
  return 1;
2576
3159
  }
2577
- const updateStamp = stamp_default();
3160
+ if (!opts["--yes"]) {
3161
+ const visibilityLabel2 = formatVisibilityLabel(displayVisibility, type);
3162
+ if (client.nonInteractive) {
3163
+ outputActionRequired(
3164
+ client,
3165
+ {
3166
+ status: "action_required",
3167
+ reason: "confirmation_required",
3168
+ message: `Update ${envName} as ${visibilityLabel2} in ${formatEnvironments(
3169
+ link,
3170
+ selectedEnv,
3171
+ customEnvironments
3172
+ )}? Use --yes to confirm.`,
3173
+ next: [
3174
+ {
3175
+ command: buildCommandWithYes(redactEnvValueArgs(client.argv))
3176
+ }
3177
+ ]
3178
+ },
3179
+ 1
3180
+ );
3181
+ }
3182
+ output_manager_default.print("\n");
3183
+ printAlignedLabel("Project", `${link.org.slug}/${project.name}`);
3184
+ printAlignedLabel(
3185
+ "Environments",
3186
+ formatEnvironments(link, selectedEnv, customEnvironments)
3187
+ );
3188
+ if (visibilityLabel2) {
3189
+ printAlignedLabel("Type", visibilityLabel2);
3190
+ }
3191
+ const confirmed = await client.input.confirm(
3192
+ "Update this Environment Variable?",
3193
+ false
3194
+ );
3195
+ if (!confirmed) {
3196
+ output_manager_default.log("Canceled");
3197
+ return 0;
3198
+ }
3199
+ }
2578
3200
  try {
2579
3201
  output_manager_default.spinner("Updating");
2580
- const keyToUpdate = type === "sensitive" ? void 0 : envName;
3202
+ const keyToUpdate = isFinalSecret() ? void 0 : envName;
2581
3203
  await updateEnvRecord(
2582
3204
  client,
2583
3205
  project.id,
@@ -2591,13 +3213,18 @@ async function update(client, argv) {
2591
3213
  );
2592
3214
  } catch (err) {
2593
3215
  if (client.nonInteractive && isAPIError(err)) {
2594
- const reason = err.slug || (err.serverMessage?.toLowerCase().includes("branch") ? "branch_not_found" : "api_error");
3216
+ const productionSecretPolicyError = getProductionSecretPolicyErrorKind(
3217
+ err.serverMessage
3218
+ );
3219
+ const reason = productionSecretPolicyError ? productionSecretPolicyError === "different-values" ? "production_secret_requires_different_value" : "production_secret_must_be_separate" : err.slug || (err.serverMessage?.toLowerCase().includes("branch") ? "branch_not_found" : "api_error");
2595
3220
  outputAgentError(
2596
3221
  client,
2597
3222
  {
2598
3223
  status: "error",
2599
3224
  reason,
2600
- message: err.serverMessage
3225
+ message: productionSecretPolicyError ? `${err.serverMessage} ${getProductionSecretPolicyRecovery(
3226
+ productionSecretPolicyError
3227
+ )}` : err.serverMessage
2601
3228
  },
2602
3229
  1
2603
3230
  );
@@ -2608,18 +3235,16 @@ async function update(client, argv) {
2608
3235
  }
2609
3236
  throw err;
2610
3237
  }
2611
- output_manager_default.print(
2612
- `${prependEmoji(
2613
- `Updated Environment Variable ${import_chalk4.default.bold(envName)} in Project ${import_chalk4.default.bold(
2614
- project.name
2615
- )} ${import_chalk4.default.gray(updateStamp())}`,
2616
- emoji("success")
2617
- )}
2618
- `
3238
+ output_manager_default.print("\n");
3239
+ printAlignedLabel("Updated", envName, { gutter: "\u2713" });
3240
+ printAlignedLabel("Project", `${link.org.slug}/${project.name}`);
3241
+ printAlignedLabel(
3242
+ "Environments",
3243
+ formatEnvironments(link, selectedEnv, customEnvironments)
2619
3244
  );
2620
- const visibilityLabel = formatVisibilityLabel(visibility, type);
3245
+ const visibilityLabel = formatVisibilityLabel(displayVisibility, type);
2621
3246
  if (visibilityLabel) {
2622
- printAlignedLabel("Visibility", visibilityLabel);
3247
+ printAlignedLabel("Type", visibilityLabel);
2623
3248
  }
2624
3249
  return 0;
2625
3250
  }