@wraps.dev/cli 2.3.4 → 2.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -1023,7 +1023,7 @@ To remove: wraps destroy --stack ${stackName}`,
|
|
|
1023
1023
|
"The Pulumi stack is locked from a previous run",
|
|
1024
1024
|
"STACK_LOCKED",
|
|
1025
1025
|
"This happens when a previous deployment was interrupted.\n\nTo unlock, run:\n rm -rf ~/.wraps/pulumi/.pulumi/locks\n\nThen try your command again.",
|
|
1026
|
-
"https://wraps.dev/docs/guides/aws-setup/troubleshooting"
|
|
1026
|
+
"https://wraps.dev/docs/guides/aws-setup/permissions/troubleshooting"
|
|
1027
1027
|
),
|
|
1028
1028
|
// SMS-specific errors
|
|
1029
1029
|
smsNotConfigured: () => new WrapsError(
|
|
@@ -1080,7 +1080,7 @@ To remove: wraps destroy --stack ${stackName}`,
|
|
|
1080
1080
|
`AWS SSO session has expired${profile ? ` for profile "${profile}"` : ""}`,
|
|
1081
1081
|
"SSO_SESSION_EXPIRED",
|
|
1082
1082
|
profile ? `Run: aws sso login --profile ${profile}` : "Run: aws sso login",
|
|
1083
|
-
"https://wraps.dev/docs/guides/aws-setup"
|
|
1083
|
+
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
1084
1084
|
),
|
|
1085
1085
|
profileNotFound: (profile, availableProfiles) => new WrapsError(
|
|
1086
1086
|
`AWS profile "${profile}" not found`,
|
|
@@ -1092,25 +1092,25 @@ Set a valid profile:
|
|
|
1092
1092
|
|
|
1093
1093
|
Or configure a new profile:
|
|
1094
1094
|
aws configure --profile ${profile}` : "No AWS profiles configured.\n\nConfigure AWS credentials:\n aws configure\n\nOr set up SSO:\n aws configure sso",
|
|
1095
|
-
"https://wraps.dev/docs/guides/aws-setup"
|
|
1095
|
+
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
1096
1096
|
),
|
|
1097
1097
|
credentialsFileMissing: () => new WrapsError(
|
|
1098
1098
|
"AWS credentials file not found",
|
|
1099
1099
|
"CREDENTIALS_FILE_MISSING",
|
|
1100
1100
|
"Configure AWS credentials:\n aws configure\n\nOr set environment variables:\n export AWS_ACCESS_KEY_ID=<your-key>\n export AWS_SECRET_ACCESS_KEY=<your-secret>",
|
|
1101
|
-
"https://wraps.dev/docs/guides/aws-setup"
|
|
1101
|
+
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
1102
1102
|
),
|
|
1103
1103
|
accessKeyInvalid: () => new WrapsError(
|
|
1104
1104
|
"AWS access key is invalid or has been deactivated",
|
|
1105
1105
|
"ACCESS_KEY_INVALID",
|
|
1106
1106
|
"Check your AWS access keys in the IAM console.\n\nReconfigure credentials:\n aws configure\n\nOr generate new access keys in AWS IAM.",
|
|
1107
|
-
"https://wraps.dev/docs/guides/aws-setup"
|
|
1107
|
+
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
1108
1108
|
),
|
|
1109
1109
|
sessionTokenExpired: () => new WrapsError(
|
|
1110
1110
|
"AWS session token has expired",
|
|
1111
1111
|
"SESSION_TOKEN_EXPIRED",
|
|
1112
1112
|
"Your temporary credentials have expired.\n\nFor SSO users:\n aws sso login\n\nFor assumed roles:\n Re-run your assume-role command",
|
|
1113
|
-
"https://wraps.dev/docs/guides/aws-setup"
|
|
1113
|
+
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
1114
1114
|
),
|
|
1115
1115
|
// IAM permission errors
|
|
1116
1116
|
iamPermissionDenied: (action, resource, suggestion) => new WrapsError(
|
|
@@ -9210,7 +9210,7 @@ function createNodeDnsProvider(options = {}) {
|
|
|
9210
9210
|
if (options.servers && options.servers.length > 0) {
|
|
9211
9211
|
resolver.setServers(options.servers);
|
|
9212
9212
|
}
|
|
9213
|
-
async function
|
|
9213
|
+
async function withTimeout2(promise, operation) {
|
|
9214
9214
|
const timeoutPromise = new Promise((_, reject) => {
|
|
9215
9215
|
setTimeout(() => {
|
|
9216
9216
|
reject(new Error(`DNS ${operation} timed out after ${timeout}ms`));
|
|
@@ -9220,7 +9220,7 @@ function createNodeDnsProvider(options = {}) {
|
|
|
9220
9220
|
}
|
|
9221
9221
|
async function resolveTxt(domain) {
|
|
9222
9222
|
try {
|
|
9223
|
-
const records = await
|
|
9223
|
+
const records = await withTimeout2(
|
|
9224
9224
|
dns.resolveTxt(domain),
|
|
9225
9225
|
`TXT lookup for ${domain}`
|
|
9226
9226
|
);
|
|
@@ -9234,7 +9234,7 @@ function createNodeDnsProvider(options = {}) {
|
|
|
9234
9234
|
}
|
|
9235
9235
|
async function resolveMx(domain) {
|
|
9236
9236
|
try {
|
|
9237
|
-
const records = await
|
|
9237
|
+
const records = await withTimeout2(
|
|
9238
9238
|
dns.resolveMx(domain),
|
|
9239
9239
|
`MX lookup for ${domain}`
|
|
9240
9240
|
);
|
|
@@ -9248,7 +9248,7 @@ function createNodeDnsProvider(options = {}) {
|
|
|
9248
9248
|
}
|
|
9249
9249
|
async function resolveA(domain) {
|
|
9250
9250
|
try {
|
|
9251
|
-
const records = await
|
|
9251
|
+
const records = await withTimeout2(
|
|
9252
9252
|
dns.resolve4(domain),
|
|
9253
9253
|
`A lookup for ${domain}`
|
|
9254
9254
|
);
|
|
@@ -9262,7 +9262,7 @@ function createNodeDnsProvider(options = {}) {
|
|
|
9262
9262
|
}
|
|
9263
9263
|
async function resolveAaaa(domain) {
|
|
9264
9264
|
try {
|
|
9265
|
-
const records = await
|
|
9265
|
+
const records = await withTimeout2(
|
|
9266
9266
|
dns.resolve6(domain),
|
|
9267
9267
|
`AAAA lookup for ${domain}`
|
|
9268
9268
|
);
|
|
@@ -9276,7 +9276,7 @@ function createNodeDnsProvider(options = {}) {
|
|
|
9276
9276
|
}
|
|
9277
9277
|
async function resolvePtr(ip) {
|
|
9278
9278
|
try {
|
|
9279
|
-
const records = await
|
|
9279
|
+
const records = await withTimeout2(
|
|
9280
9280
|
dns.reverse(ip),
|
|
9281
9281
|
`PTR lookup for ${ip}`
|
|
9282
9282
|
);
|
|
@@ -9290,7 +9290,7 @@ function createNodeDnsProvider(options = {}) {
|
|
|
9290
9290
|
}
|
|
9291
9291
|
async function resolveCaa(domain) {
|
|
9292
9292
|
try {
|
|
9293
|
-
const records = await
|
|
9293
|
+
const records = await withTimeout2(
|
|
9294
9294
|
dns.resolveCaa(domain),
|
|
9295
9295
|
`CAA lookup for ${domain}`
|
|
9296
9296
|
);
|
|
@@ -9322,7 +9322,7 @@ function createNodeDnsProvider(options = {}) {
|
|
|
9322
9322
|
}
|
|
9323
9323
|
async function resolveCname(domain) {
|
|
9324
9324
|
try {
|
|
9325
|
-
const records = await
|
|
9325
|
+
const records = await withTimeout2(
|
|
9326
9326
|
dns.resolveCname(domain),
|
|
9327
9327
|
`CNAME lookup for ${domain}`
|
|
9328
9328
|
);
|
|
@@ -11885,14 +11885,13 @@ async function tryGetSesDkimTokens(domain) {
|
|
|
11885
11885
|
// src/commands/email/config.ts
|
|
11886
11886
|
init_esm_shims();
|
|
11887
11887
|
import * as clack13 from "@clack/prompts";
|
|
11888
|
-
import * as
|
|
11888
|
+
import * as pulumi12 from "@pulumi/pulumi";
|
|
11889
11889
|
import pc14 from "picocolors";
|
|
11890
11890
|
|
|
11891
11891
|
// src/infrastructure/email-stack.ts
|
|
11892
11892
|
init_esm_shims();
|
|
11893
11893
|
init_dist();
|
|
11894
11894
|
import * as aws14 from "@pulumi/aws";
|
|
11895
|
-
import * as pulumi12 from "@pulumi/pulumi";
|
|
11896
11895
|
|
|
11897
11896
|
// src/infrastructure/resources/alerting.ts
|
|
11898
11897
|
init_esm_shims();
|
|
@@ -13066,7 +13065,7 @@ ${pc14.bold("Current Configuration:")}
|
|
|
13066
13065
|
"Generating update preview",
|
|
13067
13066
|
async () => {
|
|
13068
13067
|
await ensurePulumiWorkDir();
|
|
13069
|
-
const stack = await
|
|
13068
|
+
const stack = await pulumi12.automation.LocalWorkspace.createOrSelectStack(
|
|
13070
13069
|
{
|
|
13071
13070
|
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
13072
13071
|
projectName: "wraps-email",
|
|
@@ -13130,7 +13129,7 @@ ${pc14.bold("Current Configuration:")}
|
|
|
13130
13129
|
"Updating Wraps infrastructure (this may take 2-3 minutes)",
|
|
13131
13130
|
async () => {
|
|
13132
13131
|
await ensurePulumiWorkDir();
|
|
13133
|
-
const stack = await
|
|
13132
|
+
const stack = await pulumi12.automation.LocalWorkspace.createOrSelectStack(
|
|
13134
13133
|
{
|
|
13135
13134
|
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
13136
13135
|
projectName: "wraps-email",
|
|
@@ -13227,7 +13226,7 @@ ${pc14.green("\u2713")} ${pc14.bold("Update complete!")}
|
|
|
13227
13226
|
// src/commands/email/connect.ts
|
|
13228
13227
|
init_esm_shims();
|
|
13229
13228
|
import * as clack14 from "@clack/prompts";
|
|
13230
|
-
import * as
|
|
13229
|
+
import * as pulumi13 from "@pulumi/pulumi";
|
|
13231
13230
|
import pc15 from "picocolors";
|
|
13232
13231
|
init_events();
|
|
13233
13232
|
init_presets();
|
|
@@ -13567,7 +13566,7 @@ async function connect2(options) {
|
|
|
13567
13566
|
"Generating infrastructure preview",
|
|
13568
13567
|
async () => {
|
|
13569
13568
|
await ensurePulumiWorkDir();
|
|
13570
|
-
const stack = await
|
|
13569
|
+
const stack = await pulumi13.automation.LocalWorkspace.createOrSelectStack(
|
|
13571
13570
|
{
|
|
13572
13571
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
13573
13572
|
projectName: "wraps-email",
|
|
@@ -13631,7 +13630,7 @@ async function connect2(options) {
|
|
|
13631
13630
|
"Deploying Wraps infrastructure (this may take 2-3 minutes)",
|
|
13632
13631
|
async () => {
|
|
13633
13632
|
await ensurePulumiWorkDir();
|
|
13634
|
-
const stack = await
|
|
13633
|
+
const stack = await pulumi13.automation.LocalWorkspace.createOrSelectStack(
|
|
13635
13634
|
{
|
|
13636
13635
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
13637
13636
|
projectName: "wraps-email",
|
|
@@ -13791,8 +13790,42 @@ init_errors();
|
|
|
13791
13790
|
init_fs();
|
|
13792
13791
|
init_metadata();
|
|
13793
13792
|
import * as clack15 from "@clack/prompts";
|
|
13794
|
-
import * as
|
|
13793
|
+
import * as pulumi14 from "@pulumi/pulumi";
|
|
13795
13794
|
import pc16 from "picocolors";
|
|
13795
|
+
|
|
13796
|
+
// src/utils/shared/timeout.ts
|
|
13797
|
+
init_esm_shims();
|
|
13798
|
+
init_errors();
|
|
13799
|
+
var DEFAULT_PULUMI_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
13800
|
+
var TimeoutError = class extends WrapsError {
|
|
13801
|
+
constructor(operation, timeoutMs) {
|
|
13802
|
+
const timeoutMinutes = Math.round(timeoutMs / 6e4);
|
|
13803
|
+
super(
|
|
13804
|
+
`Operation "${operation}" timed out after ${timeoutMinutes} minute${timeoutMinutes === 1 ? "" : "s"}`,
|
|
13805
|
+
"OPERATION_TIMEOUT",
|
|
13806
|
+
"The operation took longer than expected. This can happen due to:\n - Slow network connection\n - AWS API throttling\n - Large number of resources\n\nYou can try:\n 1. Check AWS CloudFormation/Pulumi state for partial deployments\n 2. Run the command again (it will resume where it left off)\n 3. Check your AWS console for any stuck resources",
|
|
13807
|
+
"https://wraps.dev/docs/guides/aws-setup/troubleshooting"
|
|
13808
|
+
);
|
|
13809
|
+
}
|
|
13810
|
+
};
|
|
13811
|
+
async function withTimeout(promise, timeoutMs, operation) {
|
|
13812
|
+
let timeoutId;
|
|
13813
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
13814
|
+
timeoutId = setTimeout(() => {
|
|
13815
|
+
reject(new TimeoutError(operation, timeoutMs));
|
|
13816
|
+
}, timeoutMs);
|
|
13817
|
+
});
|
|
13818
|
+
try {
|
|
13819
|
+
const result = await Promise.race([promise, timeoutPromise]);
|
|
13820
|
+
return result;
|
|
13821
|
+
} finally {
|
|
13822
|
+
if (timeoutId) {
|
|
13823
|
+
clearTimeout(timeoutId);
|
|
13824
|
+
}
|
|
13825
|
+
}
|
|
13826
|
+
}
|
|
13827
|
+
|
|
13828
|
+
// src/commands/email/destroy.ts
|
|
13796
13829
|
async function getEmailIdentityInfo(domain, region) {
|
|
13797
13830
|
try {
|
|
13798
13831
|
const { SESv2Client: SESv2Client6, GetEmailIdentityCommand: GetEmailIdentityCommand5 } = await import("@aws-sdk/client-sesv2");
|
|
@@ -13893,10 +13926,10 @@ async function emailDestroy(options) {
|
|
|
13893
13926
|
"Generating destruction preview",
|
|
13894
13927
|
async () => {
|
|
13895
13928
|
await ensurePulumiWorkDir();
|
|
13896
|
-
const stackName = storedStackName || `wraps
|
|
13929
|
+
const stackName = storedStackName || `wraps-${identity.accountId}-${region}`;
|
|
13897
13930
|
let stack;
|
|
13898
13931
|
try {
|
|
13899
|
-
stack = await
|
|
13932
|
+
stack = await pulumi14.automation.LocalWorkspace.selectStack({
|
|
13900
13933
|
stackName,
|
|
13901
13934
|
workDir: getPulumiWorkDir()
|
|
13902
13935
|
});
|
|
@@ -13964,18 +13997,23 @@ async function emailDestroy(options) {
|
|
|
13964
13997
|
"Destroying email infrastructure (this may take 2-3 minutes)",
|
|
13965
13998
|
async () => {
|
|
13966
13999
|
await ensurePulumiWorkDir();
|
|
13967
|
-
const stackName = storedStackName || `wraps
|
|
14000
|
+
const stackName = storedStackName || `wraps-${identity.accountId}-${region}`;
|
|
13968
14001
|
let stack;
|
|
13969
14002
|
try {
|
|
13970
|
-
stack = await
|
|
14003
|
+
stack = await pulumi14.automation.LocalWorkspace.selectStack({
|
|
13971
14004
|
stackName,
|
|
13972
14005
|
workDir: getPulumiWorkDir()
|
|
13973
14006
|
});
|
|
13974
14007
|
} catch (_error) {
|
|
13975
14008
|
throw new Error("No email infrastructure found to destroy");
|
|
13976
14009
|
}
|
|
13977
|
-
await
|
|
13978
|
-
|
|
14010
|
+
await withTimeout(
|
|
14011
|
+
stack.destroy({ onOutput: () => {
|
|
14012
|
+
} }),
|
|
14013
|
+
// Suppress Pulumi output
|
|
14014
|
+
DEFAULT_PULUMI_TIMEOUT_MS,
|
|
14015
|
+
"Pulumi destroy"
|
|
14016
|
+
);
|
|
13979
14017
|
await stack.workspace.removeStack(stackName);
|
|
13980
14018
|
}
|
|
13981
14019
|
);
|
|
@@ -14479,7 +14517,7 @@ async function removeDomain(options) {
|
|
|
14479
14517
|
// src/commands/email/init.ts
|
|
14480
14518
|
init_esm_shims();
|
|
14481
14519
|
import * as clack17 from "@clack/prompts";
|
|
14482
|
-
import * as
|
|
14520
|
+
import * as pulumi15 from "@pulumi/pulumi";
|
|
14483
14521
|
import pc18 from "picocolors";
|
|
14484
14522
|
init_events();
|
|
14485
14523
|
init_costs();
|
|
@@ -14487,6 +14525,141 @@ init_presets();
|
|
|
14487
14525
|
init_aws();
|
|
14488
14526
|
init_errors();
|
|
14489
14527
|
init_fs();
|
|
14528
|
+
|
|
14529
|
+
// src/utils/shared/iam-check.ts
|
|
14530
|
+
init_esm_shims();
|
|
14531
|
+
var CORE_IAM_ACTIONS = [
|
|
14532
|
+
"iam:CreateRole",
|
|
14533
|
+
"iam:GetRole",
|
|
14534
|
+
"iam:PutRolePolicy",
|
|
14535
|
+
"iam:DeleteRole",
|
|
14536
|
+
"iam:DeleteRolePolicy",
|
|
14537
|
+
"iam:TagRole",
|
|
14538
|
+
"ses:CreateConfigurationSet",
|
|
14539
|
+
"ses:DeleteConfigurationSet",
|
|
14540
|
+
"ses:CreateEmailIdentity",
|
|
14541
|
+
"ses:DeleteEmailIdentity",
|
|
14542
|
+
"ses:GetEmailIdentity",
|
|
14543
|
+
"ses:PutEmailIdentityDkimAttributes"
|
|
14544
|
+
];
|
|
14545
|
+
var EVENT_TRACKING_ACTIONS = [
|
|
14546
|
+
"events:CreateEventBus",
|
|
14547
|
+
"events:DeleteEventBus",
|
|
14548
|
+
"events:PutRule",
|
|
14549
|
+
"events:DeleteRule",
|
|
14550
|
+
"events:PutTargets",
|
|
14551
|
+
"events:RemoveTargets",
|
|
14552
|
+
"sqs:CreateQueue",
|
|
14553
|
+
"sqs:DeleteQueue",
|
|
14554
|
+
"sqs:SetQueueAttributes",
|
|
14555
|
+
"sqs:GetQueueAttributes"
|
|
14556
|
+
];
|
|
14557
|
+
var DYNAMODB_ACTIONS = [
|
|
14558
|
+
"dynamodb:CreateTable",
|
|
14559
|
+
"dynamodb:DeleteTable",
|
|
14560
|
+
"dynamodb:DescribeTable",
|
|
14561
|
+
"dynamodb:UpdateTable",
|
|
14562
|
+
"dynamodb:TagResource"
|
|
14563
|
+
];
|
|
14564
|
+
var LAMBDA_ACTIONS = [
|
|
14565
|
+
"lambda:CreateFunction",
|
|
14566
|
+
"lambda:DeleteFunction",
|
|
14567
|
+
"lambda:UpdateFunctionCode",
|
|
14568
|
+
"lambda:UpdateFunctionConfiguration",
|
|
14569
|
+
"lambda:GetFunction",
|
|
14570
|
+
"lambda:AddPermission",
|
|
14571
|
+
"lambda:RemovePermission",
|
|
14572
|
+
"lambda:CreateEventSourceMapping",
|
|
14573
|
+
"lambda:DeleteEventSourceMapping"
|
|
14574
|
+
];
|
|
14575
|
+
function getRequiredActions(config2) {
|
|
14576
|
+
const actions = [...CORE_IAM_ACTIONS];
|
|
14577
|
+
if (config2.eventTracking?.enabled) {
|
|
14578
|
+
actions.push(...EVENT_TRACKING_ACTIONS);
|
|
14579
|
+
}
|
|
14580
|
+
if (config2.eventTracking?.dynamoDBHistory) {
|
|
14581
|
+
actions.push(...DYNAMODB_ACTIONS);
|
|
14582
|
+
actions.push(...LAMBDA_ACTIONS);
|
|
14583
|
+
}
|
|
14584
|
+
return [...new Set(actions)];
|
|
14585
|
+
}
|
|
14586
|
+
async function checkIAMPermissions(userArn, actions, region) {
|
|
14587
|
+
try {
|
|
14588
|
+
const { IAMClient: IAMClient3, SimulatePrincipalPolicyCommand } = await import("@aws-sdk/client-iam");
|
|
14589
|
+
const client = new IAMClient3({ region });
|
|
14590
|
+
const batchSize = 100;
|
|
14591
|
+
const batches = [];
|
|
14592
|
+
for (let i = 0; i < actions.length; i += batchSize) {
|
|
14593
|
+
batches.push(actions.slice(i, i + batchSize));
|
|
14594
|
+
}
|
|
14595
|
+
const allowedActions = [];
|
|
14596
|
+
const deniedActions = [];
|
|
14597
|
+
for (const batch of batches) {
|
|
14598
|
+
const command = new SimulatePrincipalPolicyCommand({
|
|
14599
|
+
PolicySourceArn: userArn,
|
|
14600
|
+
ActionNames: batch,
|
|
14601
|
+
// Use a wildcard resource since we're checking general permissions
|
|
14602
|
+
// More specific resource-level checks could be added later
|
|
14603
|
+
ResourceArns: ["*"]
|
|
14604
|
+
});
|
|
14605
|
+
const response = await client.send(command);
|
|
14606
|
+
for (const result of response.EvaluationResults || []) {
|
|
14607
|
+
const actionName = result.EvalActionName;
|
|
14608
|
+
const decision = result.EvalDecision;
|
|
14609
|
+
if (decision === "allowed") {
|
|
14610
|
+
if (actionName) allowedActions.push(actionName);
|
|
14611
|
+
} else if (actionName) deniedActions.push(actionName);
|
|
14612
|
+
}
|
|
14613
|
+
}
|
|
14614
|
+
return {
|
|
14615
|
+
success: deniedActions.length === 0,
|
|
14616
|
+
deniedActions,
|
|
14617
|
+
allowedActions,
|
|
14618
|
+
skipped: false
|
|
14619
|
+
};
|
|
14620
|
+
} catch (error) {
|
|
14621
|
+
if (error instanceof Error && (error.name === "AccessDenied" || error.name === "AccessDeniedException" || error.message?.includes("AccessDenied") || error.message?.includes("iam:SimulatePrincipalPolicy"))) {
|
|
14622
|
+
return {
|
|
14623
|
+
success: true,
|
|
14624
|
+
// Don't block on this
|
|
14625
|
+
deniedActions: [],
|
|
14626
|
+
allowedActions: [],
|
|
14627
|
+
skipped: true,
|
|
14628
|
+
skipReason: "Unable to verify permissions (iam:SimulatePrincipalPolicy not allowed). Deployment will proceed, but may fail if permissions are missing."
|
|
14629
|
+
};
|
|
14630
|
+
}
|
|
14631
|
+
return {
|
|
14632
|
+
success: true,
|
|
14633
|
+
deniedActions: [],
|
|
14634
|
+
allowedActions: [],
|
|
14635
|
+
skipped: true,
|
|
14636
|
+
skipReason: `Permission check failed: ${error instanceof Error ? error.message : "Unknown error"}. Proceeding with deployment.`
|
|
14637
|
+
};
|
|
14638
|
+
}
|
|
14639
|
+
}
|
|
14640
|
+
function formatDeniedActions(actions) {
|
|
14641
|
+
if (actions.length === 0) return "";
|
|
14642
|
+
const byService = {};
|
|
14643
|
+
for (const action of actions) {
|
|
14644
|
+
const [service, actionName] = action.split(":");
|
|
14645
|
+
if (!byService[service]) {
|
|
14646
|
+
byService[service] = [];
|
|
14647
|
+
}
|
|
14648
|
+
byService[service].push(actionName);
|
|
14649
|
+
}
|
|
14650
|
+
const lines = ["Missing permissions:"];
|
|
14651
|
+
for (const [service, serviceActions] of Object.entries(byService)) {
|
|
14652
|
+
lines.push(` ${service.toUpperCase()}:`);
|
|
14653
|
+
for (const action of serviceActions) {
|
|
14654
|
+
lines.push(` - ${service}:${action}`);
|
|
14655
|
+
}
|
|
14656
|
+
}
|
|
14657
|
+
lines.push("");
|
|
14658
|
+
lines.push("Run `wraps permissions --json` to see the full policy document.");
|
|
14659
|
+
return lines.join("\n");
|
|
14660
|
+
}
|
|
14661
|
+
|
|
14662
|
+
// src/commands/email/init.ts
|
|
14490
14663
|
init_metadata();
|
|
14491
14664
|
init_prompts();
|
|
14492
14665
|
async function init2(options) {
|
|
@@ -14594,6 +14767,23 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
|
|
|
14594
14767
|
process.exit(0);
|
|
14595
14768
|
}
|
|
14596
14769
|
}
|
|
14770
|
+
if (!options.preview) {
|
|
14771
|
+
const iamCheckResult = await progress.execute(
|
|
14772
|
+
"Checking IAM permissions",
|
|
14773
|
+
async () => {
|
|
14774
|
+
const requiredActions = getRequiredActions(emailConfig);
|
|
14775
|
+
return checkIAMPermissions(identity.arn, requiredActions, region);
|
|
14776
|
+
}
|
|
14777
|
+
);
|
|
14778
|
+
if (iamCheckResult.skipped && iamCheckResult.skipReason) {
|
|
14779
|
+
progress.info(pc18.dim(iamCheckResult.skipReason));
|
|
14780
|
+
} else if (!iamCheckResult.success) {
|
|
14781
|
+
clack17.log.warn(
|
|
14782
|
+
pc18.yellow("Some IAM permissions may be missing. Deployment may fail.")
|
|
14783
|
+
);
|
|
14784
|
+
clack17.log.info(formatDeniedActions(iamCheckResult.deniedActions));
|
|
14785
|
+
}
|
|
14786
|
+
}
|
|
14597
14787
|
const stackConfig = {
|
|
14598
14788
|
provider,
|
|
14599
14789
|
region,
|
|
@@ -14606,7 +14796,7 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
|
|
|
14606
14796
|
"Generating infrastructure preview",
|
|
14607
14797
|
async () => {
|
|
14608
14798
|
await ensurePulumiWorkDir();
|
|
14609
|
-
const stack = await
|
|
14799
|
+
const stack = await pulumi15.automation.LocalWorkspace.createOrSelectStack(
|
|
14610
14800
|
{
|
|
14611
14801
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
14612
14802
|
projectName: "wraps-email",
|
|
@@ -14675,7 +14865,7 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
|
|
|
14675
14865
|
"Deploying infrastructure (this may take 2-3 minutes)",
|
|
14676
14866
|
async () => {
|
|
14677
14867
|
await ensurePulumiWorkDir();
|
|
14678
|
-
const stack = await
|
|
14868
|
+
const stack = await pulumi15.automation.LocalWorkspace.createOrSelectStack(
|
|
14679
14869
|
{
|
|
14680
14870
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
14681
14871
|
projectName: "wraps-email",
|
|
@@ -14712,8 +14902,13 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
|
|
|
14712
14902
|
`wraps-${identity.accountId}-${region}`
|
|
14713
14903
|
);
|
|
14714
14904
|
await stack.setConfig("aws:region", { value: region });
|
|
14715
|
-
const upResult = await
|
|
14716
|
-
|
|
14905
|
+
const upResult = await withTimeout(
|
|
14906
|
+
stack.up({ onOutput: () => {
|
|
14907
|
+
} }),
|
|
14908
|
+
// Suppress Pulumi output
|
|
14909
|
+
DEFAULT_PULUMI_TIMEOUT_MS,
|
|
14910
|
+
"Pulumi deployment"
|
|
14911
|
+
);
|
|
14717
14912
|
const pulumiOutputs = upResult.outputs;
|
|
14718
14913
|
return {
|
|
14719
14914
|
roleArn: pulumiOutputs.roleArn?.value,
|
|
@@ -14891,7 +15086,7 @@ init_aws();
|
|
|
14891
15086
|
init_fs();
|
|
14892
15087
|
init_metadata();
|
|
14893
15088
|
import * as clack18 from "@clack/prompts";
|
|
14894
|
-
import * as
|
|
15089
|
+
import * as pulumi16 from "@pulumi/pulumi";
|
|
14895
15090
|
import pc19 from "picocolors";
|
|
14896
15091
|
async function restore(options) {
|
|
14897
15092
|
const startTime = Date.now();
|
|
@@ -14963,7 +15158,7 @@ ${pc19.bold("The following Wraps resources will be removed:")}
|
|
|
14963
15158
|
const previewResult = await progress.execute(
|
|
14964
15159
|
"Generating removal preview",
|
|
14965
15160
|
async () => {
|
|
14966
|
-
const stack = await
|
|
15161
|
+
const stack = await pulumi16.automation.LocalWorkspace.selectStack(
|
|
14967
15162
|
{
|
|
14968
15163
|
stackName: pulumiStackName,
|
|
14969
15164
|
projectName: "wraps-email",
|
|
@@ -15015,7 +15210,7 @@ ${pc19.bold("The following Wraps resources will be removed:")}
|
|
|
15015
15210
|
if (!metadata.services.email?.pulumiStackName) {
|
|
15016
15211
|
throw new Error("No Pulumi stack name found in metadata");
|
|
15017
15212
|
}
|
|
15018
|
-
const stack = await
|
|
15213
|
+
const stack = await pulumi16.automation.LocalWorkspace.selectStack(
|
|
15019
15214
|
{
|
|
15020
15215
|
stackName: metadata.services.email.pulumiStackName,
|
|
15021
15216
|
projectName: "wraps-email",
|
|
@@ -15069,7 +15264,7 @@ init_aws();
|
|
|
15069
15264
|
init_fs();
|
|
15070
15265
|
init_metadata();
|
|
15071
15266
|
import * as clack19 from "@clack/prompts";
|
|
15072
|
-
import * as
|
|
15267
|
+
import * as pulumi17 from "@pulumi/pulumi";
|
|
15073
15268
|
import pc20 from "picocolors";
|
|
15074
15269
|
async function emailStatus(options) {
|
|
15075
15270
|
const startTime = Date.now();
|
|
@@ -15105,7 +15300,7 @@ async function emailStatus(options) {
|
|
|
15105
15300
|
let stackOutputs = {};
|
|
15106
15301
|
try {
|
|
15107
15302
|
await ensurePulumiWorkDir();
|
|
15108
|
-
const stack = await
|
|
15303
|
+
const stack = await pulumi17.automation.LocalWorkspace.selectStack({
|
|
15109
15304
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
15110
15305
|
workDir: getPulumiWorkDir()
|
|
15111
15306
|
});
|
|
@@ -15182,7 +15377,7 @@ Run ${pc20.cyan("wraps email init")} to deploy email infrastructure.
|
|
|
15182
15377
|
// src/commands/email/upgrade.ts
|
|
15183
15378
|
init_esm_shims();
|
|
15184
15379
|
import * as clack20 from "@clack/prompts";
|
|
15185
|
-
import * as
|
|
15380
|
+
import * as pulumi18 from "@pulumi/pulumi";
|
|
15186
15381
|
import pc21 from "picocolors";
|
|
15187
15382
|
init_events();
|
|
15188
15383
|
init_costs();
|
|
@@ -16303,7 +16498,7 @@ ${pc21.bold("Cost Impact:")}`);
|
|
|
16303
16498
|
"Generating upgrade preview",
|
|
16304
16499
|
async () => {
|
|
16305
16500
|
await ensurePulumiWorkDir();
|
|
16306
|
-
const stack = await
|
|
16501
|
+
const stack = await pulumi18.automation.LocalWorkspace.createOrSelectStack(
|
|
16307
16502
|
{
|
|
16308
16503
|
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
16309
16504
|
projectName: "wraps-email",
|
|
@@ -16381,7 +16576,7 @@ ${pc21.bold("Cost Impact:")}`);
|
|
|
16381
16576
|
"Updating Wraps infrastructure (this may take 2-3 minutes)",
|
|
16382
16577
|
async () => {
|
|
16383
16578
|
await ensurePulumiWorkDir();
|
|
16384
|
-
const stack = await
|
|
16579
|
+
const stack = await pulumi18.automation.LocalWorkspace.createOrSelectStack(
|
|
16385
16580
|
{
|
|
16386
16581
|
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
16387
16582
|
projectName: "wraps-email",
|
|
@@ -17478,7 +17673,7 @@ function buildConsolePolicyDocument(emailConfig, smsConfig) {
|
|
|
17478
17673
|
// src/commands/shared/dashboard.ts
|
|
17479
17674
|
init_esm_shims();
|
|
17480
17675
|
import * as clack24 from "@clack/prompts";
|
|
17481
|
-
import * as
|
|
17676
|
+
import * as pulumi19 from "@pulumi/pulumi";
|
|
17482
17677
|
import getPort from "get-port";
|
|
17483
17678
|
import open from "open";
|
|
17484
17679
|
import pc26 from "picocolors";
|
|
@@ -20014,7 +20209,7 @@ async function dashboard(options) {
|
|
|
20014
20209
|
try {
|
|
20015
20210
|
await ensurePulumiWorkDir();
|
|
20016
20211
|
try {
|
|
20017
|
-
const emailStack = await
|
|
20212
|
+
const emailStack = await pulumi19.automation.LocalWorkspace.selectStack({
|
|
20018
20213
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
20019
20214
|
workDir: getPulumiWorkDir()
|
|
20020
20215
|
});
|
|
@@ -20022,7 +20217,7 @@ async function dashboard(options) {
|
|
|
20022
20217
|
} catch (_emailError) {
|
|
20023
20218
|
}
|
|
20024
20219
|
try {
|
|
20025
|
-
const smsStack = await
|
|
20220
|
+
const smsStack = await pulumi19.automation.LocalWorkspace.selectStack({
|
|
20026
20221
|
stackName: `wraps-sms-${identity.accountId}-${region}`,
|
|
20027
20222
|
workDir: getPulumiWorkDir()
|
|
20028
20223
|
});
|
|
@@ -20030,7 +20225,7 @@ async function dashboard(options) {
|
|
|
20030
20225
|
} catch (_smsError) {
|
|
20031
20226
|
}
|
|
20032
20227
|
try {
|
|
20033
|
-
const cdnStack = await
|
|
20228
|
+
const cdnStack = await pulumi19.automation.LocalWorkspace.selectStack({
|
|
20034
20229
|
stackName: `wraps-cdn-${identity.accountId}-${region}`,
|
|
20035
20230
|
workDir: getPulumiWorkDir()
|
|
20036
20231
|
});
|
|
@@ -20207,7 +20402,7 @@ init_events();
|
|
|
20207
20402
|
init_aws();
|
|
20208
20403
|
init_fs();
|
|
20209
20404
|
import * as clack26 from "@clack/prompts";
|
|
20210
|
-
import * as
|
|
20405
|
+
import * as pulumi20 from "@pulumi/pulumi";
|
|
20211
20406
|
import pc28 from "picocolors";
|
|
20212
20407
|
async function status(_options) {
|
|
20213
20408
|
const startTime = Date.now();
|
|
@@ -20223,7 +20418,7 @@ async function status(_options) {
|
|
|
20223
20418
|
const services = [];
|
|
20224
20419
|
try {
|
|
20225
20420
|
await ensurePulumiWorkDir();
|
|
20226
|
-
const emailStack = await
|
|
20421
|
+
const emailStack = await pulumi20.automation.LocalWorkspace.selectStack({
|
|
20227
20422
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
20228
20423
|
workDir: getPulumiWorkDir()
|
|
20229
20424
|
});
|
|
@@ -20242,7 +20437,7 @@ async function status(_options) {
|
|
|
20242
20437
|
services.push({ name: "Email", status: "not_deployed" });
|
|
20243
20438
|
}
|
|
20244
20439
|
try {
|
|
20245
|
-
const smsStack = await
|
|
20440
|
+
const smsStack = await pulumi20.automation.LocalWorkspace.selectStack({
|
|
20246
20441
|
stackName: `wraps-sms-${identity.accountId}-${region}`,
|
|
20247
20442
|
workDir: getPulumiWorkDir()
|
|
20248
20443
|
});
|
|
@@ -20302,13 +20497,13 @@ ${pc28.bold("Dashboard:")} ${pc28.blue("https://app.wraps.dev")}`);
|
|
|
20302
20497
|
// src/commands/sms/destroy.ts
|
|
20303
20498
|
init_esm_shims();
|
|
20304
20499
|
import * as clack27 from "@clack/prompts";
|
|
20305
|
-
import * as
|
|
20500
|
+
import * as pulumi22 from "@pulumi/pulumi";
|
|
20306
20501
|
import pc29 from "picocolors";
|
|
20307
20502
|
|
|
20308
20503
|
// src/infrastructure/sms-stack.ts
|
|
20309
20504
|
init_esm_shims();
|
|
20310
20505
|
import * as aws15 from "@pulumi/aws";
|
|
20311
|
-
import * as
|
|
20506
|
+
import * as pulumi21 from "@pulumi/pulumi";
|
|
20312
20507
|
async function roleExists3(roleName) {
|
|
20313
20508
|
try {
|
|
20314
20509
|
const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
|
|
@@ -20342,7 +20537,7 @@ async function tableExists2(tableName) {
|
|
|
20342
20537
|
async function createSMSIAMRole(config2) {
|
|
20343
20538
|
let assumeRolePolicy;
|
|
20344
20539
|
if (config2.provider === "vercel" && config2.oidcProvider) {
|
|
20345
|
-
assumeRolePolicy =
|
|
20540
|
+
assumeRolePolicy = pulumi21.interpolate`{
|
|
20346
20541
|
"Version": "2012-10-17",
|
|
20347
20542
|
"Statement": [
|
|
20348
20543
|
{
|
|
@@ -20370,7 +20565,7 @@ async function createSMSIAMRole(config2) {
|
|
|
20370
20565
|
]
|
|
20371
20566
|
}`;
|
|
20372
20567
|
} else if (config2.provider === "aws") {
|
|
20373
|
-
assumeRolePolicy =
|
|
20568
|
+
assumeRolePolicy = pulumi21.output(`{
|
|
20374
20569
|
"Version": "2012-10-17",
|
|
20375
20570
|
"Statement": [{
|
|
20376
20571
|
"Effect": "Allow",
|
|
@@ -20725,7 +20920,7 @@ async function createSMSSNSResources(config2) {
|
|
|
20725
20920
|
});
|
|
20726
20921
|
new aws15.sqs.QueuePolicy("wraps-sms-events-queue-policy", {
|
|
20727
20922
|
queueUrl: config2.queueUrl,
|
|
20728
|
-
policy:
|
|
20923
|
+
policy: pulumi21.all([config2.queueArn, topic.arn]).apply(
|
|
20729
20924
|
([queueArn, topicArn2]) => JSON.stringify({
|
|
20730
20925
|
Version: "2012-10-17",
|
|
20731
20926
|
Statement: [
|
|
@@ -20824,7 +21019,7 @@ async function deploySMSLambdaFunction(config2) {
|
|
|
20824
21019
|
});
|
|
20825
21020
|
new aws15.iam.RolePolicy("wraps-sms-lambda-policy", {
|
|
20826
21021
|
role: lambdaRole.name,
|
|
20827
|
-
policy:
|
|
21022
|
+
policy: pulumi21.all([config2.tableName, config2.queueArn]).apply(
|
|
20828
21023
|
([tableName, queueArn]) => JSON.stringify({
|
|
20829
21024
|
Version: "2012-10-17",
|
|
20830
21025
|
Statement: [
|
|
@@ -20861,7 +21056,7 @@ async function deploySMSLambdaFunction(config2) {
|
|
|
20861
21056
|
runtime: "nodejs20.x",
|
|
20862
21057
|
handler: "index.handler",
|
|
20863
21058
|
role: lambdaRole.arn,
|
|
20864
|
-
code: new
|
|
21059
|
+
code: new pulumi21.asset.FileArchive(codeDir),
|
|
20865
21060
|
timeout: 300,
|
|
20866
21061
|
// 5 minutes
|
|
20867
21062
|
memorySize: 512,
|
|
@@ -21272,7 +21467,7 @@ async function smsDestroy(options) {
|
|
|
21272
21467
|
const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
|
|
21273
21468
|
let stack;
|
|
21274
21469
|
try {
|
|
21275
|
-
stack = await
|
|
21470
|
+
stack = await pulumi22.automation.LocalWorkspace.selectStack({
|
|
21276
21471
|
stackName,
|
|
21277
21472
|
workDir: getPulumiWorkDir()
|
|
21278
21473
|
});
|
|
@@ -21329,7 +21524,7 @@ async function smsDestroy(options) {
|
|
|
21329
21524
|
const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
|
|
21330
21525
|
let stack;
|
|
21331
21526
|
try {
|
|
21332
|
-
stack = await
|
|
21527
|
+
stack = await pulumi22.automation.LocalWorkspace.selectStack({
|
|
21333
21528
|
stackName,
|
|
21334
21529
|
workDir: getPulumiWorkDir()
|
|
21335
21530
|
});
|
|
@@ -21386,7 +21581,7 @@ Run ${pc29.cyan("wraps sms init")} to deploy infrastructure again.
|
|
|
21386
21581
|
// src/commands/sms/init.ts
|
|
21387
21582
|
init_esm_shims();
|
|
21388
21583
|
import * as clack28 from "@clack/prompts";
|
|
21389
|
-
import * as
|
|
21584
|
+
import * as pulumi23 from "@pulumi/pulumi";
|
|
21390
21585
|
import pc30 from "picocolors";
|
|
21391
21586
|
init_events();
|
|
21392
21587
|
init_aws();
|
|
@@ -22033,7 +22228,7 @@ ${pc30.yellow(pc30.bold("Important Notes:"))}`);
|
|
|
22033
22228
|
"Deploying SMS infrastructure (this may take 2-3 minutes)",
|
|
22034
22229
|
async () => {
|
|
22035
22230
|
await ensurePulumiWorkDir();
|
|
22036
|
-
const stack = await
|
|
22231
|
+
const stack = await pulumi23.automation.LocalWorkspace.createOrSelectStack(
|
|
22037
22232
|
{
|
|
22038
22233
|
stackName: `wraps-sms-${identity.accountId}-${region}`,
|
|
22039
22234
|
projectName: "wraps-sms",
|
|
@@ -22379,7 +22574,7 @@ init_aws();
|
|
|
22379
22574
|
init_fs();
|
|
22380
22575
|
init_metadata();
|
|
22381
22576
|
import * as clack30 from "@clack/prompts";
|
|
22382
|
-
import * as
|
|
22577
|
+
import * as pulumi24 from "@pulumi/pulumi";
|
|
22383
22578
|
import pc32 from "picocolors";
|
|
22384
22579
|
function displaySMSStatus(options) {
|
|
22385
22580
|
const lines = [];
|
|
@@ -22444,7 +22639,7 @@ Run ${pc32.cyan("wraps sms init")} to deploy SMS infrastructure.
|
|
|
22444
22639
|
try {
|
|
22445
22640
|
await ensurePulumiWorkDir();
|
|
22446
22641
|
const stackName = metadata.services.sms.pulumiStackName || `wraps-sms-${identity.accountId}-${region}`;
|
|
22447
|
-
const stack = await
|
|
22642
|
+
const stack = await pulumi24.automation.LocalWorkspace.selectStack({
|
|
22448
22643
|
stackName,
|
|
22449
22644
|
workDir: getPulumiWorkDir()
|
|
22450
22645
|
});
|
|
@@ -22483,7 +22678,7 @@ Run ${pc32.cyan("wraps sms init")} to deploy SMS infrastructure.
|
|
|
22483
22678
|
// src/commands/sms/sync.ts
|
|
22484
22679
|
init_esm_shims();
|
|
22485
22680
|
import * as clack31 from "@clack/prompts";
|
|
22486
|
-
import * as
|
|
22681
|
+
import * as pulumi25 from "@pulumi/pulumi";
|
|
22487
22682
|
import pc33 from "picocolors";
|
|
22488
22683
|
init_events();
|
|
22489
22684
|
init_aws();
|
|
@@ -22541,7 +22736,7 @@ Run ${pc33.cyan("wraps sms init")} to deploy SMS infrastructure first.
|
|
|
22541
22736
|
outputs = await progress.execute("Syncing SMS infrastructure", async () => {
|
|
22542
22737
|
await ensurePulumiWorkDir();
|
|
22543
22738
|
const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
|
|
22544
|
-
const stack = await
|
|
22739
|
+
const stack = await pulumi25.automation.LocalWorkspace.createOrSelectStack(
|
|
22545
22740
|
{
|
|
22546
22741
|
stackName,
|
|
22547
22742
|
projectName: "wraps-sms",
|
|
@@ -22862,7 +23057,7 @@ Run ${pc34.cyan("wraps sms register")} to complete registration.
|
|
|
22862
23057
|
// src/commands/sms/upgrade.ts
|
|
22863
23058
|
init_esm_shims();
|
|
22864
23059
|
import * as clack33 from "@clack/prompts";
|
|
22865
|
-
import * as
|
|
23060
|
+
import * as pulumi26 from "@pulumi/pulumi";
|
|
22866
23061
|
import pc35 from "picocolors";
|
|
22867
23062
|
init_events();
|
|
22868
23063
|
init_aws();
|
|
@@ -23605,7 +23800,7 @@ ${pc35.bold("Cost Impact:")}`);
|
|
|
23605
23800
|
"Updating SMS infrastructure (this may take 2-3 minutes)",
|
|
23606
23801
|
async () => {
|
|
23607
23802
|
await ensurePulumiWorkDir();
|
|
23608
|
-
const stack = await
|
|
23803
|
+
const stack = await pulumi26.automation.LocalWorkspace.createOrSelectStack(
|
|
23609
23804
|
{
|
|
23610
23805
|
stackName: metadata.services.sms?.pulumiStackName || `wraps-sms-${identity.accountId}-${region}`,
|
|
23611
23806
|
projectName: "wraps-sms",
|
|
@@ -24270,6 +24465,20 @@ function printCompletionScript() {
|
|
|
24270
24465
|
|
|
24271
24466
|
// src/cli.ts
|
|
24272
24467
|
init_errors();
|
|
24468
|
+
var [nodeMajorVersion] = process.versions.node.split(".").map(Number);
|
|
24469
|
+
if (nodeMajorVersion < 20) {
|
|
24470
|
+
console.error(
|
|
24471
|
+
"\x1B[31mError: Wraps CLI requires Node.js 20 or higher.\x1B[0m"
|
|
24472
|
+
);
|
|
24473
|
+
console.error(`Current version: ${process.versions.node}`);
|
|
24474
|
+
console.error("");
|
|
24475
|
+
console.error("To upgrade Node.js:");
|
|
24476
|
+
console.error(" macOS/Linux (nvm): nvm install 20 && nvm use 20");
|
|
24477
|
+
console.error(" macOS (Homebrew): brew install node@20");
|
|
24478
|
+
console.error(" Windows: Download from https://nodejs.org/");
|
|
24479
|
+
console.error("");
|
|
24480
|
+
process.exit(1);
|
|
24481
|
+
}
|
|
24273
24482
|
var __filename2 = fileURLToPath4(import.meta.url);
|
|
24274
24483
|
var __dirname3 = dirname2(__filename2);
|
|
24275
24484
|
var packageJson = JSON.parse(
|