@glrs-dev/cli 1.1.0 → 2.0.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/CHANGELOG.md +4 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-assessor.md +77 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-builder.md +24 -116
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-planner.md +38 -160
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-scoper.md +58 -0
- package/dist/vendor/harness-opencode/dist/{chunk-BWERBERN.js → chunk-6CZPRUMJ.js} +12 -62
- package/dist/vendor/harness-opencode/dist/chunk-DZG4D3OH.js +54 -0
- package/dist/vendor/harness-opencode/dist/chunk-OYRKOEXK.js +88 -0
- package/dist/vendor/harness-opencode/dist/cli.js +1619 -3930
- package/dist/vendor/harness-opencode/dist/index.js +831 -166
- package/dist/vendor/harness-opencode/dist/{install-5JKWK6Z4.js → install-6775ZBDG.js} +1 -1
- package/dist/vendor/harness-opencode/dist/paths-WZ23ZQOV.js +18 -0
- package/dist/vendor/harness-opencode/dist/skills/code-quality/SKILL.md +45 -0
- package/dist/vendor/harness-opencode/dist/skills/code-quality/rules/building.md +125 -0
- package/dist/vendor/harness-opencode/dist/skills/code-quality/rules/gap-analysis.md +92 -0
- package/dist/vendor/harness-opencode/dist/skills/code-quality/rules/planning.md +96 -0
- package/dist/vendor/harness-opencode/dist/skills/code-quality/rules/review.md +104 -0
- package/dist/vendor/harness-opencode/package.json +1 -1
- package/package.json +1 -1
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-builder.open.md +0 -129
- package/dist/vendor/harness-opencode/dist/chunk-57EOY72Y.js +0 -174
- package/dist/vendor/harness-opencode/dist/chunk-5TAMY7P6.js +0 -67
- package/dist/vendor/harness-opencode/dist/chunk-BKTFWXLG.js +0 -204
- package/dist/vendor/harness-opencode/dist/chunk-EK7K4NTV.js +0 -747
- package/dist/vendor/harness-opencode/dist/chunk-KB7M7JXU.js +0 -145
- package/dist/vendor/harness-opencode/dist/chunk-RNRCXQ65.js +0 -56
- package/dist/vendor/harness-opencode/dist/paths-LT3QQKCF.js +0 -18
- package/dist/vendor/harness-opencode/dist/pilot/mcp/status-server.d.ts +0 -1
- package/dist/vendor/harness-opencode/dist/pilot/mcp/status-server.js +0 -228
- package/dist/vendor/harness-opencode/dist/pilot-config-7LJZ23YK.js +0 -55
- package/dist/vendor/harness-opencode/dist/runs-QWPL3TKV.js +0 -18
- package/dist/vendor/harness-opencode/dist/safety-gate-WM3EWOCY.js +0 -10
- package/dist/vendor/harness-opencode/dist/setup-hook-FHTXMAQL.js +0 -88
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/SKILL.md +0 -80
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/dag-shape.md +0 -47
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/decomposition.md +0 -63
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/first-principles.md +0 -29
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/milestones.md +0 -57
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/qa-expectations.md +0 -120
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/self-review.md +0 -46
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/task-context.md +0 -47
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/touches-scope.md +0 -81
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/verify-design.md +0 -121
- package/dist/vendor/harness-opencode/dist/tasks-KJ3WN2KY.js +0 -32
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
// src/pilot/worker/safety-gate.ts
|
|
2
|
-
import { execFile } from "child_process";
|
|
3
|
-
import { promisify } from "util";
|
|
4
|
-
import picomatch from "picomatch";
|
|
5
|
-
var execFileP = promisify(execFile);
|
|
6
|
-
var SAFETY_GATE_TOLERATE = [
|
|
7
|
-
// opencode plugin installer churn. opencode upgrades its own plugin
|
|
8
|
-
// dependency in the background, which bumps the pinned version in
|
|
9
|
-
// .opencode/package.json + .opencode/package-lock.json. Users don't
|
|
10
|
-
// author those edits; refusing to start pilot because of them is a
|
|
11
|
-
// consistent friction.
|
|
12
|
-
".opencode/**",
|
|
13
|
-
// Next.js — `next build` regenerates this every run, often outside
|
|
14
|
-
// a pilot session.
|
|
15
|
-
"**/next-env.d.ts",
|
|
16
|
-
// Next.js app-router generated types.
|
|
17
|
-
"**/.next/types/**",
|
|
18
|
-
"**/.next/dev/types/**",
|
|
19
|
-
// TypeScript project-reference build info — tsc writes these.
|
|
20
|
-
"**/*.tsbuildinfo",
|
|
21
|
-
// Snapshot test files rewritten by `vitest -u` / `jest -u`.
|
|
22
|
-
"**/__snapshots__/**",
|
|
23
|
-
"**/*.snap"
|
|
24
|
-
];
|
|
25
|
-
async function git(cwd, args) {
|
|
26
|
-
for (const a of args) {
|
|
27
|
-
if (a.includes("\0")) {
|
|
28
|
-
throw new Error(`git arg contains null byte: ${JSON.stringify(a)}`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
try {
|
|
32
|
-
const { stdout, stderr } = await execFileP("git", args, {
|
|
33
|
-
cwd,
|
|
34
|
-
timeout: 1e4,
|
|
35
|
-
maxBuffer: 1 << 20
|
|
36
|
-
});
|
|
37
|
-
return { stdout: stdout.toString(), stderr: stderr.toString(), ok: true };
|
|
38
|
-
} catch (err) {
|
|
39
|
-
const e = err;
|
|
40
|
-
return {
|
|
41
|
-
stdout: (e.stdout ?? "").toString(),
|
|
42
|
-
stderr: (e.stderr ?? "").toString(),
|
|
43
|
-
ok: false
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
async function headSha(cwd) {
|
|
48
|
-
const r = await git(cwd, ["rev-parse", "HEAD"]);
|
|
49
|
-
if (!r.ok) throw new Error(`git rev-parse HEAD failed: ${r.stderr.trim()}`);
|
|
50
|
-
return r.stdout.trim();
|
|
51
|
-
}
|
|
52
|
-
var FORBIDDEN_BRANCHES = /* @__PURE__ */ new Set(["main", "master"]);
|
|
53
|
-
function parsePorcelainPaths(stdout) {
|
|
54
|
-
const paths = [];
|
|
55
|
-
for (const line of stdout.split("\n")) {
|
|
56
|
-
if (line.length < 4) continue;
|
|
57
|
-
let rest = line.slice(3);
|
|
58
|
-
const arrow = rest.indexOf(" -> ");
|
|
59
|
-
if (arrow !== -1) rest = rest.slice(arrow + 4);
|
|
60
|
-
if (rest.startsWith('"') && rest.endsWith('"')) {
|
|
61
|
-
rest = rest.slice(1, -1);
|
|
62
|
-
}
|
|
63
|
-
paths.push(rest);
|
|
64
|
-
}
|
|
65
|
-
return paths;
|
|
66
|
-
}
|
|
67
|
-
async function checkCwdSafety(cwd) {
|
|
68
|
-
const inside = await git(cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
69
|
-
if (!inside.ok || inside.stdout.trim() !== "true") {
|
|
70
|
-
return {
|
|
71
|
-
ok: false,
|
|
72
|
-
reason: `not inside a git worktree: ${cwd}`
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
const branchRes = await git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
76
|
-
if (!branchRes.ok) {
|
|
77
|
-
return {
|
|
78
|
-
ok: false,
|
|
79
|
-
reason: `cannot determine current branch: ${branchRes.stderr.trim()}`
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
const branch = branchRes.stdout.trim();
|
|
83
|
-
if (FORBIDDEN_BRANCHES.has(branch)) {
|
|
84
|
-
return {
|
|
85
|
-
ok: false,
|
|
86
|
-
reason: `refuse to run on protected branch: ${branch}. Switch to a feature branch first.`
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
const defaultRes = await git(cwd, [
|
|
90
|
-
"symbolic-ref",
|
|
91
|
-
"--short",
|
|
92
|
-
"refs/remotes/origin/HEAD"
|
|
93
|
-
]);
|
|
94
|
-
if (defaultRes.ok) {
|
|
95
|
-
const remoteDefault = defaultRes.stdout.trim().replace(/^origin\//, "");
|
|
96
|
-
if (remoteDefault && branch === remoteDefault) {
|
|
97
|
-
return {
|
|
98
|
-
ok: false,
|
|
99
|
-
reason: `refuse to run on the remote's default branch: ${branch}. Switch to a feature branch first.`
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
const statusRes = await git(cwd, ["status", "--porcelain"]);
|
|
104
|
-
if (!statusRes.ok) {
|
|
105
|
-
return {
|
|
106
|
-
ok: false,
|
|
107
|
-
reason: `git status failed: ${statusRes.stderr.trim()}`
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
const rawStdout = statusRes.stdout.replace(/\n$/, "");
|
|
111
|
-
if (rawStdout.length === 0) {
|
|
112
|
-
return { ok: true, warnings: [] };
|
|
113
|
-
}
|
|
114
|
-
const paths = parsePorcelainPaths(rawStdout);
|
|
115
|
-
const matchTolerate = picomatch([...SAFETY_GATE_TOLERATE], { dot: true });
|
|
116
|
-
const tolerated = [];
|
|
117
|
-
const genuine = [];
|
|
118
|
-
for (const p of paths) {
|
|
119
|
-
if (matchTolerate(p)) tolerated.push(p);
|
|
120
|
-
else genuine.push(p);
|
|
121
|
-
}
|
|
122
|
-
if (genuine.length > 0) {
|
|
123
|
-
const lines = rawStdout.split("\n").slice(0, 10).map((s) => " " + s).join("\n");
|
|
124
|
-
return {
|
|
125
|
-
ok: false,
|
|
126
|
-
reason: `working tree is dirty; pilot refuses to run on uncommitted changes.
|
|
127
|
-
Commit, stash, or discard them, then re-run.
|
|
128
|
-
First 10 lines of git status --porcelain:
|
|
129
|
-
${lines}`
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
const preview = tolerated.slice(0, 5);
|
|
133
|
-
const suffix = tolerated.length > preview.length ? ` (+${tolerated.length - preview.length} more)` : "";
|
|
134
|
-
const warnings = [
|
|
135
|
-
`working tree has ${tolerated.length} modified file(s) in framework-owned paths; treating tree as clean:
|
|
136
|
-
` + preview.map((p) => ` ${p}`).join("\n") + suffix
|
|
137
|
-
];
|
|
138
|
-
return { ok: true, warnings };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export {
|
|
142
|
-
SAFETY_GATE_TOLERATE,
|
|
143
|
-
headSha,
|
|
144
|
-
checkCwdSafety
|
|
145
|
-
};
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// src/pilot/mcp/session-registry.ts
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
import { promisify } from "util";
|
|
5
|
-
var writeFile2 = promisify(fs.writeFile);
|
|
6
|
-
var rename2 = promisify(fs.rename);
|
|
7
|
-
var unlink2 = promisify(fs.unlink);
|
|
8
|
-
var mkdir2 = promisify(fs.mkdir);
|
|
9
|
-
function getSessionsPath(runDir) {
|
|
10
|
-
return path.join(runDir, "sessions.json");
|
|
11
|
-
}
|
|
12
|
-
function readSessions(runDir) {
|
|
13
|
-
const sessionsPath = getSessionsPath(runDir);
|
|
14
|
-
try {
|
|
15
|
-
const content = fs.readFileSync(sessionsPath, "utf8");
|
|
16
|
-
const parsed = JSON.parse(content);
|
|
17
|
-
if (typeof parsed !== "object" || parsed === null) {
|
|
18
|
-
return {};
|
|
19
|
-
}
|
|
20
|
-
return parsed;
|
|
21
|
-
} catch {
|
|
22
|
-
return {};
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async function registerSession(args) {
|
|
26
|
-
const { runDir, sessionId, runId, taskId } = args;
|
|
27
|
-
const sessionsPath = getSessionsPath(runDir);
|
|
28
|
-
await mkdir2(runDir, { recursive: true });
|
|
29
|
-
const current = readSessions(runDir);
|
|
30
|
-
const updated = {
|
|
31
|
-
...current,
|
|
32
|
-
[sessionId]: { runId, taskId }
|
|
33
|
-
};
|
|
34
|
-
const tempPath = `${sessionsPath}.tmp.${process.pid}.${Math.random().toString(36).slice(2, 8)}`;
|
|
35
|
-
await writeFile2(tempPath, JSON.stringify(updated, null, 2), "utf8");
|
|
36
|
-
await rename2(tempPath, sessionsPath);
|
|
37
|
-
}
|
|
38
|
-
async function unregisterSession(args) {
|
|
39
|
-
const { runDir, sessionId } = args;
|
|
40
|
-
const sessionsPath = getSessionsPath(runDir);
|
|
41
|
-
const current = readSessions(runDir);
|
|
42
|
-
if (!(sessionId in current)) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const { [sessionId]: _, ...rest } = current;
|
|
46
|
-
const tempPath = `${sessionsPath}.tmp.${process.pid}.${Math.random().toString(36).slice(2, 8)}`;
|
|
47
|
-
await writeFile2(tempPath, JSON.stringify(rest, null, 2), "utf8");
|
|
48
|
-
await rename2(tempPath, sessionsPath);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export {
|
|
52
|
-
getSessionsPath,
|
|
53
|
-
readSessions,
|
|
54
|
-
registerSession,
|
|
55
|
-
unregisterSession
|
|
56
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getPilotDir,
|
|
3
|
-
getPlansDir,
|
|
4
|
-
getRunDir,
|
|
5
|
-
getStateDbPath,
|
|
6
|
-
getTaskJsonlPath,
|
|
7
|
-
getWorkerJsonlPath,
|
|
8
|
-
resolveBaseDir
|
|
9
|
-
} from "./chunk-BKTFWXLG.js";
|
|
10
|
-
export {
|
|
11
|
-
getPilotDir,
|
|
12
|
-
getPlansDir,
|
|
13
|
-
getRunDir,
|
|
14
|
-
getStateDbPath,
|
|
15
|
-
getTaskJsonlPath,
|
|
16
|
-
getWorkerJsonlPath,
|
|
17
|
-
resolveBaseDir
|
|
18
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
readSessions
|
|
4
|
-
} from "../../chunk-RNRCXQ65.js";
|
|
5
|
-
|
|
6
|
-
// src/pilot/mcp/status-server.ts
|
|
7
|
-
import * as path from "path";
|
|
8
|
-
import { Database } from "bun:sqlite";
|
|
9
|
-
|
|
10
|
-
// src/pilot/mcp/throttle.ts
|
|
11
|
-
function canEmit(key, now, lastEmittedAt, minIntervalMs = 6e4) {
|
|
12
|
-
const mapKey = `${key.runId}|${key.taskId}`;
|
|
13
|
-
const last = lastEmittedAt.get(mapKey);
|
|
14
|
-
if (last === void 0) {
|
|
15
|
-
return { ok: true };
|
|
16
|
-
}
|
|
17
|
-
const elapsed = now - last;
|
|
18
|
-
if (elapsed >= minIntervalMs) {
|
|
19
|
-
return { ok: true };
|
|
20
|
-
}
|
|
21
|
-
return { ok: false, retryInMs: minIntervalMs - elapsed };
|
|
22
|
-
}
|
|
23
|
-
function recordEmission(key, now, lastEmittedAt) {
|
|
24
|
-
const mapKey = `${key.runId}|${key.taskId}`;
|
|
25
|
-
lastEmittedAt.set(mapKey, now);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// src/pilot/mcp/status-server.ts
|
|
29
|
-
var SESSIONS_PATH = process.env.PILOT_SESSIONS_PATH;
|
|
30
|
-
var STATE_DB_PATH = process.env.PILOT_STATE_DB_PATH;
|
|
31
|
-
var RUN_ID = process.env.PILOT_RUN_ID;
|
|
32
|
-
var throttleState = /* @__PURE__ */ new Map();
|
|
33
|
-
var MIN_INTERVAL_MS = 6e4;
|
|
34
|
-
var MAX_MESSAGE_LENGTH = 200;
|
|
35
|
-
if (!SESSIONS_PATH || !STATE_DB_PATH || !RUN_ID) {
|
|
36
|
-
console.error(
|
|
37
|
-
"Missing required environment variables: PILOT_SESSIONS_PATH, PILOT_STATE_DB_PATH, PILOT_RUN_ID"
|
|
38
|
-
);
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}
|
|
41
|
-
function sendResponse(resp) {
|
|
42
|
-
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
43
|
-
}
|
|
44
|
-
function sendError(id, code, message, data) {
|
|
45
|
-
sendResponse({
|
|
46
|
-
jsonrpc: "2.0",
|
|
47
|
-
id,
|
|
48
|
-
error: { code, message, data }
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
function sendResult(id, result) {
|
|
52
|
-
sendResponse({
|
|
53
|
-
jsonrpc: "2.0",
|
|
54
|
-
id,
|
|
55
|
-
result
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
function handleStatusUpdate(requestId, params) {
|
|
59
|
-
const sessionId = params._meta?.sessionId;
|
|
60
|
-
const message = params.arguments?.message;
|
|
61
|
-
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
62
|
-
sendError(requestId, -32602, "Invalid params: missing or invalid sessionId in _meta");
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (typeof message !== "string" || message.length === 0) {
|
|
66
|
-
sendError(requestId, -32602, "Invalid params: message must be a non-empty string");
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (message.length > MAX_MESSAGE_LENGTH) {
|
|
70
|
-
sendError(
|
|
71
|
-
requestId,
|
|
72
|
-
-32602,
|
|
73
|
-
`Invalid params: message exceeds ${MAX_MESSAGE_LENGTH} characters`
|
|
74
|
-
);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const runDir = path.dirname(SESSIONS_PATH);
|
|
78
|
-
const registry = readSessions(runDir);
|
|
79
|
-
const session = registry[sessionId];
|
|
80
|
-
if (!session) {
|
|
81
|
-
sendError(
|
|
82
|
-
requestId,
|
|
83
|
-
-32e3,
|
|
84
|
-
"Unknown session: session not found in registry",
|
|
85
|
-
{ sessionId }
|
|
86
|
-
);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
if (session.runId !== RUN_ID) {
|
|
90
|
-
sendError(
|
|
91
|
-
requestId,
|
|
92
|
-
-32e3,
|
|
93
|
-
"Session belongs to a different run",
|
|
94
|
-
{ sessionId, expectedRunId: RUN_ID, actualRunId: session.runId }
|
|
95
|
-
);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const now = Date.now();
|
|
99
|
-
const throttleKey = { runId: session.runId, taskId: session.taskId };
|
|
100
|
-
const throttleResult = canEmit(throttleKey, now, throttleState, MIN_INTERVAL_MS);
|
|
101
|
-
if (!throttleResult.ok) {
|
|
102
|
-
sendResult(requestId, {
|
|
103
|
-
success: false,
|
|
104
|
-
throttled: true,
|
|
105
|
-
retryInMs: throttleResult.retryInMs,
|
|
106
|
-
message: `Throttled: try again in ${Math.ceil(throttleResult.retryInMs / 1e3)}s`
|
|
107
|
-
});
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
try {
|
|
111
|
-
const db = new Database(STATE_DB_PATH, { readwrite: true });
|
|
112
|
-
try {
|
|
113
|
-
const ts = now;
|
|
114
|
-
const payload = { message, sessionId };
|
|
115
|
-
db.run(
|
|
116
|
-
`INSERT INTO events (run_id, task_id, ts, kind, payload) VALUES (?, ?, ?, ?, ?)`,
|
|
117
|
-
[session.runId, session.taskId, ts, "task.progress", JSON.stringify(payload)]
|
|
118
|
-
);
|
|
119
|
-
} finally {
|
|
120
|
-
db.close();
|
|
121
|
-
}
|
|
122
|
-
recordEmission(throttleKey, now, throttleState);
|
|
123
|
-
sendResult(requestId, {
|
|
124
|
-
success: true,
|
|
125
|
-
throttled: false,
|
|
126
|
-
taskId: session.taskId
|
|
127
|
-
});
|
|
128
|
-
} catch (err) {
|
|
129
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
130
|
-
sendError(
|
|
131
|
-
requestId,
|
|
132
|
-
-32e3,
|
|
133
|
-
`Failed to write event: ${errorMessage}`,
|
|
134
|
-
{ sessionId, taskId: session.taskId }
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
var TOOLS_LIST = {
|
|
139
|
-
tools: [
|
|
140
|
-
{
|
|
141
|
-
name: "provide_status_update",
|
|
142
|
-
description: "Emit a one-sentence progress update for the current task. Use this to inform the user what you're working on during long-running operations. Keep messages under 200 characters. Rate limited to once per 60 seconds.",
|
|
143
|
-
inputSchema: {
|
|
144
|
-
type: "object",
|
|
145
|
-
properties: {
|
|
146
|
-
message: {
|
|
147
|
-
type: "string",
|
|
148
|
-
description: "A one-sentence description of current progress (max 200 chars)"
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
required: ["message"]
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
};
|
|
156
|
-
function handleRequest(req) {
|
|
157
|
-
switch (req.method) {
|
|
158
|
-
case "initialize": {
|
|
159
|
-
sendResult(req.id, {
|
|
160
|
-
protocolVersion: "2024-11-05",
|
|
161
|
-
capabilities: {},
|
|
162
|
-
serverInfo: {
|
|
163
|
-
name: "pilot-status-server",
|
|
164
|
-
version: "0.1.0"
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
case "tools/list": {
|
|
170
|
-
sendResult(req.id, TOOLS_LIST);
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
case "tools/call": {
|
|
174
|
-
const params = req.params;
|
|
175
|
-
if (!params || typeof params.name !== "string") {
|
|
176
|
-
sendError(req.id, -32602, "Invalid params: expected { name: string, arguments?: object }");
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
if (params.name === "provide_status_update") {
|
|
180
|
-
handleStatusUpdate(req.id, params);
|
|
181
|
-
} else {
|
|
182
|
-
sendError(req.id, -32601, `Unknown tool: ${params.name}`);
|
|
183
|
-
}
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
default: {
|
|
187
|
-
sendError(req.id, -32601, `Method not found: ${req.method}`);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
var buffer = "";
|
|
192
|
-
process.stdin.setEncoding("utf8");
|
|
193
|
-
process.stdin.on("data", (chunk) => {
|
|
194
|
-
buffer += chunk;
|
|
195
|
-
let newlineIndex;
|
|
196
|
-
while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
|
|
197
|
-
const line = buffer.slice(0, newlineIndex).trim();
|
|
198
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
199
|
-
if (line.length === 0) continue;
|
|
200
|
-
try {
|
|
201
|
-
const req = JSON.parse(line);
|
|
202
|
-
if (req.jsonrpc !== "2.0") {
|
|
203
|
-
sendError(req.id ?? null, -32600, "Invalid Request: jsonrpc must be '2.0'");
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
handleRequest(req);
|
|
207
|
-
} catch {
|
|
208
|
-
sendError(null, -32700, "Parse error: invalid JSON");
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
process.stdin.on("end", () => {
|
|
213
|
-
if (buffer.trim().length > 0) {
|
|
214
|
-
try {
|
|
215
|
-
const req = JSON.parse(buffer.trim());
|
|
216
|
-
if (req.jsonrpc === "2.0") {
|
|
217
|
-
handleRequest(req);
|
|
218
|
-
}
|
|
219
|
-
} catch {
|
|
220
|
-
sendError(null, -32700, "Parse error: invalid JSON");
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
process.exit(0);
|
|
224
|
-
});
|
|
225
|
-
process.stdin.on("error", (err) => {
|
|
226
|
-
console.error("stdin error:", err);
|
|
227
|
-
process.exit(1);
|
|
228
|
-
});
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// src/pilot/worker/pilot-config.ts
|
|
2
|
-
import { promises as fs } from "fs";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
var PILOT_JSON_FILENAME = ".glrs/pilot.json";
|
|
5
|
-
var EMPTY_CONFIG = {
|
|
6
|
-
baseline: [],
|
|
7
|
-
after_each: []
|
|
8
|
-
};
|
|
9
|
-
async function loadPilotConfig(cwd) {
|
|
10
|
-
const filePath = path.join(cwd, PILOT_JSON_FILENAME);
|
|
11
|
-
let raw;
|
|
12
|
-
try {
|
|
13
|
-
raw = await fs.readFile(filePath, "utf8");
|
|
14
|
-
} catch {
|
|
15
|
-
return EMPTY_CONFIG;
|
|
16
|
-
}
|
|
17
|
-
let parsed;
|
|
18
|
-
try {
|
|
19
|
-
parsed = JSON.parse(raw);
|
|
20
|
-
} catch (err) {
|
|
21
|
-
throw new Error(
|
|
22
|
-
`${PILOT_JSON_FILENAME}: invalid JSON \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
`${PILOT_JSON_FILENAME}: expected a JSON object, got ${typeof parsed}`
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
const obj = parsed;
|
|
31
|
-
const baseline = parseStringArray(obj, "baseline");
|
|
32
|
-
const after_each = parseStringArray(obj, "after_each");
|
|
33
|
-
return { baseline, after_each };
|
|
34
|
-
}
|
|
35
|
-
function parseStringArray(obj, key) {
|
|
36
|
-
const val = obj[key];
|
|
37
|
-
if (val === void 0 || val === null) return [];
|
|
38
|
-
if (!Array.isArray(val)) {
|
|
39
|
-
throw new Error(
|
|
40
|
-
`${PILOT_JSON_FILENAME}: "${key}" must be an array of strings, got ${typeof val}`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
for (let i = 0; i < val.length; i++) {
|
|
44
|
-
if (typeof val[i] !== "string" || val[i].length === 0) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
`${PILOT_JSON_FILENAME}: "${key}[${i}]" must be a non-empty string`
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return val;
|
|
51
|
-
}
|
|
52
|
-
export {
|
|
53
|
-
PILOT_JSON_FILENAME,
|
|
54
|
-
loadPilotConfig
|
|
55
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createRun,
|
|
3
|
-
getRun,
|
|
4
|
-
latestRun,
|
|
5
|
-
listRuns,
|
|
6
|
-
markRunFinished,
|
|
7
|
-
markRunResumed,
|
|
8
|
-
markRunRunning
|
|
9
|
-
} from "./chunk-5TAMY7P6.js";
|
|
10
|
-
export {
|
|
11
|
-
createRun,
|
|
12
|
-
getRun,
|
|
13
|
-
latestRun,
|
|
14
|
-
listRuns,
|
|
15
|
-
markRunFinished,
|
|
16
|
-
markRunResumed,
|
|
17
|
-
markRunRunning
|
|
18
|
-
};
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
// src/pilot/worker/setup-hook.ts
|
|
2
|
-
import { spawn } from "child_process";
|
|
3
|
-
import { promises as fs } from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
var SETUP_HOOK_RELATIVE_PATH = ".glrs/hooks/pilot_setup";
|
|
6
|
-
var SETUP_HOOK_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
7
|
-
async function runSetupHook(args) {
|
|
8
|
-
const hookPath = path.join(args.cwd, SETUP_HOOK_RELATIVE_PATH);
|
|
9
|
-
const onLine = args.onLine ?? ((chunk) => void process.stderr.write(chunk));
|
|
10
|
-
const timeoutMs = args.timeoutMs ?? SETUP_HOOK_TIMEOUT_MS;
|
|
11
|
-
let stat;
|
|
12
|
-
try {
|
|
13
|
-
stat = await fs.stat(hookPath);
|
|
14
|
-
} catch {
|
|
15
|
-
return { kind: "skipped" };
|
|
16
|
-
}
|
|
17
|
-
if (process.platform !== "win32") {
|
|
18
|
-
const mode = stat.mode;
|
|
19
|
-
const executable = (mode & 73) !== 0;
|
|
20
|
-
if (!executable) {
|
|
21
|
-
return { kind: "not-executable", hookPath };
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
const start = Date.now();
|
|
25
|
-
return await new Promise((resolve) => {
|
|
26
|
-
const child = spawn(hookPath, [], {
|
|
27
|
-
cwd: args.cwd,
|
|
28
|
-
env: process.env,
|
|
29
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
30
|
-
});
|
|
31
|
-
let timedOut = false;
|
|
32
|
-
const timer = setTimeout(() => {
|
|
33
|
-
timedOut = true;
|
|
34
|
-
try {
|
|
35
|
-
child.kill("SIGTERM");
|
|
36
|
-
} catch {
|
|
37
|
-
}
|
|
38
|
-
setTimeout(() => {
|
|
39
|
-
try {
|
|
40
|
-
child.kill("SIGKILL");
|
|
41
|
-
} catch {
|
|
42
|
-
}
|
|
43
|
-
}, 500);
|
|
44
|
-
}, timeoutMs);
|
|
45
|
-
child.stdout?.on("data", (buf) => onLine(buf.toString()));
|
|
46
|
-
child.stderr?.on("data", (buf) => onLine(buf.toString()));
|
|
47
|
-
child.on("error", (err) => {
|
|
48
|
-
clearTimeout(timer);
|
|
49
|
-
resolve({
|
|
50
|
-
kind: "spawn-error",
|
|
51
|
-
hookPath,
|
|
52
|
-
error: err.message
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
child.on("close", (code, signal) => {
|
|
56
|
-
clearTimeout(timer);
|
|
57
|
-
const durationMs = Date.now() - start;
|
|
58
|
-
if (timedOut) {
|
|
59
|
-
resolve({ kind: "timed-out", hookPath, timeoutMs });
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (signal !== null && signal !== void 0) {
|
|
63
|
-
resolve({
|
|
64
|
-
kind: "failed",
|
|
65
|
-
hookPath,
|
|
66
|
-
exitCode: -1,
|
|
67
|
-
durationMs
|
|
68
|
-
});
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
if (code !== 0) {
|
|
72
|
-
resolve({
|
|
73
|
-
kind: "failed",
|
|
74
|
-
hookPath,
|
|
75
|
-
exitCode: code ?? -1,
|
|
76
|
-
durationMs
|
|
77
|
-
});
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
resolve({ kind: "ok", durationMs });
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
export {
|
|
85
|
-
SETUP_HOOK_RELATIVE_PATH,
|
|
86
|
-
SETUP_HOOK_TIMEOUT_MS,
|
|
87
|
-
runSetupHook
|
|
88
|
-
};
|