chore: archive AgentDock v1 implementation
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import fs from "node:fs";
|
||||
import fsp from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
const MAX_BUFFER_BYTES = 2 * 1024 * 1024;
|
||||
|
||||
export class SessionStore {
|
||||
constructor(directory) {
|
||||
this.directory = directory;
|
||||
this.sessions = new Map();
|
||||
}
|
||||
|
||||
async init() {
|
||||
await fsp.mkdir(this.directory, { recursive: true, mode: 0o700 });
|
||||
const files = await fsp.readdir(this.directory);
|
||||
for (const file of files.filter((name) => name.endsWith(".json"))) {
|
||||
try {
|
||||
const record = JSON.parse(await fsp.readFile(path.join(this.directory, file), "utf8"));
|
||||
if (record.status === "running") {
|
||||
record.status = "interrupted";
|
||||
record.endedAt = new Date().toISOString();
|
||||
}
|
||||
this.sessions.set(record.id, { ...record, archived: true, clients: new Set(), buffer: "" });
|
||||
} catch {
|
||||
// A partial metadata file must not prevent the runner from starting.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(record) {
|
||||
const session = { ...record, clients: new Set(), buffer: "", errorBuffer: "", archived: false };
|
||||
this.sessions.set(session.id, session);
|
||||
this.persist(session);
|
||||
return session;
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return this.sessions.get(id);
|
||||
}
|
||||
|
||||
publicRecord(session) {
|
||||
const {
|
||||
clients,
|
||||
pty,
|
||||
childProcess,
|
||||
buffer,
|
||||
errorBuffer,
|
||||
logStream,
|
||||
environment,
|
||||
archived,
|
||||
...record
|
||||
} = session;
|
||||
return { ...record, connectedClients: clients?.size || 0, archived: Boolean(archived) };
|
||||
}
|
||||
|
||||
list() {
|
||||
return [...this.sessions.values()]
|
||||
.map((session) => this.publicRecord(session))
|
||||
.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
||||
}
|
||||
|
||||
append(session, data) {
|
||||
session.buffer += data;
|
||||
if (Buffer.byteLength(session.buffer) > MAX_BUFFER_BYTES) {
|
||||
session.buffer = session.buffer.slice(-MAX_BUFFER_BYTES);
|
||||
}
|
||||
if (!session.logStream) {
|
||||
session.logStream = fs.createWriteStream(path.join(this.directory, `${session.id}.log`), {
|
||||
flags: "a",
|
||||
mode: 0o600,
|
||||
});
|
||||
}
|
||||
session.logStream.write(data);
|
||||
}
|
||||
|
||||
async readOutput(session) {
|
||||
if (session.buffer) return session.buffer;
|
||||
try {
|
||||
return await fsp.readFile(path.join(this.directory, `${session.id}.log`), "utf8");
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
finish(session, status, exitCode = null, signal = null) {
|
||||
session.status = status;
|
||||
session.exitCode = exitCode;
|
||||
session.signal = signal;
|
||||
session.endedAt = new Date().toISOString();
|
||||
session.logStream?.end();
|
||||
session.logStream = undefined;
|
||||
this.persist(session);
|
||||
}
|
||||
|
||||
persist(session) {
|
||||
const target = path.join(this.directory, `${session.id}.json`);
|
||||
const temp = `${target}.tmp`;
|
||||
const body = `${JSON.stringify(this.publicRecord(session), null, 2)}\n`;
|
||||
fsp.writeFile(temp, body, { mode: 0o600 })
|
||||
.then(() => fsp.rename(temp, target))
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user