@factiii/stack 0.1.181 → 0.1.182

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 (33) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +441 -441
  3. package/bin/stack +300 -300
  4. package/dist/cli/dev-sync.js +16 -16
  5. package/dist/plugins/addons/auth/index.js +7 -7
  6. package/dist/plugins/addons/vercel/index.js +9 -9
  7. package/dist/plugins/addons/vercel/scanfix/config.js +10 -10
  8. package/dist/plugins/addons/vercel/scanfix/token.js +15 -15
  9. package/dist/plugins/approved.json +13 -13
  10. package/dist/plugins/pipelines/aws/index.js +12 -12
  11. package/dist/plugins/pipelines/aws/policies/bootstrap-policy.json +135 -135
  12. package/dist/plugins/pipelines/aws/prod.js +1 -1
  13. package/dist/plugins/pipelines/aws/scanfix/iam.d.ts.map +1 -1
  14. package/dist/plugins/pipelines/aws/scanfix/iam.js +40 -29
  15. package/dist/plugins/pipelines/aws/scanfix/iam.js.map +1 -1
  16. package/dist/plugins/pipelines/aws/utils/aws-helpers.d.ts +3 -1
  17. package/dist/plugins/pipelines/aws/utils/aws-helpers.d.ts.map +1 -1
  18. package/dist/plugins/pipelines/aws/utils/aws-helpers.js +31 -2
  19. package/dist/plugins/pipelines/aws/utils/aws-helpers.js.map +1 -1
  20. package/dist/plugins/pipelines/factiii/prod.js +21 -21
  21. package/dist/plugins/pipelines/factiii/staging.js +23 -23
  22. package/dist/plugins/pipelines/factiii/workflows/stack-ci.yml +75 -75
  23. package/dist/plugins/pipelines/factiii/workflows/stack-cicd-prod.yml +73 -73
  24. package/dist/plugins/servers/amazon-linux/index.js +16 -16
  25. package/dist/plugins/servers/mac/index.js +12 -12
  26. package/dist/plugins/servers/mac/staging.js +2 -2
  27. package/dist/plugins/servers/ubuntu/index.js +23 -23
  28. package/dist/plugins/servers/windows/index.js +15 -15
  29. package/dist/scripts/generate-all.js +73 -73
  30. package/dist/utils/deployment-report.js +2 -2
  31. package/dist/utils/secret-prompts.js +34 -34
  32. package/dist/utils/template-generator.js +74 -74
  33. package/package.json +93 -100
@@ -142,13 +142,13 @@ class AuthAddon {
142
142
  }
143
143
  }
144
144
  static helpText = {
145
- JWT_SECRET: `
146
- JWT signing secret for @factiii/auth.
147
-
148
- This is auto-generated (256-bit random) when you run:
149
- npx stack fix --secrets
150
-
151
- The secret is stored in Ansible Vault and used to sign
145
+ JWT_SECRET: `
146
+ JWT signing secret for @factiii/auth.
147
+
148
+ This is auto-generated (256-bit random) when you run:
149
+ npx stack fix --secrets
150
+
151
+ The secret is stored in Ansible Vault and used to sign
152
152
  authentication tokens (JWT) for your application.`,
153
153
  };
154
154
  // ============================================================
@@ -128,15 +128,15 @@ class VercelAddon {
128
128
  return false;
129
129
  }
130
130
  static helpText = {
131
- VERCEL_TOKEN: `
132
- Vercel API Token for deployments.
133
-
134
- Get from: https://vercel.com/account/tokens
135
-
136
- Create a new token with:
137
- - Scope: Full Account (or specific team)
138
- - Expiration: No Expiration (or custom)
139
-
131
+ VERCEL_TOKEN: `
132
+ Vercel API Token for deployments.
133
+
134
+ Get from: https://vercel.com/account/tokens
135
+
136
+ Create a new token with:
137
+ - Scope: Full Account (or specific team)
138
+ - Expiration: No Expiration (or custom)
139
+
140
140
  The token will be stored securely in Ansible Vault.`,
141
141
  };
