@codyswann/lisa 2.226.0 → 2.226.1
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/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/scripts/install-remote-agent-aws.mjs +109 -10
- package/plugins/lisa/scripts/remote-agent-aws-setup.sh +47 -6
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/install-remote-agent-aws.mjs +109 -10
- package/plugins/lisa-agy/scripts/remote-agent-aws-setup.sh +47 -6
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/scripts/install-remote-agent-aws.mjs +109 -10
- package/plugins/lisa-copilot/scripts/remote-agent-aws-setup.sh +47 -6
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/scripts/install-remote-agent-aws.mjs +109 -10
- package/plugins/lisa-cursor/scripts/remote-agent-aws-setup.sh +47 -6
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/scripts/install-remote-agent-aws.mjs +109 -10
- package/plugins/src/base/scripts/remote-agent-aws-setup.sh +47 -6
package/package.json
CHANGED
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"form-data": ">=4.0.6"
|
|
103
103
|
},
|
|
104
104
|
"name": "@codyswann/lisa",
|
|
105
|
-
"version": "2.226.
|
|
105
|
+
"version": "2.226.1",
|
|
106
106
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
107
107
|
"main": "dist/index.js",
|
|
108
108
|
"exports": {
|
|
@@ -98,9 +98,17 @@ function installCopilotAdapter(project) {
|
|
|
98
98
|
);
|
|
99
99
|
mkdirSync(path.dirname(workflowPath), { recursive: true });
|
|
100
100
|
const marker = "Lisa remote AWS bootstrap";
|
|
101
|
+
const secretEnvironment =
|
|
102
|
+
" LISA_AWS_BOOTSTRAP_JSON: ${{ secrets.LISA_AWS_BOOTSTRAP_JSON }}";
|
|
103
|
+
const checkoutStep = [
|
|
104
|
+
" - uses: actions/checkout@v4",
|
|
105
|
+
" with:",
|
|
106
|
+
" persist-credentials: false",
|
|
107
|
+
].join("\n");
|
|
101
108
|
const step = [
|
|
102
109
|
` - name: ${marker}`,
|
|
103
110
|
" env:",
|
|
111
|
+
secretEnvironment,
|
|
104
112
|
" LISA_REMOTE_AGENT: copilot",
|
|
105
113
|
` run: ${INSTALL_COMMAND}`,
|
|
106
114
|
].join("\n");
|
|
@@ -118,7 +126,7 @@ function installCopilotAdapter(project) {
|
|
|
118
126
|
" permissions:",
|
|
119
127
|
" contents: read",
|
|
120
128
|
" steps:",
|
|
121
|
-
|
|
129
|
+
checkoutStep,
|
|
122
130
|
step,
|
|
123
131
|
"",
|
|
124
132
|
].join("\n")
|
|
@@ -127,7 +135,6 @@ function installCopilotAdapter(project) {
|
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
const current = readFileSync(workflowPath, "utf8");
|
|
130
|
-
if (current.includes(marker)) return path.relative(project, workflowPath);
|
|
131
138
|
const lines = current.split("\n");
|
|
132
139
|
const jobIndex = lines.findIndex(line =>
|
|
133
140
|
/^\s{2}copilot-setup-steps:\s*$/.test(line)
|
|
@@ -146,10 +153,95 @@ function installCopilotAdapter(project) {
|
|
|
146
153
|
"Existing copilot-setup-steps.yml must contain jobs.copilot-setup-steps.steps before Lisa can merge the AWS bootstrap step"
|
|
147
154
|
);
|
|
148
155
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
|
|
157
|
+
let changed = false;
|
|
158
|
+
const checkoutIndex = lines.findIndex(
|
|
159
|
+
(line, index) =>
|
|
160
|
+
index > stepsIndex &&
|
|
161
|
+
(jobEndIndex < 0 || index < jobEndIndex) &&
|
|
162
|
+
/^\s{6}-\s+uses:\s+actions\/checkout@/.test(line)
|
|
163
|
+
);
|
|
164
|
+
if (checkoutIndex >= 0) {
|
|
165
|
+
const nextStepIndex = lines.findIndex(
|
|
166
|
+
(line, index) => index > checkoutIndex && /^\s{6}-\s/.test(line)
|
|
167
|
+
);
|
|
168
|
+
const checkoutEnd = nextStepIndex < 0 ? lines.length : nextStepIndex;
|
|
169
|
+
const withIndex = lines.findIndex(
|
|
170
|
+
(line, index) =>
|
|
171
|
+
index > checkoutIndex &&
|
|
172
|
+
index < checkoutEnd &&
|
|
173
|
+
/^\s{8}with:\s*$/.test(line)
|
|
174
|
+
);
|
|
175
|
+
const persistenceIndex = lines.findIndex(
|
|
176
|
+
(line, index) =>
|
|
177
|
+
index > checkoutIndex &&
|
|
178
|
+
index < checkoutEnd &&
|
|
179
|
+
/^\s{10}persist-credentials:\s*/.test(line)
|
|
180
|
+
);
|
|
181
|
+
if (persistenceIndex >= 0) {
|
|
182
|
+
if (lines[persistenceIndex] !== " persist-credentials: false") {
|
|
183
|
+
lines[persistenceIndex] = " persist-credentials: false";
|
|
184
|
+
changed = true;
|
|
185
|
+
}
|
|
186
|
+
} else if (withIndex >= 0) {
|
|
187
|
+
lines.splice(withIndex + 1, 0, " persist-credentials: false");
|
|
188
|
+
changed = true;
|
|
189
|
+
} else {
|
|
190
|
+
lines.splice(
|
|
191
|
+
checkoutIndex + 1,
|
|
192
|
+
0,
|
|
193
|
+
" with:",
|
|
194
|
+
" persist-credentials: false"
|
|
195
|
+
);
|
|
196
|
+
changed = true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const markerIndex = lines.findIndex(line => line.includes(marker));
|
|
201
|
+
if (markerIndex >= 0) {
|
|
202
|
+
const nextStepIndex = lines.findIndex(
|
|
203
|
+
(line, index) => index > markerIndex && /^\s{6}-\s/.test(line)
|
|
204
|
+
);
|
|
205
|
+
const managedStepEnd = nextStepIndex < 0 ? lines.length : nextStepIndex;
|
|
206
|
+
const managedStep = lines.slice(markerIndex, managedStepEnd);
|
|
207
|
+
if (!managedStep.includes(secretEnvironment)) {
|
|
208
|
+
const environmentIndex = lines.findIndex(
|
|
209
|
+
(line, index) =>
|
|
210
|
+
index > markerIndex &&
|
|
211
|
+
index < managedStepEnd &&
|
|
212
|
+
/^\s{8}env:\s*$/.test(line)
|
|
213
|
+
);
|
|
214
|
+
if (environmentIndex < 0) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
"Existing Lisa remote AWS bootstrap step must contain an env block"
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
lines.splice(environmentIndex + 1, 0, secretEnvironment);
|
|
220
|
+
changed = true;
|
|
221
|
+
}
|
|
222
|
+
if (checkoutIndex < 0) {
|
|
223
|
+
lines.splice(stepsIndex + 1, 0, ...checkoutStep.split("\n"));
|
|
224
|
+
changed = true;
|
|
225
|
+
}
|
|
226
|
+
if (changed) writeFileSync(workflowPath, lines.join("\n"));
|
|
227
|
+
return path.relative(project, workflowPath);
|
|
228
|
+
}
|
|
229
|
+
const additions = checkoutIndex >= 0 ? [step] : [checkoutStep, step];
|
|
230
|
+
const nextCheckoutStepIndex = lines.findIndex(
|
|
231
|
+
(line, index) => index > checkoutIndex && /^\s{6}-\s/.test(line)
|
|
232
|
+
);
|
|
233
|
+
const updatedJobEndIndex = lines.findIndex(
|
|
234
|
+
(line, index) => index > jobIndex && /^\s{2}\S[^:]*:\s*$/.test(line)
|
|
235
|
+
);
|
|
236
|
+
const additionIndex =
|
|
237
|
+
checkoutIndex < 0
|
|
238
|
+
? stepsIndex + 1
|
|
239
|
+
: nextCheckoutStepIndex >= 0
|
|
240
|
+
? nextCheckoutStepIndex
|
|
241
|
+
: updatedJobEndIndex >= 0
|
|
242
|
+
? updatedJobEndIndex
|
|
243
|
+
: lines.length;
|
|
244
|
+
lines.splice(additionIndex, 0, ...additions);
|
|
153
245
|
writeFileSync(workflowPath, lines.join("\n"));
|
|
154
246
|
return path.relative(project, workflowPath);
|
|
155
247
|
}
|
|
@@ -161,13 +253,20 @@ function writeGuide(project, platform, secretName) {
|
|
|
161
253
|
guidePath,
|
|
162
254
|
`# Remote coding-agent AWS access
|
|
163
255
|
|
|
256
|
+
## Context
|
|
257
|
+
|
|
164
258
|
This repository uses Lisa's vendor-neutral AWS bootstrap. Configure the complete
|
|
165
259
|
Secrets Manager SecretString as the secret \`LISA_AWS_BOOTSTRAP_JSON\`; do not
|
|
166
260
|
split it into \`AWS_ACCESS_KEY_ID\` and \`AWS_SECRET_ACCESS_KEY\` environment
|
|
167
261
|
variables. The setup script writes a named source profile whose only permission
|
|
168
262
|
is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
|
|
169
263
|
|
|
170
|
-
##
|
|
264
|
+
## Goal
|
|
265
|
+
|
|
266
|
+
Give supported remote coding agents renewable, least-privilege AWS CLI access
|
|
267
|
+
without borrowing a developer identity or granting direct production repair.
|
|
268
|
+
|
|
269
|
+
## Changes
|
|
171
270
|
|
|
172
271
|
| Runtime | Setup |
|
|
173
272
|
|---|---|
|
|
@@ -180,7 +279,7 @@ is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
|
|
|
180
279
|
|
|
181
280
|
Generated for: \`${platform}\`.
|
|
182
281
|
|
|
183
|
-
##
|
|
282
|
+
## Implementation
|
|
184
283
|
|
|
185
284
|
The bootstrap bundle defines the available profile names. \`dev\` is the
|
|
186
285
|
default when present. Select another account explicitly, for example:
|
|
@@ -193,8 +292,6 @@ aws --profile production cloudformation describe-stacks
|
|
|
193
292
|
Production and shared profiles are observer-only. Production repair remains a
|
|
194
293
|
human-driven local-workstation operation.
|
|
195
294
|
|
|
196
|
-
## Administrator rollout
|
|
197
|
-
|
|
198
295
|
After the infrastructure pipeline deploys the remote-agent IAM stacks, retrieve
|
|
199
296
|
the complete bundle from the shared account. Do not extract or distribute its
|
|
200
297
|
individual access-key fields:
|
|
@@ -210,6 +307,8 @@ Store that exact output as the masked secret \`LISA_AWS_BOOTSTRAP_JSON\` on each
|
|
|
210
307
|
remote-agent platform. Run the setup command and require its live
|
|
211
308
|
\`sts:GetCallerIdentity\` check to pass before considering the platform ready.
|
|
212
309
|
|
|
310
|
+
## Notes
|
|
311
|
+
|
|
213
312
|
Rotate by replacing the bootstrap IAM access key through infrastructure and
|
|
214
313
|
then updating this one secret on every configured platform. Disabling or
|
|
215
314
|
deleting the bootstrap user immediately prevents new role sessions.
|
|
@@ -14,6 +14,7 @@ set -euo pipefail
|
|
|
14
14
|
umask 077
|
|
15
15
|
|
|
16
16
|
BOOTSTRAP_PROFILE="lisa-remote-agent-bootstrap"
|
|
17
|
+
AWS_CLI_VERSION="2.36.2"
|
|
17
18
|
|
|
18
19
|
fail() {
|
|
19
20
|
echo "remote-agent-aws-setup: $*" >&2
|
|
@@ -35,17 +36,17 @@ as_root() {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
install_base_tools() {
|
|
38
|
-
need curl && need unzip && need jq && return 0
|
|
39
|
+
need curl && need unzip && need jq && need gpg && return 0
|
|
39
40
|
|
|
40
41
|
if need apt-get; then
|
|
41
42
|
as_root apt-get update -y
|
|
42
|
-
as_root apt-get install -y curl unzip jq
|
|
43
|
+
as_root apt-get install -y curl unzip jq gnupg
|
|
43
44
|
elif need dnf; then
|
|
44
|
-
as_root dnf install -y curl unzip jq
|
|
45
|
+
as_root dnf install -y curl unzip jq gnupg2
|
|
45
46
|
elif need yum; then
|
|
46
|
-
as_root yum install -y curl unzip jq
|
|
47
|
+
as_root yum install -y curl unzip jq gnupg2
|
|
47
48
|
else
|
|
48
|
-
fail "install curl, unzip, and
|
|
49
|
+
fail "install curl, unzip, jq, and gpg in the remote image before running this script"
|
|
49
50
|
fi
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -62,9 +63,49 @@ install_aws_cli() {
|
|
|
62
63
|
|
|
63
64
|
temporary_directory="$(mktemp -d)"
|
|
64
65
|
trap 'rm -rf "${temporary_directory:-}"' EXIT
|
|
66
|
+
local installer_url
|
|
67
|
+
installer_url="https://awscli.amazonaws.com/awscli-exe-linux-${aws_architecture}-${AWS_CLI_VERSION}.zip"
|
|
65
68
|
curl -fsSL \
|
|
66
|
-
"
|
|
69
|
+
"$installer_url" \
|
|
67
70
|
-o "$temporary_directory/awscliv2.zip"
|
|
71
|
+
curl -fsSL "$installer_url.sig" -o "$temporary_directory/awscliv2.sig"
|
|
72
|
+
cat >"$temporary_directory/aws-cli-public-key.asc" <<'AWS_CLI_PUBLIC_KEY'
|
|
73
|
+
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
74
|
+
|
|
75
|
+
mQINBF2Cr7UBEADJZHcgusOJl7ENSyumXh85z0TRV0xJorM2B/JL0kHOyigQluUG
|
|
76
|
+
ZMLhENaG0bYatdrKP+3H91lvK050pXwnO/R7fB/FSTouki4ciIx5OuLlnJZIxSzx
|
|
77
|
+
PqGl0mkxImLNbGWoi6Lto0LYxqHN2iQtzlwTVmq9733zd3XfcXrZ3+LblHAgEt5G
|
|
78
|
+
TfNxEKJ8soPLyWmwDH6HWCnjZ/aIQRBTIQ05uVeEoYxSh6wOai7ss/KveoSNBbYz
|
|
79
|
+
gbdzoqI2Y8cgH2nbfgp3DSasaLZEdCSsIsK1u05CinE7k2qZ7KgKAUIcT/cR/grk
|
|
80
|
+
C6VwsnDU0OUCideXcQ8WeHutqvgZH1JgKDbznoIzeQHJD238GEu+eKhRHcz8/jeG
|
|
81
|
+
94zkcgJOz3KbZGYMiTh277Fvj9zzvZsbMBCedV1BTg3TqgvdX4bdkhf5cH+7NtWO
|
|
82
|
+
lrFj6UwAsGukBTAOxC0l/dnSmZhJ7Z1KmEWilro/gOrjtOxqRQutlIqG22TaqoPG
|
|
83
|
+
fYVN+en3Zwbt97kcgZDwqbuykNt64oZWc4XKCa3mprEGC3IbJTBFqglXmZ7l9ywG
|
|
84
|
+
EEUJYOlb2XrSuPWml39beWdKM8kzr1OjnlOm6+lpTRCBfo0wa9F8YZRhHPAkwKkX
|
|
85
|
+
XDeOGpWRj4ohOx0d2GWkyV5xyN14p2tQOCdOODmz80yUTgRpPVQUtOEhXQARAQAB
|
|
86
|
+
tCFBV1MgQ0xJIFRlYW0gPGF3cy1jbGlAYW1hem9uLmNvbT6JAlQEEwEIAD4CGwMF
|
|
87
|
+
CwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQT7Xbd/1cEYuAURraimMQrMRnJHXAUC
|
|
88
|
+
akV0ygUJDqP4lQAKCRCmMQrMRnJHXFHjD/9eyZLYcKuQOlLvtqSDtUBiEZf6ZZjM
|
|
89
|
+
i3ygYH8rJNtuToUH+HvSpe819urJCquXhDrlK6N+aqW0hCLtNABJG/vsafIgvIYJ
|
|
90
|
+
hSGgpgtNnQyMV1jViRWqPjbouw8OkYKBThUfT1i2Y+wn58ifs6ODBCmTexWtXspA
|
|
91
|
+
Si+Gt49xDOW0APmbOPnI+a4HJW6tVEo6MWS0WjzpiBayR3d1A4pt4YrPfSdDgpLo
|
|
92
|
+
h2SLQqlRqvvVZJaWBjhkErNFpfsBA06sDcPEOb0G8LBUbR4WOcdvhe5LubJbZuxC
|
|
93
|
+
AG9kNPCVeQP1ixwjgjXKysaxeQ6rv0VzIQgRp6tLVLWhy6AKDNvLjFSsmXZ1Wl08
|
|
94
|
+
Y/RlOHXlzLuQMRE6sR1wOdRxc9TsrNWTGiBK65cvSWOy03JeBkQQ8pesqltiyxI9
|
|
95
|
+
U21kkgiXtTSKNGfKK8pO27D81YANhRqPK7iTp6kuFiY2WtOg90KTMNlIT+Ff85Y2
|
|
96
|
+
b1rHj6Z0SrCkJujhWk3IBPic/wJgz01LEc/OAdUPlby90RJZcIBhSlWhT7mXnXIO
|
|
97
|
+
c0HWlNQrns2s3CTyYwZSiSlYe9ApeLwhjDo8NhbFuCAy61l6O5UsR4AfZxx/rGKv
|
|
98
|
+
2wFb1/RN/P4gNe6vmxZAPjR0AQcwD3tc2McimOLr/22kmPz8IH3I0X7WoSFr0Biz
|
|
99
|
+
E91G7bb0hOb/cA==
|
|
100
|
+
=knv7
|
|
101
|
+
-----END PGP PUBLIC KEY BLOCK-----
|
|
102
|
+
AWS_CLI_PUBLIC_KEY
|
|
103
|
+
mkdir -m 700 "$temporary_directory/gnupg"
|
|
104
|
+
gpg --batch --homedir "$temporary_directory/gnupg" \
|
|
105
|
+
--import "$temporary_directory/aws-cli-public-key.asc" >/dev/null 2>&1
|
|
106
|
+
gpg --batch --homedir "$temporary_directory/gnupg" \
|
|
107
|
+
--verify "$temporary_directory/awscliv2.sig" \
|
|
108
|
+
"$temporary_directory/awscliv2.zip"
|
|
68
109
|
unzip -q "$temporary_directory/awscliv2.zip" -d "$temporary_directory"
|
|
69
110
|
as_root "$temporary_directory/aws/install" \
|
|
70
111
|
--install-dir /usr/local/aws-cli \
|
|
@@ -98,9 +98,17 @@ function installCopilotAdapter(project) {
|
|
|
98
98
|
);
|
|
99
99
|
mkdirSync(path.dirname(workflowPath), { recursive: true });
|
|
100
100
|
const marker = "Lisa remote AWS bootstrap";
|
|
101
|
+
const secretEnvironment =
|
|
102
|
+
" LISA_AWS_BOOTSTRAP_JSON: ${{ secrets.LISA_AWS_BOOTSTRAP_JSON }}";
|
|
103
|
+
const checkoutStep = [
|
|
104
|
+
" - uses: actions/checkout@v4",
|
|
105
|
+
" with:",
|
|
106
|
+
" persist-credentials: false",
|
|
107
|
+
].join("\n");
|
|
101
108
|
const step = [
|
|
102
109
|
` - name: ${marker}`,
|
|
103
110
|
" env:",
|
|
111
|
+
secretEnvironment,
|
|
104
112
|
" LISA_REMOTE_AGENT: copilot",
|
|
105
113
|
` run: ${INSTALL_COMMAND}`,
|
|
106
114
|
].join("\n");
|
|
@@ -118,7 +126,7 @@ function installCopilotAdapter(project) {
|
|
|
118
126
|
" permissions:",
|
|
119
127
|
" contents: read",
|
|
120
128
|
" steps:",
|
|
121
|
-
|
|
129
|
+
checkoutStep,
|
|
122
130
|
step,
|
|
123
131
|
"",
|
|
124
132
|
].join("\n")
|
|
@@ -127,7 +135,6 @@ function installCopilotAdapter(project) {
|
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
const current = readFileSync(workflowPath, "utf8");
|
|
130
|
-
if (current.includes(marker)) return path.relative(project, workflowPath);
|
|
131
138
|
const lines = current.split("\n");
|
|
132
139
|
const jobIndex = lines.findIndex(line =>
|
|
133
140
|
/^\s{2}copilot-setup-steps:\s*$/.test(line)
|
|
@@ -146,10 +153,95 @@ function installCopilotAdapter(project) {
|
|
|
146
153
|
"Existing copilot-setup-steps.yml must contain jobs.copilot-setup-steps.steps before Lisa can merge the AWS bootstrap step"
|
|
147
154
|
);
|
|
148
155
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
|
|
157
|
+
let changed = false;
|
|
158
|
+
const checkoutIndex = lines.findIndex(
|
|
159
|
+
(line, index) =>
|
|
160
|
+
index > stepsIndex &&
|
|
161
|
+
(jobEndIndex < 0 || index < jobEndIndex) &&
|
|
162
|
+
/^\s{6}-\s+uses:\s+actions\/checkout@/.test(line)
|
|
163
|
+
);
|
|
164
|
+
if (checkoutIndex >= 0) {
|
|
165
|
+
const nextStepIndex = lines.findIndex(
|
|
166
|
+
(line, index) => index > checkoutIndex && /^\s{6}-\s/.test(line)
|
|
167
|
+
);
|
|
168
|
+
const checkoutEnd = nextStepIndex < 0 ? lines.length : nextStepIndex;
|
|
169
|
+
const withIndex = lines.findIndex(
|
|
170
|
+
(line, index) =>
|
|
171
|
+
index > checkoutIndex &&
|
|
172
|
+
index < checkoutEnd &&
|
|
173
|
+
/^\s{8}with:\s*$/.test(line)
|
|
174
|
+
);
|
|
175
|
+
const persistenceIndex = lines.findIndex(
|
|
176
|
+
(line, index) =>
|
|
177
|
+
index > checkoutIndex &&
|
|
178
|
+
index < checkoutEnd &&
|
|
179
|
+
/^\s{10}persist-credentials:\s*/.test(line)
|
|
180
|
+
);
|
|
181
|
+
if (persistenceIndex >= 0) {
|
|
182
|
+
if (lines[persistenceIndex] !== " persist-credentials: false") {
|
|
183
|
+
lines[persistenceIndex] = " persist-credentials: false";
|
|
184
|
+
changed = true;
|
|
185
|
+
}
|
|
186
|
+
} else if (withIndex >= 0) {
|
|
187
|
+
lines.splice(withIndex + 1, 0, " persist-credentials: false");
|
|
188
|
+
changed = true;
|
|
189
|
+
} else {
|
|
190
|
+
lines.splice(
|
|
191
|
+
checkoutIndex + 1,
|
|
192
|
+
0,
|
|
193
|
+
" with:",
|
|
194
|
+
" persist-credentials: false"
|
|
195
|
+
);
|
|
196
|
+
changed = true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const markerIndex = lines.findIndex(line => line.includes(marker));
|
|
201
|
+
if (markerIndex >= 0) {
|
|
202
|
+
const nextStepIndex = lines.findIndex(
|
|
203
|
+
(line, index) => index > markerIndex && /^\s{6}-\s/.test(line)
|
|
204
|
+
);
|
|
205
|
+
const managedStepEnd = nextStepIndex < 0 ? lines.length : nextStepIndex;
|
|
206
|
+
const managedStep = lines.slice(markerIndex, managedStepEnd);
|
|
207
|
+
if (!managedStep.includes(secretEnvironment)) {
|
|
208
|
+
const environmentIndex = lines.findIndex(
|
|
209
|
+
(line, index) =>
|
|
210
|
+
index > markerIndex &&
|
|
211
|
+
index < managedStepEnd &&
|
|
212
|
+
/^\s{8}env:\s*$/.test(line)
|
|
213
|
+
);
|
|
214
|
+
if (environmentIndex < 0) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
"Existing Lisa remote AWS bootstrap step must contain an env block"
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
lines.splice(environmentIndex + 1, 0, secretEnvironment);
|
|
220
|
+
changed = true;
|
|
221
|
+
}
|
|
222
|
+
if (checkoutIndex < 0) {
|
|
223
|
+
lines.splice(stepsIndex + 1, 0, ...checkoutStep.split("\n"));
|
|
224
|
+
changed = true;
|
|
225
|
+
}
|
|
226
|
+
if (changed) writeFileSync(workflowPath, lines.join("\n"));
|
|
227
|
+
return path.relative(project, workflowPath);
|
|
228
|
+
}
|
|
229
|
+
const additions = checkoutIndex >= 0 ? [step] : [checkoutStep, step];
|
|
230
|
+
const nextCheckoutStepIndex = lines.findIndex(
|
|
231
|
+
(line, index) => index > checkoutIndex && /^\s{6}-\s/.test(line)
|
|
232
|
+
);
|
|
233
|
+
const updatedJobEndIndex = lines.findIndex(
|
|
234
|
+
(line, index) => index > jobIndex && /^\s{2}\S[^:]*:\s*$/.test(line)
|
|
235
|
+
);
|
|
236
|
+
const additionIndex =
|
|
237
|
+
checkoutIndex < 0
|
|
238
|
+
? stepsIndex + 1
|
|
239
|
+
: nextCheckoutStepIndex >= 0
|
|
240
|
+
? nextCheckoutStepIndex
|
|
241
|
+
: updatedJobEndIndex >= 0
|
|
242
|
+
? updatedJobEndIndex
|
|
243
|
+
: lines.length;
|
|
244
|
+
lines.splice(additionIndex, 0, ...additions);
|
|
153
245
|
writeFileSync(workflowPath, lines.join("\n"));
|
|
154
246
|
return path.relative(project, workflowPath);
|
|
155
247
|
}
|
|
@@ -161,13 +253,20 @@ function writeGuide(project, platform, secretName) {
|
|
|
161
253
|
guidePath,
|
|
162
254
|
`# Remote coding-agent AWS access
|
|
163
255
|
|
|
256
|
+
## Context
|
|
257
|
+
|
|
164
258
|
This repository uses Lisa's vendor-neutral AWS bootstrap. Configure the complete
|
|
165
259
|
Secrets Manager SecretString as the secret \`LISA_AWS_BOOTSTRAP_JSON\`; do not
|
|
166
260
|
split it into \`AWS_ACCESS_KEY_ID\` and \`AWS_SECRET_ACCESS_KEY\` environment
|
|
167
261
|
variables. The setup script writes a named source profile whose only permission
|
|
168
262
|
is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
|
|
169
263
|
|
|
170
|
-
##
|
|
264
|
+
## Goal
|
|
265
|
+
|
|
266
|
+
Give supported remote coding agents renewable, least-privilege AWS CLI access
|
|
267
|
+
without borrowing a developer identity or granting direct production repair.
|
|
268
|
+
|
|
269
|
+
## Changes
|
|
171
270
|
|
|
172
271
|
| Runtime | Setup |
|
|
173
272
|
|---|---|
|
|
@@ -180,7 +279,7 @@ is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
|
|
|
180
279
|
|
|
181
280
|
Generated for: \`${platform}\`.
|
|
182
281
|
|
|
183
|
-
##
|
|
282
|
+
## Implementation
|
|
184
283
|
|
|
185
284
|
The bootstrap bundle defines the available profile names. \`dev\` is the
|
|
186
285
|
default when present. Select another account explicitly, for example:
|
|
@@ -193,8 +292,6 @@ aws --profile production cloudformation describe-stacks
|
|
|
193
292
|
Production and shared profiles are observer-only. Production repair remains a
|
|
194
293
|
human-driven local-workstation operation.
|
|
195
294
|
|
|
196
|
-
## Administrator rollout
|
|
197
|
-
|
|
198
295
|
After the infrastructure pipeline deploys the remote-agent IAM stacks, retrieve
|
|
199
296
|
the complete bundle from the shared account. Do not extract or distribute its
|
|
200
297
|
individual access-key fields:
|
|
@@ -210,6 +307,8 @@ Store that exact output as the masked secret \`LISA_AWS_BOOTSTRAP_JSON\` on each
|
|
|
210
307
|
remote-agent platform. Run the setup command and require its live
|
|
211
308
|
\`sts:GetCallerIdentity\` check to pass before considering the platform ready.
|
|
212
309
|
|
|
310
|
+
## Notes
|
|
311
|
+
|
|
213
312
|
Rotate by replacing the bootstrap IAM access key through infrastructure and
|
|
214
313
|
then updating this one secret on every configured platform. Disabling or
|
|
215
314
|
deleting the bootstrap user immediately prevents new role sessions.
|
|
@@ -14,6 +14,7 @@ set -euo pipefail
|
|
|
14
14
|
umask 077
|
|
15
15
|
|
|
16
16
|
BOOTSTRAP_PROFILE="lisa-remote-agent-bootstrap"
|
|
17
|
+
AWS_CLI_VERSION="2.36.2"
|
|
17
18
|
|
|
18
19
|
fail() {
|
|
19
20
|
echo "remote-agent-aws-setup: $*" >&2
|
|
@@ -35,17 +36,17 @@ as_root() {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
install_base_tools() {
|
|
38
|
-
need curl && need unzip && need jq && return 0
|
|
39
|
+
need curl && need unzip && need jq && need gpg && return 0
|
|
39
40
|
|
|
40
41
|
if need apt-get; then
|
|
41
42
|
as_root apt-get update -y
|
|
42
|
-
as_root apt-get install -y curl unzip jq
|
|
43
|
+
as_root apt-get install -y curl unzip jq gnupg
|
|
43
44
|
elif need dnf; then
|
|
44
|
-
as_root dnf install -y curl unzip jq
|
|
45
|
+
as_root dnf install -y curl unzip jq gnupg2
|
|
45
46
|
elif need yum; then
|
|
46
|
-
as_root yum install -y curl unzip jq
|
|
47
|
+
as_root yum install -y curl unzip jq gnupg2
|
|
47
48
|
else
|
|
48
|
-
fail "install curl, unzip, and
|
|
49
|
+
fail "install curl, unzip, jq, and gpg in the remote image before running this script"
|
|
49
50
|
fi
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -62,9 +63,49 @@ install_aws_cli() {
|
|
|
62
63
|
|
|
63
64
|
temporary_directory="$(mktemp -d)"
|
|
64
65
|
trap 'rm -rf "${temporary_directory:-}"' EXIT
|
|
66
|
+
local installer_url
|
|
67
|
+
installer_url="https://awscli.amazonaws.com/awscli-exe-linux-${aws_architecture}-${AWS_CLI_VERSION}.zip"
|
|
65
68
|
curl -fsSL \
|
|
66
|
-
"
|
|
69
|
+
"$installer_url" \
|
|
67
70
|
-o "$temporary_directory/awscliv2.zip"
|
|
71
|
+
curl -fsSL "$installer_url.sig" -o "$temporary_directory/awscliv2.sig"
|
|
72
|
+
cat >"$temporary_directory/aws-cli-public-key.asc" <<'AWS_CLI_PUBLIC_KEY'
|
|
73
|
+
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
74
|
+
|
|
75
|
+
mQINBF2Cr7UBEADJZHcgusOJl7ENSyumXh85z0TRV0xJorM2B/JL0kHOyigQluUG
|
|
76
|
+
ZMLhENaG0bYatdrKP+3H91lvK050pXwnO/R7fB/FSTouki4ciIx5OuLlnJZIxSzx
|
|
77
|
+
PqGl0mkxImLNbGWoi6Lto0LYxqHN2iQtzlwTVmq9733zd3XfcXrZ3+LblHAgEt5G
|
|
78
|
+
TfNxEKJ8soPLyWmwDH6HWCnjZ/aIQRBTIQ05uVeEoYxSh6wOai7ss/KveoSNBbYz
|
|
79
|
+
gbdzoqI2Y8cgH2nbfgp3DSasaLZEdCSsIsK1u05CinE7k2qZ7KgKAUIcT/cR/grk
|
|
80
|
+
C6VwsnDU0OUCideXcQ8WeHutqvgZH1JgKDbznoIzeQHJD238GEu+eKhRHcz8/jeG
|
|
81
|
+
94zkcgJOz3KbZGYMiTh277Fvj9zzvZsbMBCedV1BTg3TqgvdX4bdkhf5cH+7NtWO
|
|
82
|
+
lrFj6UwAsGukBTAOxC0l/dnSmZhJ7Z1KmEWilro/gOrjtOxqRQutlIqG22TaqoPG
|
|
83
|
+
fYVN+en3Zwbt97kcgZDwqbuykNt64oZWc4XKCa3mprEGC3IbJTBFqglXmZ7l9ywG
|
|
84
|
+
EEUJYOlb2XrSuPWml39beWdKM8kzr1OjnlOm6+lpTRCBfo0wa9F8YZRhHPAkwKkX
|
|
85
|
+
XDeOGpWRj4ohOx0d2GWkyV5xyN14p2tQOCdOODmz80yUTgRpPVQUtOEhXQARAQAB
|
|
86
|
+
tCFBV1MgQ0xJIFRlYW0gPGF3cy1jbGlAYW1hem9uLmNvbT6JAlQEEwEIAD4CGwMF
|
|
87
|
+
CwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQT7Xbd/1cEYuAURraimMQrMRnJHXAUC
|
|
88
|
+
akV0ygUJDqP4lQAKCRCmMQrMRnJHXFHjD/9eyZLYcKuQOlLvtqSDtUBiEZf6ZZjM
|
|
89
|
+
i3ygYH8rJNtuToUH+HvSpe819urJCquXhDrlK6N+aqW0hCLtNABJG/vsafIgvIYJ
|
|
90
|
+
hSGgpgtNnQyMV1jViRWqPjbouw8OkYKBThUfT1i2Y+wn58ifs6ODBCmTexWtXspA
|
|
91
|
+
Si+Gt49xDOW0APmbOPnI+a4HJW6tVEo6MWS0WjzpiBayR3d1A4pt4YrPfSdDgpLo
|
|
92
|
+
h2SLQqlRqvvVZJaWBjhkErNFpfsBA06sDcPEOb0G8LBUbR4WOcdvhe5LubJbZuxC
|
|
93
|
+
AG9kNPCVeQP1ixwjgjXKysaxeQ6rv0VzIQgRp6tLVLWhy6AKDNvLjFSsmXZ1Wl08
|
|
94
|
+
Y/RlOHXlzLuQMRE6sR1wOdRxc9TsrNWTGiBK65cvSWOy03JeBkQQ8pesqltiyxI9
|
|
95
|
+
U21kkgiXtTSKNGfKK8pO27D81YANhRqPK7iTp6kuFiY2WtOg90KTMNlIT+Ff85Y2
|
|
96
|
+
b1rHj6Z0SrCkJujhWk3IBPic/wJgz01LEc/OAdUPlby90RJZcIBhSlWhT7mXnXIO
|
|
97
|
+
c0HWlNQrns2s3CTyYwZSiSlYe9ApeLwhjDo8NhbFuCAy61l6O5UsR4AfZxx/rGKv
|
|
98
|
+
2wFb1/RN/P4gNe6vmxZAPjR0AQcwD3tc2McimOLr/22kmPz8IH3I0X7WoSFr0Biz
|
|
99
|
+
E91G7bb0hOb/cA==
|
|
100
|
+
=knv7
|
|
101
|
+
-----END PGP PUBLIC KEY BLOCK-----
|
|
102
|
+
AWS_CLI_PUBLIC_KEY
|
|
103
|
+
mkdir -m 700 "$temporary_directory/gnupg"
|
|
104
|
+
gpg --batch --homedir "$temporary_directory/gnupg" \
|
|
105
|
+
--import "$temporary_directory/aws-cli-public-key.asc" >/dev/null 2>&1
|
|
106
|
+
gpg --batch --homedir "$temporary_directory/gnupg" \
|
|
107
|
+
--verify "$temporary_directory/awscliv2.sig" \
|
|
108
|
+
"$temporary_directory/awscliv2.zip"
|
|
68
109
|
unzip -q "$temporary_directory/awscliv2.zip" -d "$temporary_directory"
|
|
69
110
|
as_root "$temporary_directory/aws/install" \
|
|
70
111
|
--install-dir /usr/local/aws-cli \
|