@codyswann/lisa 2.227.0 → 2.229.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all/copy-contents/gitignore +2 -0
- package/dist/cli/ui-cmd.d.ts +30 -2
- package/dist/cli/ui-cmd.d.ts.map +1 -1
- package/dist/cli/ui-cmd.js +191 -14
- package/dist/cli/ui-cmd.js.map +1 -1
- package/dist/cli/ui-status-json.d.ts +29 -0
- package/dist/cli/ui-status-json.d.ts.map +1 -0
- package/dist/cli/ui-status-json.js +162 -0
- package/dist/cli/ui-status-json.js.map +1 -0
- package/dist/cli/ui-status.d.ts +42 -0
- package/dist/cli/ui-status.d.ts.map +1 -0
- package/dist/cli/ui-status.js +221 -0
- package/dist/cli/ui-status.js.map +1 -0
- package/dist/core/lisa.d.ts +74 -1
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +165 -25
- package/dist/core/lisa.js.map +1 -1
- package/package.json +2 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- 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-cursor/.claude-plugin/plugin.json +1 -1
- 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/scripts/install-claude-plugins.sh +89 -9
- package/ui/README.md +16 -0
- package/ui/index.html +299 -8
|
@@ -55,9 +55,23 @@ detect_lisa_stack() {
|
|
|
55
55
|
done
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
# One-time migration marker: once the user-wide Codex registrations are
|
|
59
|
+
# confirmed retired, skip the `codex plugin marketplace list` probe (a codex
|
|
60
|
+
# CLI spawn) on every subsequent install.
|
|
61
|
+
CODEX_RETIRE_MARKER="$HOME/.codex/.lisa-legacy-plugins-retired"
|
|
62
|
+
|
|
63
|
+
write_codex_retire_marker() {
|
|
64
|
+
mkdir -p "$HOME/.codex" 2>/dev/null && touch "$CODEX_RETIRE_MARKER" 2>/dev/null || true
|
|
65
|
+
}
|
|
66
|
+
|
|
58
67
|
remove_user_wide_codex_lisa_plugins() {
|
|
59
68
|
command -v codex >/dev/null 2>&1 || return 0
|
|
60
69
|
|
|
70
|
+
# The marker is written only after the cleanup actually ran or was confirmed
|
|
71
|
+
# unnecessary — never on the CODEX_THREAD_ID deferral below, which must
|
|
72
|
+
# retry on a later install (see fix/defer-codex-retirement-postinstall).
|
|
73
|
+
[ -f "$CODEX_RETIRE_MARKER" ] && return 0
|
|
74
|
+
|
|
61
75
|
# Removing a plugin relocates its cache directory. A running Codex session
|
|
62
76
|
# has already captured absolute hook paths, so removal inside that session
|
|
63
77
|
# turns every later hook invocation into an EPIPE/broken-pipe failure. Defer
|
|
@@ -84,6 +98,7 @@ remove_user_wide_codex_lisa_plugins() {
|
|
|
84
98
|
}
|
|
85
99
|
});
|
|
86
100
|
'; then
|
|
101
|
+
write_codex_retire_marker
|
|
87
102
|
return 0
|
|
88
103
|
fi
|
|
89
104
|
|
|
@@ -102,6 +117,7 @@ remove_user_wide_codex_lisa_plugins() {
|
|
|
102
117
|
codex plugin remove "${plugin}@lisa" </dev/null >/dev/null 2>&1 || true
|
|
103
118
|
done
|
|
104
119
|
codex plugin marketplace remove lisa </dev/null >/dev/null 2>&1 || true
|
|
120
|
+
write_codex_retire_marker
|
|
105
121
|
}
|
|
106
122
|
|
|
107
123
|
# Skip running Lisa's full template engine on itself — the Lisa repo IS the
|
|
@@ -144,8 +160,23 @@ fi
|
|
|
144
160
|
# each path is written exactly once by its most-specific stack — there is no
|
|
145
161
|
# intermediate clobbered state to be interrupted in. See src/core/lisa.ts
|
|
146
162
|
# loadCopyOverwriteOwnership.
|
|
163
|
+
# Dedup guard: when the host's own package.json postinstall already invokes
|
|
164
|
+
# Lisa (wired by the ensure-lisa-postinstall migration), the package manager
|
|
165
|
+
# will run that root lifecycle script in this same install — running the full
|
|
166
|
+
# apply here too doubles the work for no benefit. The package-side apply
|
|
167
|
+
# exists for the bootstrap chicken-and-egg (#1017: a never-applied project
|
|
168
|
+
# has no postinstall to wire itself), so it still runs whenever the marker is
|
|
169
|
+
# absent. Detection matches LISA_MARKER in ensure-lisa-postinstall.ts.
|
|
170
|
+
HOST_HAS_LISA_POSTINSTALL="$(node -e "
|
|
171
|
+
const scripts = require('$PROJECT_ROOT/package.json').scripts || {};
|
|
172
|
+
const postinstall = scripts.postinstall || '';
|
|
173
|
+
process.stdout.write(postinstall.includes('node_modules/@codyswann/lisa/dist/index.js') ? 'true' : 'false');
|
|
174
|
+
" 2>/dev/null || echo false)"
|
|
175
|
+
|
|
147
176
|
if [ "$IS_LISA_SELF" != "true" ] && [ -z "${CI:-}" ]; then
|
|
148
|
-
if
|
|
177
|
+
if [ "$HOST_HAS_LISA_POSTINSTALL" = "true" ]; then
|
|
178
|
+
echo "Lisa apply deferred to the host project's own postinstall script."
|
|
179
|
+
elif ! LISA_BOOTSTRAP=1 node "$LISA_DIR/dist/index.js" --yes --skip-git-check "$PROJECT_ROOT"; then
|
|
149
180
|
echo "⚠️ Warning: Lisa template application failed. Migration may be incomplete." >&2
|
|
150
181
|
fi
|
|
151
182
|
fi
|
|
@@ -259,6 +290,38 @@ fi
|
|
|
259
290
|
# Install plugins only when claude CLI is available
|
|
260
291
|
if ! command -v claude &>/dev/null; then exit 0; fi
|
|
261
292
|
|
|
293
|
+
# Version-gated plugin sync (perf). `claude plugin marketplace update` is a
|
|
294
|
+
# network git pull of the whole Lisa repo and every `claude plugin install`
|
|
295
|
+
# spawns the Claude CLI (seconds each); re-running all of it on every install
|
|
296
|
+
# made a single `bun add` cost minutes. A full sync still runs whenever the
|
|
297
|
+
# installed Lisa version differs from the .claude/.lisa-plugins-synced marker
|
|
298
|
+
# (shared with Lisa.apply's registerPlugins); between version changes only
|
|
299
|
+
# plugins missing for this project are installed, so the self-heal property
|
|
300
|
+
# (a removed plugin comes back on the next install) survives at the cost of a
|
|
301
|
+
# single `claude plugin list` call.
|
|
302
|
+
LISA_VERSION="$(node -e "console.log(require('$LISA_DIR/package.json').version || '')" 2>/dev/null || true)"
|
|
303
|
+
PLUGIN_SYNC_MARKER="$PROJECT_ROOT/.claude/.lisa-plugins-synced"
|
|
304
|
+
FORCE_PLUGIN_SYNC=true
|
|
305
|
+
if [ -n "$LISA_VERSION" ] && [ -f "$PLUGIN_SYNC_MARKER" ] \
|
|
306
|
+
&& [ "$(cat "$PLUGIN_SYNC_MARKER" 2>/dev/null)" = "$LISA_VERSION" ]; then
|
|
307
|
+
FORCE_PLUGIN_SYNC=false
|
|
308
|
+
fi
|
|
309
|
+
|
|
310
|
+
INSTALLED_PLUGINS_FOR_PROJECT=""
|
|
311
|
+
if command -v jq >/dev/null 2>&1; then
|
|
312
|
+
INSTALLED_PLUGINS_FOR_PROJECT="$(claude plugin list --json 2>/dev/null \
|
|
313
|
+
| jq -r --arg cwd "$PROJECT_ROOT" '.[] | select(.projectPath == $cwd) | .id' 2>/dev/null || true)"
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
install_plugin_if_missing() {
|
|
317
|
+
local plugin="$1"
|
|
318
|
+
if [ "$FORCE_PLUGIN_SYNC" != "true" ] && [ -n "$INSTALLED_PLUGINS_FOR_PROJECT" ] \
|
|
319
|
+
&& printf '%s\n' "$INSTALLED_PLUGINS_FOR_PROJECT" | grep -qxF "$plugin"; then
|
|
320
|
+
return 0
|
|
321
|
+
fi
|
|
322
|
+
claude plugin install "$plugin" --scope project </dev/null 2>&1 || true
|
|
323
|
+
}
|
|
324
|
+
|
|
262
325
|
# The Lisa marketplace is registered via extraKnownMarketplaces in .claude/settings.json
|
|
263
326
|
# pointing to the GitHub repo (CodySwannGT/lisa). Built plugins are committed to the repo
|
|
264
327
|
# so relative paths in marketplace.json resolve correctly.
|
|
@@ -272,7 +335,10 @@ if ! command -v claude &>/dev/null; then exit 0; fi
|
|
|
272
335
|
# "Update now" action with: "Local plugins cannot be updated remotely."
|
|
273
336
|
# If we detect a non-github marketplace named "lisa", uninstall the plugins
|
|
274
337
|
# sourced from it and remove the registration so the github source can take over.
|
|
275
|
-
|
|
338
|
+
# Gated on version change: the stale registration can only exist in state
|
|
339
|
+
# written by an older Lisa, so re-probing it on every same-version install
|
|
340
|
+
# spends a claude CLI spawn to re-confirm a fact that cannot have changed.
|
|
341
|
+
if [ "$FORCE_PLUGIN_SYNC" = "true" ] && command -v jq >/dev/null 2>&1; then
|
|
276
342
|
STALE_LISA_SOURCE=$(claude plugin marketplace list --json 2>/dev/null \
|
|
277
343
|
| jq -r '.[] | select(.name == "lisa" and .source != "github") | .source' 2>/dev/null \
|
|
278
344
|
| head -n 1)
|
|
@@ -339,7 +405,12 @@ heal_local_classification() {
|
|
|
339
405
|
|
|
340
406
|
if command -v jq >/dev/null 2>&1; then
|
|
341
407
|
# Refresh the cached marketplace.json so we're reading the latest schema.
|
|
342
|
-
|
|
408
|
+
# Gated: the pull is a network git fetch of the whole Lisa repo; once this
|
|
409
|
+
# version's plugins are synced and the project's heal-v2 marker exists, a
|
|
410
|
+
# fresh pull cannot change the outcome of the schema check below.
|
|
411
|
+
if [ "$FORCE_PLUGIN_SYNC" = "true" ] || [ ! -f "$PROJECT_ROOT/.claude/$HEAL_V2_MARKER_NAME" ]; then
|
|
412
|
+
claude plugin marketplace update lisa </dev/null >/dev/null 2>&1 || true
|
|
413
|
+
fi
|
|
343
414
|
|
|
344
415
|
MARKETPLACE_JSON_PATH="$HOME/.claude/plugins/marketplaces/lisa/.claude-plugin/marketplace.json"
|
|
345
416
|
NEW_SCHEMA="false"
|
|
@@ -364,8 +435,8 @@ if command -v jq >/dev/null 2>&1; then
|
|
|
364
435
|
fi
|
|
365
436
|
fi
|
|
366
437
|
|
|
367
|
-
# Always
|
|
368
|
-
|
|
438
|
+
# Always ensure the base plugin (universal governance for all projects)
|
|
439
|
+
install_plugin_if_missing "lisa@lisa"
|
|
369
440
|
|
|
370
441
|
# Detect which stack plugin to install from .claude/settings.json
|
|
371
442
|
LISA_STACK="$(detect_lisa_stack "$SETTINGS_FILE")"
|
|
@@ -374,13 +445,13 @@ LISA_STACK="$(detect_lisa_stack "$SETTINGS_FILE")"
|
|
|
374
445
|
case "$LISA_STACK" in
|
|
375
446
|
rails) ;; # Rails doesn't get typescript plugin
|
|
376
447
|
*)
|
|
377
|
-
|
|
448
|
+
install_plugin_if_missing "lisa-typescript@lisa"
|
|
378
449
|
;;
|
|
379
450
|
esac
|
|
380
451
|
|
|
381
452
|
# Install stack-specific plugin if not plain typescript
|
|
382
453
|
if [ -n "$LISA_STACK" ]; then
|
|
383
|
-
|
|
454
|
+
install_plugin_if_missing "lisa-${LISA_STACK}@lisa"
|
|
384
455
|
fi
|
|
385
456
|
|
|
386
457
|
# Uninstall old monolithic plugins during migration
|
|
@@ -403,7 +474,7 @@ for plugin in \
|
|
|
403
474
|
"skill-creator@claude-plugins-official" \
|
|
404
475
|
"atlassian@claude-plugins-official" \
|
|
405
476
|
"safety-net@cc-marketplace"; do
|
|
406
|
-
|
|
477
|
+
install_plugin_if_missing "$plugin"
|
|
407
478
|
done
|
|
408
479
|
|
|
409
480
|
# Install stack-specific third-party plugins
|
|
@@ -411,6 +482,15 @@ if [ "$LISA_STACK" = "expo" ] || [ "$LISA_STACK" = "harper-fabric" ]; then
|
|
|
411
482
|
for plugin in \
|
|
412
483
|
"playwright@claude-plugins-official" \
|
|
413
484
|
"posthog@claude-plugins-official"; do
|
|
414
|
-
|
|
485
|
+
install_plugin_if_missing "$plugin"
|
|
415
486
|
done
|
|
416
487
|
fi
|
|
488
|
+
|
|
489
|
+
# Record the fully-synced Lisa version so same-version installs skip the
|
|
490
|
+
# marketplace pulls and forced reinstalls above. Written last so any earlier
|
|
491
|
+
# failure exits (set -e) without recording a sync that did not finish.
|
|
492
|
+
if [ -n "$LISA_VERSION" ]; then
|
|
493
|
+
mkdir -p "$PROJECT_ROOT/.claude" 2>/dev/null \
|
|
494
|
+
&& printf '%s' "$LISA_VERSION" > "$PLUGIN_SYNC_MARKER" 2>/dev/null \
|
|
495
|
+
|| true
|
|
496
|
+
fi
|
package/ui/README.md
CHANGED
|
@@ -27,6 +27,21 @@ live config it falls back to Lisa's shipped defaults:
|
|
|
27
27
|
open ui/index.html
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
## Live status contract
|
|
31
|
+
|
|
32
|
+
When served by `lisa ui`, the page fetches `GET /api/status` from the same
|
|
33
|
+
loopback origin and renders each probe in a compact status strip. A probe has
|
|
34
|
+
exactly one of three states: a real `value`, `unknown` with both a
|
|
35
|
+
machine-readable reason and human-readable message, or `not-applicable`.
|
|
36
|
+
Throwing and timing-out probes degrade independently to `unknown`, so one
|
|
37
|
+
unavailable integration cannot block the page or cause a value to be invented.
|
|
38
|
+
Concurrent requests share one in-flight snapshot. In-process probes must be
|
|
39
|
+
asynchronous and cooperate with the supplied abort signal; JSON depth and size
|
|
40
|
+
budgets also bound result normalization. The first reference probe scopes
|
|
41
|
+
`gh auth status --active` to the project's origin hostname; the
|
|
42
|
+
remaining live integrations are delivered by the console work that builds on
|
|
43
|
+
this transport.
|
|
44
|
+
|
|
30
45
|
## Config sync (`lisa sync`)
|
|
31
46
|
|
|
32
47
|
`lisa sync [path] [--dry-run] [--json]` makes `.lisa.config.json` (with the
|
|
@@ -153,6 +168,7 @@ visible in the section.
|
|
|
153
168
|
| General (`harness`, `tracker`, `source`, `repo`, package manager) | `src/core/config.ts`, `plugins/src/base/rules/reference/config-resolution.md` |
|
|
154
169
|
| Project types (8 stacks + template strategies) | `src/detection/`, `src/strategies/`, `<stack>/` template dirs |
|
|
155
170
|
| Coding agents (claude/codex/cursor/agy/copilot/opencode/fleet) | `src/core/lisa.ts`, `scripts/generate-*-plugin-artifacts.mjs` |
|
|
171
|
+
| Remote Environment (variable presence + active-agent startup scripts) | `src/cli/ui-cmd.ts`, `plugins/src/base/scripts/remote-agent-aws-setup.sh` |
|
|
156
172
|
| Work tracker (JIRA / GitHub Issues / Linear) | `config-resolution.md`, `lisa-setup-*` skills |
|
|
157
173
|
| PRD source (Notion / Confluence / Linear / GitHub) | `config-resolution.md`, `lisa-setup-*` skills |
|
|
158
174
|
| Deploy & environments (`deploy.*`, `github.environments`) | `scripts/lisa-github-environments.sh` |
|
package/ui/index.html
CHANGED
|
@@ -289,6 +289,45 @@
|
|
|
289
289
|
margin-top: 3px;
|
|
290
290
|
max-width: 26ch;
|
|
291
291
|
}
|
|
292
|
+
.live-status-panel {
|
|
293
|
+
display: flex;
|
|
294
|
+
align-items: center;
|
|
295
|
+
gap: 12px;
|
|
296
|
+
min-height: 42px;
|
|
297
|
+
padding: 7px 20px;
|
|
298
|
+
background: var(--surface2);
|
|
299
|
+
border-bottom: 1px solid var(--line);
|
|
300
|
+
}
|
|
301
|
+
.live-status-panel h2 {
|
|
302
|
+
margin: 0;
|
|
303
|
+
font-size: 12px;
|
|
304
|
+
color: var(--muted);
|
|
305
|
+
text-transform: uppercase;
|
|
306
|
+
letter-spacing: 0.06em;
|
|
307
|
+
}
|
|
308
|
+
.live-status-list {
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: center;
|
|
311
|
+
flex-wrap: wrap;
|
|
312
|
+
gap: 8px;
|
|
313
|
+
}
|
|
314
|
+
.live-status-item {
|
|
315
|
+
display: inline-flex;
|
|
316
|
+
align-items: center;
|
|
317
|
+
gap: 6px;
|
|
318
|
+
padding: 3px 9px;
|
|
319
|
+
border: 1px solid var(--line);
|
|
320
|
+
border-radius: 99px;
|
|
321
|
+
background: var(--surface);
|
|
322
|
+
font-family: var(--mono);
|
|
323
|
+
font-size: 11px;
|
|
324
|
+
}
|
|
325
|
+
.live-status-item .status-why {
|
|
326
|
+
display: inline;
|
|
327
|
+
margin: 0;
|
|
328
|
+
max-width: 48ch;
|
|
329
|
+
overflow-wrap: anywhere;
|
|
330
|
+
}
|
|
292
331
|
|
|
293
332
|
.check-item {
|
|
294
333
|
display: flex;
|
|
@@ -1203,6 +1242,18 @@
|
|
|
1203
1242
|
◐
|
|
1204
1243
|
</button>
|
|
1205
1244
|
</header>
|
|
1245
|
+
<section
|
|
1246
|
+
class="live-status-panel"
|
|
1247
|
+
aria-labelledby="liveStatusHeading"
|
|
1248
|
+
aria-live="polite"
|
|
1249
|
+
aria-atomic="true"
|
|
1250
|
+
role="status"
|
|
1251
|
+
>
|
|
1252
|
+
<h2 id="liveStatusHeading">Live status</h2>
|
|
1253
|
+
<div class="live-status-list" id="liveStatusList" role="list">
|
|
1254
|
+
<span class="live-status-item" role="listitem">Loading…</span>
|
|
1255
|
+
</div>
|
|
1256
|
+
</section>
|
|
1206
1257
|
<div class="app">
|
|
1207
1258
|
<nav class="sidebar" id="nav" aria-label="Settings sections"></nav>
|
|
1208
1259
|
<main class="main" id="main"></main>
|
|
@@ -1259,6 +1310,7 @@
|
|
|
1259
1310
|
"stacks",
|
|
1260
1311
|
"starters",
|
|
1261
1312
|
"agents",
|
|
1313
|
+
"remote",
|
|
1262
1314
|
],
|
|
1263
1315
|
},
|
|
1264
1316
|
{
|
|
@@ -2348,6 +2400,33 @@
|
|
|
2348
2400
|
],
|
|
2349
2401
|
};
|
|
2350
2402
|
|
|
2403
|
+
/* -------------------------- Remote environment -------------------------- */
|
|
2404
|
+
DATA.sections.remote = {
|
|
2405
|
+
title: "Remote Environment",
|
|
2406
|
+
desc: "AWS CLI bootstrap readiness for the project's active remote coding agents. Secret values are never sent to the browser — the console receives presence checks only.",
|
|
2407
|
+
src: "scripts/remote-agent-aws-setup.sh · remote platform environments",
|
|
2408
|
+
blocks: [
|
|
2409
|
+
{
|
|
2410
|
+
type: "table",
|
|
2411
|
+
card: "Variables",
|
|
2412
|
+
note: "Set status reflects variables visible to the lisa ui process; values remain hidden",
|
|
2413
|
+
cols: ["Variable", "Purpose", "Status"],
|
|
2414
|
+
rows: [],
|
|
2415
|
+
},
|
|
2416
|
+
{
|
|
2417
|
+
type: "table",
|
|
2418
|
+
card: "Startup Script",
|
|
2419
|
+
note: "Only agents enabled by the current harness are listed",
|
|
2420
|
+
cols: ["Agent", "Remote environment setup", "Project artifact"],
|
|
2421
|
+
rows: [],
|
|
2422
|
+
},
|
|
2423
|
+
{
|
|
2424
|
+
type: "callout",
|
|
2425
|
+
text: "<b>One bootstrap bundle, renewable sessions.</b> Store the complete Secrets Manager value as <code>LISA_AWS_BOOTSTRAP_JSON</code>. Each startup command writes the assume-only source credential and renewable role profiles; do not configure <code>AWS_ACCESS_KEY_ID</code> or <code>AWS_SECRET_ACCESS_KEY</code> directly.",
|
|
2426
|
+
},
|
|
2427
|
+
],
|
|
2428
|
+
};
|
|
2429
|
+
|
|
2351
2430
|
/* ---------------------------------- Tracker ---------------------------------- */
|
|
2352
2431
|
DATA.sections.tracker = {
|
|
2353
2432
|
title: "Work tracker",
|
|
@@ -5783,26 +5862,238 @@
|
|
|
5783
5862
|
if (control.type === "static" && typeof value === "string")
|
|
5784
5863
|
control.value = value;
|
|
5785
5864
|
}
|
|
5865
|
+
const REMOTE_AGENT_ORDER = [
|
|
5866
|
+
"claude",
|
|
5867
|
+
"codex",
|
|
5868
|
+
"cursor",
|
|
5869
|
+
"agy",
|
|
5870
|
+
"copilot",
|
|
5871
|
+
"opencode",
|
|
5872
|
+
];
|
|
5873
|
+
const REMOTE_STARTUP = {
|
|
5874
|
+
claude: {
|
|
5875
|
+
command:
|
|
5876
|
+
"LISA_REMOTE_AGENT=claude bash scripts/remote-agent-aws-setup.sh",
|
|
5877
|
+
artifacts: ["scripts/remote-agent-aws-setup.sh"],
|
|
5878
|
+
},
|
|
5879
|
+
codex: {
|
|
5880
|
+
command:
|
|
5881
|
+
"LISA_REMOTE_AGENT=codex bash scripts/remote-agent-aws-setup.sh",
|
|
5882
|
+
artifacts: ["scripts/remote-agent-aws-setup.sh"],
|
|
5883
|
+
},
|
|
5884
|
+
cursor: {
|
|
5885
|
+
command:
|
|
5886
|
+
".cursor/environment.json → LISA_REMOTE_AGENT=cursor bash scripts/remote-agent-aws-setup.sh",
|
|
5887
|
+
artifacts: [
|
|
5888
|
+
"scripts/remote-agent-aws-setup.sh",
|
|
5889
|
+
".cursor/environment.json",
|
|
5890
|
+
],
|
|
5891
|
+
},
|
|
5892
|
+
agy: {
|
|
5893
|
+
command:
|
|
5894
|
+
"LISA_REMOTE_AGENT=agy bash scripts/remote-agent-aws-setup.sh (user-managed host)",
|
|
5895
|
+
artifacts: ["scripts/remote-agent-aws-setup.sh"],
|
|
5896
|
+
},
|
|
5897
|
+
copilot: {
|
|
5898
|
+
command:
|
|
5899
|
+
".github/workflows/copilot-setup-steps.yml → bash scripts/remote-agent-aws-setup.sh",
|
|
5900
|
+
artifacts: [
|
|
5901
|
+
"scripts/remote-agent-aws-setup.sh",
|
|
5902
|
+
".github/workflows/copilot-setup-steps.yml",
|
|
5903
|
+
],
|
|
5904
|
+
},
|
|
5905
|
+
opencode: {
|
|
5906
|
+
command:
|
|
5907
|
+
"LISA_REMOTE_AGENT=opencode bash scripts/remote-agent-aws-setup.sh (opencode serve host)",
|
|
5908
|
+
artifacts: ["scripts/remote-agent-aws-setup.sh"],
|
|
5909
|
+
},
|
|
5910
|
+
};
|
|
5911
|
+
function configureRemoteEnvironment(cfg) {
|
|
5912
|
+
const snapshot = window.LISA_REMOTE_ENVIRONMENT || {
|
|
5913
|
+
variables: {},
|
|
5914
|
+
artifacts: {},
|
|
5915
|
+
};
|
|
5916
|
+
const harness = cfg && cfg.harness ? cfg.harness : "fleet";
|
|
5917
|
+
const activeAgents =
|
|
5918
|
+
harness === "fleet" || harness === "all"
|
|
5919
|
+
? REMOTE_AGENT_ORDER
|
|
5920
|
+
: REMOTE_AGENT_ORDER.includes(harness)
|
|
5921
|
+
? [harness]
|
|
5922
|
+
: ["claude"];
|
|
5923
|
+
const variableRows = [
|
|
5924
|
+
[
|
|
5925
|
+
"LISA_AWS_BOOTSTRAP_JSON",
|
|
5926
|
+
"Required secret — complete vendor-neutral bootstrap bundle",
|
|
5927
|
+
],
|
|
5928
|
+
[
|
|
5929
|
+
"LISA_REMOTE_AGENT",
|
|
5930
|
+
"Platform label used as the AWS role session name (startup commands set it inline)",
|
|
5931
|
+
],
|
|
5932
|
+
[
|
|
5933
|
+
"LISA_AWS_DEFAULT_PROFILE",
|
|
5934
|
+
"Optional default account profile; dev is used when unset",
|
|
5935
|
+
],
|
|
5936
|
+
].map(([name, purpose]) => {
|
|
5937
|
+
const isSet = snapshot.variables[name] === true;
|
|
5938
|
+
return [
|
|
5939
|
+
{ t: "mono", v: name },
|
|
5940
|
+
{ t: "text", v: purpose },
|
|
5941
|
+
{
|
|
5942
|
+
t: "badge",
|
|
5943
|
+
v: isSet ? "set" : "not set",
|
|
5944
|
+
cls: isSet
|
|
5945
|
+
? "pass"
|
|
5946
|
+
: name.endsWith("DEFAULT_PROFILE")
|
|
5947
|
+
? "warn"
|
|
5948
|
+
: "fail",
|
|
5949
|
+
},
|
|
5950
|
+
];
|
|
5951
|
+
});
|
|
5952
|
+
const startupRows = activeAgents.map(agent => {
|
|
5953
|
+
const startup = REMOTE_STARTUP[agent];
|
|
5954
|
+
const ready = startup.artifacts.every(
|
|
5955
|
+
artifact => snapshot.artifacts[artifact] === true
|
|
5956
|
+
);
|
|
5957
|
+
return [
|
|
5958
|
+
{ t: "mono", v: agent },
|
|
5959
|
+
{ t: "mono", v: startup.command },
|
|
5960
|
+
{
|
|
5961
|
+
t: "badge",
|
|
5962
|
+
v: ready ? "installed" : "missing",
|
|
5963
|
+
cls: ready ? "pass" : "fail",
|
|
5964
|
+
},
|
|
5965
|
+
];
|
|
5966
|
+
});
|
|
5967
|
+
DATA.sections.remote.blocks[0].rows = variableRows;
|
|
5968
|
+
DATA.sections.remote.blocks[1].rows = startupRows;
|
|
5969
|
+
}
|
|
5786
5970
|
function hydrateFromLiveConfig() {
|
|
5787
|
-
const cfg = window.LISA_LIVE_CONFIG;
|
|
5788
|
-
if (
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5971
|
+
const cfg = window.LISA_LIVE_CONFIG || {};
|
|
5972
|
+
if (window.LISA_LIVE_CONFIG) {
|
|
5973
|
+
Object.values(DATA.sections).forEach(section => {
|
|
5974
|
+
walkRows(section.blocks, row => {
|
|
5975
|
+
if (!row.key || !row.control) return;
|
|
5976
|
+
const value = liveGet(cfg, row.key);
|
|
5977
|
+
if (value !== undefined) hydrateControl(row.control, value);
|
|
5978
|
+
});
|
|
5794
5979
|
});
|
|
5795
|
-
}
|
|
5980
|
+
}
|
|
5981
|
+
configureRemoteEnvironment(cfg);
|
|
5796
5982
|
const repoEl = document.querySelector(".project-switch .repo");
|
|
5797
5983
|
if (repoEl && cfg.github && cfg.github.org && cfg.github.repo) {
|
|
5798
5984
|
repoEl.textContent = cfg.github.org + "/" + cfg.github.repo;
|
|
5799
5985
|
}
|
|
5800
5986
|
}
|
|
5801
5987
|
|
|
5988
|
+
/* --- live-status hydration (same-origin GET /api/status) --- */
|
|
5989
|
+
function liveStatusValue(value) {
|
|
5990
|
+
if (typeof value === "string") return value;
|
|
5991
|
+
try {
|
|
5992
|
+
return JSON.stringify(value);
|
|
5993
|
+
} catch {
|
|
5994
|
+
return String(value);
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
function normalizeLiveStatusResult(result) {
|
|
5998
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
5999
|
+
return {
|
|
6000
|
+
state: "unknown",
|
|
6001
|
+
reason: "invalid-result",
|
|
6002
|
+
message: "Probe returned an invalid status result",
|
|
6003
|
+
};
|
|
6004
|
+
}
|
|
6005
|
+
const keys = Object.keys(result).sort().join(",");
|
|
6006
|
+
if (result.state === "value" && keys === "state,value") return result;
|
|
6007
|
+
if (result.state === "not-applicable" && keys === "state")
|
|
6008
|
+
return result;
|
|
6009
|
+
if (
|
|
6010
|
+
result.state === "unknown" &&
|
|
6011
|
+
keys === "message,reason,state" &&
|
|
6012
|
+
typeof result.reason === "string" &&
|
|
6013
|
+
result.reason.trim().length > 0 &&
|
|
6014
|
+
typeof result.message === "string" &&
|
|
6015
|
+
result.message.trim().length > 0
|
|
6016
|
+
) {
|
|
6017
|
+
return result;
|
|
6018
|
+
}
|
|
6019
|
+
return {
|
|
6020
|
+
state: "unknown",
|
|
6021
|
+
reason: "invalid-result",
|
|
6022
|
+
message: "Probe returned an invalid status result",
|
|
6023
|
+
};
|
|
6024
|
+
}
|
|
6025
|
+
function liveStatusProbeMap(snapshot) {
|
|
6026
|
+
if (
|
|
6027
|
+
!snapshot ||
|
|
6028
|
+
typeof snapshot !== "object" ||
|
|
6029
|
+
Array.isArray(snapshot) ||
|
|
6030
|
+
!Object.hasOwn(snapshot, "probes") ||
|
|
6031
|
+
!snapshot.probes ||
|
|
6032
|
+
typeof snapshot.probes !== "object" ||
|
|
6033
|
+
Array.isArray(snapshot.probes)
|
|
6034
|
+
) {
|
|
6035
|
+
throw new TypeError("Status response omitted a valid probes map");
|
|
6036
|
+
}
|
|
6037
|
+
return snapshot.probes;
|
|
6038
|
+
}
|
|
6039
|
+
function renderLiveStatuses(probes) {
|
|
6040
|
+
const list = document.getElementById("liveStatusList");
|
|
6041
|
+
list.innerHTML = "";
|
|
6042
|
+
const entries = Object.entries(probes || {});
|
|
6043
|
+
if (entries.length === 0) {
|
|
6044
|
+
const empty = el("span", "live-status-item", "No probes");
|
|
6045
|
+
empty.setAttribute("role", "listitem");
|
|
6046
|
+
list.appendChild(empty);
|
|
6047
|
+
return;
|
|
6048
|
+
}
|
|
6049
|
+
entries.forEach(([id, candidate]) => {
|
|
6050
|
+
const result = normalizeLiveStatusResult(candidate);
|
|
6051
|
+
const item = el("span", "live-status-item");
|
|
6052
|
+
item.setAttribute("role", "listitem");
|
|
6053
|
+
item.appendChild(el("b", "", esc(id)));
|
|
6054
|
+
if (result.state === "value") {
|
|
6055
|
+
item.appendChild(
|
|
6056
|
+
el("span", "badge pass", esc(liveStatusValue(result.value)))
|
|
6057
|
+
);
|
|
6058
|
+
} else if (result.state === "not-applicable") {
|
|
6059
|
+
item.appendChild(el("span", "badge default", "not applicable"));
|
|
6060
|
+
} else {
|
|
6061
|
+
item.appendChild(el("span", "badge warn", "unknown"));
|
|
6062
|
+
item.appendChild(
|
|
6063
|
+
el(
|
|
6064
|
+
"span",
|
|
6065
|
+
"status-why",
|
|
6066
|
+
esc(result.reason + ": " + result.message)
|
|
6067
|
+
)
|
|
6068
|
+
);
|
|
6069
|
+
}
|
|
6070
|
+
list.appendChild(item);
|
|
6071
|
+
});
|
|
6072
|
+
}
|
|
6073
|
+
async function hydrateLiveStatuses() {
|
|
6074
|
+
try {
|
|
6075
|
+
const response = await fetch("/api/status", {
|
|
6076
|
+
headers: { accept: "application/json" },
|
|
6077
|
+
});
|
|
6078
|
+
if (!response.ok) throw new Error("HTTP " + response.status);
|
|
6079
|
+
const snapshot = await response.json();
|
|
6080
|
+
renderLiveStatuses(liveStatusProbeMap(snapshot));
|
|
6081
|
+
} catch (error) {
|
|
6082
|
+
renderLiveStatuses({
|
|
6083
|
+
transport: {
|
|
6084
|
+
state: "unknown",
|
|
6085
|
+
reason: "status-unavailable",
|
|
6086
|
+
message: error instanceof Error ? error.message : String(error),
|
|
6087
|
+
},
|
|
6088
|
+
});
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
|
|
5802
6092
|
/* --- boot (deferred so an injected live-config tag after this script is seen) --- */
|
|
5803
6093
|
function boot() {
|
|
5804
6094
|
hydrateFromLiveConfig();
|
|
5805
6095
|
renderAll();
|
|
6096
|
+
void hydrateLiveStatuses();
|
|
5806
6097
|
showSection(location.hash ? location.hash.slice(1) : "overview");
|
|
5807
6098
|
if (!document.getElementById("section-" + activeSection))
|
|
5808
6099
|
showSection("overview");
|