Centralize encrypted bot profiles
Check / deno (pull_request) Failing after 42s

This commit is contained in:
Danny Kim
2026-09-24 04:56:11 +09:00
parent ba9e0c0787
commit 80f3d922d1
22 changed files with 1973 additions and 526 deletions
+60
View File
@@ -0,0 +1,60 @@
import type { RuntimeProfile } from "./runtime.ts";
export function agentCommand(
profile: RuntimeProfile,
command: string,
args: string[],
options: Omit<Deno.CommandOptions, "args" | "uid" | "gid"> = {},
): Deno.Command {
return new Deno.Command(command, {
...options,
args,
uid: profile.uid,
gid: profile.gid,
});
}
async function profileProcessIds(uid: number): Promise<number[]> {
const ids: number[] = [];
for await (const entry of Deno.readDir("/proc")) {
if (!entry.isDirectory || !/^\d+$/.test(entry.name)) continue;
const status = await Deno.readTextFile(`/proc/${entry.name}/status`).catch(
() => null,
);
const owner = status?.match(/^Uid:\s+(\d+)/m)?.[1];
if (owner !== undefined && Number(owner) === uid) {
ids.push(Number(entry.name));
}
}
return ids;
}
export async function killProfileProcesses(uid: number): Promise<void> {
for (let attempt = 0; attempt < 100; attempt++) {
const ids = await profileProcessIds(uid);
if (ids.length === 0) return;
for (const pid of ids) {
try {
Deno.kill(pid, "SIGKILL");
} catch (error) {
if (!(error instanceof Deno.errors.NotFound)) throw error;
}
}
await new Promise((resolve) => setTimeout(resolve, 10));
}
throw new Error(`profile UID ${uid} still owns processes after cleanup`);
}
export async function readProfileFile(
profile: RuntimeProfile,
path: string,
): Promise<string> {
const stat = await Deno.lstat(path);
if (
!stat.isFile || stat.isSymlink || stat.uid !== profile.uid ||
(stat.mode! & 0o077) !== 0
) {
throw new Error(`${path}: expected a profile-owned regular private file`);
}
return await Deno.readTextFile(path);
}