142
142
  // ============================================================
@@ -259,13 +259,13 @@ exports.fixes = [
259
259
  return false;
260
260
  }
261
261
  },
262
- manualFix: `
263
- Add Vercel to stack.yml:
264
-
265
- vercel: {}
266
-
267
- Then run: npx stack fix
268
- (Auto-creates project, detects framework, and saves IDs via Vercel API)
262
+ manualFix: `
263
+ Add Vercel to stack.yml:
264
+
265
+ vercel: {}
266
+
267
+ Then run: npx stack fix
268
+ (Auto-creates project, detects framework, and saves IDs via Vercel API)
269
269
  `,
270
270
  },
271
271
  {
@@ -335,9 +335,9 @@ Then run: npx stack fix
335
335
  return false;
336
336
  }
337
337
  },
338
- manualFix: `
339
- Run: npx stack fix
340
- (Auto-creates .vercel/project.json from Vercel API — no CLI needed)
338
+ manualFix: `
339
+ Run: npx stack fix
340
+ (Auto-creates .vercel/project.json from Vercel API — no CLI needed)
341
341
  `,
342
342
  },
343
343
  {
@@ -107,12 +107,12 @@ exports.fixes = [
107
107
  return false;
108
108
  }
109
109
  },
110
- manualFix: `
111
- Store VERCEL_TOKEN in Ansible Vault manually:
112
-
113
- npx stack deploy --secrets set VERCEL_TOKEN
114
-
115
- Or get token from: https://vercel.com/account/tokens
110
+ manualFix: `
111
+ Store VERCEL_TOKEN in Ansible Vault manually:
112
+
113
+ npx stack deploy --secrets set VERCEL_TOKEN
114
+
115
+ Or get token from: https://vercel.com/account/tokens
116
116
  `,
117
117
  },
118
118
  {
@@ -128,15 +128,15 @@ Or get token from: https://vercel.com/account/tokens
128
128
  return !process.env.VERCEL_TOKEN;
129
129
  },
130
130
  fix: null,
131
- manualFix: `
132
- VERCEL_TOKEN is not required in your environment during development.
133
- It will be automatically read from Ansible Vault during deployment.
134
-
135
- If you want to set it in your shell for testing:
136
- export VERCEL_TOKEN="your-token-here"
137
-
138
- Or add to your shell profile (~/.bashrc, ~/.zshrc):
139
- export VERCEL_TOKEN="$(npx stack deploy --secrets get VERCEL_TOKEN)"
131
+ manualFix: `
132
+ VERCEL_TOKEN is not required in your environment during development.
133
+ It will be automatically read from Ansible Vault during deployment.
134
+
135
+ If you want to set it in your shell for testing:
136
+ export VERCEL_TOKEN="your-token-here"
137
+
138
+ Or add to your shell profile (~/.bashrc, ~/.zshrc):
139
+ export VERCEL_TOKEN="$(npx stack deploy --secrets get VERCEL_TOKEN)"
140
140
  `,
141
141
  },
142
142
  ];
@@ -1,13 +1,13 @@
1
- {
2
- "version": 1,
3
- "description": "List of approved/validated external plugins for Factiii Stack",
4
- "approved": [
5
- "@factiii/stack-plugin-expo",
6
- "@factiii/stack-plugin-prisma-trpc",
7
- "@factiii/stack-plugin-nextjs"
8
- ],
9
- "lastUpdated": "2024-12-16"
10
- }
11
-
12
-
13
-
1
+ {
2
+ "version": 1,
3
+ "description": "List of approved/validated external plugins for Factiii Stack",
4
+ "approved": [
5
+ "@factiii/stack-plugin-expo",
6
+ "@factiii/stack-plugin-prisma-trpc",
7
+ "@factiii/stack-plugin-nextjs"
8
+ ],
9
+ "lastUpdated": "2024-12-16"
10
+ }
11
+
12
+
13
+
@@ -166,17 +166,17 @@ class AWSPipeline {
166
166
  'free-tier': free_tier_js_1.default,
167
167
  };
