@codyswann/lisa 4.35.5 → 4.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all/copy-overwrite/scripts/check-third-party-action-pins.mjs +554 -0
- package/dist/core/downstream-references.d.ts.map +1 -1
- package/dist/core/downstream-references.js +4 -0
- package/dist/core/downstream-references.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +10 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/third-party-action-pins.d.ts +84 -0
- package/dist/core/third-party-action-pins.d.ts.map +1 -0
- package/dist/core/third-party-action-pins.js +153 -0
- package/dist/core/third-party-action-pins.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +11 -4
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/migrations/ensure-third-party-action-pins.d.ts +123 -0
- package/dist/migrations/ensure-third-party-action-pins.d.ts.map +1 -0
- package/dist/migrations/ensure-third-party-action-pins.js +238 -0
- package/dist/migrations/ensure-third-party-action-pins.js.map +1 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +3 -0
- package/dist/migrations/index.js.map +1 -1
- package/package.json +4 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/scripts/check-third-party-action-pins.mjs +111 -14
- package/scripts/two-channel-couplings.json +1 -1
- package/typescript/copy-overwrite/.prettierignore +1 -0
- package/typescript/package-lisa/package.lisa.json +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.36.1",
|
|
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": "4.
|
|
3
|
+
"version": "4.36.1",
|
|
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": "4.
|
|
3
|
+
"version": "4.36.1",
|
|
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": "4.
|
|
3
|
+
"version": "4.36.1",
|
|
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": "4.
|
|
3
|
+
"version": "4.36.1",
|
|
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"
|
|
@@ -69,16 +69,41 @@
|
|
|
69
69
|
* - the file list comes from `git ls-files`, so the gate sees exactly what a
|
|
70
70
|
* release would carry.
|
|
71
71
|
*
|
|
72
|
+
* ## `--warn`: how this arrives in a consumer without breaking it
|
|
73
|
+
*
|
|
74
|
+
* This file also ships to consumers through `all/copy-overwrite/scripts/`,
|
|
75
|
+
* which — unlike `create-only` — reaches every repository that already exists,
|
|
76
|
+
* on its next update. That reach is exactly why it must not arrive blocking.
|
|
77
|
+
*
|
|
78
|
+
* A consumer's seeded workflows carry mutable refs *today*: that is the defect
|
|
79
|
+
* #3588 exists to migrate away. A detector that hard-failed on arrival would
|
|
80
|
+
* redden the entire installed base at once, for a condition those repositories
|
|
81
|
+
* did not introduce and could not have fixed yet, since the migration that
|
|
82
|
+
* fixes it lands in the same update. That is not hypothetical — a guard added
|
|
83
|
+
* to a workflow consumed at `@main` failed closed on a pre-existing consumer
|
|
84
|
+
* misconfiguration and took four repositories' releases down for five hours;
|
|
85
|
+
* the repair was to keep it loud and stop it blocking (#3755, #3757).
|
|
86
|
+
*
|
|
87
|
+
* So `--warn` reports every finding and exits 0, and the shipped consumer
|
|
88
|
+
* script entry uses it. Tightening to blocking is a separate, deliberate
|
|
89
|
+
* decision for once the installed base has had a chance to migrate — one flag
|
|
90
|
+
* on one line — not something to "finish" while consumers are still mutable.
|
|
91
|
+
*
|
|
92
|
+
* `--warn` also softens the empty-scan usage error, because a consumer with no
|
|
93
|
+
* workflow files is an ordinary shape rather than a broken invocation. A
|
|
94
|
+
* misspelled flag stays a hard error in every mode: that one is the caller's.
|
|
95
|
+
*
|
|
72
96
|
* CLI:
|
|
73
|
-
* node scripts/check-third-party-action-pins.mjs [--root <dir>] [--json]
|
|
97
|
+
* node scripts/check-third-party-action-pins.mjs [--root <dir>] [--json] [--warn]
|
|
74
98
|
*
|
|
75
99
|
* Exit codes (mirroring the sibling gates in this directory):
|
|
76
|
-
* 0 — every third-party reference is a 40-hex SHA carrying a version comment
|
|
100
|
+
* 0 — every third-party reference is a 40-hex SHA carrying a version comment;
|
|
101
|
+
* or `--warn` was passed and the findings are being reported, not enforced.
|
|
77
102
|
* 1 — >=1 reference is mutable, or pinned without a version comment.
|
|
78
103
|
* 2 — operational/usage error: unknown flag, a flag missing its value,
|
|
79
104
|
* `--root` absent or not a git repository, git unavailable, or zero
|
|
80
105
|
* workflow files discovered. Finding nothing to check is a broken
|
|
81
|
-
* invocation, not conformance.
|
|
106
|
+
* invocation, not conformance — except under `--warn`, see above.
|
|
82
107
|
*
|
|
83
108
|
* @module scripts/check-third-party-action-pins
|
|
84
109
|
*/
|
|
@@ -134,14 +159,60 @@ const NO_VERSION = "missing-version-comment";
|
|
|
134
159
|
* a trailing `# v1.2.3` can be read. Local (`./path`) and container
|
|
135
160
|
* (`docker://`) references have no owner and never match.
|
|
136
161
|
*/
|
|
137
|
-
const USES_RE =
|
|
138
|
-
|
|
162
|
+
const USES_RE = /^[ \t]*(?:-[ \t]*)?uses:[ \t]*(?<value>[^\s"'#]+)/;
|
|
163
|
+
|
|
164
|
+
/** A well-formed action owner. Anchored, so matching is linear. */
|
|
165
|
+
const OWNER_RE = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
|
|
166
|
+
|
|
167
|
+
/** A well-formed action path below the owner. Anchored, so linear. */
|
|
168
|
+
const ACTION_PATH_RE = /^[A-Za-z0-9_.][A-Za-z0-9_./-]*$/;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Split a `uses:` value into owner, action path, and ref.
|
|
172
|
+
*
|
|
173
|
+
* The shape is validated with anchored parts rather than described as one
|
|
174
|
+
* pattern. Spelling `owner/repo/path@ref` as adjacent character classes gives
|
|
175
|
+
* the engine several ways to divide the same text, which is super-linear on a
|
|
176
|
+
* long line; splitting first and validating each piece has one.
|
|
177
|
+
*
|
|
178
|
+
* @param {string} value - the raw value of a `uses:` line.
|
|
179
|
+
* @returns {{ owner: string, rest: string, ref: string } | null} parts, or null
|
|
180
|
+
* when this is not an `owner/repo@ref` reference (a local `./path` or a
|
|
181
|
+
* `docker://` image never is).
|
|
182
|
+
*/
|
|
183
|
+
function parseUsesValue(value) {
|
|
184
|
+
const at = value.lastIndexOf("@");
|
|
185
|
+
if (at <= 0) return null;
|
|
186
|
+
const action = value.slice(0, at);
|
|
187
|
+
const ref = value.slice(at + 1);
|
|
188
|
+
const slash = action.indexOf("/");
|
|
189
|
+
if (ref === "" || slash <= 0 || action.includes("://")) return null;
|
|
190
|
+
const owner = action.slice(0, slash);
|
|
191
|
+
const rest = action.slice(slash + 1);
|
|
192
|
+
if (!OWNER_RE.test(owner) || !ACTION_PATH_RE.test(rest)) return null;
|
|
193
|
+
return { owner, rest, ref };
|
|
194
|
+
}
|
|
139
195
|
|
|
140
196
|
/** A full commit SHA. Abbreviated SHAs are ambiguous and do not count. */
|
|
141
197
|
const FULL_SHA_RE = /^[0-9a-f]{40}$/;
|
|
142
198
|
|
|
143
|
-
/**
|
|
144
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Read the version out of a line's trailing `# ...` comment.
|
|
201
|
+
*
|
|
202
|
+
* Deliberately not a regex. Every spelling of "text after a hash to end of
|
|
203
|
+
* line" is either a backtracking shape or an unanchored `.*$` the engine
|
|
204
|
+
* retries from each position, and neither is worth it for a one-character
|
|
205
|
+
* search.
|
|
206
|
+
*
|
|
207
|
+
* @param {string} trailer - everything on the line after the `uses:` value.
|
|
208
|
+
* @returns {string | null} the comment text, or null when there is none.
|
|
209
|
+
*/
|
|
210
|
+
function versionComment(trailer) {
|
|
211
|
+
const hash = trailer.indexOf("#");
|
|
212
|
+
if (hash < 0) return null;
|
|
213
|
+
const version = trailer.slice(hash + 1).trim();
|
|
214
|
+
return version === "" ? null : version;
|
|
215
|
+
}
|
|
145
216
|
|
|
146
217
|
/** Max bytes of `git ls-files` output (7k+ tracked paths is ~0.3 MB today). */
|
|
147
218
|
const MAX_GIT_OUTPUT_BYTES = 64 * 1024 * 1024;
|
|
@@ -182,15 +253,19 @@ export function findActionRefs(content) {
|
|
|
182
253
|
for (let index = 0; index < lines.length; index++) {
|
|
183
254
|
const match = USES_RE.exec(lines[index]);
|
|
184
255
|
if (match === null) continue;
|
|
185
|
-
const
|
|
186
|
-
|
|
256
|
+
const parsed = parseUsesValue(match.groups.value);
|
|
257
|
+
if (parsed === null) continue;
|
|
258
|
+
const { owner, ref, rest } = parsed;
|
|
187
259
|
refs.push({
|
|
188
260
|
action: `${owner}/${rest}`,
|
|
189
261
|
kind: classifyOwner(owner),
|
|
190
262
|
line: index + 1,
|
|
191
263
|
owner,
|
|
192
264
|
ref,
|
|
193
|
-
|
|
265
|
+
// The rest of the line is sliced rather than captured: a trailing `.*`
|
|
266
|
+
// after the value's character class gives the engine two ways to divide
|
|
267
|
+
// the same text, which is the super-linear shape.
|
|
268
|
+
version: versionComment(lines[index].slice(match[0].length)),
|
|
194
269
|
});
|
|
195
270
|
}
|
|
196
271
|
return refs;
|
|
@@ -350,10 +425,13 @@ function humanReport(report) {
|
|
|
350
425
|
export function parseArgs(argv) {
|
|
351
426
|
let root = null;
|
|
352
427
|
let json = false;
|
|
428
|
+
let warn = false;
|
|
353
429
|
for (let i = 0; i < argv.length; i++) {
|
|
354
430
|
const arg = argv[i];
|
|
355
431
|
if (arg === "--json") {
|
|
356
432
|
json = true;
|
|
433
|
+
} else if (arg === "--warn") {
|
|
434
|
+
warn = true;
|
|
357
435
|
} else if (arg === "--root") {
|
|
358
436
|
const next = argv[i + 1];
|
|
359
437
|
if (next === undefined || next.startsWith("--")) {
|
|
@@ -365,7 +443,7 @@ export function parseArgs(argv) {
|
|
|
365
443
|
throw new UsageError(`unknown argument: ${arg}`);
|
|
366
444
|
}
|
|
367
445
|
}
|
|
368
|
-
return { json, root: path.resolve(root ?? REPO_ROOT) };
|
|
446
|
+
return { json, warn, root: path.resolve(root ?? REPO_ROOT) };
|
|
369
447
|
}
|
|
370
448
|
|
|
371
449
|
/**
|
|
@@ -406,14 +484,22 @@ export function main(argv, io = {}) {
|
|
|
406
484
|
const out = io.stdout ?? process.stdout;
|
|
407
485
|
const err = io.stderr ?? process.stderr;
|
|
408
486
|
let opts;
|
|
409
|
-
let scan;
|
|
410
487
|
try {
|
|
411
488
|
opts = parseArgs(argv);
|
|
412
|
-
scan = collectWorkflows(opts.root);
|
|
413
489
|
} catch (error) {
|
|
490
|
+
// A misspelled flag is the caller's mistake and stays loud in every mode.
|
|
414
491
|
err.write(`error: ${error.message}\n`);
|
|
415
492
|
return 2;
|
|
416
493
|
}
|
|
494
|
+
let scan;
|
|
495
|
+
try {
|
|
496
|
+
scan = collectWorkflows(opts.root);
|
|
497
|
+
} catch (error) {
|
|
498
|
+
// Under --warn this is a repository with nothing to scan, which for a
|
|
499
|
+
// consumer is an ordinary shape rather than a broken invocation.
|
|
500
|
+
err.write(`error: ${error.message}\n`);
|
|
501
|
+
return opts.warn ? 0 : 2;
|
|
502
|
+
}
|
|
417
503
|
|
|
418
504
|
let checked = 0;
|
|
419
505
|
let thirdParty = 0;
|
|
@@ -445,7 +531,18 @@ export function main(argv, io = {}) {
|
|
|
445
531
|
out.write(
|
|
446
532
|
`${opts.json ? JSON.stringify(report, null, 2) : humanReport(report)}\n`
|
|
447
533
|
);
|
|
448
|
-
|
|
534
|
+
if (report.findings.length === 0) {
|
|
535
|
+
return 0;
|
|
536
|
+
}
|
|
537
|
+
if (opts.warn) {
|
|
538
|
+
out.write(
|
|
539
|
+
"\nReporting only: this check is not blocking here. Nothing above stops " +
|
|
540
|
+
"a build.\nRun `npx @codyswann/lisa@latest .` to pin these " +
|
|
541
|
+
"references, then re-run.\n"
|
|
542
|
+
);
|
|
543
|
+
return 0;
|
|
544
|
+
}
|
|
545
|
+
return 1;
|
|
449
546
|
}
|
|
450
547
|
|
|
451
548
|
if (invokedAsScript(import.meta.url)) {
|
|
@@ -73,6 +73,7 @@ scripts/check-npm-publish-landed.mjs
|
|
|
73
73
|
scripts/check-release-package-identity.mjs
|
|
74
74
|
scripts/check-skipped-required-checks.mjs
|
|
75
75
|
scripts/check-state-classification.mjs
|
|
76
|
+
scripts/check-third-party-action-pins.mjs
|
|
76
77
|
scripts/check-third-party-review-evidence.mjs
|
|
77
78
|
scripts/check-threshold-ratchet.mjs
|
|
78
79
|
scripts/check-verification-coverage.mjs
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"test:node": "LISA_TEST_SCRATCH_PREFIXES='[\"node-\"]' lisa-test-run --profile typescript --adapter direct -- node scripts/lisa-test-node.mjs",
|
|
27
27
|
"check:work-item": "node scripts/lisa-work-item.mjs validate-pr",
|
|
28
28
|
"check:conflict-markers": "node scripts/check-conflict-markers.mjs",
|
|
29
|
+
"check:third-party-action-pins": "node scripts/check-third-party-action-pins.mjs --warn",
|
|
29
30
|
"check:work-item:push": "node scripts/lisa-work-item.mjs validate-push",
|
|
30
31
|
"check:verification": "node scripts/check-verification-coverage.mjs"
|
|
31
32
|
},
|