@codyswann/lisa 2.322.3 → 2.322.5
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/dist/core/upstream-evidence-manifest.js +4 -4
- 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-remote-dispatch/SKILL.md +3 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-remote-dispatch/scripts/dispatch.mjs +50 -4
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +16 -8
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +16 -7
- package/plugins/lisa/skills/lisa-remote-dispatch/SKILL.md +4 -1
- package/plugins/lisa/skills/lisa-remote-dispatch/scripts/dispatch.mjs +50 -4
- package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +16 -8
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +16 -7
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-remote-dispatch/SKILL.md +4 -1
- package/plugins/lisa-agy/skills/lisa-remote-dispatch/scripts/dispatch.mjs +50 -4
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +16 -8
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +16 -7
- 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/skills/lisa-remote-dispatch/SKILL.md +4 -1
- package/plugins/lisa-copilot/skills/lisa-remote-dispatch/scripts/dispatch.mjs +50 -4
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +16 -8
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +16 -7
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/lisa-remote-dispatch/SKILL.md +4 -1
- package/plugins/lisa-cursor/skills/lisa-remote-dispatch/scripts/dispatch.mjs +50 -4
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +16 -8
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +16 -7
- 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/skills/lisa-remote-dispatch/SKILL.md +4 -1
- package/plugins/src/base/skills/lisa-remote-dispatch/scripts/dispatch.mjs +50 -4
- package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +16 -8
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +16 -7
|
@@ -526,8 +526,8 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
526
526
|
"plugins/src/base/skills/lisa-qa-queue/SKILL.md": "1d5f009f60004bf75b5d79bca370096ce40e1d41d585dc8afd4389f21bba84ae",
|
|
527
527
|
"plugins/src/base/skills/lisa-quality-review/SKILL.md": "17ba45f4c4b7877c0b0c9a3c1cc1cb9e18c642ead74ff7263698d8034d3241da",
|
|
528
528
|
"plugins/src/base/skills/lisa-queue-status/SKILL.md": "87c6d34d0511d4afd812d1af3fee80fa3b7aa47db8449760b12f2699fedc1d78",
|
|
529
|
-
"plugins/src/base/skills/lisa-remote-dispatch/SKILL.md": "
|
|
530
|
-
"plugins/src/base/skills/lisa-remote-dispatch/scripts/dispatch.mjs": "
|
|
529
|
+
"plugins/src/base/skills/lisa-remote-dispatch/SKILL.md": "f3c48120a206d01db45f849ca5b61690d572abc16bc36d559a4cacc8f9422c06",
|
|
530
|
+
"plugins/src/base/skills/lisa-remote-dispatch/scripts/dispatch.mjs": "475963ed64d9e72a6cbd6cbb61c8b57a419f2e1805e3ddfa76066047c590deeb",
|
|
531
531
|
"plugins/src/base/skills/lisa-repair-intake/SKILL.md": "4f85ac1381631e9f250d9cf3239baba633ae0df9ba1959f8835ed662592e2d77",
|
|
532
532
|
"plugins/src/base/skills/lisa-reproduce-bug/SKILL.md": "4d460993fac6021219ca23eee29b1b9afa7c37479dddc662b2fcfaca510edd68",
|
|
533
533
|
"plugins/src/base/skills/lisa-research/SKILL.md": "19de7c5910b117c4b3b1bb7ae42cf9c8b6ff2a376b2e21961526bc7ebde18e85",
|
|
@@ -559,10 +559,10 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
559
559
|
"plugins/src/base/skills/lisa-setup-linear/SKILL.md": "ec76f832a6df6f58b163f7f5ba6826ed2fcbddc91e44a8372e874858e4d0c518",
|
|
560
560
|
"plugins/src/base/skills/lisa-setup-notion/SKILL.md": "f5a1e9290789fd1c33675168d30461fb24a11c98a433ef777c1f536bc2f905ef",
|
|
561
561
|
"plugins/src/base/skills/lisa-setup-remote-aws/SKILL.md": "80fbf157f9c562c033886c25a99b37356602edd9e61cd2d492f339769ddcf97e",
|
|
562
|
-
"plugins/src/base/skills/lisa-setup-remote-env/SKILL.md": "
|
|
562
|
+
"plugins/src/base/skills/lisa-setup-remote-env/SKILL.md": "4fe124678cfc2b37188370af3b4e16895fcbad4d4b6f13b0fc7ac02bd99b0d26",
|
|
563
563
|
"plugins/src/base/skills/lisa-setup-remote-env/assets/session-start.sh": "6e3871ec2f8d56b8ebb85376ebdd3956f3ed8fa4f7b278c2ed90ca9b8845897c",
|
|
564
564
|
"plugins/src/base/skills/lisa-setup-remote-env/assets/setup.sh": "7f7cf8a2248dbaa31f2f039fdaf147d083427a21633f7cf06ad612f38344d6c1",
|
|
565
|
-
"plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs": "
|
|
565
|
+
"plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs": "e8f1e74dd29104dc880eb2540024549d18d2a9c958b2b6fce0610ab0afd55cbb",
|
|
566
566
|
"plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs": "ff66d33ba41a068d09be18f81ac68988238c2afa1d4e3733ae906b73eea74324",
|
|
567
567
|
"plugins/src/base/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs": "82bc2a27a0a5afb2ff413a88365c619fd7f0eaada5de3ccabf2000938f0607a4",
|
|
568
568
|
"plugins/src/base/skills/lisa-setup-sonar/SKILL.md": "53fbd8acce4b5e47e88195a7b90d5be424fa6ef7ad872f52c50467c690664c3b",
|
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.322.
|
|
118
|
+
"version": "2.322.5",
|
|
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": {
|
|
@@ -16,9 +16,12 @@ It changes **where** work happens and nothing about **what** happens. The remote
|
|
|
16
16
|
| --- | --- |
|
|
17
17
|
| omitted / `local` | The calling skill proceeds normally. Nothing is dispatched. |
|
|
18
18
|
| `codex-cloud` | Submit to the project's Codex Cloud environment and return. |
|
|
19
|
+
| `claude-web` | Fire the project's Claude routine, which starts a cloud session, and return. |
|
|
19
20
|
|
|
20
21
|
Any other value is **rejected explicitly**. A silently ignored `executionEnv` would run the work locally while the operator believes it went remote, and nothing downstream would contradict that belief.
|
|
21
22
|
|
|
23
|
+
For the same reason, the parameter is accepted as both `executionEnv=…` and `--executionEnv=…`, and a *dashed* parameter this skill does not recognise is an error rather than payload. The bare form is the documented one, but the dashed form is what anyone who has used a CLI will type, and a misspelling of either is indistinguishable from not asking at all — which is the one failure an operator cannot act on. A bare `key=value` still belongs to the payload, since prose may legitimately contain an equals sign.
|
|
24
|
+
|
|
22
25
|
If a behaviour must differ between local and remote, it belongs in the calling skill as an explicit branch — never here. The moment this file starts encoding domain behaviour, there are two implementations to keep in sync.
|
|
23
26
|
|
|
24
27
|
## The invocation stays thin
|
|
@@ -68,11 +68,25 @@ const LEDGER = join(".lisa", "remote-dispatch.json");
|
|
|
68
68
|
/** This file's directory, for locating the sibling secrets skill. */
|
|
69
69
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
70
70
|
|
|
71
|
+
/** Every parameter this program reads. Anything else is a typo. */
|
|
72
|
+
const KNOWN_PARAMS = new Set(["executionEnv"]);
|
|
73
|
+
|
|
71
74
|
/**
|
|
72
75
|
* Split `key=value` parameters from the rest of an invocation.
|
|
73
76
|
*
|
|
74
77
|
* The remainder is passed through untouched. It is the caller's payload — a
|
|
75
78
|
* ticket key, a description — and this program has no business interpreting it.
|
|
79
|
+
*
|
|
80
|
+
* A leading `--` is accepted and stripped. The bare form is the documented one,
|
|
81
|
+
* but `--executionEnv=claude-web` is what anyone who has used a CLI this decade
|
|
82
|
+
* types, and it previously fell through to the payload and left the surface at
|
|
83
|
+
* its default. That is precisely the outcome this skill says it exists to
|
|
84
|
+
* prevent: work runs locally while the operator believes it went remote, and
|
|
85
|
+
* nothing downstream contradicts them.
|
|
86
|
+
*
|
|
87
|
+
* An unrecognised parameter is rejected for the same reason. A misspelled
|
|
88
|
+
* `executionEnvv=claude-web` is indistinguishable from not asking at all, and
|
|
89
|
+
* silence is the one response that cannot be acted on.
|
|
76
90
|
* @param {string} input Raw argument string.
|
|
77
91
|
* @returns {{params: Record<string, string>, rest: string}} Parsed invocation.
|
|
78
92
|
*/
|
|
@@ -83,9 +97,19 @@ export function parseInvocation(input) {
|
|
|
83
97
|
.trim()
|
|
84
98
|
.split(/\s+/)
|
|
85
99
|
.filter(Boolean)) {
|
|
86
|
-
const match = /^([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
const match = /^(--)?([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
|
|
101
|
+
// Only a dashed token is required to be a known parameter. A bare `x=y` may
|
|
102
|
+
// legitimately be part of a payload — a description, a query — and this
|
|
103
|
+
// program has no business rejecting the caller's prose.
|
|
104
|
+
if (match && (match[1] || KNOWN_PARAMS.has(match[2]))) {
|
|
105
|
+
if (!KNOWN_PARAMS.has(match[2])) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`unknown parameter "${match[2]}".\n` +
|
|
108
|
+
`Supported: ${[...KNOWN_PARAMS].join(", ")}.`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
params[match[2]] = match[3];
|
|
112
|
+
} else rest.push(token);
|
|
89
113
|
}
|
|
90
114
|
return { params, rest: rest.join(" ") };
|
|
91
115
|
}
|
|
@@ -416,6 +440,28 @@ function resolveBearerToken(block) {
|
|
|
416
440
|
}
|
|
417
441
|
}
|
|
418
442
|
|
|
443
|
+
/**
|
|
444
|
+
* How each agent spells "run this skill".
|
|
445
|
+
*
|
|
446
|
+
* Codex invokes a skill with `$name`, Claude with `/name`. The prefix is the
|
|
447
|
+
* whole invocation: get it wrong and the agent reads a sentence that merely
|
|
448
|
+
* mentions a skill rather than a command that runs one — and being a capable
|
|
449
|
+
* model it will often do *something*, which is worse than failing, because the
|
|
450
|
+
* run looks successful while executing none of the skill's contract.
|
|
451
|
+
*
|
|
452
|
+
* Nothing downstream catches it either. The routine accepts any text, so the
|
|
453
|
+
* dispatch succeeds, a session identifier is recorded, and the ledger says the
|
|
454
|
+
* work was handed off. Only reading the session shows otherwise.
|
|
455
|
+
* @param {string} surface Execution surface.
|
|
456
|
+
* @param {string} skill Skill slug, without a prefix.
|
|
457
|
+
* @param {string} rest The payload the skill is invoked with.
|
|
458
|
+
* @returns {string} The thin invocation to send.
|
|
459
|
+
*/
|
|
460
|
+
export function buildInvocation(surface, skill, rest) {
|
|
461
|
+
const prefix = surface === "claude-web" ? "/" : "$";
|
|
462
|
+
return `${prefix}${skill} ${rest}`.trim();
|
|
463
|
+
}
|
|
464
|
+
|
|
419
465
|
async function main() {
|
|
420
466
|
const { skill, raw } = splitSkillFlag(process.argv.slice(2));
|
|
421
467
|
const { params, rest } = parseInvocation(raw);
|
|
@@ -432,7 +478,7 @@ async function main() {
|
|
|
432
478
|
// The invocation stays thin on purpose. Every durable instruction lives in
|
|
433
479
|
// the repository-local skill, so an interactive run, a scheduled run, and a
|
|
434
480
|
// recovery run all execute one contract.
|
|
435
|
-
const prompt =
|
|
481
|
+
const prompt = buildInvocation(surface, skill, rest);
|
|
436
482
|
|
|
437
483
|
if (surface === "claude-web") {
|
|
438
484
|
const { sessionId, url } = await dispatchClaudeWeb(block, prompt, rest);
|
|
@@ -13,8 +13,8 @@ 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: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
17
|
-
maintenance: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
16
|
+
setup: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
17
|
+
maintenance: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
**That line is identical for every project AND every surface.** Nothing in it names the
|
|
@@ -34,10 +34,18 @@ and neither mentions `$HOME`. An earlier version of this field used
|
|
|
34
34
|
the checkout is not under `$HOME` at all: the glob matches nothing and bash reports
|
|
35
35
|
`No such file or directory` for a path still containing a literal `*`.
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
**Every** match is prepared, not the first. A Claude Code web environment can hold more than
|
|
38
|
+
one checkout, and stopping at the first glob hit would prepare whichever repository sorts
|
|
39
|
+
first and silently ignore the rest — arbitrary rather than merely limited. Each script
|
|
40
|
+
anchors itself on its own repository root, and each project's secrets land under its own
|
|
41
|
+
`secrets.namespace`, so preparing several is well defined rather than a collision.
|
|
42
|
+
|
|
43
|
+
The exit status is the **first failure**, and every checkout is still attempted: one broken
|
|
44
|
+
repository must not hide the state of the others, and it must not report success either.
|
|
45
|
+
|
|
46
|
+
The explicit `exit 1` on `n=0` means a missing entrypoint says so rather than the field
|
|
47
|
+
silently succeeding — a `for` loop over a glob that matches nothing otherwise exits `0`,
|
|
48
|
+
which is the quiet failure this whole section exists to avoid.
|
|
41
49
|
|
|
42
50
|
They are the same command. A container may be built fresh or resumed from cache; every step is idempotent and version-aware, so running it twice is correct, and running it on resume is what picks up a rotated value, an edited note, or a changed version pin.
|
|
43
51
|
|
|
@@ -156,8 +164,8 @@ When emitting, produce exactly:
|
|
|
156
164
|
```text
|
|
157
165
|
Environment name: <project> remote executor
|
|
158
166
|
Repository: <org>/<repo> (must be the default checkout)
|
|
159
|
-
Setup script: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
160
|
-
Maintenance: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
167
|
+
Setup script: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
168
|
+
Maintenance: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
161
169
|
(identical for every project — the script finds the
|
|
162
170
|
checkout and installs from the committed lockfile)
|
|
163
171
|
Environment vars: LISA_SECRETS_SURFACE=codex-cloud
|
package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs
CHANGED
|
@@ -308,20 +308,29 @@ export function installAssets(cwd = process.cwd()) {
|
|
|
308
308
|
* at all — so a `$HOME` glob matched nothing there and bash was handed a path
|
|
309
309
|
* still containing a literal asterisk.
|
|
310
310
|
*
|
|
311
|
-
* Both candidates are therefore tried relative to cwd
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
311
|
+
* Both candidates are therefore tried relative to cwd, and EVERY match is
|
|
312
|
+
* prepared rather than the first. A Claude Code web environment can hold more
|
|
313
|
+
* than one checkout, so stopping at the first hit would prepare whichever
|
|
314
|
+
* repository sorts first and silently ignore the rest — arbitrary rather than
|
|
315
|
+
* merely limited. Each script anchors itself on its own repository root and
|
|
316
|
+
* each project materializes under its own `secrets.namespace`, so preparing
|
|
317
|
+
* several is well defined rather than a collision.
|
|
318
|
+
*
|
|
319
|
+
* The status is the first failure and every checkout is still attempted: one
|
|
320
|
+
* broken repository must neither hide the others nor report success. The
|
|
321
|
+
* explicit `exit 1` on no matches matters because a `for` loop over a glob that
|
|
322
|
+
* matches nothing otherwise exits 0 — the quiet success this guards against.
|
|
316
323
|
*
|
|
317
324
|
* A field that named the repository and package manager was a string a human
|
|
318
325
|
* had to get right, in a settings box with no review, no version history and no
|
|
319
326
|
* test — and the logic it encoded belongs in a file that has all three.
|
|
320
327
|
*/
|
|
321
328
|
export const SETUP_FIELD =
|
|
329
|
+
"n=0; rc=0; " +
|
|
322
330
|
"for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; " +
|
|
323
|
-
'do [ -f "$f" ]
|
|
324
|
-
'echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1'
|
|
331
|
+
'do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; ' +
|
|
332
|
+
'[ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; ' +
|
|
333
|
+
'exit "$rc"';
|
|
325
334
|
|
|
326
335
|
/**
|
|
327
336
|
* The settings block that wires the session-start hook into a repository.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lisa-remote-dispatch
|
|
3
|
-
description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
|
|
3
|
+
description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
|
|
4
4
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -16,9 +16,12 @@ It changes **where** work happens and nothing about **what** happens. The remote
|
|
|
16
16
|
| --- | --- |
|
|
17
17
|
| omitted / `local` | The calling skill proceeds normally. Nothing is dispatched. |
|
|
18
18
|
| `codex-cloud` | Submit to the project's Codex Cloud environment and return. |
|
|
19
|
+
| `claude-web` | Fire the project's Claude routine, which starts a cloud session, and return. |
|
|
19
20
|
|
|
20
21
|
Any other value is **rejected explicitly**. A silently ignored `executionEnv` would run the work locally while the operator believes it went remote, and nothing downstream would contradict that belief.
|
|
21
22
|
|
|
23
|
+
For the same reason, the parameter is accepted as both `executionEnv=…` and `--executionEnv=…`, and a *dashed* parameter this skill does not recognise is an error rather than payload. The bare form is the documented one, but the dashed form is what anyone who has used a CLI will type, and a misspelling of either is indistinguishable from not asking at all — which is the one failure an operator cannot act on. A bare `key=value` still belongs to the payload, since prose may legitimately contain an equals sign.
|
|
24
|
+
|
|
22
25
|
If a behaviour must differ between local and remote, it belongs in the calling skill as an explicit branch — never here. The moment this file starts encoding domain behaviour, there are two implementations to keep in sync.
|
|
23
26
|
|
|
24
27
|
## The invocation stays thin
|
|
@@ -68,11 +68,25 @@ const LEDGER = join(".lisa", "remote-dispatch.json");
|
|
|
68
68
|
/** This file's directory, for locating the sibling secrets skill. */
|
|
69
69
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
70
70
|
|
|
71
|
+
/** Every parameter this program reads. Anything else is a typo. */
|
|
72
|
+
const KNOWN_PARAMS = new Set(["executionEnv"]);
|
|
73
|
+
|
|
71
74
|
/**
|
|
72
75
|
* Split `key=value` parameters from the rest of an invocation.
|
|
73
76
|
*
|
|
74
77
|
* The remainder is passed through untouched. It is the caller's payload — a
|
|
75
78
|
* ticket key, a description — and this program has no business interpreting it.
|
|
79
|
+
*
|
|
80
|
+
* A leading `--` is accepted and stripped. The bare form is the documented one,
|
|
81
|
+
* but `--executionEnv=claude-web` is what anyone who has used a CLI this decade
|
|
82
|
+
* types, and it previously fell through to the payload and left the surface at
|
|
83
|
+
* its default. That is precisely the outcome this skill says it exists to
|
|
84
|
+
* prevent: work runs locally while the operator believes it went remote, and
|
|
85
|
+
* nothing downstream contradicts them.
|
|
86
|
+
*
|
|
87
|
+
* An unrecognised parameter is rejected for the same reason. A misspelled
|
|
88
|
+
* `executionEnvv=claude-web` is indistinguishable from not asking at all, and
|
|
89
|
+
* silence is the one response that cannot be acted on.
|
|
76
90
|
* @param {string} input Raw argument string.
|
|
77
91
|
* @returns {{params: Record<string, string>, rest: string}} Parsed invocation.
|
|
78
92
|
*/
|
|
@@ -83,9 +97,19 @@ export function parseInvocation(input) {
|
|
|
83
97
|
.trim()
|
|
84
98
|
.split(/\s+/)
|
|
85
99
|
.filter(Boolean)) {
|
|
86
|
-
const match = /^([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
const match = /^(--)?([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
|
|
101
|
+
// Only a dashed token is required to be a known parameter. A bare `x=y` may
|
|
102
|
+
// legitimately be part of a payload — a description, a query — and this
|
|
103
|
+
// program has no business rejecting the caller's prose.
|
|
104
|
+
if (match && (match[1] || KNOWN_PARAMS.has(match[2]))) {
|
|
105
|
+
if (!KNOWN_PARAMS.has(match[2])) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`unknown parameter "${match[2]}".\n` +
|
|
108
|
+
`Supported: ${[...KNOWN_PARAMS].join(", ")}.`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
params[match[2]] = match[3];
|
|
112
|
+
} else rest.push(token);
|
|
89
113
|
}
|
|
90
114
|
return { params, rest: rest.join(" ") };
|
|
91
115
|
}
|
|
@@ -416,6 +440,28 @@ function resolveBearerToken(block) {
|
|
|
416
440
|
}
|
|
417
441
|
}
|
|
418
442
|
|
|
443
|
+
/**
|
|
444
|
+
* How each agent spells "run this skill".
|
|
445
|
+
*
|
|
446
|
+
* Codex invokes a skill with `$name`, Claude with `/name`. The prefix is the
|
|
447
|
+
* whole invocation: get it wrong and the agent reads a sentence that merely
|
|
448
|
+
* mentions a skill rather than a command that runs one — and being a capable
|
|
449
|
+
* model it will often do *something*, which is worse than failing, because the
|
|
450
|
+
* run looks successful while executing none of the skill's contract.
|
|
451
|
+
*
|
|
452
|
+
* Nothing downstream catches it either. The routine accepts any text, so the
|
|
453
|
+
* dispatch succeeds, a session identifier is recorded, and the ledger says the
|
|
454
|
+
* work was handed off. Only reading the session shows otherwise.
|
|
455
|
+
* @param {string} surface Execution surface.
|
|
456
|
+
* @param {string} skill Skill slug, without a prefix.
|
|
457
|
+
* @param {string} rest The payload the skill is invoked with.
|
|
458
|
+
* @returns {string} The thin invocation to send.
|
|
459
|
+
*/
|
|
460
|
+
export function buildInvocation(surface, skill, rest) {
|
|
461
|
+
const prefix = surface === "claude-web" ? "/" : "$";
|
|
462
|
+
return `${prefix}${skill} ${rest}`.trim();
|
|
463
|
+
}
|
|
464
|
+
|
|
419
465
|
async function main() {
|
|
420
466
|
const { skill, raw } = splitSkillFlag(process.argv.slice(2));
|
|
421
467
|
const { params, rest } = parseInvocation(raw);
|
|
@@ -432,7 +478,7 @@ async function main() {
|
|
|
432
478
|
// The invocation stays thin on purpose. Every durable instruction lives in
|
|
433
479
|
// the repository-local skill, so an interactive run, a scheduled run, and a
|
|
434
480
|
// recovery run all execute one contract.
|
|
435
|
-
const prompt =
|
|
481
|
+
const prompt = buildInvocation(surface, skill, rest);
|
|
436
482
|
|
|
437
483
|
if (surface === "claude-web") {
|
|
438
484
|
const { sessionId, url } = await dispatchClaudeWeb(block, prompt, rest);
|
|
@@ -13,8 +13,8 @@ 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: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
17
|
-
maintenance: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
16
|
+
setup: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
17
|
+
maintenance: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
**That line is identical for every project AND every surface.** Nothing in it names the
|
|
@@ -34,10 +34,18 @@ and neither mentions `$HOME`. An earlier version of this field used
|
|
|
34
34
|
the checkout is not under `$HOME` at all: the glob matches nothing and bash reports
|
|
35
35
|
`No such file or directory` for a path still containing a literal `*`.
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
**Every** match is prepared, not the first. A Claude Code web environment can hold more than
|
|
38
|
+
one checkout, and stopping at the first glob hit would prepare whichever repository sorts
|
|
39
|
+
first and silently ignore the rest — arbitrary rather than merely limited. Each script
|
|
40
|
+
anchors itself on its own repository root, and each project's secrets land under its own
|
|
41
|
+
`secrets.namespace`, so preparing several is well defined rather than a collision.
|
|
42
|
+
|
|
43
|
+
The exit status is the **first failure**, and every checkout is still attempted: one broken
|
|
44
|
+
repository must not hide the state of the others, and it must not report success either.
|
|
45
|
+
|
|
46
|
+
The explicit `exit 1` on `n=0` means a missing entrypoint says so rather than the field
|
|
47
|
+
silently succeeding — a `for` loop over a glob that matches nothing otherwise exits `0`,
|
|
48
|
+
which is the quiet failure this whole section exists to avoid.
|
|
41
49
|
|
|
42
50
|
They are the same command. A container may be built fresh or resumed from cache; every step is idempotent and version-aware, so running it twice is correct, and running it on resume is what picks up a rotated value, an edited note, or a changed version pin.
|
|
43
51
|
|
|
@@ -156,8 +164,8 @@ When emitting, produce exactly:
|
|
|
156
164
|
```text
|
|
157
165
|
Environment name: <project> remote executor
|
|
158
166
|
Repository: <org>/<repo> (must be the default checkout)
|
|
159
|
-
Setup script: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
160
|
-
Maintenance: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
167
|
+
Setup script: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
168
|
+
Maintenance: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
161
169
|
(identical for every project — the script finds the
|
|
162
170
|
checkout and installs from the committed lockfile)
|
|
163
171
|
Environment vars: LISA_SECRETS_SURFACE=codex-cloud
|
|
@@ -308,20 +308,29 @@ export function installAssets(cwd = process.cwd()) {
|
|
|
308
308
|
* at all — so a `$HOME` glob matched nothing there and bash was handed a path
|
|
309
309
|
* still containing a literal asterisk.
|
|
310
310
|
*
|
|
311
|
-
* Both candidates are therefore tried relative to cwd
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
311
|
+
* Both candidates are therefore tried relative to cwd, and EVERY match is
|
|
312
|
+
* prepared rather than the first. A Claude Code web environment can hold more
|
|
313
|
+
* than one checkout, so stopping at the first hit would prepare whichever
|
|
314
|
+
* repository sorts first and silently ignore the rest — arbitrary rather than
|
|
315
|
+
* merely limited. Each script anchors itself on its own repository root and
|
|
316
|
+
* each project materializes under its own `secrets.namespace`, so preparing
|
|
317
|
+
* several is well defined rather than a collision.
|
|
318
|
+
*
|
|
319
|
+
* The status is the first failure and every checkout is still attempted: one
|
|
320
|
+
* broken repository must neither hide the others nor report success. The
|
|
321
|
+
* explicit `exit 1` on no matches matters because a `for` loop over a glob that
|
|
322
|
+
* matches nothing otherwise exits 0 — the quiet success this guards against.
|
|
316
323
|
*
|
|
317
324
|
* A field that named the repository and package manager was a string a human
|
|
318
325
|
* had to get right, in a settings box with no review, no version history and no
|
|
319
326
|
* test — and the logic it encoded belongs in a file that has all three.
|
|
320
327
|
*/
|
|
321
328
|
export const SETUP_FIELD =
|
|
329
|
+
"n=0; rc=0; " +
|
|
322
330
|
"for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; " +
|
|
323
|
-
'do [ -f "$f" ]
|
|
324
|
-
'echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1'
|
|
331
|
+
'do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; ' +
|
|
332
|
+
'[ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; ' +
|
|
333
|
+
'exit "$rc"';
|
|
325
334
|
|
|
326
335
|
/**
|
|
327
336
|
* The settings block that wires the session-start hook into a repository.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lisa-remote-dispatch
|
|
3
|
-
description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
|
|
3
|
+
description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
|
|
4
4
|
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -16,9 +16,12 @@ It changes **where** work happens and nothing about **what** happens. The remote
|
|
|
16
16
|
| --- | --- |
|
|
17
17
|
| omitted / `local` | The calling skill proceeds normally. Nothing is dispatched. |
|
|
18
18
|
| `codex-cloud` | Submit to the project's Codex Cloud environment and return. |
|
|
19
|
+
| `claude-web` | Fire the project's Claude routine, which starts a cloud session, and return. |
|
|
19
20
|
|
|
20
21
|
Any other value is **rejected explicitly**. A silently ignored `executionEnv` would run the work locally while the operator believes it went remote, and nothing downstream would contradict that belief.
|
|
21
22
|
|
|
23
|
+
For the same reason, the parameter is accepted as both `executionEnv=…` and `--executionEnv=…`, and a *dashed* parameter this skill does not recognise is an error rather than payload. The bare form is the documented one, but the dashed form is what anyone who has used a CLI will type, and a misspelling of either is indistinguishable from not asking at all — which is the one failure an operator cannot act on. A bare `key=value` still belongs to the payload, since prose may legitimately contain an equals sign.
|
|
24
|
+
|
|
22
25
|
If a behaviour must differ between local and remote, it belongs in the calling skill as an explicit branch — never here. The moment this file starts encoding domain behaviour, there are two implementations to keep in sync.
|
|
23
26
|
|
|
24
27
|
## The invocation stays thin
|
|
@@ -68,11 +68,25 @@ const LEDGER = join(".lisa", "remote-dispatch.json");
|
|
|
68
68
|
/** This file's directory, for locating the sibling secrets skill. */
|
|
69
69
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
70
70
|
|
|
71
|
+
/** Every parameter this program reads. Anything else is a typo. */
|
|
72
|
+
const KNOWN_PARAMS = new Set(["executionEnv"]);
|
|
73
|
+
|
|
71
74
|
/**
|
|
72
75
|
* Split `key=value` parameters from the rest of an invocation.
|
|
73
76
|
*
|
|
74
77
|
* The remainder is passed through untouched. It is the caller's payload — a
|
|
75
78
|
* ticket key, a description — and this program has no business interpreting it.
|
|
79
|
+
*
|
|
80
|
+
* A leading `--` is accepted and stripped. The bare form is the documented one,
|
|
81
|
+
* but `--executionEnv=claude-web` is what anyone who has used a CLI this decade
|
|
82
|
+
* types, and it previously fell through to the payload and left the surface at
|
|
83
|
+
* its default. That is precisely the outcome this skill says it exists to
|
|
84
|
+
* prevent: work runs locally while the operator believes it went remote, and
|
|
85
|
+
* nothing downstream contradicts them.
|
|
86
|
+
*
|
|
87
|
+
* An unrecognised parameter is rejected for the same reason. A misspelled
|
|
88
|
+
* `executionEnvv=claude-web` is indistinguishable from not asking at all, and
|
|
89
|
+
* silence is the one response that cannot be acted on.
|
|
76
90
|
* @param {string} input Raw argument string.
|
|
77
91
|
* @returns {{params: Record<string, string>, rest: string}} Parsed invocation.
|
|
78
92
|
*/
|
|
@@ -83,9 +97,19 @@ export function parseInvocation(input) {
|
|
|
83
97
|
.trim()
|
|
84
98
|
.split(/\s+/)
|
|
85
99
|
.filter(Boolean)) {
|
|
86
|
-
const match = /^([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
const match = /^(--)?([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
|
|
101
|
+
// Only a dashed token is required to be a known parameter. A bare `x=y` may
|
|
102
|
+
// legitimately be part of a payload — a description, a query — and this
|
|
103
|
+
// program has no business rejecting the caller's prose.
|
|
104
|
+
if (match && (match[1] || KNOWN_PARAMS.has(match[2]))) {
|
|
105
|
+
if (!KNOWN_PARAMS.has(match[2])) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`unknown parameter "${match[2]}".\n` +
|
|
108
|
+
`Supported: ${[...KNOWN_PARAMS].join(", ")}.`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
params[match[2]] = match[3];
|
|
112
|
+
} else rest.push(token);
|
|
89
113
|
}
|
|
90
114
|
return { params, rest: rest.join(" ") };
|
|
91
115
|
}
|
|
@@ -416,6 +440,28 @@ function resolveBearerToken(block) {
|
|
|
416
440
|
}
|
|
417
441
|
}
|
|
418
442
|
|
|
443
|
+
/**
|
|
444
|
+
* How each agent spells "run this skill".
|
|
445
|
+
*
|
|
446
|
+
* Codex invokes a skill with `$name`, Claude with `/name`. The prefix is the
|
|
447
|
+
* whole invocation: get it wrong and the agent reads a sentence that merely
|
|
448
|
+
* mentions a skill rather than a command that runs one — and being a capable
|
|
449
|
+
* model it will often do *something*, which is worse than failing, because the
|
|
450
|
+
* run looks successful while executing none of the skill's contract.
|
|
451
|
+
*
|
|
452
|
+
* Nothing downstream catches it either. The routine accepts any text, so the
|
|
453
|
+
* dispatch succeeds, a session identifier is recorded, and the ledger says the
|
|
454
|
+
* work was handed off. Only reading the session shows otherwise.
|
|
455
|
+
* @param {string} surface Execution surface.
|
|
456
|
+
* @param {string} skill Skill slug, without a prefix.
|
|
457
|
+
* @param {string} rest The payload the skill is invoked with.
|
|
458
|
+
* @returns {string} The thin invocation to send.
|
|
459
|
+
*/
|
|
460
|
+
export function buildInvocation(surface, skill, rest) {
|
|
461
|
+
const prefix = surface === "claude-web" ? "/" : "$";
|
|
462
|
+
return `${prefix}${skill} ${rest}`.trim();
|
|
463
|
+
}
|
|
464
|
+
|
|
419
465
|
async function main() {
|
|
420
466
|
const { skill, raw } = splitSkillFlag(process.argv.slice(2));
|
|
421
467
|
const { params, rest } = parseInvocation(raw);
|
|
@@ -432,7 +478,7 @@ async function main() {
|
|
|
432
478
|
// The invocation stays thin on purpose. Every durable instruction lives in
|
|
433
479
|
// the repository-local skill, so an interactive run, a scheduled run, and a
|
|
434
480
|
// recovery run all execute one contract.
|
|
435
|
-
const prompt =
|
|
481
|
+
const prompt = buildInvocation(surface, skill, rest);
|
|
436
482
|
|
|
437
483
|
if (surface === "claude-web") {
|
|
438
484
|
const { sessionId, url } = await dispatchClaudeWeb(block, prompt, rest);
|
|
@@ -13,8 +13,8 @@ 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: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
17
|
-
maintenance: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
16
|
+
setup: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
17
|
+
maintenance: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
**That line is identical for every project AND every surface.** Nothing in it names the
|
|
@@ -34,10 +34,18 @@ and neither mentions `$HOME`. An earlier version of this field used
|
|
|
34
34
|
the checkout is not under `$HOME` at all: the glob matches nothing and bash reports
|
|
35
35
|
`No such file or directory` for a path still containing a literal `*`.
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
**Every** match is prepared, not the first. A Claude Code web environment can hold more than
|
|
38
|
+
one checkout, and stopping at the first glob hit would prepare whichever repository sorts
|
|
39
|
+
first and silently ignore the rest — arbitrary rather than merely limited. Each script
|
|
40
|
+
anchors itself on its own repository root, and each project's secrets land under its own
|
|
41
|
+
`secrets.namespace`, so preparing several is well defined rather than a collision.
|
|
42
|
+
|
|
43
|
+
The exit status is the **first failure**, and every checkout is still attempted: one broken
|
|
44
|
+
repository must not hide the state of the others, and it must not report success either.
|
|
45
|
+
|
|
46
|
+
The explicit `exit 1` on `n=0` means a missing entrypoint says so rather than the field
|
|
47
|
+
silently succeeding — a `for` loop over a glob that matches nothing otherwise exits `0`,
|
|
48
|
+
which is the quiet failure this whole section exists to avoid.
|
|
41
49
|
|
|
42
50
|
They are the same command. A container may be built fresh or resumed from cache; every step is idempotent and version-aware, so running it twice is correct, and running it on resume is what picks up a rotated value, an edited note, or a changed version pin.
|
|
43
51
|
|
|
@@ -156,8 +164,8 @@ When emitting, produce exactly:
|
|
|
156
164
|
```text
|
|
157
165
|
Environment name: <project> remote executor
|
|
158
166
|
Repository: <org>/<repo> (must be the default checkout)
|
|
159
|
-
Setup script: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
160
|
-
Maintenance: for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ]
|
|
167
|
+
Setup script: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
168
|
+
Maintenance: n=0; rc=0; for f in scripts/lisa-remote-env/setup.sh */scripts/lisa-remote-env/setup.sh; do [ -f "$f" ] || continue; n=$((n+1)); bash "$f" || rc=$?; done; [ "$n" -gt 0 ] || { echo "lisa-remote-env entrypoint not found under $PWD" >&2; exit 1; }; exit "$rc"
|
|
161
169
|
(identical for every project — the script finds the
|
|
162
170
|
checkout and installs from the committed lockfile)
|
|
163
171
|
Environment vars: LISA_SECRETS_SURFACE=codex-cloud
|