@codyswann/lisa 2.225.5 → 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/README.md +7 -0
- package/all/create-only/scripts/remote-agent-aws-setup.sh +11 -0
- 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/.codex-plugin/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
- package/plugins/lisa/.codex-plugin/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-aws/SKILL.md +53 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-aws/agents/openai.yaml +4 -0
- package/plugins/lisa/commands/setup-remote-aws.md +6 -0
- package/plugins/lisa/scripts/install-remote-agent-aws.mjs +347 -0
- package/plugins/lisa/scripts/remote-agent-aws-setup.sh +204 -0
- package/plugins/lisa/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
- package/plugins/lisa/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
- package/plugins/lisa/skills/lisa-setup-remote-aws/SKILL.md +53 -0
- package/plugins/lisa/skills/lisa-setup-remote-aws/agents/openai.yaml +4 -0
- package/plugins/lisa-agy/commands/lisa/setup-remote-aws.md +6 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/scripts/install-remote-agent-aws.mjs +347 -0
- package/plugins/lisa-agy/scripts/remote-agent-aws-setup.sh +204 -0
- package/plugins/lisa-agy/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
- package/plugins/lisa-agy/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
- package/plugins/lisa-agy/skills/lisa-setup-remote-aws/SKILL.md +53 -0
- 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/commands/lisa/setup-remote-aws.md +6 -0
- package/plugins/lisa-copilot/scripts/install-remote-agent-aws.mjs +347 -0
- package/plugins/lisa-copilot/scripts/remote-agent-aws-setup.sh +204 -0
- package/plugins/lisa-copilot/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
- package/plugins/lisa-copilot/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
- package/plugins/lisa-copilot/skills/lisa-setup-remote-aws/SKILL.md +53 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/setup-remote-aws.md +6 -0
- package/plugins/lisa-cursor/scripts/install-remote-agent-aws.mjs +347 -0
- package/plugins/lisa-cursor/scripts/remote-agent-aws-setup.sh +204 -0
- package/plugins/lisa-cursor/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
- package/plugins/lisa-cursor/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
- package/plugins/lisa-cursor/skills/lisa-setup-remote-aws/SKILL.md +53 -0
- 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/commands/setup-remote-aws.md +6 -0
- package/plugins/src/base/scripts/install-remote-agent-aws.mjs +347 -0
- package/plugins/src/base/scripts/remote-agent-aws-setup.sh +204 -0
- package/plugins/src/base/skills/lisa-analyze-claude-remote/SKILL.md +21 -18
- package/plugins/src/base/skills/lisa-generate-claude-remote-build-script/SKILL.md +15 -27
- package/plugins/src/base/skills/lisa-setup-remote-aws/SKILL.md +53 -0
- package/scripts/claude-remote-setup.sh +9 -2
- package/scripts/remote-agent-aws-setup.sh +17 -0
package/README.md
CHANGED
|
@@ -84,6 +84,13 @@ Skill bodies are not copied into the project and unrelated Lisa stacks are not
|
|
|
84
84
|
loaded. Project settings use `[features].hooks`; the deprecated `codex_hooks`
|
|
85
85
|
key is removed during reconciliation.
|
|
86
86
|
|
|
87
|
+
Remote coding environments use one vendor-neutral AWS bootstrap rather than
|
|
88
|
+
repository-specific or agent-specific IAM users. Run `/lisa:setup-remote-aws`
|
|
89
|
+
to install the common setup script and native Cursor/Copilot adapters; Claude,
|
|
90
|
+
Codex, Cursor, Copilot, and user-managed Antigravity/OpenCode hosts all consume
|
|
91
|
+
the same `LISA_AWS_BOOTSTRAP_JSON` bundle. See
|
|
92
|
+
[Remote coding-agent AWS access](docs/remote-agent-aws.md).
|
|
93
|
+
|
|
87
94
|
The supported stacks, setup flags, and exact invocation evolve as the project grows, so ask for the current set rather than copying a list that may have moved on:
|
|
88
95
|
|
|
89
96
|
> **Prompt for your coding agent**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Installed by Lisa. The authoritative implementation is shipped by the
|
|
3
|
+
# lisa-setup-remote-aws skill and the @codyswann/lisa package.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
LOCAL_LISA_SCRIPT="$(npm root 2>/dev/null)/@codyswann/lisa/plugins/lisa/scripts/remote-agent-aws-setup.sh"
|
|
8
|
+
[ -x "$LOCAL_LISA_SCRIPT" ] && exec "$LOCAL_LISA_SCRIPT" "$@"
|
|
9
|
+
|
|
10
|
+
echo "remote-agent-aws-setup: install @codyswann/lisa before running this setup script" >&2
|
|
11
|
+
exit 1
|
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.
|
|
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": {
|
|
@@ -150,24 +150,27 @@ Group the findings as:
|
|
|
150
150
|
Terraform providers).
|
|
151
151
|
- `aws sso login`, `aws:signin:*`, `sso_start_url`, `sso_account_id`, `sso_role_name`, or
|
|
152
152
|
`sso_session` in docs or config.
|
|
153
|
-
- `
|
|
154
|
-
`AWS_PROFILE`, role ARN, external ID, account, or
|
|
153
|
+
- `LISA_AWS_BOOTSTRAP_JSON`, legacy `AWS_ACCESS_KEY_ID` /
|
|
154
|
+
`AWS_SECRET_ACCESS_KEY`, `AWS_PROFILE`, role ARN, external ID, account, or
|
|
155
|
+
CloudWatch/STS references.
|
|
155
156
|
|
|
156
157
|
If AWS is present, emit an AWS credential finding and inventory entry. If the repo's current
|
|
157
158
|
local path is `aws sso login` or an `aws:signin:*` script, classify that local path as a `GAP`
|
|
158
159
|
for headless remote routines: it is an interactive browser/device-authorization flow and cannot
|
|
159
160
|
complete in the cloud. Route to the AWS row in the Credential reference instead of suggesting
|
|
160
|
-
SSO auth. The finding must name
|
|
161
|
-
with only
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
SSO auth. The finding must name Lisa's headless substrate: one dedicated IAM
|
|
162
|
+
user with only `sts:AssumeRole`; one complete Secrets Manager SecretString
|
|
163
|
+
set as `LISA_AWS_BOOTSTRAP_JSON`; and `/lisa:setup-remote-aws`, which writes a
|
|
164
|
+
named source profile plus automatically refreshed role profiles. Explicitly
|
|
165
|
+
reject standard `AWS_ACCESS_KEY_ID` variables because they can bypass the
|
|
166
|
+
intended assume-role profile.
|
|
164
167
|
|
|
165
168
|
When the repository contains concrete non-secret AWS metadata (role ARNs, account aliases,
|
|
166
169
|
profile names, regions, or ExternalId values), include it in an `awsProfiles` inventory array so
|
|
167
170
|
`/lisa:generate-claude-remote-build-script` can write matching `~/.aws/config` profiles. Never
|
|
168
|
-
invent account IDs, ExternalIds, role names, or regions. If AWS is detected
|
|
169
|
-
is absent,
|
|
170
|
-
|
|
171
|
+
invent account IDs, ExternalIds, role names, or regions. If AWS is detected
|
|
172
|
+
but profile metadata is absent, require a cdkstarter bootstrap bundle rather
|
|
173
|
+
than asking the repository to reconstruct account metadata by hand.
|
|
171
174
|
|
|
172
175
|
5. **MCP servers** — read every committed `.mcp.json`. For each server report transport and auth.
|
|
173
176
|
Project-scoped HTTP/SSE servers with no interactive auth are `OK`. Flag stdio servers as
|
|
@@ -276,14 +279,14 @@ unsuffixed `…_TOKEN`/`…_KEY` is the simplest to set in a single-account rout
|
|
|
276
279
|
- Access: the personal API key inherits the user's workspace permissions — the user must be able to read/create/update Issues in the destination team.
|
|
277
280
|
|
|
278
281
|
### AWS — host-project operations, logs, deploys, CDK/Serverless/SST, or AWS SDK usage
|
|
279
|
-
- Headless substrate:
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
282
|
+
- Headless substrate: cdkstarter's vendor-neutral remote-agent kit. Its single
|
|
283
|
+
Secrets Manager SecretString contains the assume-only access key, ExternalId,
|
|
284
|
+
and per-account role metadata. `/lisa:setup-remote-aws` writes a private named
|
|
285
|
+
source profile and renewable role profiles; agents use
|
|
286
|
+
`aws --profile <profile> ...`.
|
|
287
|
+
- Env: `LISA_AWS_BOOTSTRAP_JSON` (secret, required) and
|
|
288
|
+
`LISA_REMOTE_AGENT` (plain platform label). Do not set `AWS_ACCESS_KEY_ID` or
|
|
289
|
+
`AWS_SECRET_ACCESS_KEY` in the remote environment.
|
|
287
290
|
- Allowlist: `*.amazonaws.com`, or narrower service hosts such as `sts.amazonaws.com`,
|
|
288
291
|
`logs.<region>.amazonaws.com`, `cloudwatch.<region>.amazonaws.com`,
|
|
289
292
|
`xray.<region>.amazonaws.com`, `ssm.<region>.amazonaws.com`, and service-specific endpoints the
|
|
@@ -366,7 +369,7 @@ so the generator can render acquisition comments into its template:
|
|
|
366
369
|
{
|
|
367
370
|
"name": "dev",
|
|
368
371
|
"roleArn": "arn:aws:iam::<account-id>:role/<role-name>",
|
|
369
|
-
"credentialSource": "
|
|
372
|
+
"credentialSource": "lisa-remote-agent-bootstrap",
|
|
370
373
|
"externalId": "<project-external-id>",
|
|
371
374
|
"region": "us-east-1"
|
|
372
375
|
}
|
|
@@ -71,9 +71,9 @@ tracker/source, plus the host project's own package manager and tooling — not
|
|
|
71
71
|
include `JAM_PAT`, `SONAR_TOKEN`, or similar documented substrate env vars
|
|
72
72
|
only as optional secrets, with their acquire/scope comments when the analysis
|
|
73
73
|
supplied them. Never invent values or promote dormant substrates to required.
|
|
74
|
-
When AWS entries are present, list
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
When AWS entries are present, list only `LISA_AWS_BOOTSTRAP_JSON` as the
|
|
75
|
+
required secret and `LISA_REMOTE_AGENT=claude` as plain configuration. Never
|
|
76
|
+
emit standard `AWS_ACCESS_KEY_ID` variables and never recommend `aws sso login`.
|
|
77
77
|
|
|
78
78
|
3a. **Emit substrate setup snippets.** When the inventory marks an MCP
|
|
79
79
|
`headlessUsable: true` through a documented substrate, render the matching
|
|
@@ -91,18 +91,11 @@ tracker/source, plus the host project's own package manager and tooling — not
|
|
|
91
91
|
transport supports static-token auth. Do not print a Jam `.mcp.json` header snippet because
|
|
92
92
|
Jam's preferred headless substrate is its PAT-authenticated CLI.
|
|
93
93
|
|
|
94
|
-
3b. **
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
`credential_source = Environment`, `region` when present, and `external_id` when present.
|
|
100
|
-
- Keep regions and ExternalIds as non-secret project metadata from the inventory; never invent
|
|
101
|
-
account IDs, role names, profile names, regions, or ExternalIds.
|
|
102
|
-
- Warn and skip profile writing when AWS profile metadata is absent, while still listing the
|
|
103
|
-
required `AWS_*` env var names in the secret template.
|
|
104
|
-
- Include a comment that agents should use `aws --profile <name> ...` and must not run
|
|
105
|
-
`aws sso login` in headless routines.
|
|
94
|
+
3b. **Install the shared AWS bootstrap.** When AWS is present, invoke
|
|
95
|
+
`/lisa:setup-remote-aws --platform=claude`. Reuse the resulting
|
|
96
|
+
`scripts/remote-agent-aws-setup.sh`; do not generate a second credential or
|
|
97
|
+
profile implementation. Add `bash scripts/remote-agent-aws-setup.sh` to the
|
|
98
|
+
generated cloud setup after required package installation.
|
|
106
99
|
|
|
107
100
|
4. **Emit the allowlist + gaps notice.** List any custom domains the setup or runtime reaches
|
|
108
101
|
(from `networkAccess.allowlistDomains`, falling back to legacy `allowlistDomains`) that the user
|
|
@@ -139,9 +132,9 @@ shape, not a fixed payload):
|
|
|
139
132
|
# # Note: GH_TOKEN is for gh CLI only. Raw git uses Claude's connected-GitHub proxy/identity;
|
|
140
133
|
# # sibling repos reached only by raw git do not need to be in this token scope.
|
|
141
134
|
# - GH_TOKEN=<token> # REQUIRED, github is the active tracker+source
|
|
142
|
-
# -
|
|
143
|
-
#
|
|
144
|
-
# -
|
|
135
|
+
# - LISA_AWS_BOOTSTRAP_JSON=<complete SecretString> # REQUIRED for AWS
|
|
136
|
+
# PLAIN:
|
|
137
|
+
# - LISA_REMOTE_AGENT=claude
|
|
145
138
|
# NETWORK: set the environment to Custom and allowlist these non-default domains if not on Full:
|
|
146
139
|
# - <networkAccess.allowlistDomains, if any>
|
|
147
140
|
set -uo pipefail
|
|
@@ -184,13 +177,8 @@ need gh || (sudo apt-get update -y && sudo apt-get install -y gh)
|
|
|
184
177
|
need jq || sudo apt-get install -y jq
|
|
185
178
|
require gh; require jq
|
|
186
179
|
|
|
187
|
-
# --- AWS assume-role profiles, when inventory
|
|
188
|
-
|
|
189
|
-
mkdir -p "$HOME/.aws"
|
|
190
|
-
# Generated stanzas use credential_source=Environment and contain no secrets.
|
|
191
|
-
# Agents should run: aws --profile <profile> ...
|
|
192
|
-
# Do not run aws sso login in headless routines; SSO requires an interactive browser/device flow.
|
|
193
|
-
fi
|
|
180
|
+
# --- AWS assume-role profiles, when AWS inventory is active ---
|
|
181
|
+
bash scripts/remote-agent-aws-setup.sh
|
|
194
182
|
|
|
195
183
|
# --- optional, only with --include-optional ---
|
|
196
184
|
# (docker / ruby / chromium / etc., guarded)
|
|
@@ -213,8 +201,8 @@ to stop are: the analysis could not run, or the `--out` path is not writable.
|
|
|
213
201
|
raw git/cross-repo clone guidance must not expand the token scope to sibling repositories.
|
|
214
202
|
- Never emit an install for a tool the analysis did not surface, and never install `OPTIONAL` tools
|
|
215
203
|
unless `--include-optional` is set.
|
|
216
|
-
- When AWS is in the inventory,
|
|
217
|
-
|
|
204
|
+
- When AWS is in the inventory, reuse `/lisa:setup-remote-aws` and its shared
|
|
205
|
+
setup script; never emit a second profile implementation or `aws sso login`.
|
|
218
206
|
- Prefer `networkAccess.allowlistDomains` over legacy top-level `allowlistDomains`; never emit
|
|
219
207
|
domains already covered by the routine environment's default Trusted list.
|
|
220
208
|
- Keep the script idempotent and detect-before-install so it is safe to re-run and cache.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-setup-remote-aws
|
|
3
|
+
description: "Install and validate Lisa's…"
|
|
4
|
+
allowed-tools: ["Bash", "Read", "Write", "Glob", "Grep"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Set up remote-agent AWS access: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
Install the vendor-neutral AWS bootstrap into the current repository.
|
|
10
|
+
|
|
11
|
+
## Arguments
|
|
12
|
+
|
|
13
|
+
- `--platform=all|claude|codex|cursor|copilot|agy|opencode` (default `all`)
|
|
14
|
+
- `--project=<path>` (default current working directory)
|
|
15
|
+
- `--secret-name=<name>` (default `remote-agent-credentials`)
|
|
16
|
+
|
|
17
|
+
## Procedure
|
|
18
|
+
|
|
19
|
+
1. Resolve the plugin root from `PLUGIN_ROOT`, `CLAUDE_PLUGIN_ROOT`,
|
|
20
|
+
`CURSOR_PLUGIN_ROOT`, or the installed `@codyswann/lisa/plugins/lisa` package.
|
|
21
|
+
2. Run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
node "$PLUGIN_ROOT/scripts/install-remote-agent-aws.mjs" $ARGUMENTS
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
3. Inspect every path returned by the installer. It must install
|
|
28
|
+
`scripts/remote-agent-aws-setup.sh`; for Cursor it merges
|
|
29
|
+
`.cursor/environment.json`; for Copilot it creates or merges
|
|
30
|
+
`.github/workflows/copilot-setup-steps.yml`; and it always writes
|
|
31
|
+
`docs/remote-agent-aws.md`.
|
|
32
|
+
4. Run `bash -n scripts/remote-agent-aws-setup.sh`.
|
|
33
|
+
5. Never write or request the actual bootstrap SecretString in repository
|
|
34
|
+
files. Tell the operator to retrieve the `remote-agent-credentials` secret
|
|
35
|
+
from the shared AWS account and paste its complete SecretString into the
|
|
36
|
+
platform secret named `LISA_AWS_BOOTSTRAP_JSON`. The default cdkstarter
|
|
37
|
+
secret name is `remote-agent-credentials`; downstream infrastructure may
|
|
38
|
+
configure a different name. Pass that name with `--secret-name` so the
|
|
39
|
+
generated runbook contains the exact retrieval command.
|
|
40
|
+
6. Report the external step that remains: configure that one secret in the
|
|
41
|
+
remote environment and launch a smoke session.
|
|
42
|
+
|
|
43
|
+
## Contract
|
|
44
|
+
|
|
45
|
+
A future coding agent is supported without an AWS change when its remote
|
|
46
|
+
environment provides a Linux shell, a setup/start hook, one opaque secret,
|
|
47
|
+
a writable home directory, and HTTPS access to AWS STS plus the allowed service
|
|
48
|
+
endpoints. Set `LISA_REMOTE_AGENT` to a stable lowercase platform label; it is
|
|
49
|
+
used only as `role_session_name`.
|
|
50
|
+
|
|
51
|
+
Do not create per-repository or per-agent IAM users. Do not expose the bootstrap
|
|
52
|
+
key through standard `AWS_ACCESS_KEY_ID` variables. Do not add production repair
|
|
53
|
+
permissions; production repair is a human-driven local-workstation workflow.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Install Lisa's vendor-neutral AWS CLI bootstrap and platform adapters for remote coding environments."
|
|
3
|
+
argument-hint: "[--platform=all|claude|codex|cursor|copilot|agy|opencode] [--project=<path>] [--secret-name=<name>]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use the /lisa-setup-remote-aws skill to install and validate remote coding-agent AWS access. $ARGUMENTS
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Install Lisa's vendor-neutral remote AWS bootstrap into a host project.
|
|
4
|
+
*
|
|
5
|
+
* This installer writes only non-secret repository artifacts. The bootstrap
|
|
6
|
+
* SecretString is configured directly in each remote-agent platform.
|
|
7
|
+
* @module install-remote-agent-aws
|
|
8
|
+
*/
|
|
9
|
+
import {
|
|
10
|
+
chmodSync,
|
|
11
|
+
existsSync,
|
|
12
|
+
mkdirSync,
|
|
13
|
+
readFileSync,
|
|
14
|
+
writeFileSync,
|
|
15
|
+
} from "node:fs";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
const SCRIPT_NAME = "remote-agent-aws-setup.sh";
|
|
20
|
+
const INSTALL_COMMAND = `bash scripts/${SCRIPT_NAME}`;
|
|
21
|
+
const VALID_PLATFORMS = new Set([
|
|
22
|
+
"all",
|
|
23
|
+
"claude",
|
|
24
|
+
"codex",
|
|
25
|
+
"cursor",
|
|
26
|
+
"copilot",
|
|
27
|
+
"agy",
|
|
28
|
+
"opencode",
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
function parseArguments(arguments_) {
|
|
32
|
+
let project = process.cwd();
|
|
33
|
+
let platform = "all";
|
|
34
|
+
let secretName = "remote-agent-credentials";
|
|
35
|
+
for (const argument of arguments_) {
|
|
36
|
+
if (argument.startsWith("--project=")) {
|
|
37
|
+
project = path.resolve(argument.slice("--project=".length));
|
|
38
|
+
} else if (argument.startsWith("--platform=")) {
|
|
39
|
+
platform = argument.slice("--platform=".length).toLowerCase();
|
|
40
|
+
} else if (argument.startsWith("--secret-name=")) {
|
|
41
|
+
secretName = argument.slice("--secret-name=".length);
|
|
42
|
+
} else {
|
|
43
|
+
throw new Error(`Unknown argument: ${argument}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!VALID_PLATFORMS.has(platform)) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Unsupported platform '${platform}'. Expected one of: ${[...VALID_PLATFORMS].join(", ")}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (!/^[A-Za-z0-9/_+=.@-]+$/.test(secretName)) {
|
|
52
|
+
throw new Error("--secret-name must be a valid Secrets Manager name");
|
|
53
|
+
}
|
|
54
|
+
return { project, platform, secretName };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function writeSetupScript(project) {
|
|
58
|
+
const source = path.join(
|
|
59
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
60
|
+
SCRIPT_NAME
|
|
61
|
+
);
|
|
62
|
+
const destination = path.join(project, "scripts", SCRIPT_NAME);
|
|
63
|
+
mkdirSync(path.dirname(destination), { recursive: true });
|
|
64
|
+
writeFileSync(destination, readFileSync(source));
|
|
65
|
+
chmodSync(destination, 0o700);
|
|
66
|
+
return path.relative(project, destination);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function installCursorAdapter(project) {
|
|
70
|
+
const environmentPath = path.join(project, ".cursor", "environment.json");
|
|
71
|
+
mkdirSync(path.dirname(environmentPath), { recursive: true });
|
|
72
|
+
|
|
73
|
+
const environment = existsSync(environmentPath)
|
|
74
|
+
? JSON.parse(readFileSync(environmentPath, "utf8"))
|
|
75
|
+
: {};
|
|
76
|
+
const existingInstall = environment.install;
|
|
77
|
+
if (existingInstall !== undefined && typeof existingInstall !== "string") {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`.cursor/environment.json install must be a string before Lisa can merge ${INSTALL_COMMAND}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const lisaInstall = `LISA_REMOTE_AGENT=cursor ${INSTALL_COMMAND}`;
|
|
83
|
+
if (!existingInstall?.includes(SCRIPT_NAME)) {
|
|
84
|
+
environment.install = existingInstall
|
|
85
|
+
? `${lisaInstall} && ${existingInstall}`
|
|
86
|
+
: lisaInstall;
|
|
87
|
+
}
|
|
88
|
+
writeFileSync(environmentPath, `${JSON.stringify(environment, null, 2)}\n`);
|
|
89
|
+
return path.relative(project, environmentPath);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function installCopilotAdapter(project) {
|
|
93
|
+
const workflowPath = path.join(
|
|
94
|
+
project,
|
|
95
|
+
".github",
|
|
96
|
+
"workflows",
|
|
97
|
+
"copilot-setup-steps.yml"
|
|
98
|
+
);
|
|
99
|
+
mkdirSync(path.dirname(workflowPath), { recursive: true });
|
|
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");
|
|
108
|
+
const step = [
|
|
109
|
+
` - name: ${marker}`,
|
|
110
|
+
" env:",
|
|
111
|
+
secretEnvironment,
|
|
112
|
+
" LISA_REMOTE_AGENT: copilot",
|
|
113
|
+
` run: ${INSTALL_COMMAND}`,
|
|
114
|
+
].join("\n");
|
|
115
|
+
|
|
116
|
+
if (!existsSync(workflowPath)) {
|
|
117
|
+
writeFileSync(
|
|
118
|
+
workflowPath,
|
|
119
|
+
[
|
|
120
|
+
'name: "Copilot Setup Steps"',
|
|
121
|
+
"on:",
|
|
122
|
+
" workflow_dispatch:",
|
|
123
|
+
"jobs:",
|
|
124
|
+
" copilot-setup-steps:",
|
|
125
|
+
" runs-on: ubuntu-latest",
|
|
126
|
+
" permissions:",
|
|
127
|
+
" contents: read",
|
|
128
|
+
" steps:",
|
|
129
|
+
checkoutStep,
|
|
130
|
+
step,
|
|
131
|
+
"",
|
|
132
|
+
].join("\n")
|
|
133
|
+
);
|
|
134
|
+
return path.relative(project, workflowPath);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const current = readFileSync(workflowPath, "utf8");
|
|
138
|
+
const lines = current.split("\n");
|
|
139
|
+
const jobIndex = lines.findIndex(line =>
|
|
140
|
+
/^\s{2}copilot-setup-steps:\s*$/.test(line)
|
|
141
|
+
);
|
|
142
|
+
const jobEndIndex = lines.findIndex(
|
|
143
|
+
(line, index) => index > jobIndex && /^\s{2}\S[^:]*:\s*$/.test(line)
|
|
144
|
+
);
|
|
145
|
+
const stepsIndex = lines.findIndex(
|
|
146
|
+
(line, index) =>
|
|
147
|
+
index > jobIndex &&
|
|
148
|
+
(jobEndIndex < 0 || index < jobEndIndex) &&
|
|
149
|
+
/^\s{4}steps:\s*$/.test(line)
|
|
150
|
+
);
|
|
151
|
+
if (jobIndex < 0 || stepsIndex < 0) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
"Existing copilot-setup-steps.yml must contain jobs.copilot-setup-steps.steps before Lisa can merge the AWS bootstrap step"
|
|
154
|
+
);
|
|
155
|
+
}
|
|
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);
|
|
245
|
+
writeFileSync(workflowPath, lines.join("\n"));
|
|
246
|
+
return path.relative(project, workflowPath);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function writeGuide(project, platform, secretName) {
|
|
250
|
+
const guidePath = path.join(project, "docs", "remote-agent-aws.md");
|
|
251
|
+
mkdirSync(path.dirname(guidePath), { recursive: true });
|
|
252
|
+
writeFileSync(
|
|
253
|
+
guidePath,
|
|
254
|
+
`# Remote coding-agent AWS access
|
|
255
|
+
|
|
256
|
+
## Context
|
|
257
|
+
|
|
258
|
+
This repository uses Lisa's vendor-neutral AWS bootstrap. Configure the complete
|
|
259
|
+
Secrets Manager SecretString as the secret \`LISA_AWS_BOOTSTRAP_JSON\`; do not
|
|
260
|
+
split it into \`AWS_ACCESS_KEY_ID\` and \`AWS_SECRET_ACCESS_KEY\` environment
|
|
261
|
+
variables. The setup script writes a named source profile whose only permission
|
|
262
|
+
is \`sts:AssumeRole\`, then creates automatically refreshed role profiles.
|
|
263
|
+
|
|
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
|
|
270
|
+
|
|
271
|
+
| Runtime | Setup |
|
|
272
|
+
|---|---|
|
|
273
|
+
| Claude | Add \`LISA_AWS_BOOTSTRAP_JSON\` to the cloud environment, set \`LISA_REMOTE_AGENT=claude\`, and run \`${INSTALL_COMMAND}\` in the setup script. |
|
|
274
|
+
| Codex | Add \`LISA_AWS_BOOTSTRAP_JSON\` as a setup secret, set \`LISA_REMOTE_AGENT=codex\`, and run \`${INSTALL_COMMAND}\` in the setup script. Permit required AWS endpoints during the agent phase. |
|
|
275
|
+
| Cursor | Add the secret to the selected cloud environment. \`.cursor/environment.json\` runs the setup script with \`LISA_REMOTE_AGENT=cursor\`. |
|
|
276
|
+
| Copilot | Add the value as an organization-level **Agents** secret. \`copilot-setup-steps.yml\` runs the setup script with \`LISA_REMOTE_AGENT=copilot\`. |
|
|
277
|
+
| Antigravity | Direct AWS CLI is supported only on a user-managed remote host. Google's managed preview currently documents proxy-injected HTTP credentials, not arbitrary AWS credential files. |
|
|
278
|
+
| OpenCode | Run the setup script on the VPS/container that hosts \`opencode serve\`, with \`LISA_REMOTE_AGENT=opencode\`. OpenCode does not supply the host. |
|
|
279
|
+
|
|
280
|
+
Generated for: \`${platform}\`.
|
|
281
|
+
|
|
282
|
+
## Implementation
|
|
283
|
+
|
|
284
|
+
The bootstrap bundle defines the available profile names. \`dev\` is the
|
|
285
|
+
default when present. Select another account explicitly, for example:
|
|
286
|
+
|
|
287
|
+
\`\`\`bash
|
|
288
|
+
aws --profile staging sts get-caller-identity
|
|
289
|
+
aws --profile production cloudformation describe-stacks
|
|
290
|
+
\`\`\`
|
|
291
|
+
|
|
292
|
+
Production and shared profiles are observer-only. Production repair remains a
|
|
293
|
+
human-driven local-workstation operation.
|
|
294
|
+
|
|
295
|
+
After the infrastructure pipeline deploys the remote-agent IAM stacks, retrieve
|
|
296
|
+
the complete bundle from the shared account. Do not extract or distribute its
|
|
297
|
+
individual access-key fields:
|
|
298
|
+
|
|
299
|
+
\`\`\`bash
|
|
300
|
+
aws --profile shared secretsmanager get-secret-value \\
|
|
301
|
+
--secret-id ${secretName} \\
|
|
302
|
+
--query SecretString \\
|
|
303
|
+
--output text
|
|
304
|
+
\`\`\`
|
|
305
|
+
|
|
306
|
+
Store that exact output as the masked secret \`LISA_AWS_BOOTSTRAP_JSON\` on each
|
|
307
|
+
remote-agent platform. Run the setup command and require its live
|
|
308
|
+
\`sts:GetCallerIdentity\` check to pass before considering the platform ready.
|
|
309
|
+
|
|
310
|
+
## Notes
|
|
311
|
+
|
|
312
|
+
Rotate by replacing the bootstrap IAM access key through infrastructure and
|
|
313
|
+
then updating this one secret on every configured platform. Disabling or
|
|
314
|
+
deleting the bootstrap user immediately prevents new role sessions.
|
|
315
|
+
`
|
|
316
|
+
);
|
|
317
|
+
return path.relative(project, guidePath);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function installRemoteAgentAws(arguments_ = process.argv.slice(2)) {
|
|
321
|
+
const { project, platform, secretName } = parseArguments(arguments_);
|
|
322
|
+
if (!existsSync(project))
|
|
323
|
+
throw new Error(`Project does not exist: ${project}`);
|
|
324
|
+
|
|
325
|
+
const written = [writeSetupScript(project)];
|
|
326
|
+
if (platform === "all" || platform === "cursor") {
|
|
327
|
+
written.push(installCursorAdapter(project));
|
|
328
|
+
}
|
|
329
|
+
if (platform === "all" || platform === "copilot") {
|
|
330
|
+
written.push(installCopilotAdapter(project));
|
|
331
|
+
}
|
|
332
|
+
written.push(writeGuide(project, platform, secretName));
|
|
333
|
+
return { project, platform, secretName, written };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
337
|
+
try {
|
|
338
|
+
process.stdout.write(
|
|
339
|
+
`${JSON.stringify(installRemoteAgentAws(), null, 2)}\n`
|
|
340
|
+
);
|
|
341
|
+
} catch (error) {
|
|
342
|
+
process.stderr.write(
|
|
343
|
+
`install-remote-agent-aws: ${error instanceof Error ? error.message : String(error)}\n`
|
|
344
|
+
);
|
|
345
|
+
process.exitCode = 1;
|
|
346
|
+
}
|
|
347
|
+
}
|