168
168
  static helpText = {
169
- SSH: `
170
- SSH private key for accessing the EC2 instance.
171
-
172
- Option A: Auto-generate via AWS (recommended)
173
- - Factiii will create an EC2 Key Pair via AWS API
174
-
175
- Option B: Use existing key
169
+ SSH: `
170
+ SSH private key for accessing the EC2 instance.
171
+
172
+ Option A: Auto-generate via AWS (recommended)
173
+ - Factiii will create an EC2 Key Pair via AWS API
174
+
175
+ Option B: Use existing key
176
176
  ssh-keygen -t ed25519 -C "deploy-key" -f ~/.ssh/deploy_key`,
177
- AWS_SECRET_ACCESS_KEY: `
178
- AWS Secret Access Key
179
-
177
+ AWS_SECRET_ACCESS_KEY: `
178
+ AWS Secret Access Key
179
+
180
180
  Get from AWS Console: IAM -> Users -> Security credentials`,
181
181
  };
182
182
  // ============================================================
@@ -498,8 +498,8 @@ class AWSPipeline {
498
498
  }
499
499
  try {
500
500
  const repoName = config.name ?? 'app';
501
- await AWSPipeline.sshExec(envConfig, `
502
- cd ~/.factiii && docker compose stop ${repoName}-prod
501
+ await AWSPipeline.sshExec(envConfig, `
502
+ cd ~/.factiii && docker compose stop ${repoName}-prod
503
503
  `);
504
504
  return { success: true, message: 'Production containers stopped' };
505
505
  }
@@ -1,135 +1,135 @@
1
- {
2
- "Version": "2012-10-17",
3
- "Statement": [
4
- {
5
- "Sid": "FactiiiEC2Full",
6
- "Effect": "Allow",
7
- "Action": [
8
- "ec2:CreateVpc",
9
- "ec2:DeleteVpc",
10
- "ec2:DescribeVpcs",
11
- "ec2:ModifyVpcAttribute",
12
- "ec2:CreateSubnet",
13
- "ec2:DeleteSubnet",
14
- "ec2:DescribeSubnets",
15
- "ec2:ModifySubnetAttribute",
16
- "ec2:CreateInternetGateway",
17
- "ec2:DeleteInternetGateway",
18
- "ec2:AttachInternetGateway",
19
- "ec2:DetachInternetGateway",
20
- "ec2:DescribeInternetGateways",
21
- "ec2:CreateRouteTable",
22
- "ec2:DeleteRouteTable",
23
- "ec2:CreateRoute",
24
- "ec2:AssociateRouteTable",
25
- "ec2:DescribeRouteTables",
26
- "ec2:CreateSecurityGroup",
27
- "ec2:DeleteSecurityGroup",
28
- "ec2:AuthorizeSecurityGroupIngress",
29
- "ec2:RevokeSecurityGroupIngress",
30
- "ec2:DescribeSecurityGroups",
31
- "ec2:CreateKeyPair",
32
- "ec2:DeleteKeyPair",
33
- "ec2:DescribeKeyPairs",
34
- "ec2:RunInstances",
35
- "ec2:TerminateInstances",
36
- "ec2:DescribeInstances",
37
- "ec2:AllocateAddress",
38
- "ec2:ReleaseAddress",
39
- "ec2:AssociateAddress",
40
- "ec2:DescribeAddresses",
41
- "ec2:DescribeAvailabilityZones",
42
- "ec2:DescribeImages",
43
- "ec2:CreateTags"
44
- ],
45
- "Resource": "*"
46
- },
47
- {
48
- "Sid": "FactiiiRDSFull",
49
- "Effect": "Allow",
50
- "Action": [
51
- "rds:CreateDBInstance",
52
- "rds:DeleteDBInstance",
53
- "rds:DescribeDBInstances",
54
- "rds:CreateDBSubnetGroup",
55
- "rds:DeleteDBSubnetGroup",
56
- "rds:DescribeDBSubnetGroups",
57
- "rds:AddTagsToResource",
58
- "rds:ListTagsForResource"
59
- ],
60
- "Resource": "*"
61
- },
62
- {
63
- "Sid": "FactiiiS3Full",
64
- "Effect": "Allow",
65
- "Action": [
66
- "s3:CreateBucket",
67
- "s3:DeleteBucket",
68
- "s3:ListBucket",
69
- "s3:PutBucketEncryption",
70
- "s3:PutBucketPublicAccessBlock",
71
- "s3:PutBucketCORS",
72
- "s3:GetBucketEncryption",
73
- "s3:GetBucketPublicAccessBlock",
74
- "s3:GetBucketCORS",
75
- "s3:PutObject",
76
- "s3:GetObject",
77
- "s3:ListAllMyBuckets"
78
- ],
79
- "Resource": "*"
80
- },
81
- {
82
- "Sid": "FactiiiECRFull",
83
- "Effect": "Allow",
84
- "Action": [
85
- "ecr:CreateRepository",
86
- "ecr:DeleteRepository",
87
- "ecr:DescribeRepositories",
88
- "ecr:GetAuthorizationToken",
89
- "ecr:PutLifecyclePolicy",
90
- "ecr:BatchGetImage",
91
- "ecr:BatchCheckLayerAvailability",
92
- "ecr:PutImage",
93
- "ecr:InitiateLayerUpload",
94
- "ecr:UploadLayerPart",
95
- "ecr:CompleteLayerUpload"
96
- ],
97
- "Resource": "*"
98
- },
99
- {
100
- "Sid": "FactiiiSES",
101
- "Effect": "Allow",
102
- "Action": [
103
- "ses:VerifyDomainIdentity",
104
- "ses:VerifyDomainDkim",
105
- "ses:GetAccountSendingEnabled",
106
- "ses:GetIdentityVerificationAttributes",
107
- "ses:GetIdentityDkimAttributes"
108
- ],
109
- "Resource": "*"
110
- },
111
- {
112
- "Sid": "FactiiiIAMLimited",
113
- "Effect": "Allow",
114
- "Action": [
115
- "iam:CreateUser",
116
- "iam:DeleteUser",
117
- "iam:GetUser",
118
- "iam:PutUserPolicy",
119
- "iam:DeleteUserPolicy",
120
- "iam:CreateAccessKey",
121
- "iam:ListAccessKeys",
122
- "iam:ListUsers"
123
- ],
124
- "Resource": "*"
125
- },
126
- {
127
- "Sid": "FactiiiSTS",
128
- "Effect": "Allow",
129
- "Action": [
130
- "sts:GetCallerIdentity"
131
- ],
132
- "Resource": "*"
133
- }
134
- ]
135
- }
1
+ {
2
+ "Version": "2012-10-17",
3
+ "Statement": [
4
+ {
5
+ "Sid": "FactiiiEC2Full",
6
+ "Effect": "Allow",
7
+ "Action": [
8
+ "ec2:CreateVpc",
9
+ "ec2:DeleteVpc",
10
+ "ec2:DescribeVpcs",
11
+ "ec2:ModifyVpcAttribute",
12
+ "ec2:CreateSubnet",
13
+ "ec2:DeleteSubnet",
14
+ "ec2:DescribeSubnets",
15
+ "ec2:ModifySubnetAttribute",
16
+ "ec2:CreateInternetGateway",
17
+ "ec2:DeleteInternetGateway",
18
+ "ec2:AttachInternetGateway",
19
+ "ec2:DetachInternetGateway",
20
+ "ec2:DescribeInternetGateways",
21
+ "ec2:CreateRouteTable",
22
+ "ec2:DeleteRouteTable",
23
+ "ec2:CreateRoute",
24
+ "ec2:AssociateRouteTable",
25
+ "ec2:DescribeRouteTables",
26
+ "ec2:CreateSecurityGroup",
27
+ "ec2:DeleteSecurityGroup",
28
+ "ec2:AuthorizeSecurityGroupIngress",
29
+ "ec2:RevokeSecurityGroupIngress",
30
+ "ec2:DescribeSecurityGroups",
31
+ "ec2:CreateKeyPair",
32
+ "ec2:DeleteKeyPair",
33
+ "ec2:DescribeKeyPairs",
34
+ "ec2:RunInstances",
35
+ "ec2:TerminateInstances",
36
+ "ec2:DescribeInstances",
37
+ "ec2:AllocateAddress",
38
+ "ec2:ReleaseAddress",
39
+ "ec2:AssociateAddress",
40
+ "ec2:DescribeAddresses",
41
+ "ec2:DescribeAvailabilityZones",
42
+ "ec2:DescribeImages",
43
+ "ec2:CreateTags"
44
+ ],
45
+ "Resource": "*"
46
+ },
47
+ {
48
+ "Sid": "FactiiiRDSFull",
49
+ "Effect": "Allow",
50
+ "Action": [
51
+ "rds:CreateDBInstance",
52
+ "rds:DeleteDBInstance",
53
+ "rds:DescribeDBInstances",
54
+ "rds:CreateDBSubnetGroup",
55
+ "rds:DeleteDBSubnetGroup",
56
+ "rds:DescribeDBSubnetGroups",
57
+ "rds:AddTagsToResource",
58
+ "rds:ListTagsForResource"
59
+ ],
60
+ "Resource": "*"
61
+ },
62
+ {
63
+ "Sid": "FactiiiS3Full",
64
+ "Effect": "Allow",
65
+ "Action": [
66
+ "s3:CreateBucket",
67
+ "s3:DeleteBucket",
68
+ "s3:ListBucket",
69
+ "s3:PutBucketEncryption",
70
+ "s3:PutBucketPublicAccessBlock",
71
+ "s3:PutBucketCORS",
72
+ "s3:GetBucketEncryption",
73
+ "s3:GetBucketPublicAccessBlock",
74
+ "s3:GetBucketCORS",
75
+ "s3:PutObject",
76
+ "s3:GetObject",
77
+ "s3:ListAllMyBuckets"
78
+ ],
79
+ "Resource": "*"
80
+ },
81
+ {
82
+ "Sid": "FactiiiECRFull",
83
+ "Effect": "Allow",
84
+ "Action": [
85
+ "ecr:CreateRepository",
86
+ "ecr:DeleteRepository",
87
+ "ecr:DescribeRepositories",
88
+ "ecr:GetAuthorizationToken",
89
+ "ecr:PutLifecyclePolicy",
90
+ "ecr:BatchGetImage",
91
+ "ecr:BatchCheckLayerAvailability",
92
+ "ecr:PutImage",
93
+ "ecr:InitiateLayerUpload",
94
+ "ecr:UploadLayerPart",
95
+ "ecr:CompleteLayerUpload"
96
+ ],
97
+ "Resource": "*"
98
+ },
99
+ {
100
+ "Sid": "FactiiiSES",
101
+ "Effect": "Allow",
102
+ "Action": [
103
+ "ses:VerifyDomainIdentity",
104
+ "ses:VerifyDomainDkim",
105
+ "ses:GetAccountSendingEnabled",
106
+ "ses:GetIdentityVerificationAttributes",
107
+ "ses:GetIdentityDkimAttributes"
108
+ ],
109
+ "Resource": "*"
110
+ },
111
+ {
112
+ "Sid": "FactiiiIAMLimited",
113
+ "Effect": "Allow",
114
+ "Action": [
115
+ "iam:CreateUser",
116
+ "iam:DeleteUser",
117
+ "iam:GetUser",
118
+ "iam:PutUserPolicy",
119
+ "iam:DeleteUserPolicy",
120
+ "iam:CreateAccessKey",
121
+ "iam:ListAccessKeys",
122
+ "iam:ListUsers"
123
+ ],
124
+ "Resource": "*"
125
+ },
126
+ {
127
+ "Sid": "FactiiiSTS",
128
+ "Effect": "Allow",
129
+ "Action": [
130
+ "sts:GetCallerIdentity"
131
+ ],
132
+ "Resource": "*"
133
+ }
134
+ ]
135
+ }
@@ -154,7 +154,7 @@ async function writeEnvFile(envConfig, repoDir, environment, envVarsString) {
154
154
  else {
155
155
  // We're remote - SSH to write
156
156
  console.log(` 📝 Writing ${envFileName} on remote server (${envVars.length} variables)...`);
157
- await sshExecCommand(envConfig, `cat > ${repoDir}/${envFileName} << 'ENVEOF'
157
+ await sshExecCommand(envConfig, `cat > ${repoDir}/${envFileName} << 'ENVEOF'
158
158
  ${envFileContent}ENVEOF`);
159
159
  }
160
160
  }
