auto-mode 1.0.1 → 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/dist/cli.js +150 -12
- package/dist/index.d.ts +20 -1
- package/dist/index.js +1 -1
- package/dist/parse-action-request-DtReuNOc.js +5160 -0
- package/dist/{parse-action-request-BfkmQxEC.js → schemas-Dc2424NY.js} +83 -5238
- package/dist/start-judge-server-Drj5c8js.js +169 -0
- package/dist/start-judge-session-9R_bGhxY.js +37956 -0
- package/dist/start-judge-session-Ws6eqDt4.js +2 -0
- package/package.json +2 -1
- package/policy/containment.md +3 -3
- package/policy/judge.md +5 -12
- package/policy/classifier.md +0 -282
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { d as string, s as number, u as strictObject } from "./schemas-Dc2424NY.js";
|
|
2
|
+
import { t as startJudgeSession } from "./start-judge-session-9R_bGhxY.js";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { chmod, mkdir, rename, rm, stat } from "node:fs/promises";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { connect, createServer } from "node:net";
|
|
7
|
+
//#region src/judge/judge-server-request-schema.ts
|
|
8
|
+
const judgeServerRequestSchema = strictObject({
|
|
9
|
+
model: string().min(1),
|
|
10
|
+
timeoutMs: number().int().positive(),
|
|
11
|
+
system: string(),
|
|
12
|
+
user: string()
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/judge/start-judge-server.ts
|
|
16
|
+
async function startJudgeServer(options) {
|
|
17
|
+
await mkdir(dirname(options.socketPath), {
|
|
18
|
+
recursive: true,
|
|
19
|
+
mode: 448
|
|
20
|
+
});
|
|
21
|
+
if (await isAnswering(options.socketPath)) return null;
|
|
22
|
+
const server = createServer();
|
|
23
|
+
const session = startJudgeSession(options.env);
|
|
24
|
+
const closed = Promise.withResolvers();
|
|
25
|
+
let ownIno = null;
|
|
26
|
+
let idleTimer = null;
|
|
27
|
+
let active = 0;
|
|
28
|
+
let stopped = false;
|
|
29
|
+
const stop = () => {
|
|
30
|
+
if (stopped) return;
|
|
31
|
+
stopped = true;
|
|
32
|
+
if (idleTimer !== null) clearTimeout(idleTimer);
|
|
33
|
+
session.stop();
|
|
34
|
+
server.close(() => {
|
|
35
|
+
removeOwnSocket(options.socketPath, ownIno, closed.resolve);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
const waitIdle = () => {
|
|
39
|
+
idleTimer = setTimeout(stop, options.idleMs);
|
|
40
|
+
idleTimer.unref();
|
|
41
|
+
};
|
|
42
|
+
const runConnection = async (socket) => {
|
|
43
|
+
active += 1;
|
|
44
|
+
if (idleTimer !== null) {
|
|
45
|
+
clearTimeout(idleTimer);
|
|
46
|
+
idleTimer = null;
|
|
47
|
+
}
|
|
48
|
+
await runReview(socket, session);
|
|
49
|
+
active -= 1;
|
|
50
|
+
if (active === 0) waitIdle();
|
|
51
|
+
};
|
|
52
|
+
server.on("connection", (socket) => {
|
|
53
|
+
runConnection(socket);
|
|
54
|
+
});
|
|
55
|
+
const listenPath = join(dirname(options.socketPath), `j-${randomBytes(4).toString("hex")}`);
|
|
56
|
+
await startListening(server, listenPath);
|
|
57
|
+
await chmod(listenPath, 384);
|
|
58
|
+
const own = await stat(listenPath);
|
|
59
|
+
ownIno = own.ino;
|
|
60
|
+
await rename(listenPath, options.socketPath);
|
|
61
|
+
if ((await stat(options.socketPath).catch(() => null))?.ino !== own.ino) {
|
|
62
|
+
stop();
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
waitIdle();
|
|
66
|
+
return {
|
|
67
|
+
closed: closed.promise,
|
|
68
|
+
stop
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
async function removeOwnSocket(socketPath, ino, onRemoved) {
|
|
72
|
+
const current = await stat(socketPath).catch(() => null);
|
|
73
|
+
if (ino !== null && current?.ino === ino) await rm(socketPath, { force: true });
|
|
74
|
+
onRemoved();
|
|
75
|
+
}
|
|
76
|
+
function startListening(server, socketPath) {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
server.once("error", reject);
|
|
79
|
+
server.listen(socketPath, () => {
|
|
80
|
+
server.off("error", reject);
|
|
81
|
+
resolve();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function isAnswering(socketPath) {
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
const probe = connect(socketPath);
|
|
88
|
+
probe.once("connect", () => {
|
|
89
|
+
probe.destroy();
|
|
90
|
+
resolve(true);
|
|
91
|
+
});
|
|
92
|
+
probe.once("error", () => {
|
|
93
|
+
resolve(false);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const MAX_REQUEST_BYTES = 4194304;
|
|
98
|
+
async function runReview(socket, session) {
|
|
99
|
+
const controller = new AbortController();
|
|
100
|
+
let answered = false;
|
|
101
|
+
const stopUnanswered = () => {
|
|
102
|
+
if (!answered) controller.abort();
|
|
103
|
+
};
|
|
104
|
+
socket.on("end", stopUnanswered);
|
|
105
|
+
socket.on("close", stopUnanswered);
|
|
106
|
+
socket.on("error", () => {});
|
|
107
|
+
const line = await readLine(socket, MAX_REQUEST_BYTES);
|
|
108
|
+
const request = judgeServerRequestSchema.safeParse(parseJSON(line));
|
|
109
|
+
if (!request.success) {
|
|
110
|
+
answered = true;
|
|
111
|
+
writeResponse(socket, {
|
|
112
|
+
kind: "failure",
|
|
113
|
+
aborted: false,
|
|
114
|
+
message: "malformed request"
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(request.data.timeoutMs)]);
|
|
119
|
+
let response;
|
|
120
|
+
try {
|
|
121
|
+
response = {
|
|
122
|
+
kind: "reply",
|
|
123
|
+
...await session.review(request.data.model, {
|
|
124
|
+
system: request.data.system,
|
|
125
|
+
user: request.data.user
|
|
126
|
+
}, signal)
|
|
127
|
+
};
|
|
128
|
+
} catch (error) {
|
|
129
|
+
response = {
|
|
130
|
+
kind: "failure",
|
|
131
|
+
aborted: signal.aborted,
|
|
132
|
+
message: error instanceof Error ? error.message : String(error)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
answered = true;
|
|
136
|
+
writeResponse(socket, response);
|
|
137
|
+
}
|
|
138
|
+
function readLine(socket, maxBytes) {
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
const chunks = [];
|
|
141
|
+
let size = 0;
|
|
142
|
+
const stopReading = () => {
|
|
143
|
+
socket.off("data", onData);
|
|
144
|
+
socket.off("end", stopReading);
|
|
145
|
+
socket.off("close", stopReading);
|
|
146
|
+
resolve(Buffer.concat(chunks).toString("utf8").split("\n")[0] ?? "");
|
|
147
|
+
};
|
|
148
|
+
const onData = (chunk) => {
|
|
149
|
+
chunks.push(chunk);
|
|
150
|
+
size += chunk.length;
|
|
151
|
+
if (size > maxBytes || chunk.includes(10)) stopReading();
|
|
152
|
+
};
|
|
153
|
+
socket.on("data", onData);
|
|
154
|
+
socket.once("end", stopReading);
|
|
155
|
+
socket.once("close", stopReading);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function writeResponse(socket, response) {
|
|
159
|
+
if (!socket.destroyed) socket.end(`${JSON.stringify(response)}\n`);
|
|
160
|
+
}
|
|
161
|
+
function parseJSON(text) {
|
|
162
|
+
try {
|
|
163
|
+
return JSON.parse(text);
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
export { startJudgeServer };
|