@codyswann/lisa 4.4.1 → 4.4.2
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 +12 -12
- 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-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-delivery-deletion-conflicts.mjs +2 -2
- package/scripts/check-derived-artifacts.mjs +24 -5
- package/scripts/check-orphan-test-processes.mjs +2 -2
- package/scripts/check-security-floors.mjs +20 -9
- package/scripts/check-template-workflow-refs.mjs +23 -8
- package/scripts/check-workflow-package-paths.mjs +12 -8
- package/scripts/generate-lisa-owned-hash-ledger.mjs +12 -4
- package/scripts/generate-upstream-evidence-manifest.mjs +4 -4
- package/scripts/verify-health-agentic-built.mjs +4 -3
- package/scripts/verify-health-consumer-built.mjs +3 -2
- package/scripts/verify-health-contract-built.mjs +3 -2
- package/scripts/verify-health-deterministic-built.mjs +5 -4
|
@@ -1073,18 +1073,18 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
1073
1073
|
"rails/github-rulesets/quality-checks.json": "0ec1c366589edbaa6b8f45f940cef3ada5ed7b1a956d98d3fa6034b0f78fbc7c",
|
|
1074
1074
|
"rails/merge/.claude/settings.json": "9c49f8c7c453f8749c90def3e22d412c3345c533d24b30dc7745ffa052ad6fa1",
|
|
1075
1075
|
"scripts/build-plugins.sh": "e405d9ae41c361f77fcf60a6ea4054b3f927c663131fa661295a70c911e11189",
|
|
1076
|
-
"scripts/check-delivery-deletion-conflicts.mjs": "
|
|
1077
|
-
"scripts/check-derived-artifacts.mjs": "
|
|
1076
|
+
"scripts/check-delivery-deletion-conflicts.mjs": "77eed58c9ff076c4438a5fe01c343f25c1a1c8f29a8f5861d1e1081b1739d300",
|
|
1077
|
+
"scripts/check-derived-artifacts.mjs": "2911e2153f6accceacec81fa0255afe9b75cc58c69476db1505eb08878f6672d",
|
|
1078
1078
|
"scripts/check-duplicate-versions.mjs": "26b26351dd2735dfd8d9a2436b89e876ce2ed93871d0b264f611215dd03d70ae",
|
|
1079
1079
|
"scripts/check-learnings-budget.ts": "0112c857910880a5e53c4fa6bccc3ad7f060854d3402cb36bd81216181e54160",
|
|
1080
|
-
"scripts/check-orphan-test-processes.mjs": "
|
|
1080
|
+
"scripts/check-orphan-test-processes.mjs": "123822f43b53a84b619ed97d5c18069feb5964b8216248968a7ab165e345106a",
|
|
1081
1081
|
"scripts/check-plugins-sync.sh": "3a99cbff0e8ec5e7c944690eae003a6d51b51c85436d77bd37faea4d553728af",
|
|
1082
1082
|
"scripts/check-required-check-promotions.mjs": "ae7a3d80beb98d8069af12d2d588e76f73f7a329b1f883ddbe1818bb1b5cafa9",
|
|
1083
1083
|
"scripts/check-rules-pairing.sh": "4d4a0d9e8d36794a22020f419c879d7336b1c5bfe883acdcc826d26764560c7a",
|
|
1084
|
-
"scripts/check-security-floors.mjs": "
|
|
1084
|
+
"scripts/check-security-floors.mjs": "6122404ea247c7d70718472e44bb007c0c5dad2fcc064c8a2d797cd9d0f85b29",
|
|
1085
1085
|
"scripts/check-state-classification.mjs": "26baaa85c0758fa41f1f66257f7d3a31ea4dd5cf17a9c0acaa3e89caaaf6265a",
|
|
1086
|
-
"scripts/check-template-workflow-refs.mjs": "
|
|
1087
|
-
"scripts/check-workflow-package-paths.mjs": "
|
|
1086
|
+
"scripts/check-template-workflow-refs.mjs": "f784a822ae82f41b7501d61e45a1badeecb84f118083f4c53c66402d6c50ffa2",
|
|
1087
|
+
"scripts/check-workflow-package-paths.mjs": "1777c3aea3f3642e1ab2fd64966cca7ea21bf5330b3e5697cde3122c9189e44e",
|
|
1088
1088
|
"scripts/claude-remote-setup.sh": "0e33accf8aa057c70497f01c38bef9f9f3801d649272f583578d89201b242655",
|
|
1089
1089
|
"scripts/clean-dist.mjs": "04970d768464acbf96ebeb77c16a6a3b3da56c744eef0da64c573c6152ee0dc7",
|
|
1090
1090
|
"scripts/cleanup-amplify-branches.sh": "eb9db90b07818dd156dd48d04b6350a10b39be8ffe0267d72cc13705c8eb5350",
|
|
@@ -1101,8 +1101,8 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
1101
1101
|
"scripts/generate-codex-plugin-artifacts.mjs": "facb86910c07d1b8025868d24425fdaea7b6efbe282bace5ee5bf23957d6b51a",
|
|
1102
1102
|
"scripts/generate-copilot-plugin-artifacts.mjs": "7af923118c49101e6ba46da73a8eee8aee9b797e074618aa5345913e638d7cdb",
|
|
1103
1103
|
"scripts/generate-cursor-plugin-artifacts.mjs": "45d3bde7b6c55310f0d9436dd68a1bdeefb6fa817be4d15ef745421b39a2cf47",
|
|
1104
|
-
"scripts/generate-lisa-owned-hash-ledger.mjs": "
|
|
1105
|
-
"scripts/generate-upstream-evidence-manifest.mjs": "
|
|
1104
|
+
"scripts/generate-lisa-owned-hash-ledger.mjs": "7f6c5456c50cd3ab33f528eff40a13f501521bbe6dd0e068afd599e5da1c1f89",
|
|
1105
|
+
"scripts/generate-upstream-evidence-manifest.mjs": "8f18972834d6be9771501c35da30de95539a7e7b3fb3d7605e0bc31477812a5f",
|
|
1106
1106
|
"scripts/github-status-check.sh": "c6a4a13ff5cf689fcbd7a6aca718f9e7d46d54ccb6687f662b055c1ca20e792f",
|
|
1107
1107
|
"scripts/install-claude-plugins.sh": "0e96ebbae1e59304bb432286eccba38900a6008b80f2c53a1ec2c95801d4087c",
|
|
1108
1108
|
"scripts/internal-agy-skill-policy.json": "c2ce87d2eeebfdc9f24d6486425c010cf9289376f8a459f4767ca22d2bf8670d",
|
|
@@ -1141,10 +1141,10 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
1141
1141
|
"scripts/test-intent-routing.sh": "97b9dc86cbd805df8a7fdb8c99ffab9b8c5e751ba2e84c05420b2a124f80635d",
|
|
1142
1142
|
"scripts/update-node-version.ts": "dcfec9f8666f65925deb9efdc8a4bebb6c3f41d9fa1c1365be23edb82c40fffa",
|
|
1143
1143
|
"scripts/update-test-skill-paths.mjs": "e26a3ac716c33013e22bf5223a58457452f5af2038218a0e3a51c244eab5ddc9",
|
|
1144
|
-
"scripts/verify-health-agentic-built.mjs": "
|
|
1145
|
-
"scripts/verify-health-consumer-built.mjs": "
|
|
1146
|
-
"scripts/verify-health-contract-built.mjs": "
|
|
1147
|
-
"scripts/verify-health-deterministic-built.mjs": "
|
|
1144
|
+
"scripts/verify-health-agentic-built.mjs": "4b0d20b2c94390ff6f5af881fc50c5c225215a282891db0b3e3d84a7e6455367",
|
|
1145
|
+
"scripts/verify-health-consumer-built.mjs": "bd0676f7e76e4ebceb3a94c89a2b40f4c0266ab0ef7280a9bebbcb388c2731c3",
|
|
1146
|
+
"scripts/verify-health-contract-built.mjs": "4041780d412bee7b9e1bbe24d908bac64705184a8b0698f0d5bb748ec36b0bcd",
|
|
1147
|
+
"scripts/verify-health-deterministic-built.mjs": "9016663d16c93d0d0d74b41a957d200cd3aa8ef5b12e0879d24ba5ea041ab307",
|
|
1148
1148
|
"scripts/verify-learner-frontmatter-built.mjs": "182e5e99f52257d3079ee1bc42b45c3dcca8b93a968205ddb3f3af26f2558b47",
|
|
1149
1149
|
"tsconfig/base.json": "d04a105ec81aa9dc69d230d4a83406d920c305b6fc13c641ebafde0c41fc658f",
|
|
1150
1150
|
"tsconfig/build.json": "a743350ea93b0e8d2151c974c5a8a4cbcf946cd7f06c40d83146079bde70afe1",
|
package/package.json
CHANGED
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
"zod-validation-error": "^4.0.0"
|
|
146
146
|
},
|
|
147
147
|
"name": "@codyswann/lisa",
|
|
148
|
-
"version": "4.4.
|
|
148
|
+
"version": "4.4.2",
|
|
149
149
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
150
150
|
"main": "dist/index.js",
|
|
151
151
|
"exports": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.2",
|
|
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.4.
|
|
3
|
+
"version": "4.4.2",
|
|
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.4.
|
|
3
|
+
"version": "4.4.2",
|
|
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.4.
|
|
3
|
+
"version": "4.4.2",
|
|
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.4.
|
|
3
|
+
"version": "4.4.2",
|
|
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"
|
|
@@ -82,12 +82,12 @@
|
|
|
82
82
|
*
|
|
83
83
|
* @module scripts/check-delivery-deletion-conflicts
|
|
84
84
|
*/
|
|
85
|
-
import { execFileSync } from "node:child_process";
|
|
86
85
|
import fs from "node:fs";
|
|
87
86
|
import path from "node:path";
|
|
88
87
|
import process from "node:process";
|
|
89
88
|
import { fileURLToPath } from "node:url";
|
|
90
89
|
|
|
90
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
91
91
|
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
92
92
|
|
|
93
93
|
const REPO_ROOT = path.resolve(
|
|
@@ -250,7 +250,7 @@ export function matchDeletion(delivered, deleted) {
|
|
|
250
250
|
function listTrackedFiles(root) {
|
|
251
251
|
let stdout;
|
|
252
252
|
try {
|
|
253
|
-
stdout =
|
|
253
|
+
stdout = boundedExecFileSync("git", ["-C", root, "ls-files", "-z"], {
|
|
254
254
|
encoding: "utf8",
|
|
255
255
|
maxBuffer: MAX_GIT_OUTPUT_BYTES,
|
|
256
256
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
* on a commit that owed nothing, which is the safe direction to be wrong in.
|
|
45
45
|
* @module scripts/check-derived-artifacts
|
|
46
46
|
*/
|
|
47
|
-
import { execFileSync } from "node:child_process";
|
|
48
47
|
import path from "node:path";
|
|
49
48
|
import process from "node:process";
|
|
50
49
|
|
|
50
|
+
import { boundedExecFileSync, isChildTimeout } from "./lib/bounded-spawn.mjs";
|
|
51
51
|
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
52
52
|
|
|
53
53
|
const repoRoot = path.resolve(import.meta.dirname, "..");
|
|
@@ -118,18 +118,26 @@ export function requiresManifestCheck(stagedPaths) {
|
|
|
118
118
|
* Renames report their destination (`-M` plus `--diff-filter=R`), and deletions
|
|
119
119
|
* are included because removing a template is also a way to move what the
|
|
120
120
|
* artifacts describe.
|
|
121
|
-
*
|
|
121
|
+
*
|
|
122
|
+
* A KILLED child re-raises rather than returning empty. An empty list means
|
|
123
|
+
* "nothing staged touches the artifacts", which makes this guard SKIP — so a
|
|
124
|
+
* busy machine would let through the commit that moves a template without
|
|
125
|
+
* regenerating what describes it. "I could not ask" is not "there is nothing
|
|
126
|
+
* to check", and only one of those is safe to act on.
|
|
127
|
+
* @returns {string[]} Staged paths, empty when git said there were none
|
|
128
|
+
* @throws {Error} When the child was killed at its deadline
|
|
122
129
|
*/
|
|
123
130
|
export function stagedPaths() {
|
|
124
131
|
try {
|
|
125
|
-
return
|
|
132
|
+
return boundedExecFileSync(
|
|
126
133
|
"git",
|
|
127
134
|
["diff", "--cached", "--name-only", "--diff-filter=ACMRD"],
|
|
128
135
|
{ cwd: repoRoot, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
129
136
|
)
|
|
130
137
|
.split("\n")
|
|
131
138
|
.filter(Boolean);
|
|
132
|
-
} catch {
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (isChildTimeout(error)) throw error;
|
|
133
141
|
return [];
|
|
134
142
|
}
|
|
135
143
|
}
|
|
@@ -172,13 +180,24 @@ export function readableFailure(raw) {
|
|
|
172
180
|
*/
|
|
173
181
|
function runCheck(script) {
|
|
174
182
|
try {
|
|
175
|
-
|
|
183
|
+
boundedExecFileSync(process.execPath, [script, "--check"], {
|
|
176
184
|
cwd: repoRoot,
|
|
177
185
|
encoding: "utf8",
|
|
178
186
|
stdio: ["ignore", "pipe", "pipe"],
|
|
179
187
|
});
|
|
180
188
|
return { ok: true, output: "" };
|
|
181
189
|
} catch (error) {
|
|
190
|
+
// Already fail-closed — `ok: false` fails the guard either way — so the
|
|
191
|
+
// catch stays. What it must NOT do is stay silent about a kill: a killed
|
|
192
|
+
// child hands back EMPTY streams, so a timeout would otherwise render as a
|
|
193
|
+
// generator that failed with no diagnostic, which reads as a content
|
|
194
|
+
// problem and sends the operator to the wrong place entirely.
|
|
195
|
+
if (isChildTimeout(error)) {
|
|
196
|
+
return {
|
|
197
|
+
ok: false,
|
|
198
|
+
output: `${script} was killed before it finished. This is a timeout, not a check failure — nothing about the artifact was measured.`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
182
201
|
const stderr = error?.stderr ?? "";
|
|
183
202
|
const stdout = error?.stdout ?? "";
|
|
184
203
|
return { ok: false, output: readableFailure(`${stdout}${stderr}`) };
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
*
|
|
45
45
|
* @module scripts/check-orphan-test-processes
|
|
46
46
|
*/
|
|
47
|
-
import { execFileSync } from "node:child_process";
|
|
48
47
|
import process from "node:process";
|
|
48
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
49
49
|
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -168,7 +168,7 @@ export function selectOrphans(rows, minAgeSeconds) {
|
|
|
168
168
|
*/
|
|
169
169
|
function readProcessTable() {
|
|
170
170
|
// eslint-disable-next-line sonarjs/no-os-command-from-path -- fixed executable and argv
|
|
171
|
-
return
|
|
171
|
+
return boundedExecFileSync("ps", ["-eo", "pid,ppid,etime,command"], {
|
|
172
172
|
encoding: "utf8",
|
|
173
173
|
maxBuffer: 32 * 1024 * 1024,
|
|
174
174
|
});
|
|
@@ -23,10 +23,11 @@
|
|
|
23
23
|
* @module scripts/check-security-floors
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { execFileSync } from "node:child_process";
|
|
27
26
|
import { existsSync, readFileSync } from "node:fs";
|
|
28
27
|
import { globSync } from "node:fs";
|
|
29
28
|
|
|
29
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
30
|
+
|
|
30
31
|
/**
|
|
31
32
|
* Fixed absolute git locations, tried in order rather than a bare command name
|
|
32
33
|
* so a writeable directory early on `PATH` cannot decide which binary runs.
|
|
@@ -129,18 +130,28 @@ const NOT_A_MANIFEST = [
|
|
|
129
130
|
/**
|
|
130
131
|
* Every manifest git tracks, so the glob can be checked against reality.
|
|
131
132
|
*
|
|
132
|
-
* Returns null when the question cannot be asked — outside a checkout,
|
|
133
|
-
*
|
|
134
|
-
* missing": the whole defect being
|
|
133
|
+
* Returns null when the question cannot be asked — outside a checkout, with no
|
|
134
|
+
* git, or with the child killed at its deadline. A null is reported as "not
|
|
135
|
+
* reconciled", never folded into "nothing missing": the whole defect being
|
|
136
|
+
* fixed here is a scan that narrowed silently.
|
|
137
|
+
*
|
|
138
|
+
* The swallow is therefore KEPT for a timeout rather than kept by inheritance.
|
|
139
|
+
* A kill is one more way the question cannot be asked, and this function
|
|
140
|
+
* already routes "cannot ask" to the safe answer — so re-raising would trade a
|
|
141
|
+
* correct report for a crash and gain nothing.
|
|
135
142
|
* @returns {string[]|null} Tracked manifest paths, or null when unknowable.
|
|
136
143
|
*/
|
|
137
144
|
function trackedManifests() {
|
|
138
145
|
try {
|
|
139
|
-
return
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
return boundedExecFileSync(
|
|
147
|
+
GIT_BIN,
|
|
148
|
+
["ls-files", "-z", "*package.lisa.json"],
|
|
149
|
+
{
|
|
150
|
+
encoding: "utf8",
|
|
151
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
152
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
153
|
+
}
|
|
154
|
+
)
|
|
144
155
|
.split("\0")
|
|
145
156
|
.filter(Boolean)
|
|
146
157
|
.filter(
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
*
|
|
58
58
|
* @module scripts/check-template-workflow-refs
|
|
59
59
|
*/
|
|
60
|
-
import { execFileSync } from "node:child_process";
|
|
61
60
|
import fs from "node:fs";
|
|
62
61
|
import path from "node:path";
|
|
63
62
|
import process from "node:process";
|
|
64
63
|
import { fileURLToPath } from "node:url";
|
|
65
64
|
|
|
65
|
+
import { boundedExecFileSync, isChildTimeout } from "./lib/bounded-spawn.mjs";
|
|
66
66
|
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
67
67
|
|
|
68
68
|
const REPO_ROOT = path.resolve(
|
|
@@ -124,7 +124,7 @@ export function findWorkflowRefs(content) {
|
|
|
124
124
|
function listTrackedFiles(root) {
|
|
125
125
|
let stdout;
|
|
126
126
|
try {
|
|
127
|
-
stdout =
|
|
127
|
+
stdout = boundedExecFileSync("git", ["-C", root, "ls-files", "-z"], {
|
|
128
128
|
encoding: "utf8",
|
|
129
129
|
maxBuffer: MAX_GIT_OUTPUT_BYTES,
|
|
130
130
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -140,13 +140,19 @@ function listTrackedFiles(root) {
|
|
|
140
140
|
/**
|
|
141
141
|
* Whether `ref` names an object present in this clone.
|
|
142
142
|
*
|
|
143
|
+
* A KILLED child re-raises. `false` here does not mean "unknown", it means
|
|
144
|
+
* "this reference is broken" — a verdict the caller reports. So a busy machine
|
|
145
|
+
* would manufacture a failure about a reference that is perfectly fine, and a
|
|
146
|
+
* guard that invents failures is one people learn to re-run rather than read.
|
|
147
|
+
* That is the sibling harm to failing open, and it is just as corrosive.
|
|
143
148
|
* @param {string} root - the repository root.
|
|
144
149
|
* @param {string} ref - a tag, branch, or SHA.
|
|
145
150
|
* @returns {boolean} whether the ref resolves locally.
|
|
151
|
+
* @throws {Error} When the child was killed at its deadline
|
|
146
152
|
*/
|
|
147
153
|
function refExists(root, ref) {
|
|
148
154
|
try {
|
|
149
|
-
|
|
155
|
+
boundedExecFileSync(
|
|
150
156
|
"git",
|
|
151
157
|
["-C", root, "rev-parse", "--verify", `${ref}^{commit}`],
|
|
152
158
|
{
|
|
@@ -154,7 +160,8 @@ function refExists(root, ref) {
|
|
|
154
160
|
}
|
|
155
161
|
);
|
|
156
162
|
return true;
|
|
157
|
-
} catch {
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (isChildTimeout(error)) throw error;
|
|
158
165
|
return false;
|
|
159
166
|
}
|
|
160
167
|
}
|
|
@@ -162,18 +169,26 @@ function refExists(root, ref) {
|
|
|
162
169
|
/**
|
|
163
170
|
* Whether `target` exists in the tree named by `ref`.
|
|
164
171
|
*
|
|
172
|
+
* A KILLED child re-raises, for the reason given on `refExists` above: `false`
|
|
173
|
+
* is a verdict of "absent at that ref", not an admission of ignorance.
|
|
165
174
|
* @param {string} root - the repository root.
|
|
166
175
|
* @param {string} ref - a tag, branch, or SHA known to resolve.
|
|
167
176
|
* @param {string} target - a repo-relative path.
|
|
168
177
|
* @returns {boolean} whether the path exists at that ref.
|
|
178
|
+
* @throws {Error} When the child was killed at its deadline
|
|
169
179
|
*/
|
|
170
180
|
function pathExistsAtRef(root, ref, target) {
|
|
171
181
|
try {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
182
|
+
boundedExecFileSync(
|
|
183
|
+
"git",
|
|
184
|
+
["-C", root, "cat-file", "-e", `${ref}:${target}`],
|
|
185
|
+
{
|
|
186
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
187
|
+
}
|
|
188
|
+
);
|
|
175
189
|
return true;
|
|
176
|
-
} catch {
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (isChildTimeout(error)) throw error;
|
|
177
192
|
return false;
|
|
178
193
|
}
|
|
179
194
|
}
|
|
@@ -77,7 +77,6 @@
|
|
|
77
77
|
*
|
|
78
78
|
* @module scripts/check-workflow-package-paths
|
|
79
79
|
*/
|
|
80
|
-
import { execFileSync } from "node:child_process";
|
|
81
80
|
import {
|
|
82
81
|
existsSync,
|
|
83
82
|
mkdirSync,
|
|
@@ -89,6 +88,7 @@ import { mkdtempSync } from "node:fs";
|
|
|
89
88
|
import { tmpdir } from "node:os";
|
|
90
89
|
import path from "node:path";
|
|
91
90
|
|
|
91
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
92
92
|
import { invokedAsScript } from "./lib/invoked-as-script.mjs";
|
|
93
93
|
|
|
94
94
|
/** The package whose released layout a workflow may make claims about. */
|
|
@@ -179,7 +179,7 @@ function parseArgs(argv) {
|
|
|
179
179
|
* @returns {readonly string[]} Repo-relative workflow paths
|
|
180
180
|
*/
|
|
181
181
|
function trackedWorkflows(root) {
|
|
182
|
-
const output =
|
|
182
|
+
const output = boundedExecFileSync(
|
|
183
183
|
"git",
|
|
184
184
|
["-C", root, "ls-files", ".github/workflows"],
|
|
185
185
|
{ encoding: "utf8" }
|
|
@@ -285,7 +285,7 @@ export function collectClaims(root) {
|
|
|
285
285
|
*/
|
|
286
286
|
function publishedVersions() {
|
|
287
287
|
const versions = JSON.parse(
|
|
288
|
-
|
|
288
|
+
boundedExecFileSync("npm", ["view", PACKAGE_NAME, "versions", "--json"], {
|
|
289
289
|
encoding: "utf8",
|
|
290
290
|
stdio: ["ignore", "pipe", "pipe"],
|
|
291
291
|
maxBuffer: 16 * 1024 * 1024,
|
|
@@ -357,7 +357,7 @@ function fetchReleaseListings(chosen, cacheDir) {
|
|
|
357
357
|
continue;
|
|
358
358
|
}
|
|
359
359
|
const staging = mkdtempSync(path.join(tmpdir(), "lisa-release-"));
|
|
360
|
-
|
|
360
|
+
boundedExecFileSync(
|
|
361
361
|
"npm",
|
|
362
362
|
["pack", `${PACKAGE_NAME}@${version}`, "--pack-destination", staging],
|
|
363
363
|
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
@@ -366,10 +366,14 @@ function fetchReleaseListings(chosen, cacheDir) {
|
|
|
366
366
|
if (tarball === undefined) {
|
|
367
367
|
throw new Error(`npm pack produced no tarball for ${version}`);
|
|
368
368
|
}
|
|
369
|
-
const entries =
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
369
|
+
const entries = boundedExecFileSync(
|
|
370
|
+
"tar",
|
|
371
|
+
["-tzf", path.join(staging, tarball)],
|
|
372
|
+
{
|
|
373
|
+
encoding: "utf8",
|
|
374
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
375
|
+
}
|
|
376
|
+
)
|
|
373
377
|
.split("\n")
|
|
374
378
|
.filter(Boolean);
|
|
375
379
|
listings[version] = entries;
|
|
@@ -19,11 +19,12 @@
|
|
|
19
19
|
* legitimately stale file as host-modified and stop refreshing it — failing
|
|
20
20
|
* safe, but permanently.
|
|
21
21
|
*/
|
|
22
|
-
import { execFileSync } from "node:child_process";
|
|
23
22
|
import { createHash } from "node:crypto";
|
|
24
23
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
25
24
|
import path from "node:path";
|
|
26
25
|
|
|
26
|
+
import { boundedExecFileSync, isChildTimeout } from "./lib/bounded-spawn.mjs";
|
|
27
|
+
|
|
27
28
|
const repoRoot = path.resolve(import.meta.dirname, "..");
|
|
28
29
|
const outputPath = path.join(repoRoot, "src/core/lisa-owned-hash-ledger.ts");
|
|
29
30
|
const COPY_OVERWRITE = "copy-overwrite";
|
|
@@ -35,18 +36,25 @@ const ENFORCEMENT_TREE = "scripts/";
|
|
|
35
36
|
*
|
|
36
37
|
* Blobs are read as `binary` so arbitrary bytes survive the round trip into the
|
|
37
38
|
* Buffer that gets hashed; decoding as UTF-8 would corrupt any non-text file.
|
|
39
|
+
* A KILLED child re-raises. Every caller reads this as content, and an empty
|
|
40
|
+
* string is a perfectly well-formed answer meaning "no files" — so a timeout
|
|
41
|
+
* on the `ls-files` call would build a ledger that vouches for NOTHING and
|
|
42
|
+
* report it as a complete one. A ledger's whole job is to vouch for bytes; one
|
|
43
|
+
* assembled from silence is worse than none, because it looks authoritative.
|
|
38
44
|
* @param {readonly string[]} args - Arguments passed to git
|
|
39
|
-
* @returns {string} Command stdout, or an empty string when git
|
|
45
|
+
* @returns {string} Command stdout, or an empty string when git said nothing
|
|
46
|
+
* @throws {Error} When the child was killed at its deadline
|
|
40
47
|
*/
|
|
41
48
|
function git(args) {
|
|
42
49
|
try {
|
|
43
|
-
return
|
|
50
|
+
return boundedExecFileSync("git", args, {
|
|
44
51
|
cwd: repoRoot,
|
|
45
52
|
encoding: "buffer",
|
|
46
53
|
maxBuffer: 64 * 1024 * 1024,
|
|
47
54
|
stdio: ["ignore", "pipe", "ignore"],
|
|
48
55
|
}).toString("binary");
|
|
49
|
-
} catch {
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (isChildTimeout(error)) throw error;
|
|
50
58
|
return "";
|
|
51
59
|
}
|
|
52
60
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/** Generate the exact hash-pinned allowlist for public upstream evidence. */
|
|
3
|
-
import { execFileSync } from "node:child_process";
|
|
4
3
|
import { createHash } from "node:crypto";
|
|
5
4
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
5
|
import path from "node:path";
|
|
7
6
|
|
|
7
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
8
8
|
import {
|
|
9
9
|
describeStaleManifest,
|
|
10
10
|
diagnoseStaleManifest,
|
|
@@ -86,7 +86,7 @@ function repositoryPath(url) {
|
|
|
86
86
|
// comparison is by path: `url.<base>.insteadOf` rewrites what `git remote
|
|
87
87
|
// get-url` returns, so it answers "how do I reach this" rather than "what is
|
|
88
88
|
// this". Falls back to the resolved URL only if no value is configured.
|
|
89
|
-
const originUrl =
|
|
89
|
+
const originUrl = boundedExecFileSync(
|
|
90
90
|
"git",
|
|
91
91
|
["config", "--get", "remote.origin.url"],
|
|
92
92
|
{ cwd: repoRoot, encoding: "utf8" }
|
|
@@ -100,7 +100,7 @@ if (repositoryPath(originUrl) !== canonicalRepository.toLowerCase()) {
|
|
|
100
100
|
);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
const tracked =
|
|
103
|
+
const tracked = boundedExecFileSync("git", ["ls-files", "-z"], {
|
|
104
104
|
cwd: repoRoot,
|
|
105
105
|
encoding: "utf8",
|
|
106
106
|
})
|
|
@@ -135,7 +135,7 @@ const existingPublicCommits = [
|
|
|
135
135
|
.filter(Boolean);
|
|
136
136
|
const refreshPublicCommits = process.argv.includes("--refresh-public-commits");
|
|
137
137
|
const discoveredPublicCommits = refreshPublicCommits
|
|
138
|
-
?
|
|
138
|
+
? boundedExecFileSync("git", ["rev-list", "--remotes=origin"], {
|
|
139
139
|
cwd: repoRoot,
|
|
140
140
|
encoding: "utf8",
|
|
141
141
|
})
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/** Empirical proof for the shipped optional agentic Health composition API. */
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
5
4
|
import { createHash } from "node:crypto";
|
|
6
5
|
import {
|
|
7
6
|
lstat,
|
|
@@ -17,6 +16,8 @@ import {
|
|
|
17
16
|
import { tmpdir } from "node:os";
|
|
18
17
|
import path from "node:path";
|
|
19
18
|
|
|
19
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
20
|
+
|
|
20
21
|
// Literals named once — each was repeated enough times that a typo in one
|
|
21
22
|
// copy would diverge silently.
|
|
22
23
|
const TEMPLATES_MANAGED = "templates.managed";
|
|
@@ -62,14 +63,14 @@ const root = process.cwd();
|
|
|
62
63
|
const workspace = await mkdtemp(path.join(tmpdir(), "lisa-health-agentic-"));
|
|
63
64
|
const project = path.join(workspace, "host");
|
|
64
65
|
const isolatedBin = path.join(workspace, "bin");
|
|
65
|
-
const gitExecutable =
|
|
66
|
+
const gitExecutable = boundedExecFileSync("which", ["git"], {
|
|
66
67
|
encoding: "utf8",
|
|
67
68
|
}).trim();
|
|
68
69
|
const healthFile = path.join(project, ".lisa", "health", "latest.json");
|
|
69
70
|
const startedAt = Date.now();
|
|
70
71
|
|
|
71
72
|
const run = (executable, argv, options = {}) =>
|
|
72
|
-
|
|
73
|
+
boundedExecFileSync(executable, argv, {
|
|
73
74
|
cwd: project,
|
|
74
75
|
encoding: "utf8",
|
|
75
76
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/** Built-package proof for the real two-phase Health CLI consumer. */
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
5
4
|
import {
|
|
6
5
|
lstat,
|
|
7
6
|
mkdir,
|
|
@@ -14,6 +13,8 @@ import {
|
|
|
14
13
|
import { tmpdir } from "node:os";
|
|
15
14
|
import path from "node:path";
|
|
16
15
|
|
|
16
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
17
|
+
|
|
17
18
|
// Literals named once — each was repeated enough times that a typo in one
|
|
18
19
|
// copy would diverge silently.
|
|
19
20
|
const LATEST_JSON = "latest.json";
|
|
@@ -53,7 +54,7 @@ async function createProject(name) {
|
|
|
53
54
|
|
|
54
55
|
/** Run the compiled CLI and capture its exact standard-output bytes. */
|
|
55
56
|
function runCli(project, argv, input) {
|
|
56
|
-
return
|
|
57
|
+
return boundedExecFileSync(
|
|
57
58
|
process.execPath,
|
|
58
59
|
[cli, "--no-update-check", "health", ...argv, project],
|
|
59
60
|
{
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/** Empirical proof for the shipped Health v1 contract and real built CLI. */
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
5
4
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
5
|
import { tmpdir } from "node:os";
|
|
7
6
|
import path from "node:path";
|
|
8
7
|
|
|
8
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
9
|
+
|
|
9
10
|
let assertions = 0;
|
|
10
11
|
const check = (condition, message) => {
|
|
11
12
|
assertions += 1;
|
|
@@ -23,7 +24,7 @@ try {
|
|
|
23
24
|
path.join(project, ".lisa.config.json"),
|
|
24
25
|
`${JSON.stringify({ tracker: "github", github: { org: "acme", repo: "app" } }, null, 2)}\n`
|
|
25
26
|
);
|
|
26
|
-
|
|
27
|
+
boundedExecFileSync(
|
|
27
28
|
process.execPath,
|
|
28
29
|
[path.join(root, "dist/index.js"), "sync", project, "--json"],
|
|
29
30
|
{
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/** Empirical proof for the shipped deterministic Health fast path. */
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
|
-
import { execFileSync } from "node:child_process";
|
|
5
4
|
import { createHash } from "node:crypto";
|
|
6
5
|
import {
|
|
7
6
|
chmod,
|
|
@@ -18,6 +17,8 @@ import {
|
|
|
18
17
|
import { tmpdir } from "node:os";
|
|
19
18
|
import path from "node:path";
|
|
20
19
|
|
|
20
|
+
import { boundedExecFileSync } from "./lib/bounded-spawn.mjs";
|
|
21
|
+
|
|
21
22
|
// Literals named once — each was repeated enough times that a typo in one
|
|
22
23
|
// copy would diverge silently.
|
|
23
24
|
const PACKAGE_JSON = "package.json";
|
|
@@ -40,14 +41,14 @@ const root = process.cwd();
|
|
|
40
41
|
const workspace = await mkdtemp(path.join(tmpdir(), "lisa-health-fast-path-"));
|
|
41
42
|
const project = path.join(workspace, "host");
|
|
42
43
|
const isolatedBin = path.join(workspace, "bin");
|
|
43
|
-
const gitExecutable =
|
|
44
|
+
const gitExecutable = boundedExecFileSync("which", ["git"], {
|
|
44
45
|
encoding: "utf8",
|
|
45
46
|
}).trim();
|
|
46
47
|
const healthFile = path.join(project, ".lisa", "health", "latest.json");
|
|
47
48
|
const startedAt = Date.now();
|
|
48
49
|
|
|
49
50
|
const run = (executable, argv, options = {}) =>
|
|
50
|
-
|
|
51
|
+
boundedExecFileSync(executable, argv, {
|
|
51
52
|
cwd: project,
|
|
52
53
|
encoding: "utf8",
|
|
53
54
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -466,7 +467,7 @@ try {
|
|
|
466
467
|
path.join(unsafeProject, LISA_CONFIG_JSON),
|
|
467
468
|
'{"tracker":"github","harness":"codex"}\n'
|
|
468
469
|
);
|
|
469
|
-
|
|
470
|
+
boundedExecFileSync("mkfifo", [path.join(unsafeProject, PACKAGE_JSON)]);
|
|
470
471
|
const unsafeStarted = Date.now();
|
|
471
472
|
const unsafe = await health.runDeterministicHealth(unsafeProject, {
|
|
472
473
|
lisaRoot: root,
|