@@ -1 +1 @@
1
- {"version":3,"file":"iam.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/pipelines/aws/scanfix/iam.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAiB,GAAG,EAAE,MAAM,4BAA4B,CAAC;AA4PrE,eAAO,MAAM,QAAQ,EAAE,GAAG,EA2MzB,CAAC"}
1
+ {"version":3,"file":"iam.d.ts","sourceRoot":"","sources":["../../../../../src/plugins/pipelines/aws/scanfix/iam.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAiB,GAAG,EAAE,MAAM,4BAA4B,CAAC;AAuQrE,eAAO,MAAM,QAAQ,EAAE,GAAG,EA2MzB,CAAC"}
@@ -181,15 +181,26 @@ function getProdPolicy(projectName, region, accountId) {
181
181
  async function ensureIamAccess(config, region) {
182
182
  if (await (0, aws_helpers_js_1.canManageIam)(region))
183
183
  return true;
184
- const callerArn = await (0, aws_helpers_js_1.getCallerArn)(region);
185
184
  const { confirm } = await Promise.resolve().then(() => __importStar(require('../../../../utils/secret-prompts.js')));
185
+ const configKeyId = (0, aws_helpers_js_1.getAwsConfig)(config).accessKeyId;
186
+ const identity = await (0, aws_helpers_js_1.getCallerArn)(region);
187
+ // Determine if we can't connect at all vs connected but lacking permissions
188
+ const canConnect = !!identity;
186
189
  console.log('');
187
190
  console.log(' ============================================================');
188
- console.log(' AWS CREDENTIALS CANNOT CREATE IAM USERS');
189
- console.log(' ============================================================');
190
- console.log(' Logged in as: ' + (callerArn ?? 'unknown'));
191
- console.log(' This account does not have permission to create IAM users.');
192
- console.log(' You need admin credentials to continue.');
191
+ if (canConnect) {
192
+ console.log(' ACCESS KEY ' + (configKeyId ?? 'unknown') + ' DOES NOT HAVE IAM PERMISSIONS');
193
+ console.log(' ============================================================');
194
+ console.log(' Logged in as: ' + identity);
195
+ console.log(' This IAM user does not have permission to create IAM users.');
196
+ console.log(' The access_key_id in stack.yml needs to belong to an admin user.');
197
+ }
198
+ else {
199
+ console.log(' CANNOT CONNECT TO AWS WITH ACCESS KEY ' + (configKeyId ?? 'unknown'));
200
+ console.log(' ============================================================');
201
+ console.log(' The access_key_id in stack.yml could not authenticate.');
202
+ console.log(' Check that this key exists and has not been deactivated in AWS.');
203
+ }
193
204
  console.log(' ============================================================');
194
205
  console.log('');
195
206
  // Check if vault has credentials we can swap to
@@ -210,7 +221,7 @@ async function ensureIamAccess(config, region) {
210
221
  }
211
222
  }
212
223
  if (vaultHasCreds) {
213
- const swap = await confirm(' Load admin credentials from Ansible Vault?', true);
224
+ const swap = await confirm(' Load credentials from Ansible Vault instead?', true);
214
225
  if (swap) {
215
226
  try {
216
227
  const { AnsibleVaultSecrets } = await Promise.resolve().then(() => __importStar(require('../../../../utils/ansible-vault-secrets.js')));
@@ -218,13 +229,13 @@ async function ensureIamAccess(config, region) {
218
229
  vault_path: config.ansible.vault_path,
219
230
  vault_password_file: config.ansible.vault_password_file,
220
231
  });
221
- const accessKeyId = await vault.getSecret('AWS_ACCESS_KEY_ID');
232
+ const vaultKeyId = await vault.getSecret('AWS_ACCESS_KEY_ID');
222
233
  const secretKey = await vault.getSecret('AWS_SECRET_ACCESS_KEY');
223
- if (!accessKeyId || !secretKey) {
234
+ if (!vaultKeyId || !secretKey) {
224
235
  console.log(' Failed to read credentials from vault.');
225
236
  return false;
226
237
  }
227
- (0, aws_helpers_js_1.writeAwsCredentials)(accessKeyId, secretKey, region);
238
+ (0, aws_helpers_js_1.writeAwsCredentials)(vaultKeyId, secretKey, region);
228
239
  (0, aws_helpers_js_1.clearClientCache)(); // Pick up new credentials
229
240
  const newArn = await (0, aws_helpers_js_1.getCallerArn)(region);
230
241
  console.log(' [OK] Switched to: ' + (newArn ?? 'unknown'));
@@ -233,9 +244,9 @@ async function ensureIamAccess(config, region) {
233
244
  return true;
234
245
  }
235
246
  console.log('');
236
- console.log(' Still no IAM permission. The vault credentials need admin access.');
247
+ console.log(' Vault credentials (' + vaultKeyId + ') also lack IAM permissions.');
237
248
  console.log('');
238
- console.log(' To fix, update the vault credentials:');
249
+ console.log(' To fix, store admin credentials in the vault:');
239
250
  console.log(' npx stack deploy --secrets set AWS_ACCESS_KEY_ID');
240
251
  console.log(' npx stack deploy --secrets set AWS_SECRET_ACCESS_KEY');
241
252
  console.log(' Then run: npx stack fix');
@@ -270,37 +281,37 @@ async function ensureIamAccess(config, region) {
270
281
  }
271
282
  exports.iamFixes = [
272
283
  {
273
- id: 'aws-iam-dev-user-missing',
284
+ id: 'aws-iam-admin-user-missing',
274
285
  stage: 'secrets',
275
286
  severity: 'warning',
276
- description: '👤 IAM dev user not created (read-only access for dev workflows)',
287
+ description: '👤 IAM admin user not created (recess for dev workflows)',
277
288
  scan: async (config) => {
278
289
  if (!(0, aws_helpers_js_1.isAwsConfigured)(config))
279
290
  return false;
280
291
  const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
281
292
  const projectName = (0, aws_helpers_js_1.getProjectName)(config);
282
- return !(await (0, aws_helpers_js_1.findIamUser)('factiii-' + projectName + '-dev', region));
293
+ return !(await (0, aws_helpers_js_1.findIamUser)('factiii-' + projectName + '-admin', region));
283
294
  },
284
295
  fix: async (config) => {
285
296
  const { region } = (0, aws_helpers_js_1.getAwsConfig)(config);
286
297
  const projectName = (0, aws_helpers_js_1.getProjectName)(config);
287
- const userName = 'factiii-' + projectName + '-dev';
298
+ const userName = 'factiii-' + projectName + '-admin';
288
299
  if (!(await ensureIamAccess(config, region)))
289
300
  return false;
290
301
  console.log('');
291
302
  console.log(' ============================================================');
292
- console.log(' CREATE IAM DEV USER');
303
+ console.log(' CREATE IAM ADMIN USER');
293
304
  console.log(' ============================================================');
294
- console.log(' Will create IAM user "' + userName + '" with read-only policy:');
305
+ console.log(' Will create IAM user "' + userName + '" with admin policy:');
295
306
  console.log(' - ECR: pull images, list repositories');
296
307
  console.log(' - S3: read objects from project bucket');
297
308
  console.log(' - EC2/RDS: describe (view) resources');
298
309
  console.log('');
299
- console.log(' This user is for local development and CI read-only access.');
310
+ console.log(' This user is for local development and CI access.');
300
311
  console.log(' ============================================================');
301
312
  console.log('');
302
313
  const { confirm } = await Promise.resolve().then(() => __importStar(require('../../../../utils/secret-prompts.js')));
303
- const proceed = await confirm(' Create IAM dev user "' + userName + '"?', true);
314
+ const proceed = await confirm(' Create IAM admin user "' + userName + '"?', true);
304
315
  if (!proceed) {
305
316
  console.log(' [--] Skipped — you can create it later with: npx stack fix --secrets');
306
317
  return true;
@@ -320,16 +331,16 @@ exports.iamFixes = [
320
331
  const policy = getDevPolicy(projectName, region, accountId);
321
332
  await iam.send(new aws_helpers_js_1.PutUserPolicyCommand({
322
333
  UserName: userName,
323
- PolicyName: 'factiii-' + projectName + '-dev-policy',
334
+ PolicyName: 'factiii-' + projectName + '-admin-policy',
324
335
  PolicyDocument: policy,
325
336
  }));
326
- console.log(' Attached dev policy (read-only ECR, S3, EC2, RDS)');
337
+ console.log(' Attached admin policy (ECR, S3, EC2, RDS)');
327
338
  // Create access key
328
339
  const keyResult = await iam.send(new aws_helpers_js_1.CreateAccessKeyCommand({ UserName: userName }));
329
340
  const accessKeyId = keyResult.AccessKey?.AccessKeyId;
330
341
  const secretKey = keyResult.AccessKey?.SecretAccessKey;
331
342
  console.log('');
332
- console.log(' Dev credentials (save these!):');
343
+ console.log(' Admin credentials (save these!):');
333
344
  console.log(' Access Key ID: ' + accessKeyId);
334
345
  console.log(' Secret Access Key: ' + secretKey);
335
346
  console.log('');
@@ -337,26 +348,26 @@ exports.iamFixes = [
337
348
  return true;
338
349
  }
339
350
  catch (e) {
340
- console.log(' Failed to create dev IAM user: ' + (e instanceof Error ? e.message : String(e)));
351
+ console.log(' Failed to create admin IAM user: ' + (e instanceof Error ? e.message : String(e)));
341
352
  return false;
342
353
  }
343
354
  },
344
355
  manualFix: [
345
356
  '============================================================',
346
- 'IAM DEV USER SETUP',
357
+ 'IAM ADMIN USER SETUP',
347
358
  '============================================================',
348
359
  '',
349
- ' This creates a read-only IAM user for local dev and CI.',
360
+ ' This creates an admin IAM user for local dev and CI.',
350
361
  ' Permissions: ECR pull, S3 read, EC2/RDS describe.',
351
362
  '',
352
363
  ' Auto-fix: npx stack fix --secrets (creates user + policy + access key)',
353
364
  '',
354
365
  ' Or manually in AWS Console:',
355
366
  ' 1. Go to IAM > Users > Create user',
356
- ' 2. Name: factiii-{project}-dev',
357
- ' 3. Attach inline policy with read-only ECR, S3, EC2, RDS access',
367
+ ' 2. Name: factiii-{project}-admin',
368
+ ' 3. Attach inline policy with ECR, S3, EC2, RDS access',
358
369
  ' 4. Create access key: User > Security credentials > Create access key > CLI',
359
- ' 5. Store secret in vault: npx stack deploy --secrets set AWS_DEV_SECRET_ACCESS_KEY',
370
+ ' 5. Store secret in vault: npx stack deploy --secrets set AWS_SECRET_ACCESS_KEY',
360
371
  '',
361
372
  '============================================================',
362
373
  ].join('\n'),