@codyswann/lisa 2.316.2 → 2.317.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/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +139 -9
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- 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-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-remote-dispatch/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +137 -51
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
- package/plugins/lisa/commands/implement.md +5 -3
- package/plugins/lisa/commands/setup/remote-env.md +7 -0
- package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa/skills/lisa-remote-dispatch/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/lisa/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
- package/plugins/lisa-agy/commands/lisa/implement.md +5 -3
- package/plugins/lisa-agy/commands/lisa/setup/remote-env.md +7 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa-agy/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa-agy/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa-agy/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa-agy/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa-agy/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa-agy/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/commands/lisa/implement.md +5 -3
- package/plugins/lisa-copilot/commands/lisa/setup/remote-env.md +7 -0
- package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa-copilot/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa-copilot/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa-copilot/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa-copilot/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa-copilot/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa-copilot/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/implement.md +5 -3
- package/plugins/lisa-cursor/commands/lisa/setup/remote-env.md +7 -0
- package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa-cursor/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa-cursor/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa-cursor/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa-cursor/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa-cursor/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa-cursor/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/commands/implement.md +5 -3
- package/plugins/src/base/commands/setup/remote-env.md +7 -0
- package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/src/base/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/src/base/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/src/base/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/src/base/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/src/base/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/src/base/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/src/base/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/src/base/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/src/base/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/src/base/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Decide what a remote environment already has and what it still needs.
|
|
4
|
+
*
|
|
5
|
+
* Remote environments arrive with a base image that already provides much of
|
|
6
|
+
* what a project needs, so the manifest has two entry kinds rather than one:
|
|
7
|
+
*
|
|
8
|
+
* require — assert present, fail setup with a clear message if missing
|
|
9
|
+
* install — provision it, pinned and checksummed
|
|
10
|
+
*
|
|
11
|
+
* The distinction is not bookkeeping. A base image is **not a contract**: the
|
|
12
|
+
* vendor can change it. A project quietly depending on a preinstalled tool
|
|
13
|
+
* should break loudly at setup when that happens, rather than mysteriously
|
|
14
|
+
* mid-task weeks later. `require` is what converts an implicit assumption into
|
|
15
|
+
* an explicit check.
|
|
16
|
+
*
|
|
17
|
+
* Planning is separated from execution so the whole decision table is testable
|
|
18
|
+
* without a container, a network, or a real binary anywhere.
|
|
19
|
+
* @module toolchain
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Compare dotted version strings numerically.
|
|
24
|
+
*
|
|
25
|
+
* String comparison gets this wrong in the case that matters: "10" sorts before
|
|
26
|
+
* "9", so a base-image bump to a newer major would read as a downgrade and pass
|
|
27
|
+
* a minimum-version check it should fail.
|
|
28
|
+
* @param {string} a Left version.
|
|
29
|
+
* @param {string} b Right version.
|
|
30
|
+
* @returns {number} Negative, zero, or positive.
|
|
31
|
+
*/
|
|
32
|
+
export function compareVersions(a, b) {
|
|
33
|
+
const left = String(a).split(".").map(Number);
|
|
34
|
+
const right = String(b).split(".").map(Number);
|
|
35
|
+
for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
|
|
36
|
+
const diff = (left[i] || 0) - (right[i] || 0);
|
|
37
|
+
if (diff) return diff;
|
|
38
|
+
}
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Pull a dotted version out of whatever a tool prints for `--version`.
|
|
44
|
+
*
|
|
45
|
+
* Tools are wildly inconsistent here ("bws 2.1.0", "v20.11.0", "Python 3.12.1"),
|
|
46
|
+
* and normalising with a regex is more honest than maintaining a per-tool parser
|
|
47
|
+
* table that silently rots.
|
|
48
|
+
* @param {string} output Raw `--version` output.
|
|
49
|
+
* @returns {string|null} The version, or null when none is recognisable.
|
|
50
|
+
*/
|
|
51
|
+
export function extractVersion(output) {
|
|
52
|
+
const match = /(\d+(?:\.\d+)*)/.exec(String(output ?? ""));
|
|
53
|
+
return match ? match[1] : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Classify one required tool against what the environment actually provides.
|
|
58
|
+
* @param {object} tool Manifest entry.
|
|
59
|
+
* @param {{version: string|null, present: boolean}} found Probe result.
|
|
60
|
+
* @returns {{name: string, action: string, reason: string}} The decision.
|
|
61
|
+
*/
|
|
62
|
+
function planRequired(tool, found) {
|
|
63
|
+
if (!found.present) {
|
|
64
|
+
return {
|
|
65
|
+
name: tool.name,
|
|
66
|
+
action: "missing",
|
|
67
|
+
reason:
|
|
68
|
+
`${tool.name} is required but not present. The base image is not a ` +
|
|
69
|
+
`contract — if it used to provide this, the vendor has changed it. ` +
|
|
70
|
+
`Either add an install entry or pin a different image.`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (
|
|
74
|
+
tool.minVersion &&
|
|
75
|
+
compareVersions(found.version ?? "0", tool.minVersion) < 0
|
|
76
|
+
) {
|
|
77
|
+
return {
|
|
78
|
+
name: tool.name,
|
|
79
|
+
action: "missing",
|
|
80
|
+
reason:
|
|
81
|
+
`${tool.name} ${found.version ?? "(unknown)"} is older than the ` +
|
|
82
|
+
`required ${tool.minVersion}. Presence alone is not enough when a ` +
|
|
83
|
+
`project depends on a specific version.`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
name: tool.name,
|
|
88
|
+
action: "present",
|
|
89
|
+
reason: `${tool.name} ${found.version ?? ""}`.trim(),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Classify one installable tool against what is already on disk.
|
|
95
|
+
*
|
|
96
|
+
* Detect first, install second. Skipping a matching version is what makes setup
|
|
97
|
+
* and maintenance the same script, and it is the cheap path when a container
|
|
98
|
+
* resumes from cache rather than being built fresh.
|
|
99
|
+
* @param {object} tool Manifest entry.
|
|
100
|
+
* @param {{version: string|null, present: boolean}} found Probe result.
|
|
101
|
+
* @returns {{name: string, action: string, reason: string}} The decision.
|
|
102
|
+
*/
|
|
103
|
+
function planInstallable(tool, found) {
|
|
104
|
+
if (!tool.version) {
|
|
105
|
+
return {
|
|
106
|
+
name: tool.name,
|
|
107
|
+
action: "invalid",
|
|
108
|
+
reason: `${tool.name} has no pinned version. An unpinned install is not reproducible.`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (found.present && found.version === tool.version) {
|
|
112
|
+
return {
|
|
113
|
+
name: tool.name,
|
|
114
|
+
action: "skip",
|
|
115
|
+
reason: `${tool.name} ${tool.version} already installed`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
name: tool.name,
|
|
120
|
+
action: "install",
|
|
121
|
+
reason: found.present
|
|
122
|
+
? `${tool.name} ${found.version ?? "(unknown)"} does not match pin ${tool.version}`
|
|
123
|
+
: `${tool.name} ${tool.version} not installed`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Produce the complete plan for a toolchain manifest.
|
|
129
|
+
* @param {{require?: object[], install?: object[]}} tools Manifest.
|
|
130
|
+
* @param {(name: string) => {version: string|null, present: boolean}} probe Version probe.
|
|
131
|
+
* @returns {Array<{name: string, action: string, reason: string}>} Ordered decisions.
|
|
132
|
+
*/
|
|
133
|
+
export function planToolchain(tools, probe) {
|
|
134
|
+
const plan = [];
|
|
135
|
+
for (const tool of tools.require ?? [])
|
|
136
|
+
plan.push(planRequired(tool, probe(tool.name)));
|
|
137
|
+
for (const tool of tools.install ?? [])
|
|
138
|
+
plan.push(planInstallable(tool, probe(tool.name)));
|
|
139
|
+
return plan;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Reject a manifest entry that could install something unverifiable.
|
|
144
|
+
*
|
|
145
|
+
* A pinned version with no checksum still trusts whatever the URL serves today.
|
|
146
|
+
* Requiring both, changed together in one reviewed commit, is what makes an
|
|
147
|
+
* unexpected archive fail before installation rather than after.
|
|
148
|
+
* @param {object} tool Manifest entry.
|
|
149
|
+
*/
|
|
150
|
+
export function assertPinned(tool) {
|
|
151
|
+
if (tool.install === "release-zip") {
|
|
152
|
+
if (!tool.url || !tool.sha256) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`${tool.name}: a release-zip install needs both url and sha256.\n` +
|
|
155
|
+
`A version bump must move the checksum in the same reviewed commit.`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (tool.install === "npm-global") {
|
|
161
|
+
if (!tool.package)
|
|
162
|
+
throw new Error(`${tool.name}: npm-global install needs a package`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
throw new Error(
|
|
166
|
+
`${tool.name}: unknown install method "${tool.install}".\n` +
|
|
167
|
+
`Supported: release-zip, npm-global.`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Prove a remote environment is actually configured the way it was meant to be.
|
|
4
|
+
*
|
|
5
|
+
* This runs identically whatever provisioned the environment — an API, a driven
|
|
6
|
+
* browser, or a human pasting emitted config. That is the point: trust comes
|
|
7
|
+
* from the read-back, not from the mechanism, so the weakest provisioning tier
|
|
8
|
+
* ends up as trustworthy as the strongest.
|
|
9
|
+
*
|
|
10
|
+
* Every assertion is made **without printing a value**. Presence, mode, and
|
|
11
|
+
* shape are all checkable without revealing content, and an environment proof
|
|
12
|
+
* that leaks the thing it is proving is not a proof worth having.
|
|
13
|
+
*
|
|
14
|
+
* One rule this encodes the hard way: never verify against a vendor's own UI.
|
|
15
|
+
* A dashboard counter reported zero tasks for an environment that had
|
|
16
|
+
* demonstrably completed one, because the underlying records carried no
|
|
17
|
+
* environment identifier. Assert against durable state instead.
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* verify-remote-env.mjs
|
|
21
|
+
* @module verify-remote-env
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { execFileSync } from "node:child_process";
|
|
25
|
+
import { existsSync, statSync } from "node:fs";
|
|
26
|
+
|
|
27
|
+
import { readRemoteEnvConfig } from "./setup-remote-env.mjs";
|
|
28
|
+
import { extractVersion } from "./toolchain.mjs";
|
|
29
|
+
|
|
30
|
+
/** Collected results, so every check runs before anything reports failure. */
|
|
31
|
+
const results = [];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Record one assertion.
|
|
35
|
+
* @param {boolean} ok Whether it held.
|
|
36
|
+
* @param {string} label What was checked.
|
|
37
|
+
* @param {string} [detail] Extra context, never a secret value.
|
|
38
|
+
*/
|
|
39
|
+
function check(ok, label, detail = "") {
|
|
40
|
+
results.push({ ok, label, detail });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Assert a path exists with an exact permission mode.
|
|
45
|
+
*
|
|
46
|
+
* The mode is the assertion, not merely existence. A world-readable secrets
|
|
47
|
+
* file is present and wrong, and "present" is what a weaker check would report.
|
|
48
|
+
* @param {string} path Filesystem path.
|
|
49
|
+
* @param {number} mode Expected permission bits.
|
|
50
|
+
* @param {string} label What this path is.
|
|
51
|
+
*/
|
|
52
|
+
function checkMode(path, mode, label) {
|
|
53
|
+
if (!existsSync(path)) {
|
|
54
|
+
check(false, label, "absent");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const actual = statSync(path).mode & 0o777;
|
|
58
|
+
check(
|
|
59
|
+
actual === mode,
|
|
60
|
+
label,
|
|
61
|
+
actual === mode
|
|
62
|
+
? `mode ${mode.toString(8)}`
|
|
63
|
+
: `mode ${actual.toString(8)}, expected ${mode.toString(8)}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Run a sibling skill's script and capture its output.
|
|
69
|
+
* @param {string[]} args Node arguments.
|
|
70
|
+
* @returns {{ok: boolean, out: string}} Result.
|
|
71
|
+
*/
|
|
72
|
+
function node(args) {
|
|
73
|
+
try {
|
|
74
|
+
const out = execFileSync("node", args, {
|
|
75
|
+
encoding: "utf8",
|
|
76
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
77
|
+
});
|
|
78
|
+
return { ok: true, out: out.trim() };
|
|
79
|
+
} catch (err) {
|
|
80
|
+
return { ok: false, out: String(err.stderr ?? err.message).trim() };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Assert each declared tool is present at its pinned or minimum version.
|
|
86
|
+
* @param {object} tools Toolchain manifest.
|
|
87
|
+
*/
|
|
88
|
+
function verifyToolchain(tools) {
|
|
89
|
+
for (const tool of tools.require ?? []) {
|
|
90
|
+
try {
|
|
91
|
+
const out = execFileSync(tool.name, ["--version"], {
|
|
92
|
+
encoding: "utf8",
|
|
93
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
94
|
+
});
|
|
95
|
+
check(true, `tool ${tool.name}`, extractVersion(out) ?? "present");
|
|
96
|
+
} catch {
|
|
97
|
+
check(false, `tool ${tool.name}`, "required but not present");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const tool of tools.install ?? []) {
|
|
101
|
+
try {
|
|
102
|
+
const out = execFileSync(tool.name, ["--version"], {
|
|
103
|
+
encoding: "utf8",
|
|
104
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
105
|
+
});
|
|
106
|
+
const found = extractVersion(out);
|
|
107
|
+
check(
|
|
108
|
+
found === tool.version,
|
|
109
|
+
`tool ${tool.name}`,
|
|
110
|
+
`${found} (pin ${tool.version})`
|
|
111
|
+
);
|
|
112
|
+
} catch {
|
|
113
|
+
check(
|
|
114
|
+
false,
|
|
115
|
+
`tool ${tool.name}`,
|
|
116
|
+
`pinned ${tool.version} but not installed`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Assert the working tree is clean.
|
|
124
|
+
*
|
|
125
|
+
* A remote environment that starts dirty will carry unrelated changes into
|
|
126
|
+
* whatever it produces, and the resulting diff is no longer attributable to the
|
|
127
|
+
* task that ran.
|
|
128
|
+
*/
|
|
129
|
+
function verifyCleanCheckout() {
|
|
130
|
+
try {
|
|
131
|
+
const out = execFileSync("git", ["status", "--porcelain"], {
|
|
132
|
+
encoding: "utf8",
|
|
133
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
134
|
+
});
|
|
135
|
+
check(
|
|
136
|
+
out.trim() === "",
|
|
137
|
+
"clean checkout",
|
|
138
|
+
out.trim() ? "working tree is dirty" : ""
|
|
139
|
+
);
|
|
140
|
+
} catch {
|
|
141
|
+
check(false, "clean checkout", "not a git repository");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function main() {
|
|
146
|
+
const cfg = readRemoteEnvConfig();
|
|
147
|
+
const secretsDir = process.argv[2];
|
|
148
|
+
|
|
149
|
+
verifyToolchain(cfg.tools);
|
|
150
|
+
|
|
151
|
+
const surface = node([
|
|
152
|
+
new URL(
|
|
153
|
+
"../../lisa-secrets-access/scripts/resolve-secret.mjs",
|
|
154
|
+
import.meta.url
|
|
155
|
+
).pathname,
|
|
156
|
+
"surface",
|
|
157
|
+
]);
|
|
158
|
+
check(surface.ok, "surface detected", surface.out);
|
|
159
|
+
|
|
160
|
+
if (secretsDir) {
|
|
161
|
+
checkMode(secretsDir, 0o700, "secrets directory");
|
|
162
|
+
checkMode(`${secretsDir}/secrets.env`, 0o600, "values file");
|
|
163
|
+
checkMode(`${secretsDir}/secret-notes.json`, 0o600, "notes file");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
verifyCleanCheckout();
|
|
167
|
+
|
|
168
|
+
let failed = 0;
|
|
169
|
+
for (const r of results) {
|
|
170
|
+
if (!r.ok) failed += 1;
|
|
171
|
+
console.log(
|
|
172
|
+
` ${r.ok ? "ok " : "FAIL"} ${r.label.padEnd(28)} ${r.detail}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (failed) throw new Error(`${failed} environment check(s) failed`);
|
|
176
|
+
console.log("\nRemote environment verified without revealing any value.");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
180
|
+
try {
|
|
181
|
+
main();
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.error(err.message);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.317.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.317.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.317.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.317.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.317.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|