@codyswann/lisa 2.332.0 → 2.332.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/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +3 -2
- 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-secrets-access/scripts/validate-config.mjs +47 -14
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +19 -1
- package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -14
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +19 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -14
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +19 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -14
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +19 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -14
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +19 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +47 -14
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +19 -1
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.332.
|
|
118
|
+
"version": "2.332.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": {
|
|
@@ -76,13 +76,37 @@ function bindingsFor(surface) {
|
|
|
76
76
|
/**
|
|
77
77
|
* Install methods the toolchain runner supports.
|
|
78
78
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
79
|
+
* This is a deliberate copy of `INSTALL_METHODS` in `toolchain.mjs`, which is
|
|
80
|
+
* the list that actually decides. Skills are distributed as self-contained
|
|
81
|
+
* directories and nothing imports across a skill boundary, so importing the
|
|
82
|
+
* authority here would couple two skills' distribution for one array.
|
|
83
|
+
*
|
|
84
|
+
* The copy has drifted twice. First `release-tar` was missing while the runner
|
|
85
|
+
* had supported it for as long as gh has been pinned. Then `release-tree` and
|
|
86
|
+
* `release-binary` were both missing, which rejected a valid `jq` pin. Each
|
|
87
|
+
* time the only safeguard was a comment asking the next author to remember.
|
|
88
|
+
* A conformance test now compares the two sets directly, so adding a kind on
|
|
89
|
+
* either side fails until both know it.
|
|
84
90
|
*/
|
|
85
|
-
const INSTALL_METHODS = new Set([
|
|
91
|
+
const INSTALL_METHODS = new Set([
|
|
92
|
+
"release-zip",
|
|
93
|
+
"release-tar",
|
|
94
|
+
"release-tree",
|
|
95
|
+
"release-binary",
|
|
96
|
+
"npm-global",
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Download kinds — every method that fetches a URL, so every one that needs a
|
|
101
|
+
* checksum. Spelled as "not npm-global" rather than as a list, because the next
|
|
102
|
+
* download kind should inherit the obligation by default; the drift above came
|
|
103
|
+
* from lists that had to be remembered.
|
|
104
|
+
* @param {string} method An install method.
|
|
105
|
+
* @returns {boolean} Whether the method fetches a pinned artifact.
|
|
106
|
+
*/
|
|
107
|
+
function isDownloadKind(method) {
|
|
108
|
+
return INSTALL_METHODS.has(method) && method !== "npm-global";
|
|
109
|
+
}
|
|
86
110
|
|
|
87
111
|
/**
|
|
88
112
|
* Validate the `secrets` block.
|
|
@@ -171,19 +195,28 @@ function validateInstallArtifact(label, entry, problems) {
|
|
|
171
195
|
);
|
|
172
196
|
return;
|
|
173
197
|
}
|
|
174
|
-
//
|
|
175
|
-
// trusts whatever the URL serves today exactly as much as a zip
|
|
176
|
-
// checking only one of them meant the tool Lisa's guardrails shell
|
|
177
|
-
// gh, which ships a tarball on Linux — was the one going unverified.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
) {
|
|
198
|
+
// Every download kind, not an enumerated few. A tarball pinned without a
|
|
199
|
+
// checksum trusts whatever the URL serves today exactly as much as a zip
|
|
200
|
+
// does, and checking only one of them meant the tool Lisa's guardrails shell
|
|
201
|
+
// out to — gh, which ships a tarball on Linux — was the one going unverified.
|
|
202
|
+
// A `release-binary` matters most of all: the artifact is directly
|
|
203
|
+
// executable, so a wrong one needs no unpacking step to run.
|
|
204
|
+
if (isDownloadKind(entry.install) && !(entry.url && entry.sha256)) {
|
|
182
205
|
problems.push(
|
|
183
206
|
`remoteEnv install ${label} needs both url and sha256. A pinned ` +
|
|
184
207
|
`version with no checksum still trusts whatever the URL serves today.`
|
|
185
208
|
);
|
|
186
209
|
}
|
|
210
|
+
// A tree is the one kind with no sane default entry point: the archive root
|
|
211
|
+
// is a directory, so an omitted `binary` would install a directory as a
|
|
212
|
+
// command. The runner refuses it, and a validator that accepted it would just
|
|
213
|
+
// move the failure from review to provisioning.
|
|
214
|
+
if (entry.install === "release-tree" && !entry.binary) {
|
|
215
|
+
problems.push(
|
|
216
|
+
`remoteEnv install ${label} needs "binary" — the path to the entry ` +
|
|
217
|
+
`point INSIDE the archive, such as "maestro/bin/maestro".`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
187
220
|
if (entry.install === "npm-global" && !entry.package) {
|
|
188
221
|
problems.push(`remoteEnv install ${label} needs a package`);
|
|
189
222
|
}
|
|
@@ -301,6 +301,24 @@ export function planToolchain(
|
|
|
301
301
|
return plan;
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Every install method the runner can execute — the authoritative list.
|
|
306
|
+
*
|
|
307
|
+
* `validate-config.mjs` maintains its own copy because skills are distributed
|
|
308
|
+
* as self-contained directories and nothing else imports across a skill
|
|
309
|
+
* boundary. A comment saying "keep these in step" has now failed twice — first
|
|
310
|
+
* for `release-tar`, then for `release-tree` and `release-binary` together — so
|
|
311
|
+
* the pairing is enforced by a conformance test against this export instead.
|
|
312
|
+
* Adding a kind here fails that test until the validator learns it too.
|
|
313
|
+
*/
|
|
314
|
+
export const INSTALL_METHODS = Object.freeze([
|
|
315
|
+
"release-zip",
|
|
316
|
+
"release-tar",
|
|
317
|
+
"release-tree",
|
|
318
|
+
"release-binary",
|
|
319
|
+
"npm-global",
|
|
320
|
+
]);
|
|
321
|
+
|
|
304
322
|
/**
|
|
305
323
|
* Reject a manifest entry that could install something unverifiable.
|
|
306
324
|
*
|
|
@@ -373,6 +391,6 @@ export function assertPinned(tool) {
|
|
|
373
391
|
}
|
|
374
392
|
throw new Error(
|
|
375
393
|
`${tool.name}: unknown install method "${tool.install}".\n` +
|
|
376
|
-
`Supported:
|
|
394
|
+
`Supported: ${INSTALL_METHODS.join(", ")}.`
|
|
377
395
|
);
|
|
378
396
|
}
|
|
@@ -76,13 +76,37 @@ function bindingsFor(surface) {
|
|
|
76
76
|
/**
|
|
77
77
|
* Install methods the toolchain runner supports.
|
|
78
78
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
79
|
+
* This is a deliberate copy of `INSTALL_METHODS` in `toolchain.mjs`, which is
|
|
80
|
+
* the list that actually decides. Skills are distributed as self-contained
|
|
81
|
+
* directories and nothing imports across a skill boundary, so importing the
|
|
82
|
+
* authority here would couple two skills' distribution for one array.
|
|
83
|
+
*
|
|
84
|
+
* The copy has drifted twice. First `release-tar` was missing while the runner
|
|
85
|
+
* had supported it for as long as gh has been pinned. Then `release-tree` and
|
|
86
|
+
* `release-binary` were both missing, which rejected a valid `jq` pin. Each
|
|
87
|
+
* time the only safeguard was a comment asking the next author to remember.
|
|
88
|
+
* A conformance test now compares the two sets directly, so adding a kind on
|
|
89
|
+
* either side fails until both know it.
|
|
84
90
|
*/
|
|
85
|
-
const INSTALL_METHODS = new Set([
|
|
91
|
+
const INSTALL_METHODS = new Set([
|
|
92
|
+
"release-zip",
|
|
93
|
+
"release-tar",
|
|
94
|
+
"release-tree",
|
|
95
|
+
"release-binary",
|
|
96
|
+
"npm-global",
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Download kinds — every method that fetches a URL, so every one that needs a
|
|
101
|
+
* checksum. Spelled as "not npm-global" rather than as a list, because the next
|
|
102
|
+
* download kind should inherit the obligation by default; the drift above came
|
|
103
|
+
* from lists that had to be remembered.
|
|
104
|
+
* @param {string} method An install method.
|
|
105
|
+
* @returns {boolean} Whether the method fetches a pinned artifact.
|
|
106
|
+
*/
|
|
107
|
+
function isDownloadKind(method) {
|
|
108
|
+
return INSTALL_METHODS.has(method) && method !== "npm-global";
|
|
109
|
+
}
|
|
86
110
|
|
|
87
111
|
/**
|
|
88
112
|
* Validate the `secrets` block.
|
|
@@ -171,19 +195,28 @@ function validateInstallArtifact(label, entry, problems) {
|
|
|
171
195
|
);
|
|
172
196
|
return;
|
|
173
197
|
}
|
|
174
|
-
//
|
|
175
|
-
// trusts whatever the URL serves today exactly as much as a zip
|
|
176
|
-
// checking only one of them meant the tool Lisa's guardrails shell
|
|
177
|
-
// gh, which ships a tarball on Linux — was the one going unverified.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
) {
|
|
198
|
+
// Every download kind, not an enumerated few. A tarball pinned without a
|
|
199
|
+
// checksum trusts whatever the URL serves today exactly as much as a zip
|
|
200
|
+
// does, and checking only one of them meant the tool Lisa's guardrails shell
|
|
201
|
+
// out to — gh, which ships a tarball on Linux — was the one going unverified.
|
|
202
|
+
// A `release-binary` matters most of all: the artifact is directly
|
|
203
|
+
// executable, so a wrong one needs no unpacking step to run.
|
|
204
|
+
if (isDownloadKind(entry.install) && !(entry.url && entry.sha256)) {
|
|
182
205
|
problems.push(
|
|
183
206
|
`remoteEnv install ${label} needs both url and sha256. A pinned ` +
|
|
184
207
|
`version with no checksum still trusts whatever the URL serves today.`
|
|
185
208
|
);
|
|
186
209
|
}
|
|
210
|
+
// A tree is the one kind with no sane default entry point: the archive root
|
|
211
|
+
// is a directory, so an omitted `binary` would install a directory as a
|
|
212
|
+
// command. The runner refuses it, and a validator that accepted it would just
|
|
213
|
+
// move the failure from review to provisioning.
|
|
214
|
+
if (entry.install === "release-tree" && !entry.binary) {
|
|
215
|
+
problems.push(
|
|
216
|
+
`remoteEnv install ${label} needs "binary" — the path to the entry ` +
|
|
217
|
+
`point INSIDE the archive, such as "maestro/bin/maestro".`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
187
220
|
if (entry.install === "npm-global" && !entry.package) {
|
|
188
221
|
problems.push(`remoteEnv install ${label} needs a package`);
|
|
189
222
|
}
|
|
@@ -301,6 +301,24 @@ export function planToolchain(
|
|
|
301
301
|
return plan;
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Every install method the runner can execute — the authoritative list.
|
|
306
|
+
*
|
|
307
|
+
* `validate-config.mjs` maintains its own copy because skills are distributed
|
|
308
|
+
* as self-contained directories and nothing else imports across a skill
|
|
309
|
+
* boundary. A comment saying "keep these in step" has now failed twice — first
|
|
310
|
+
* for `release-tar`, then for `release-tree` and `release-binary` together — so
|
|
311
|
+
* the pairing is enforced by a conformance test against this export instead.
|
|
312
|
+
* Adding a kind here fails that test until the validator learns it too.
|
|
313
|
+
*/
|
|
314
|
+
export const INSTALL_METHODS = Object.freeze([
|
|
315
|
+
"release-zip",
|
|
316
|
+
"release-tar",
|
|
317
|
+
"release-tree",
|
|
318
|
+
"release-binary",
|
|
319
|
+
"npm-global",
|
|
320
|
+
]);
|
|
321
|
+
|
|
304
322
|
/**
|
|
305
323
|
* Reject a manifest entry that could install something unverifiable.
|
|
306
324
|
*
|
|
@@ -373,6 +391,6 @@ export function assertPinned(tool) {
|
|
|
373
391
|
}
|
|
374
392
|
throw new Error(
|
|
375
393
|
`${tool.name}: unknown install method "${tool.install}".\n` +
|
|
376
|
-
`Supported:
|
|
394
|
+
`Supported: ${INSTALL_METHODS.join(", ")}.`
|
|
377
395
|
);
|
|
378
396
|
}
|
|
@@ -76,13 +76,37 @@ function bindingsFor(surface) {
|
|
|
76
76
|
/**
|
|
77
77
|
* Install methods the toolchain runner supports.
|
|
78
78
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
79
|
+
* This is a deliberate copy of `INSTALL_METHODS` in `toolchain.mjs`, which is
|
|
80
|
+
* the list that actually decides. Skills are distributed as self-contained
|
|
81
|
+
* directories and nothing imports across a skill boundary, so importing the
|
|
82
|
+
* authority here would couple two skills' distribution for one array.
|
|
83
|
+
*
|
|
84
|
+
* The copy has drifted twice. First `release-tar` was missing while the runner
|
|
85
|
+
* had supported it for as long as gh has been pinned. Then `release-tree` and
|
|
86
|
+
* `release-binary` were both missing, which rejected a valid `jq` pin. Each
|
|
87
|
+
* time the only safeguard was a comment asking the next author to remember.
|
|
88
|
+
* A conformance test now compares the two sets directly, so adding a kind on
|
|
89
|
+
* either side fails until both know it.
|
|
84
90
|
*/
|
|
85
|
-
const INSTALL_METHODS = new Set([
|
|
91
|
+
const INSTALL_METHODS = new Set([
|
|
92
|
+
"release-zip",
|
|
93
|
+
"release-tar",
|
|
94
|
+
"release-tree",
|
|
95
|
+
"release-binary",
|
|
96
|
+
"npm-global",
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Download kinds — every method that fetches a URL, so every one that needs a
|
|
101
|
+
* checksum. Spelled as "not npm-global" rather than as a list, because the next
|
|
102
|
+
* download kind should inherit the obligation by default; the drift above came
|
|
103
|
+
* from lists that had to be remembered.
|
|
104
|
+
* @param {string} method An install method.
|
|
105
|
+
* @returns {boolean} Whether the method fetches a pinned artifact.
|
|
106
|
+
*/
|
|
107
|
+
function isDownloadKind(method) {
|
|
108
|
+
return INSTALL_METHODS.has(method) && method !== "npm-global";
|
|
109
|
+
}
|
|
86
110
|
|
|
87
111
|
/**
|
|
88
112
|
* Validate the `secrets` block.
|
|
@@ -171,19 +195,28 @@ function validateInstallArtifact(label, entry, problems) {
|
|
|
171
195
|
);
|
|
172
196
|
return;
|
|
173
197
|
}
|
|
174
|
-
//
|
|
175
|
-
// trusts whatever the URL serves today exactly as much as a zip
|
|
176
|
-
// checking only one of them meant the tool Lisa's guardrails shell
|
|
177
|
-
// gh, which ships a tarball on Linux — was the one going unverified.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
) {
|
|
198
|
+
// Every download kind, not an enumerated few. A tarball pinned without a
|
|
199
|
+
// checksum trusts whatever the URL serves today exactly as much as a zip
|
|
200
|
+
// does, and checking only one of them meant the tool Lisa's guardrails shell
|
|
201
|
+
// out to — gh, which ships a tarball on Linux — was the one going unverified.
|
|
202
|
+
// A `release-binary` matters most of all: the artifact is directly
|
|
203
|
+
// executable, so a wrong one needs no unpacking step to run.
|
|
204
|
+
if (isDownloadKind(entry.install) && !(entry.url && entry.sha256)) {
|
|
182
205
|
problems.push(
|
|
183
206
|
`remoteEnv install ${label} needs both url and sha256. A pinned ` +
|
|
184
207
|
`version with no checksum still trusts whatever the URL serves today.`
|
|
185
208
|
);
|
|
186
209
|
}
|
|
210
|
+
// A tree is the one kind with no sane default entry point: the archive root
|
|
211
|
+
// is a directory, so an omitted `binary` would install a directory as a
|
|
212
|
+
// command. The runner refuses it, and a validator that accepted it would just
|
|
213
|
+
// move the failure from review to provisioning.
|
|
214
|
+
if (entry.install === "release-tree" && !entry.binary) {
|
|
215
|
+
problems.push(
|
|
216
|
+
`remoteEnv install ${label} needs "binary" — the path to the entry ` +
|
|
217
|
+
`point INSIDE the archive, such as "maestro/bin/maestro".`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
187
220
|
if (entry.install === "npm-global" && !entry.package) {
|
|
188
221
|
problems.push(`remoteEnv install ${label} needs a package`);
|
|
189
222
|
}
|
|
@@ -301,6 +301,24 @@ export function planToolchain(
|
|
|
301
301
|
return plan;
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Every install method the runner can execute — the authoritative list.
|
|
306
|
+
*
|
|
307
|
+
* `validate-config.mjs` maintains its own copy because skills are distributed
|
|
308
|
+
* as self-contained directories and nothing else imports across a skill
|
|
309
|
+
* boundary. A comment saying "keep these in step" has now failed twice — first
|
|
310
|
+
* for `release-tar`, then for `release-tree` and `release-binary` together — so
|
|
311
|
+
* the pairing is enforced by a conformance test against this export instead.
|
|
312
|
+
* Adding a kind here fails that test until the validator learns it too.
|
|
313
|
+
*/
|
|
314
|
+
export const INSTALL_METHODS = Object.freeze([
|
|
315
|
+
"release-zip",
|
|
316
|
+
"release-tar",
|
|
317
|
+
"release-tree",
|
|
318
|
+
"release-binary",
|
|
319
|
+
"npm-global",
|
|
320
|
+
]);
|
|
321
|
+
|
|
304
322
|
/**
|
|
305
323
|
* Reject a manifest entry that could install something unverifiable.
|
|
306
324
|
*
|
|
@@ -373,6 +391,6 @@ export function assertPinned(tool) {
|
|
|
373
391
|
}
|
|
374
392
|
throw new Error(
|
|
375
393
|
`${tool.name}: unknown install method "${tool.install}".\n` +
|
|
376
|
-
`Supported:
|
|
394
|
+
`Supported: ${INSTALL_METHODS.join(", ")}.`
|
|
377
395
|
);
|
|
378
396
|
}
|
|
@@ -76,13 +76,37 @@ function bindingsFor(surface) {
|
|
|
76
76
|
/**
|
|
77
77
|
* Install methods the toolchain runner supports.
|
|
78
78
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
79
|
+
* This is a deliberate copy of `INSTALL_METHODS` in `toolchain.mjs`, which is
|
|
80
|
+
* the list that actually decides. Skills are distributed as self-contained
|
|
81
|
+
* directories and nothing imports across a skill boundary, so importing the
|
|
82
|
+
* authority here would couple two skills' distribution for one array.
|
|
83
|
+
*
|
|
84
|
+
* The copy has drifted twice. First `release-tar` was missing while the runner
|
|
85
|
+
* had supported it for as long as gh has been pinned. Then `release-tree` and
|
|
86
|
+
* `release-binary` were both missing, which rejected a valid `jq` pin. Each
|
|
87
|
+
* time the only safeguard was a comment asking the next author to remember.
|
|
88
|
+
* A conformance test now compares the two sets directly, so adding a kind on
|
|
89
|
+
* either side fails until both know it.
|
|
84
90
|
*/
|
|
85
|
-
const INSTALL_METHODS = new Set([
|
|
91
|
+
const INSTALL_METHODS = new Set([
|
|
92
|
+
"release-zip",
|
|
93
|
+
"release-tar",
|
|
94
|
+
"release-tree",
|
|
95
|
+
"release-binary",
|
|
96
|
+
"npm-global",
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Download kinds — every method that fetches a URL, so every one that needs a
|
|
101
|
+
* checksum. Spelled as "not npm-global" rather than as a list, because the next
|
|
102
|
+
* download kind should inherit the obligation by default; the drift above came
|
|
103
|
+
* from lists that had to be remembered.
|
|
104
|
+
* @param {string} method An install method.
|
|
105
|
+
* @returns {boolean} Whether the method fetches a pinned artifact.
|
|
106
|
+
*/
|
|
107
|
+
function isDownloadKind(method) {
|
|
108
|
+
return INSTALL_METHODS.has(method) && method !== "npm-global";
|
|
109
|
+
}
|
|
86
110
|
|
|
87
111
|
/**
|
|
88
112
|
* Validate the `secrets` block.
|
|
@@ -171,19 +195,28 @@ function validateInstallArtifact(label, entry, problems) {
|
|
|
171
195
|
);
|
|
172
196
|
return;
|
|
173
197
|
}
|
|
174
|
-
//
|
|
175
|
-
// trusts whatever the URL serves today exactly as much as a zip
|
|
176
|
-
// checking only one of them meant the tool Lisa's guardrails shell
|
|
177
|
-
// gh, which ships a tarball on Linux — was the one going unverified.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
) {
|
|
198
|
+
// Every download kind, not an enumerated few. A tarball pinned without a
|
|
199
|
+
// checksum trusts whatever the URL serves today exactly as much as a zip
|
|
200
|
+
// does, and checking only one of them meant the tool Lisa's guardrails shell
|
|
201
|
+
// out to — gh, which ships a tarball on Linux — was the one going unverified.
|
|
202
|
+
// A `release-binary` matters most of all: the artifact is directly
|
|
203
|
+
// executable, so a wrong one needs no unpacking step to run.
|
|
204
|
+
if (isDownloadKind(entry.install) && !(entry.url && entry.sha256)) {
|
|
182
205
|
problems.push(
|
|
183
206
|
`remoteEnv install ${label} needs both url and sha256. A pinned ` +
|
|
184
207
|
`version with no checksum still trusts whatever the URL serves today.`
|
|
185
208
|
);
|
|
186
209
|
}
|
|
210
|
+
// A tree is the one kind with no sane default entry point: the archive root
|
|
211
|
+
// is a directory, so an omitted `binary` would install a directory as a
|
|
212
|
+
// command. The runner refuses it, and a validator that accepted it would just
|
|
213
|
+
// move the failure from review to provisioning.
|
|
214
|
+
if (entry.install === "release-tree" && !entry.binary) {
|
|
215
|
+
problems.push(
|
|
216
|
+
`remoteEnv install ${label} needs "binary" — the path to the entry ` +
|
|
217
|
+
`point INSIDE the archive, such as "maestro/bin/maestro".`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
187
220
|
if (entry.install === "npm-global" && !entry.package) {
|
|
188
221
|
problems.push(`remoteEnv install ${label} needs a package`);
|
|
189
222
|
}
|
|
@@ -301,6 +301,24 @@ export function planToolchain(
|
|
|
301
301
|
return plan;
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Every install method the runner can execute — the authoritative list.
|
|
306
|
+
*
|
|
307
|
+
* `validate-config.mjs` maintains its own copy because skills are distributed
|
|
308
|
+
* as self-contained directories and nothing else imports across a skill
|
|
309
|
+
* boundary. A comment saying "keep these in step" has now failed twice — first
|
|
310
|
+
* for `release-tar`, then for `release-tree` and `release-binary` together — so
|
|
311
|
+
* the pairing is enforced by a conformance test against this export instead.
|
|
312
|
+
* Adding a kind here fails that test until the validator learns it too.
|
|
313
|
+
*/
|
|
314
|
+
export const INSTALL_METHODS = Object.freeze([
|
|
315
|
+
"release-zip",
|
|
316
|
+
"release-tar",
|
|
317
|
+
"release-tree",
|
|
318
|
+
"release-binary",
|
|
319
|
+
"npm-global",
|
|
320
|
+
]);
|
|
321
|
+
|
|
304
322
|
/**
|
|
305
323
|
* Reject a manifest entry that could install something unverifiable.
|
|
306
324
|
*
|
|
@@ -373,6 +391,6 @@ export function assertPinned(tool) {
|
|
|
373
391
|
}
|
|
374
392
|
throw new Error(
|
|
375
393
|
`${tool.name}: unknown install method "${tool.install}".\n` +
|
|
376
|
-
`Supported:
|
|
394
|
+
`Supported: ${INSTALL_METHODS.join(", ")}.`
|
|
377
395
|
);
|
|
378
396
|
}
|
|
@@ -76,13 +76,37 @@ function bindingsFor(surface) {
|
|
|
76
76
|
/**
|
|
77
77
|
* Install methods the toolchain runner supports.
|
|
78
78
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
79
|
+
* This is a deliberate copy of `INSTALL_METHODS` in `toolchain.mjs`, which is
|
|
80
|
+
* the list that actually decides. Skills are distributed as self-contained
|
|
81
|
+
* directories and nothing imports across a skill boundary, so importing the
|
|
82
|
+
* authority here would couple two skills' distribution for one array.
|
|
83
|
+
*
|
|
84
|
+
* The copy has drifted twice. First `release-tar` was missing while the runner
|
|
85
|
+
* had supported it for as long as gh has been pinned. Then `release-tree` and
|
|
86
|
+
* `release-binary` were both missing, which rejected a valid `jq` pin. Each
|
|
87
|
+
* time the only safeguard was a comment asking the next author to remember.
|
|
88
|
+
* A conformance test now compares the two sets directly, so adding a kind on
|
|
89
|
+
* either side fails until both know it.
|
|
84
90
|
*/
|
|
85
|
-
const INSTALL_METHODS = new Set([
|
|
91
|
+
const INSTALL_METHODS = new Set([
|
|
92
|
+
"release-zip",
|
|
93
|
+
"release-tar",
|
|
94
|
+
"release-tree",
|
|
95
|
+
"release-binary",
|
|
96
|
+
"npm-global",
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Download kinds — every method that fetches a URL, so every one that needs a
|
|
101
|
+
* checksum. Spelled as "not npm-global" rather than as a list, because the next
|
|
102
|
+
* download kind should inherit the obligation by default; the drift above came
|
|
103
|
+
* from lists that had to be remembered.
|
|
104
|
+
* @param {string} method An install method.
|
|
105
|
+
* @returns {boolean} Whether the method fetches a pinned artifact.
|
|
106
|
+
*/
|
|
107
|
+
function isDownloadKind(method) {
|
|
108
|
+
return INSTALL_METHODS.has(method) && method !== "npm-global";
|
|
109
|
+
}
|
|
86
110
|
|
|
87
111
|
/**
|
|
88
112
|
* Validate the `secrets` block.
|
|
@@ -171,19 +195,28 @@ function validateInstallArtifact(label, entry, problems) {
|
|
|
171
195
|
);
|
|
172
196
|
return;
|
|
173
197
|
}
|
|
174
|
-
//
|
|
175
|
-
// trusts whatever the URL serves today exactly as much as a zip
|
|
176
|
-
// checking only one of them meant the tool Lisa's guardrails shell
|
|
177
|
-
// gh, which ships a tarball on Linux — was the one going unverified.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
) {
|
|
198
|
+
// Every download kind, not an enumerated few. A tarball pinned without a
|
|
199
|
+
// checksum trusts whatever the URL serves today exactly as much as a zip
|
|
200
|
+
// does, and checking only one of them meant the tool Lisa's guardrails shell
|
|
201
|
+
// out to — gh, which ships a tarball on Linux — was the one going unverified.
|
|
202
|
+
// A `release-binary` matters most of all: the artifact is directly
|
|
203
|
+
// executable, so a wrong one needs no unpacking step to run.
|
|
204
|
+
if (isDownloadKind(entry.install) && !(entry.url && entry.sha256)) {
|
|
182
205
|
problems.push(
|
|
183
206
|
`remoteEnv install ${label} needs both url and sha256. A pinned ` +
|
|
184
207
|
`version with no checksum still trusts whatever the URL serves today.`
|
|
185
208
|
);
|
|
186
209
|
}
|
|
210
|
+
// A tree is the one kind with no sane default entry point: the archive root
|
|
211
|
+
// is a directory, so an omitted `binary` would install a directory as a
|
|
212
|
+
// command. The runner refuses it, and a validator that accepted it would just
|
|
213
|
+
// move the failure from review to provisioning.
|
|
214
|
+
if (entry.install === "release-tree" && !entry.binary) {
|
|
215
|
+
problems.push(
|
|
216
|
+
`remoteEnv install ${label} needs "binary" — the path to the entry ` +
|
|
217
|
+
`point INSIDE the archive, such as "maestro/bin/maestro".`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
187
220
|
if (entry.install === "npm-global" && !entry.package) {
|
|
188
221
|
problems.push(`remoteEnv install ${label} needs a package`);
|
|
189
222
|
}
|