omp-conductor 0.19.1 → 0.19.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/package.json +2 -2
- package/src/check-browser-js.ts +72 -0
- package/src/dashboard/app.js +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omp-conductor",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A 24/7 dispatcher that takes ready GitHub issues to green, mergeable PRs using omp coding sessions, with tiered escalation first to an orchestrator session and then to a human.",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"LICENSE"
|
|
30
30
|
],
|
|
31
31
|
"scripts": {
|
|
32
|
-
"check": "tsc --noEmit && bun run src/check-trailing-newlines.ts",
|
|
32
|
+
"check": "tsc --noEmit && bun run src/check-trailing-newlines.ts && bun run src/check-browser-js.ts",
|
|
33
33
|
"test": "bun test",
|
|
34
34
|
"schema": "bun run src/generate-schema.ts"
|
|
35
35
|
},
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate: every browser script this package serves must parse.
|
|
3
|
+
*
|
|
4
|
+
* Run via `bun run check` from the package root, beside the trailing-newline
|
|
5
|
+
* gate. `tsc` never sees `src/dashboard/app.js` — it is plain browser JS,
|
|
6
|
+
* shipped as an asset and loaded by a `<script>` tag — and no test executes
|
|
7
|
+
* it, so nothing in the pipeline had an opinion about whether it was even
|
|
8
|
+
* syntactically valid.
|
|
9
|
+
*
|
|
10
|
+
* It was not. 0.19.0 shipped an `app.js` carrying four `__omp_shell("…")`
|
|
11
|
+
* fragments where `!answer.ok` and `!confirmDestructive(` should have been:
|
|
12
|
+
* the authoring session wrote the file through a Python eval path, whose
|
|
13
|
+
* IPython-style `!cmd` shell escape rewrote every line that began with `!`.
|
|
14
|
+
* A single SyntaxError takes the whole script with it, so the dashboard
|
|
15
|
+
* rendered its static `<h1>` and nothing else — no token prompt, no fleet —
|
|
16
|
+
* and looked, from the outside, like an auth or data problem (#981).
|
|
17
|
+
*
|
|
18
|
+
* A parse is the whole check. It is not a linter and has no opinion about
|
|
19
|
+
* style: it asks the one question a served script must answer yes to.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { execFileSync } from "node:child_process";
|
|
23
|
+
import { readFileSync } from "node:fs";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
|
|
26
|
+
/** Tracked browser scripts, repo-root-relative. */
|
|
27
|
+
export const BROWSER_SCRIPTS: readonly string[] = ["omp/src/dashboard/app.js"];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The parse error for one script, or null when it parses. Uses Bun's own
|
|
31
|
+
* transpiler: a full parse of the source, without executing a line of it (the
|
|
32
|
+
* script drives a DOM this process does not have).
|
|
33
|
+
*/
|
|
34
|
+
export function parseFailure(source: string, path: string): string | null {
|
|
35
|
+
try {
|
|
36
|
+
new Bun.Transpiler({ loader: "js", target: "browser" }).transformSync(source);
|
|
37
|
+
return null;
|
|
38
|
+
} catch (err) {
|
|
39
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40
|
+
return `${path}: ${message.split("\n")[0]}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** One violation line per unparseable script, so a run names every offender. */
|
|
45
|
+
export function findViolations(
|
|
46
|
+
paths: readonly string[],
|
|
47
|
+
read: (path: string) => string,
|
|
48
|
+
): string[] {
|
|
49
|
+
const violations: string[] = [];
|
|
50
|
+
for (const path of paths) {
|
|
51
|
+
const failure = parseFailure(read(path), path);
|
|
52
|
+
if (failure !== null) violations.push(failure);
|
|
53
|
+
}
|
|
54
|
+
return violations;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function repoRoot(): string {
|
|
58
|
+
return execFileSync("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (import.meta.main) {
|
|
62
|
+
const root = repoRoot();
|
|
63
|
+
const violations = findViolations(BROWSER_SCRIPTS, (path) =>
|
|
64
|
+
readFileSync(join(root, path), "utf8"),
|
|
65
|
+
);
|
|
66
|
+
if (violations.length > 0) {
|
|
67
|
+
for (const violation of violations) console.error(violation);
|
|
68
|
+
console.error(`browser script gate: ${violations.length} script(s) do not parse`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
console.log("browser script gate: ok");
|
|
72
|
+
}
|
package/src/dashboard/app.js
CHANGED
|
@@ -140,7 +140,7 @@ async function runControl(path, body, describe) {
|
|
|
140
140
|
: JSON.stringify(answer.body);
|
|
141
141
|
showControlResult(
|
|
142
142
|
answer.ok ? `${describe}: ok` : `${describe} refused (${answer.status}): ${detail}`,
|
|
143
|
-
|
|
143
|
+
!answer.ok,
|
|
144
144
|
);
|
|
145
145
|
} catch (err) {
|
|
146
146
|
showControlResult(`${describe} failed: ${String(err.message ?? err)}`, true);
|
|
@@ -188,7 +188,7 @@ controls.addEventListener("click", (event) => {
|
|
|
188
188
|
if (reason === null || reason.trim() === "") return;
|
|
189
189
|
if (
|
|
190
190
|
action === "hold" &&
|
|
191
|
-
|
|
191
|
+
!confirmDestructive(
|
|
192
192
|
"Hold stops new claims AND disarms ticks. Re-arming needs a Telegram challenge answered in the chat. Continue?",
|
|
193
193
|
)
|
|
194
194
|
) {
|
|
@@ -447,7 +447,7 @@ function renderRunControls(issue) {
|
|
|
447
447
|
const reason = window.prompt(`Reason for stopping #${issue} (recorded in the run's report):`);
|
|
448
448
|
if (reason === null || reason.trim() === "") return;
|
|
449
449
|
if (
|
|
450
|
-
|
|
450
|
+
!confirmDestructive(
|
|
451
451
|
`Stop #${issue}? This settles the run terminally, salvages its tree and frees the slot. It cannot be resumed.`,
|
|
452
452
|
)
|
|
453
453
|
) {
|
|
@@ -474,7 +474,7 @@ function renderRunControls(issue) {
|
|
|
474
474
|
);
|
|
475
475
|
button("Unblock --force", "destructive", () => {
|
|
476
476
|
if (
|
|
477
|
-
|
|
477
|
+
!confirmDestructive(
|
|
478
478
|
`Force-unblock #${issue}? This accepts the loss of uncommitted work in its worktree, which may be the only copy.`,
|
|
479
479
|
)
|
|
480
480
|
) {
|