@codyswann/lisa 2.335.0 → 2.336.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/all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh +25 -23
- package/dist/core/upstream-evidence-manifest.js +5 -5
- 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-secrets-access/scripts/surfaces.mjs +42 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +33 -6
- package/plugins/lisa/hooks/sonar-secrets.sh +25 -23
- package/plugins/lisa/skills/lisa-secrets-access/scripts/surfaces.mjs +42 -2
- package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +33 -6
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/surfaces.mjs +42 -2
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +33 -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/hooks/sonar-secrets.sh +25 -23
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/surfaces.mjs +42 -2
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +33 -6
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/sonar-secrets.sh +25 -23
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/surfaces.mjs +42 -2
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +33 -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/hooks/sonar-secrets.sh +25 -23
- package/plugins/src/base/skills/lisa-secrets-access/scripts/surfaces.mjs +42 -2
- package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +33 -6
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
set -uo pipefail
|
|
40
40
|
|
|
41
41
|
event="${1:-}"
|
|
42
|
-
[ -n "$event" ] || exit 0
|
|
42
|
+
[[ -n "$event" ]] || exit 0
|
|
43
43
|
|
|
44
44
|
# Nothing installed, nothing to enforce. Matches the vendor shim's own guard.
|
|
45
45
|
command -v sonar >/dev/null 2>&1 || exit 0
|
|
@@ -48,7 +48,7 @@ command -v sonar >/dev/null 2>&1 || exit 0
|
|
|
48
48
|
# deliberately no way to turn the *blocking* back on from the environment: the
|
|
49
49
|
# decision that a missing token must not stop work belongs in this file, where it
|
|
50
50
|
# is reviewable, not in whatever shell happened to launch the agent.
|
|
51
|
-
[ "${LISA_SONAR_HOOK:-on}"
|
|
51
|
+
[[ "${LISA_SONAR_HOOK:-on}" == "off" ]] && exit 0
|
|
52
52
|
|
|
53
53
|
payload="$(cat)"
|
|
54
54
|
|
|
@@ -68,23 +68,25 @@ is_inactive() {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
# Run the resolver with a ceiling, because the provider call crosses the network
|
|
71
|
-
# and this sits in front of every prompt and every file read.
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
71
|
+
# and this sits in front of every prompt and every file read.
|
|
72
|
+
#
|
|
73
|
+
# The value never touches the filesystem. An earlier version captured it through
|
|
74
|
+
# `mktemp` and deleted the file afterwards, which is a race, not a cleanup: a
|
|
75
|
+
# kill between the write and the `rm` leaves the token readable on disk (CWE-922)
|
|
76
|
+
# — the precise failure the one-store rule in lisa-secrets-access exists to
|
|
77
|
+
# prevent, reintroduced by the code enforcing it. Process substitution gives a
|
|
78
|
+
# `/dev/fd` pipe instead, so there is no path on disk to leak and nothing to
|
|
79
|
+
# clean up on any exit path, signalled or not.
|
|
80
|
+
#
|
|
81
|
+
# `read -t` supplies the ceiling. `timeout` is absent on a stock macOS and
|
|
82
|
+
# `coproc` needs Bash 4 (the hooks run under /bin/bash 3.2 there), so this is the
|
|
83
|
+
# portable form. A timeout leaves `value` empty, which the caller treats as "the
|
|
84
|
+
# provider had nothing" — the warn path, never a block. `|| true` because `read`
|
|
85
|
+
# also reports failure at EOF on the resolver's deliberately unterminated output.
|
|
75
86
|
resolve_secret() {
|
|
76
|
-
local resolver="$1" name="$2"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
pid=$!
|
|
80
|
-
while kill -0 "$pid" 2>/dev/null; do
|
|
81
|
-
[ "$waited" -ge 10 ] && { kill -9 "$pid" 2>/dev/null; break; }
|
|
82
|
-
sleep 1
|
|
83
|
-
waited=$((waited + 1))
|
|
84
|
-
done
|
|
85
|
-
wait "$pid" 2>/dev/null
|
|
86
|
-
cat "$out"
|
|
87
|
-
rm -f "$out"
|
|
87
|
+
local resolver="$1" name="$2" value=""
|
|
88
|
+
IFS= read -r -t 10 value < <(node "$resolver" get "$name" 2>/dev/null) || true
|
|
89
|
+
printf '%s' "$value"
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
out="$(run_scanner)"
|
|
@@ -103,21 +105,21 @@ if is_inactive "$out"; then
|
|
|
103
105
|
"$repo_root/.codex/skills/lisa-secrets-access/scripts/resolve-secret.mjs" \
|
|
104
106
|
"$repo_root/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs" \
|
|
105
107
|
"$repo_root/node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs"; do
|
|
106
|
-
if [ -f "$candidate" ]; then
|
|
108
|
+
if [[ -f "$candidate" ]]; then
|
|
107
109
|
resolver="$candidate"
|
|
108
110
|
break
|
|
109
111
|
fi
|
|
110
112
|
done
|
|
111
113
|
|
|
112
|
-
if [ -n "$resolver" ] && command -v node >/dev/null 2>&1; then
|
|
114
|
+
if [[ -n "$resolver" ]] && command -v node >/dev/null 2>&1; then
|
|
113
115
|
token="$(resolve_secret "$resolver" SONARQUBE_CLI_TOKEN)"
|
|
114
|
-
if [ -n "$token" ]; then
|
|
116
|
+
if [[ -n "$token" ]]; then
|
|
115
117
|
# Exported into this process only. Writing it anywhere durable would create
|
|
116
118
|
# a second live copy of a credential whose single store is the provider —
|
|
117
119
|
# see the one-store rule in lisa-secrets-access.
|
|
118
120
|
export SONARQUBE_CLI_TOKEN="$token"
|
|
119
121
|
org="$(resolve_secret "$resolver" SONARQUBE_CLI_ORG)"
|
|
120
|
-
[ -n "$org" ] && export SONARQUBE_CLI_ORG="$org"
|
|
122
|
+
[[ -n "$org" ]] && export SONARQUBE_CLI_ORG="$org"
|
|
121
123
|
out="$(run_scanner)"
|
|
122
124
|
fi
|
|
123
125
|
unset token org
|
|
@@ -135,5 +137,5 @@ if is_inactive "$out"; then
|
|
|
135
137
|
exit 0
|
|
136
138
|
fi
|
|
137
139
|
|
|
138
|
-
[ -n "$out" ] && printf '%s' "$out"
|
|
140
|
+
[[ -n "$out" ]] && printf '%s' "$out"
|
|
139
141
|
exit 0
|
|
@@ -9,7 +9,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
9
9
|
"all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh": "a6ed91576efebb6ba40cfa4e9d8a3e8566314909210ae8d5860be62a3feb4cff",
|
|
10
10
|
"all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh": "3b5f074f3ab5708030af507eea962389f3b067c5dbdc76580bd9e44d3ac6c603",
|
|
11
11
|
"all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh": "062c69eea85157f1e941ec3626d8912aa9f182af6ccdbe19687588a6074db5ce",
|
|
12
|
-
"all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh": "
|
|
12
|
+
"all/copy-overwrite/scripts/lisa-hooks/sonar-secrets.sh": "945453fb275a58aa7185e150e6b05e5810739750928ab5bd6edfbf87d1c8aa0d",
|
|
13
13
|
"all/copy-overwrite/scripts/lisa-work-item.mjs": "6a8be8577242588f14ab52b25b2a22b6def53aefa9513b4d3d7c49f4d8027079",
|
|
14
14
|
"all/create-only/.claude/rules/PROJECT_RULES.md": "6e1c7923ad2a15356babc60c97e7f97be728d790af285b6a7cd7d1b4d39cd97f",
|
|
15
15
|
"all/create-only/.lisaignore": "3c9827e7e98daa46510b5357cd6dd9406da1821e64cc46d2eea9f311cd6d06b9",
|
|
@@ -313,7 +313,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
313
313
|
"plugins/src/base/hooks/parity-safety-net.sh": "062c69eea85157f1e941ec3626d8912aa9f182af6ccdbe19687588a6074db5ce",
|
|
314
314
|
"plugins/src/base/hooks/setup-jira-cli.sh": "a675f6b17ce7f0d6b9fb7daeba6d2bc59bcb48ef79b034076ea73b2b1e72ee09",
|
|
315
315
|
"plugins/src/base/hooks/shell-write-nudge.sh": "69839af423f8792b1e52c71097316c3264425553031627c0a2f9409a9d5becd0",
|
|
316
|
-
"plugins/src/base/hooks/sonar-secrets.sh": "
|
|
316
|
+
"plugins/src/base/hooks/sonar-secrets.sh": "945453fb275a58aa7185e150e6b05e5810739750928ab5bd6edfbf87d1c8aa0d",
|
|
317
317
|
"plugins/src/base/hooks/threshold-ratchet-compare.mjs": "4535a145c5c5cbee13bdf970bc91d390a264043604e607288cdd6bf28de56192",
|
|
318
318
|
"plugins/src/base/hooks/threshold-ratchet-families.mjs": "db14245ed89b483912af287bdee917a7a921dc21bb29b49be37fc799c05c413f",
|
|
319
319
|
"plugins/src/base/hooks/threshold-ratchet.mjs": "04f660131bf50864239eb09cbf4d9555e53921254f6ab45ff2b85c173e160901",
|
|
@@ -560,7 +560,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
560
560
|
"plugins/src/base/skills/lisa-secrets-access/scripts/read-secret-note.mjs": "1471d2108058f3059e242f9c71208c99fdbc212bde32de34b798eaa9a27ffbcc",
|
|
561
561
|
"plugins/src/base/skills/lisa-secrets-access/scripts/resolve-secret.mjs": "04ab89bc1f890526ba3b693cd3b022abc5a24e078b93c08fe7dfd586452b70b1",
|
|
562
562
|
"plugins/src/base/skills/lisa-secrets-access/scripts/rotate-secret.mjs": "546dc1c2f279cee6db56c0f55c01aabae0c316842f32d3219c5af6d638936a9c",
|
|
563
|
-
"plugins/src/base/skills/lisa-secrets-access/scripts/surfaces.mjs": "
|
|
563
|
+
"plugins/src/base/skills/lisa-secrets-access/scripts/surfaces.mjs": "21d3408de74f2691d61649862b086dfc37a5ce2553bcb17366fcd5d1c50a4ae8",
|
|
564
564
|
"plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs": "62d1b87d1b852dc00c00ea9d132d9d6953f4ef77beb5fa4b93652c4424f33274",
|
|
565
565
|
"plugins/src/base/skills/lisa-security-review/SKILL.md": "5a980eaf5efc4fcce66e75521ec5b1eda143a5b0d36e7157234a705dac94e3f5",
|
|
566
566
|
"plugins/src/base/skills/lisa-security-zap-scan/SKILL.md": "a5189442dbf970cc49fd089ab03c386c8a205b856e09501f6ad1966321e7e4b5",
|
|
@@ -578,10 +578,10 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
578
578
|
"plugins/src/base/skills/lisa-setup-local-env/scripts/local-env.mjs": "bff68aa66d6d4cd7cb9d0e39be691c6daa232edb097f8a601815432a32cca6a6",
|
|
579
579
|
"plugins/src/base/skills/lisa-setup-notion/SKILL.md": "f5a1e9290789fd1c33675168d30461fb24a11c98a433ef777c1f536bc2f905ef",
|
|
580
580
|
"plugins/src/base/skills/lisa-setup-remote-aws/SKILL.md": "80fbf157f9c562c033886c25a99b37356602edd9e61cd2d492f339769ddcf97e",
|
|
581
|
-
"plugins/src/base/skills/lisa-setup-remote-env/SKILL.md": "
|
|
581
|
+
"plugins/src/base/skills/lisa-setup-remote-env/SKILL.md": "b5993444364b361c7b365092ab424bc1d0eee61705a5a2471e4e1f443e30d78f",
|
|
582
582
|
"plugins/src/base/skills/lisa-setup-remote-env/assets/session-start.sh": "cb63d08b14ab7aa2d405e6770e0cf7db5d6588ae1dcb924ea6224b026fcff496",
|
|
583
583
|
"plugins/src/base/skills/lisa-setup-remote-env/assets/setup.sh": "c30d98a5645f033fc1d3a723043691b9ad997ae5666df539ff6aa19c563431b2",
|
|
584
|
-
"plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs": "
|
|
584
|
+
"plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs": "830a2c75fabb0069ebe1bf3f5ec1c1f4dd3437fea6c059b41b8ab4092277025f",
|
|
585
585
|
"plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs": "496de5aed034692fee421dc3a1fbad9f85ed9e1d0c83a1080f781a486d0274e6",
|
|
586
586
|
"plugins/src/base/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs": "82bc2a27a0a5afb2ff413a88365c619fd7f0eaada5de3ccabf2000938f0607a4",
|
|
587
587
|
"plugins/src/base/skills/lisa-setup-sonar/SKILL.md": "b18e07ae942f75aad5730c0535840ef5f7115072f1b6cc62afac83196b28d6a3",
|
package/package.json
CHANGED
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"brace-expansion": ">=5.0.9"
|
|
116
116
|
},
|
|
117
117
|
"name": "@codyswann/lisa",
|
|
118
|
-
"version": "2.
|
|
118
|
+
"version": "2.336.1",
|
|
119
119
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
120
120
|
"main": "dist/index.js",
|
|
121
121
|
"exports": {
|
|
@@ -165,9 +165,19 @@ export function materializedPaths(namespace, env = process.env) {
|
|
|
165
165
|
* @param {string} [cwd] Directory to look in.
|
|
166
166
|
* @returns {object} Resolved configuration with defaults applied.
|
|
167
167
|
*/
|
|
168
|
-
export function readConfig(cwd = process.cwd()) {
|
|
168
|
+
export function readConfig(cwd = process.cwd(), env = process.env) {
|
|
169
169
|
const path = join(cwd, ".lisa.config.json");
|
|
170
|
-
|
|
170
|
+
// No repository is a real, supported state — not just a missing file.
|
|
171
|
+
//
|
|
172
|
+
// A Claude Tag channel session runs as the organization with no user account,
|
|
173
|
+
// and a repository "doesn't enter a session until a request names it". So a
|
|
174
|
+
// session can legitimately have no checkout and therefore no config, while
|
|
175
|
+
// still needing the credentials and profiles every other surface gets.
|
|
176
|
+
//
|
|
177
|
+
// The only values a config would supply here are the namespace and provider,
|
|
178
|
+
// so they can come from the environment instead. Everything else keeps its
|
|
179
|
+
// default, and an environment that sets neither still gets the old behaviour.
|
|
180
|
+
if (!existsSync(path)) return withSurface(fromEnvironment(env));
|
|
171
181
|
let cfg;
|
|
172
182
|
try {
|
|
173
183
|
cfg = JSON.parse(readFileSync(path, "utf8")).secrets;
|
|
@@ -186,6 +196,36 @@ export function readConfig(cwd = process.cwd()) {
|
|
|
186
196
|
});
|
|
187
197
|
}
|
|
188
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Build a configuration from the environment, for sessions with no checkout.
|
|
201
|
+
*
|
|
202
|
+
* `LISA_SECRETS_NAMESPACE` is the one value with no safe default: it names the
|
|
203
|
+
* directory secrets are written to and the tenant they belong to, so guessing
|
|
204
|
+
* it would write one tenant's credentials under another's name. Absent, this
|
|
205
|
+
* returns the ordinary defaults and the caller behaves exactly as before.
|
|
206
|
+
*
|
|
207
|
+
* The bootstrap key follows the namespace by convention
|
|
208
|
+
* (`BWS_ACCESS_TOKEN_<namespace>`) so a channel environment needs one variable
|
|
209
|
+
* set, not two.
|
|
210
|
+
* @param {object} env The environment to read.
|
|
211
|
+
* @returns {object} A configuration, defaulted where the environment is silent.
|
|
212
|
+
*/
|
|
213
|
+
function fromEnvironment(env) {
|
|
214
|
+
const namespace = (env.LISA_SECRETS_NAMESPACE ?? "").trim();
|
|
215
|
+
if (!namespace) return DEFAULTS;
|
|
216
|
+
|
|
217
|
+
const provider = (env.LISA_SECRETS_PROVIDER ?? "").trim() || "bitwarden";
|
|
218
|
+
return {
|
|
219
|
+
...DEFAULTS,
|
|
220
|
+
provider,
|
|
221
|
+
namespace: assertNamespace(namespace),
|
|
222
|
+
bootstrap: {
|
|
223
|
+
...DEFAULTS.bootstrap,
|
|
224
|
+
key: env.LISA_SECRETS_BOOTSTRAP_KEY ?? `BWS_ACCESS_TOKEN_${namespace}`,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
189
229
|
/**
|
|
190
230
|
* Attach the resolved surface and its capabilities to a configuration.
|
|
191
231
|
* @param {object} cfg Configuration without surface resolution.
|
|
@@ -13,7 +13,7 @@ Prepare a remote surface so a host project can execute there. Today that means *
|
|
|
13
13
|
The remote environment's own configuration fields stay **one line into the repository**. Nothing else is pasted into a vendor UI.
|
|
14
14
|
|
|
15
15
|
```text
|
|
16
|
-
setup: n=0; rc=0; seen=""; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh "$HOME"/scripts/lisa-remote-env/setup.sh "$HOME"/*/scripts/lisa-remote-env/setup.sh /workspace/scripts/lisa-remote-env/setup.sh /workspace/*/scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ]
|
|
16
|
+
setup: n=0; rc=0; seen=""; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh "$HOME"/scripts/lisa-remote-env/setup.sh "$HOME"/*/scripts/lisa-remote-env/setup.sh /workspace/scripts/lisa-remote-env/setup.sh /workspace/*/scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] && exit "$rc"; g=0; for c in ./.git ./*/.git "$HOME"/*/.git /workspace/*/.git; do [ -e "$c" ] && g=1; done; if [ "$g" -eq 1 ]; then echo "lisa-remote-env entrypoint not found, but a checkout is present. PWD=$PWD HOME=$HOME" >&2; ls -1 . "$HOME" /workspace 2>&1 | head -40 >&2; exit 1; fi; if [ -n "${LISA_SECRETS_NAMESPACE:-}" ]; then echo "No checkout; preparing tools and credentials for $LISA_SECRETS_NAMESPACE."; npx -y @codyswann/lisa@latest workstation --provider "${LISA_SECRETS_PROVIDER:-bitwarden}" --yes || true; npx -y @codyswann/lisa@latest remote-env --phase=secrets || true; else echo "No checkout and no LISA_SECRETS_NAMESPACE; nothing to prepare."; fi; exit 0
|
|
17
17
|
maintenance: n=0; rc=0; seen=""; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh "$HOME"/scripts/lisa-remote-env/setup.sh "$HOME"/*/scripts/lisa-remote-env/setup.sh /workspace/scripts/lisa-remote-env/setup.sh /workspace/*/scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found. PWD=$PWD HOME=$HOME" >&2; ls -1 . "$HOME" /workspace 2>&1 | head -40 >&2; exit 1; }; exit "$rc"
|
|
18
18
|
```
|
|
19
19
|
|
package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs
CHANGED
|
@@ -559,9 +559,26 @@ export function installAssets(cwd = process.cwd()) {
|
|
|
559
559
|
* whenever cwd happens to be one of the roots.
|
|
560
560
|
*
|
|
561
561
|
* The status is the first failure and every checkout is still attempted: one
|
|
562
|
-
* broken repository must neither hide the others nor report success.
|
|
563
|
-
*
|
|
564
|
-
*
|
|
562
|
+
* broken repository must neither hide the others nor report success.
|
|
563
|
+
*
|
|
564
|
+
* Preparing nothing has two causes, and they are not the same failure:
|
|
565
|
+
*
|
|
566
|
+
* - A checkout is present but carries no entrypoint. That is a
|
|
567
|
+
* misconfiguration, and a `for` loop over a glob that matches nothing exits
|
|
568
|
+
* 0 on its own — the quiet success this has always guarded against. It
|
|
569
|
+
* still fails loudly, printing the layout it found rather than only the
|
|
570
|
+
* path it wanted.
|
|
571
|
+
* - There is NO checkout. A Claude Tag channel session runs as the
|
|
572
|
+
* organization with no user account, and a repository does not enter the
|
|
573
|
+
* session until a request names one, so this is an ordinary state rather
|
|
574
|
+
* than an error. Failing here would be severe: a setup script that exits
|
|
575
|
+
* non-zero stops the session from starting at all, so the old blanket
|
|
576
|
+
* `exit 1` would have killed every repo-less channel session.
|
|
577
|
+
*
|
|
578
|
+
* With no checkout, `LISA_SECRETS_NAMESPACE` decides whether there is anything
|
|
579
|
+
* to do: it names the tenant, which is the one value no default can supply.
|
|
580
|
+
* Given it, the machine itself is prepared — tools and credentials, no repo
|
|
581
|
+
* required. Without it there is nothing to prepare and nothing to fail about.
|
|
565
582
|
*
|
|
566
583
|
* Failure prints the layout it found rather than only the path it wanted. This
|
|
567
584
|
* field lives in a vendor settings box with a slow edit-and-retry loop, and a
|
|
@@ -580,9 +597,19 @@ export const SETUP_FIELD =
|
|
|
580
597
|
'do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); ' +
|
|
581
598
|
'case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; ' +
|
|
582
599
|
'n=$((n+1)); bash "$f" || rc=$?; done; ' +
|
|
583
|
-
'[ "$n" -gt 0 ]
|
|
584
|
-
'
|
|
585
|
-
'
|
|
600
|
+
'[ "$n" -gt 0 ] && exit "$rc"; ' +
|
|
601
|
+
'g=0; for c in ./.git ./*/.git "$HOME"/*/.git /workspace/*/.git; ' +
|
|
602
|
+
'do [ -e "$c" ] && g=1; done; ' +
|
|
603
|
+
'if [ "$g" -eq 1 ]; then ' +
|
|
604
|
+
'echo "lisa-remote-env entrypoint not found, but a checkout is present. ' +
|
|
605
|
+
'PWD=$PWD HOME=$HOME" >&2; ls -1 . "$HOME" /workspace 2>&1 | head -40 >&2; exit 1; fi; ' +
|
|
606
|
+
'if [ -n "${LISA_SECRETS_NAMESPACE:-}" ]; then ' +
|
|
607
|
+
'echo "No checkout; preparing tools and credentials for $LISA_SECRETS_NAMESPACE."; ' +
|
|
608
|
+
"npx -y @codyswann/lisa@latest workstation " +
|
|
609
|
+
'--provider "${LISA_SECRETS_PROVIDER:-bitwarden}" --yes || true; ' +
|
|
610
|
+
"npx -y @codyswann/lisa@latest remote-env --phase=secrets || true; " +
|
|
611
|
+
'else echo "No checkout and no LISA_SECRETS_NAMESPACE; nothing to prepare."; fi; ' +
|
|
612
|
+
"exit 0";
|
|
586
613
|
|
|
587
614
|
/**
|
|
588
615
|
* Register the session-start hook at USER scope, so it fires wherever the
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
set -uo pipefail
|
|
40
40
|
|
|
41
41
|
event="${1:-}"
|
|
42
|
-
[ -n "$event" ] || exit 0
|
|
42
|
+
[[ -n "$event" ]] || exit 0
|
|
43
43
|
|
|
44
44
|
# Nothing installed, nothing to enforce. Matches the vendor shim's own guard.
|
|
45
45
|
command -v sonar >/dev/null 2>&1 || exit 0
|
|
@@ -48,7 +48,7 @@ command -v sonar >/dev/null 2>&1 || exit 0
|
|
|
48
48
|
# deliberately no way to turn the *blocking* back on from the environment: the
|
|
49
49
|
# decision that a missing token must not stop work belongs in this file, where it
|
|
50
50
|
# is reviewable, not in whatever shell happened to launch the agent.
|
|
51
|
-
[ "${LISA_SONAR_HOOK:-on}"
|
|
51
|
+
[[ "${LISA_SONAR_HOOK:-on}" == "off" ]] && exit 0
|
|
52
52
|
|
|
53
53
|
payload="$(cat)"
|
|
54
54
|
|
|
@@ -68,23 +68,25 @@ is_inactive() {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
# Run the resolver with a ceiling, because the provider call crosses the network
|
|
71
|
-
# and this sits in front of every prompt and every file read.
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
71
|
+
# and this sits in front of every prompt and every file read.
|
|
72
|
+
#
|
|
73
|
+
# The value never touches the filesystem. An earlier version captured it through
|
|
74
|
+
# `mktemp` and deleted the file afterwards, which is a race, not a cleanup: a
|
|
75
|
+
# kill between the write and the `rm` leaves the token readable on disk (CWE-922)
|
|
76
|
+
# — the precise failure the one-store rule in lisa-secrets-access exists to
|
|
77
|
+
# prevent, reintroduced by the code enforcing it. Process substitution gives a
|
|
78
|
+
# `/dev/fd` pipe instead, so there is no path on disk to leak and nothing to
|
|
79
|
+
# clean up on any exit path, signalled or not.
|
|
80
|
+
#
|
|
81
|
+
# `read -t` supplies the ceiling. `timeout` is absent on a stock macOS and
|
|
82
|
+
# `coproc` needs Bash 4 (the hooks run under /bin/bash 3.2 there), so this is the
|
|
83
|
+
# portable form. A timeout leaves `value` empty, which the caller treats as "the
|
|
84
|
+
# provider had nothing" — the warn path, never a block. `|| true` because `read`
|
|
85
|
+
# also reports failure at EOF on the resolver's deliberately unterminated output.
|
|
75
86
|
resolve_secret() {
|
|
76
|
-
local resolver="$1" name="$2"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
pid=$!
|
|
80
|
-
while kill -0 "$pid" 2>/dev/null; do
|
|
81
|
-
[ "$waited" -ge 10 ] && { kill -9 "$pid" 2>/dev/null; break; }
|
|
82
|
-
sleep 1
|
|
83
|
-
waited=$((waited + 1))
|
|
84
|
-
done
|
|
85
|
-
wait "$pid" 2>/dev/null
|
|
86
|
-
cat "$out"
|
|
87
|
-
rm -f "$out"
|
|
87
|
+
local resolver="$1" name="$2" value=""
|
|
88
|
+
IFS= read -r -t 10 value < <(node "$resolver" get "$name" 2>/dev/null) || true
|
|
89
|
+
printf '%s' "$value"
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
out="$(run_scanner)"
|
|
@@ -103,21 +105,21 @@ if is_inactive "$out"; then
|
|
|
103
105
|
"$repo_root/.codex/skills/lisa-secrets-access/scripts/resolve-secret.mjs" \
|
|
104
106
|
"$repo_root/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs" \
|
|
105
107
|
"$repo_root/node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs"; do
|
|
106
|
-
if [ -f "$candidate" ]; then
|
|
108
|
+
if [[ -f "$candidate" ]]; then
|
|
107
109
|
resolver="$candidate"
|
|
108
110
|
break
|
|
109
111
|
fi
|
|
110
112
|
done
|
|
111
113
|
|
|
112
|
-
if [ -n "$resolver" ] && command -v node >/dev/null 2>&1; then
|
|
114
|
+
if [[ -n "$resolver" ]] && command -v node >/dev/null 2>&1; then
|
|
113
115
|
token="$(resolve_secret "$resolver" SONARQUBE_CLI_TOKEN)"
|
|
114
|
-
if [ -n "$token" ]; then
|
|
116
|
+
if [[ -n "$token" ]]; then
|
|
115
117
|
# Exported into this process only. Writing it anywhere durable would create
|
|
116
118
|
# a second live copy of a credential whose single store is the provider —
|
|
117
119
|
# see the one-store rule in lisa-secrets-access.
|
|
118
120
|
export SONARQUBE_CLI_TOKEN="$token"
|
|
119
121
|
org="$(resolve_secret "$resolver" SONARQUBE_CLI_ORG)"
|
|
120
|
-
[ -n "$org" ] && export SONARQUBE_CLI_ORG="$org"
|
|
122
|
+
[[ -n "$org" ]] && export SONARQUBE_CLI_ORG="$org"
|
|
121
123
|
out="$(run_scanner)"
|
|
122
124
|
fi
|
|
123
125
|
unset token org
|
|
@@ -135,5 +137,5 @@ if is_inactive "$out"; then
|
|
|
135
137
|
exit 0
|
|
136
138
|
fi
|
|
137
139
|
|
|
138
|
-
[ -n "$out" ] && printf '%s' "$out"
|
|
140
|
+
[[ -n "$out" ]] && printf '%s' "$out"
|
|
139
141
|
exit 0
|
|
@@ -165,9 +165,19 @@ export function materializedPaths(namespace, env = process.env) {
|
|
|
165
165
|
* @param {string} [cwd] Directory to look in.
|
|
166
166
|
* @returns {object} Resolved configuration with defaults applied.
|
|
167
167
|
*/
|
|
168
|
-
export function readConfig(cwd = process.cwd()) {
|
|
168
|
+
export function readConfig(cwd = process.cwd(), env = process.env) {
|
|
169
169
|
const path = join(cwd, ".lisa.config.json");
|
|
170
|
-
|
|
170
|
+
// No repository is a real, supported state — not just a missing file.
|
|
171
|
+
//
|
|
172
|
+
// A Claude Tag channel session runs as the organization with no user account,
|
|
173
|
+
// and a repository "doesn't enter a session until a request names it". So a
|
|
174
|
+
// session can legitimately have no checkout and therefore no config, while
|
|
175
|
+
// still needing the credentials and profiles every other surface gets.
|
|
176
|
+
//
|
|
177
|
+
// The only values a config would supply here are the namespace and provider,
|
|
178
|
+
// so they can come from the environment instead. Everything else keeps its
|
|
179
|
+
// default, and an environment that sets neither still gets the old behaviour.
|
|
180
|
+
if (!existsSync(path)) return withSurface(fromEnvironment(env));
|
|
171
181
|
let cfg;
|
|
172
182
|
try {
|
|
173
183
|
cfg = JSON.parse(readFileSync(path, "utf8")).secrets;
|
|
@@ -186,6 +196,36 @@ export function readConfig(cwd = process.cwd()) {
|
|
|
186
196
|
});
|
|
187
197
|
}
|
|
188
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Build a configuration from the environment, for sessions with no checkout.
|
|
201
|
+
*
|
|
202
|
+
* `LISA_SECRETS_NAMESPACE` is the one value with no safe default: it names the
|
|
203
|
+
* directory secrets are written to and the tenant they belong to, so guessing
|
|
204
|
+
* it would write one tenant's credentials under another's name. Absent, this
|
|
205
|
+
* returns the ordinary defaults and the caller behaves exactly as before.
|
|
206
|
+
*
|
|
207
|
+
* The bootstrap key follows the namespace by convention
|
|
208
|
+
* (`BWS_ACCESS_TOKEN_<namespace>`) so a channel environment needs one variable
|
|
209
|
+
* set, not two.
|
|
210
|
+
* @param {object} env The environment to read.
|
|
211
|
+
* @returns {object} A configuration, defaulted where the environment is silent.
|
|
212
|
+
*/
|
|
213
|
+
function fromEnvironment(env) {
|
|
214
|
+
const namespace = (env.LISA_SECRETS_NAMESPACE ?? "").trim();
|
|
215
|
+
if (!namespace) return DEFAULTS;
|
|
216
|
+
|
|
217
|
+
const provider = (env.LISA_SECRETS_PROVIDER ?? "").trim() || "bitwarden";
|
|
218
|
+
return {
|
|
219
|
+
...DEFAULTS,
|
|
220
|
+
provider,
|
|
221
|
+
namespace: assertNamespace(namespace),
|
|
222
|
+
bootstrap: {
|
|
223
|
+
...DEFAULTS.bootstrap,
|
|
224
|
+
key: env.LISA_SECRETS_BOOTSTRAP_KEY ?? `BWS_ACCESS_TOKEN_${namespace}`,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
189
229
|
/**
|
|
190
230
|
* Attach the resolved surface and its capabilities to a configuration.
|
|
191
231
|
* @param {object} cfg Configuration without surface resolution.
|
|
@@ -13,7 +13,7 @@ Prepare a remote surface so a host project can execute there. Today that means *
|
|
|
13
13
|
The remote environment's own configuration fields stay **one line into the repository**. Nothing else is pasted into a vendor UI.
|
|
14
14
|
|
|
15
15
|
```text
|
|
16
|
-
setup: n=0; rc=0; seen=""; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh "$HOME"/scripts/lisa-remote-env/setup.sh "$HOME"/*/scripts/lisa-remote-env/setup.sh /workspace/scripts/lisa-remote-env/setup.sh /workspace/*/scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ]
|
|
16
|
+
setup: n=0; rc=0; seen=""; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh "$HOME"/scripts/lisa-remote-env/setup.sh "$HOME"/*/scripts/lisa-remote-env/setup.sh /workspace/scripts/lisa-remote-env/setup.sh /workspace/*/scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] && exit "$rc"; g=0; for c in ./.git ./*/.git "$HOME"/*/.git /workspace/*/.git; do [ -e "$c" ] && g=1; done; if [ "$g" -eq 1 ]; then echo "lisa-remote-env entrypoint not found, but a checkout is present. PWD=$PWD HOME=$HOME" >&2; ls -1 . "$HOME" /workspace 2>&1 | head -40 >&2; exit 1; fi; if [ -n "${LISA_SECRETS_NAMESPACE:-}" ]; then echo "No checkout; preparing tools and credentials for $LISA_SECRETS_NAMESPACE."; npx -y @codyswann/lisa@latest workstation --provider "${LISA_SECRETS_PROVIDER:-bitwarden}" --yes || true; npx -y @codyswann/lisa@latest remote-env --phase=secrets || true; else echo "No checkout and no LISA_SECRETS_NAMESPACE; nothing to prepare."; fi; exit 0
|
|
17
17
|
maintenance: n=0; rc=0; seen=""; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh "$HOME"/scripts/lisa-remote-env/setup.sh "$HOME"/*/scripts/lisa-remote-env/setup.sh /workspace/scripts/lisa-remote-env/setup.sh /workspace/*/scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found. PWD=$PWD HOME=$HOME" >&2; ls -1 . "$HOME" /workspace 2>&1 | head -40 >&2; exit 1; }; exit "$rc"
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -559,9 +559,26 @@ export function installAssets(cwd = process.cwd()) {
|
|
|
559
559
|
* whenever cwd happens to be one of the roots.
|
|
560
560
|
*
|
|
561
561
|
* The status is the first failure and every checkout is still attempted: one
|
|
562
|
-
* broken repository must neither hide the others nor report success.
|
|
563
|
-
*
|
|
564
|
-
*
|
|
562
|
+
* broken repository must neither hide the others nor report success.
|
|
563
|
+
*
|
|
564
|
+
* Preparing nothing has two causes, and they are not the same failure:
|
|
565
|
+
*
|
|
566
|
+
* - A checkout is present but carries no entrypoint. That is a
|
|
567
|
+
* misconfiguration, and a `for` loop over a glob that matches nothing exits
|
|
568
|
+
* 0 on its own — the quiet success this has always guarded against. It
|
|
569
|
+
* still fails loudly, printing the layout it found rather than only the
|
|
570
|
+
* path it wanted.
|
|
571
|
+
* - There is NO checkout. A Claude Tag channel session runs as the
|
|
572
|
+
* organization with no user account, and a repository does not enter the
|
|
573
|
+
* session until a request names one, so this is an ordinary state rather
|
|
574
|
+
* than an error. Failing here would be severe: a setup script that exits
|
|
575
|
+
* non-zero stops the session from starting at all, so the old blanket
|
|
576
|
+
* `exit 1` would have killed every repo-less channel session.
|
|
577
|
+
*
|
|
578
|
+
* With no checkout, `LISA_SECRETS_NAMESPACE` decides whether there is anything
|
|
579
|
+
* to do: it names the tenant, which is the one value no default can supply.
|
|
580
|
+
* Given it, the machine itself is prepared — tools and credentials, no repo
|
|
581
|
+
* required. Without it there is nothing to prepare and nothing to fail about.
|
|
565
582
|
*
|
|
566
583
|
* Failure prints the layout it found rather than only the path it wanted. This
|
|
567
584
|
* field lives in a vendor settings box with a slow edit-and-retry loop, and a
|
|
@@ -580,9 +597,19 @@ export const SETUP_FIELD =
|
|
|
580
597
|
'do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); ' +
|
|
581
598
|
'case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; ' +
|
|
582
599
|
'n=$((n+1)); bash "$f" || rc=$?; done; ' +
|
|
583
|
-
'[ "$n" -gt 0 ]
|
|
584
|
-
'
|
|
585
|
-
'
|
|
600
|
+
'[ "$n" -gt 0 ] && exit "$rc"; ' +
|
|
601
|
+
'g=0; for c in ./.git ./*/.git "$HOME"/*/.git /workspace/*/.git; ' +
|
|
602
|
+
'do [ -e "$c" ] && g=1; done; ' +
|
|
603
|
+
'if [ "$g" -eq 1 ]; then ' +
|
|
604
|
+
'echo "lisa-remote-env entrypoint not found, but a checkout is present. ' +
|
|
605
|
+
'PWD=$PWD HOME=$HOME" >&2; ls -1 . "$HOME" /workspace 2>&1 | head -40 >&2; exit 1; fi; ' +
|
|
606
|
+
'if [ -n "${LISA_SECRETS_NAMESPACE:-}" ]; then ' +
|
|
607
|
+
'echo "No checkout; preparing tools and credentials for $LISA_SECRETS_NAMESPACE."; ' +
|
|
608
|
+
"npx -y @codyswann/lisa@latest workstation " +
|
|
609
|
+
'--provider "${LISA_SECRETS_PROVIDER:-bitwarden}" --yes || true; ' +
|
|
610
|
+
"npx -y @codyswann/lisa@latest remote-env --phase=secrets || true; " +
|
|
611
|
+
'else echo "No checkout and no LISA_SECRETS_NAMESPACE; nothing to prepare."; fi; ' +
|
|
612
|
+
"exit 0";
|
|
586
613
|
|
|
587
614
|
/**
|
|
588
615
|
* Register the session-start hook at USER scope, so it fires wherever the
|
|
@@ -165,9 +165,19 @@ export function materializedPaths(namespace, env = process.env) {
|
|
|
165
165
|
* @param {string} [cwd] Directory to look in.
|
|
166
166
|
* @returns {object} Resolved configuration with defaults applied.
|
|
167
167
|
*/
|
|
168
|
-
export function readConfig(cwd = process.cwd()) {
|
|
168
|
+
export function readConfig(cwd = process.cwd(), env = process.env) {
|
|
169
169
|
const path = join(cwd, ".lisa.config.json");
|
|
170
|
-
|
|
170
|
+
// No repository is a real, supported state — not just a missing file.
|
|
171
|
+
//
|
|
172
|
+
// A Claude Tag channel session runs as the organization with no user account,
|
|
173
|
+
// and a repository "doesn't enter a session until a request names it". So a
|
|
174
|
+
// session can legitimately have no checkout and therefore no config, while
|
|
175
|
+
// still needing the credentials and profiles every other surface gets.
|
|
176
|
+
//
|
|
177
|
+
// The only values a config would supply here are the namespace and provider,
|
|
178
|
+
// so they can come from the environment instead. Everything else keeps its
|
|
179
|
+
// default, and an environment that sets neither still gets the old behaviour.
|
|
180
|
+
if (!existsSync(path)) return withSurface(fromEnvironment(env));
|
|
171
181
|
let cfg;
|
|
172
182
|
try {
|
|
173
183
|
cfg = JSON.parse(readFileSync(path, "utf8")).secrets;
|
|
@@ -186,6 +196,36 @@ export function readConfig(cwd = process.cwd()) {
|
|
|
186
196
|
});
|
|
187
197
|
}
|
|
188
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Build a configuration from the environment, for sessions with no checkout.
|
|
201
|
+
*
|
|
202
|
+
* `LISA_SECRETS_NAMESPACE` is the one value with no safe default: it names the
|
|
203
|
+
* directory secrets are written to and the tenant they belong to, so guessing
|
|
204
|
+
* it would write one tenant's credentials under another's name. Absent, this
|
|
205
|
+
* returns the ordinary defaults and the caller behaves exactly as before.
|
|
206
|
+
*
|
|
207
|
+
* The bootstrap key follows the namespace by convention
|
|
208
|
+
* (`BWS_ACCESS_TOKEN_<namespace>`) so a channel environment needs one variable
|
|
209
|
+
* set, not two.
|
|
210
|
+
* @param {object} env The environment to read.
|
|
211
|
+
* @returns {object} A configuration, defaulted where the environment is silent.
|
|
212
|
+
*/
|
|
213
|
+
function fromEnvironment(env) {
|
|
214
|
+
const namespace = (env.LISA_SECRETS_NAMESPACE ?? "").trim();
|
|
215
|
+
if (!namespace) return DEFAULTS;
|
|
216
|
+
|
|
217
|
+
const provider = (env.LISA_SECRETS_PROVIDER ?? "").trim() || "bitwarden";
|
|
218
|
+
return {
|
|
219
|
+
...DEFAULTS,
|
|
220
|
+
provider,
|
|
221
|
+
namespace: assertNamespace(namespace),
|
|
222
|
+
bootstrap: {
|
|
223
|
+
...DEFAULTS.bootstrap,
|
|
224
|
+
key: env.LISA_SECRETS_BOOTSTRAP_KEY ?? `BWS_ACCESS_TOKEN_${namespace}`,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
189
229
|
/**
|
|
190
230
|
* Attach the resolved surface and its capabilities to a configuration.
|
|
191
231
|
* @param {object} cfg Configuration without surface resolution.
|
|
@@ -13,7 +13,7 @@ Prepare a remote surface so a host project can execute there. Today that means *
|
|
|
13
13
|
The remote environment's own configuration fields stay **one line into the repository**. Nothing else is pasted into a vendor UI.
|
|
14
14
|
|
|
15
15
|
```text
|
|
16
|
-
setup: n=0; rc=0; seen=""; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh "$HOME"/scripts/lisa-remote-env/setup.sh "$HOME"/*/scripts/lisa-remote-env/setup.sh /workspace/scripts/lisa-remote-env/setup.sh /workspace/*/scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ]
|
|
16
|
+
setup: n=0; rc=0; seen=""; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh "$HOME"/scripts/lisa-remote-env/setup.sh "$HOME"/*/scripts/lisa-remote-env/setup.sh /workspace/scripts/lisa-remote-env/setup.sh /workspace/*/scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] && exit "$rc"; g=0; for c in ./.git ./*/.git "$HOME"/*/.git /workspace/*/.git; do [ -e "$c" ] && g=1; done; if [ "$g" -eq 1 ]; then echo "lisa-remote-env entrypoint not found, but a checkout is present. PWD=$PWD HOME=$HOME" >&2; ls -1 . "$HOME" /workspace 2>&1 | head -40 >&2; exit 1; fi; if [ -n "${LISA_SECRETS_NAMESPACE:-}" ]; then echo "No checkout; preparing tools and credentials for $LISA_SECRETS_NAMESPACE."; npx -y @codyswann/lisa@latest workstation --provider "${LISA_SECRETS_PROVIDER:-bitwarden}" --yes || true; npx -y @codyswann/lisa@latest remote-env --phase=secrets || true; else echo "No checkout and no LISA_SECRETS_NAMESPACE; nothing to prepare."; fi; exit 0
|
|
17
17
|
maintenance: n=0; rc=0; seen=""; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh "$HOME"/scripts/lisa-remote-env/setup.sh "$HOME"/*/scripts/lisa-remote-env/setup.sh /workspace/scripts/lisa-remote-env/setup.sh /workspace/*/scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; d=$(cd "$(dirname "$f")" && pwd -P); case " $seen " in *" $d "*) continue;; esac; seen="$seen $d"; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found. PWD=$PWD HOME=$HOME" >&2; ls -1 . "$HOME" /workspace 2>&1 | head -40 >&2; exit 1; }; exit "$rc"
|
|
18
18
|
```
|
|
19
19
|
|