Document that package.json aliases may target only direct subfolders of src #21

Merged
temeddix merged 27 commits from alias-targets into main 2026-10-18 14:37:50 +00:00
5 changed files with 40 additions and 59 deletions
Showing only changes of commit cd1864e1a9 - Show all commits
+5 -10
View File
@@ -60,15 +60,6 @@ export async function retryInvalidToken<T extends { status: { success: boolean }
return result;
}
function hasCodexTokens(tokens: unknown): boolean {
return (
isObject(tokens) &&
["access_token", "refresh_token", "id_token", "account_id"].every(
(name) => typeof tokens[name] === "string" && tokens[name] !== "",
)
);
}
async function readCodexAuth(path: string): Promise<string> {
const stat = await lstat(path);
if (!stat.isFile() || stat.isSymbolicLink()) {
@@ -84,11 +75,15 @@ async function readCodexAuth(path: string): Promise<string> {
} catch {
throw new Error("invalid Codex login");
}
const tokens = isObject(value) ? value.tokens : undefined;
if (
!isObject(value) ||
typeof value.auth_mode !== "string" ||
value.auth_mode === "" ||
!hasCodexTokens(value.tokens)
!isObject(tokens) ||
!["access_token", "refresh_token", "id_token", "account_id"].every(
(name) => typeof tokens[name] === "string" && tokens[name] !== "",
)
) {
throw new Error("invalid Codex login");
}
+5 -11
View File
@@ -49,16 +49,6 @@ async function profileProcessIds(uid: number): Promise<number[]> {
.map(Number);
}
function killProcess(pid: number): void {
try {
process.kill(pid, "SIGKILL");
} catch (error) {
if (!isNotFound(error)) {
throw error;
}
}
}
// Kills the UID's processes every 10 ms until none is left, for up to 100 rounds.
export async function killProfileProcesses(uid: number, attempt = 0): Promise<void> {
if (attempt === 100) {
@@ -69,7 +59,11 @@ export async function killProfileProcesses(uid: number, attempt = 0): Promise<vo
return;
}
for (const pid of ids) {
killProcess(pid);
try {
process.kill(pid, "SIGKILL");
} catch (error) {
if (!isNotFound(error)) throw error;
}
}
await sleep(10);
await killProfileProcesses(uid, attempt + 1);
+6 -2
View File
@@ -5,8 +5,8 @@ import process from "node:process";
import { isNotFound } from "./errors.ts";
import { isObject } from "./json.ts";
import { failureFooter, isFailureReason, isProfileType, withProfile } from "./profiles.ts";
import type { ProfileFailure } from "./profiles.ts";
import { failureFooter, withProfile } from "./profiles.ts";
import type { FailureReason, ProfileFailure } from "./profiles.ts";
import { readRuntimeManifest } from "./runtime.ts";
import type { RuntimeManifest, RuntimeOutcome } from "./runtime.ts";
@@ -30,6 +30,10 @@ const VERDICTS: Record<string, [event: string, mark: string]> = {
Approved: ["APPROVED", "✅"],
"Changes requested": ["REQUEST_CHANGES", "🛑"],
};
const PROFILE_TYPES: Set<unknown> = new Set(["claude", "codex", "opencode"]);
const FAILURE_REASONS: Set<unknown> = new Set(["sign-in failed", "usage exhausted", "failed"]);
const isProfileType = (value: unknown): value is ProfileFailure["type"] => PROFILE_TYPES.has(value);
const isFailureReason = (value: unknown): value is FailureReason => FAILURE_REASONS.has(value);
const failureComment = (failures: ProfileFailure[]): string =>
`AI bot failed.\n\n${failureFooter(failures)}`;
+1 -14
View File
@@ -1,18 +1,9 @@
export type ProfileType = "claude" | "codex" | "opencode";
export interface BotProfile {
type: ProfileType;
type: "claude" | "codex" | "opencode";
model: string;
token?: string;
}
const PROFILE_TYPES: Set<unknown> = new Set(["claude", "codex", "opencode"]);
const FAILURE_REASONS: Set<unknown> = new Set(["sign-in failed", "usage exhausted", "failed"]);
export function isProfileType(value: unknown): value is ProfileType {
return PROFILE_TYPES.has(value);
}
export function profileFooter(profile: Pick<BotProfile, "type" | "model">): string {
return `> Profile: \`${profile.type}\` · \`${profile.model}\``;
}
@@ -23,10 +14,6 @@ export function withProfile(body: string, profile: Pick<BotProfile, "type" | "mo
export type FailureReason = "sign-in failed" | "usage exhausted" | "failed";
export function isFailureReason(value: unknown): value is FailureReason {
return FAILURE_REASONS.has(value);
}
export type ProfileFailure = Pick<BotProfile, "type" | "model"> & {
reason: FailureReason;
};
+23 -22
View File
@@ -39,24 +39,6 @@ async function writePrivate(path: string, value: string, uid: number, gid: numbe
await chown(path, uid, gid);
}
async function prepareProfile(
runtimeDir: string,
profile: BotProfile,
index: number,
): Promise<RuntimeProfile> {
const uid = FIRST_PROFILE_UID + index;
const home = join(runtimeDir, `profile-${index}`);
await mkdir(home, { mode: 0o700 });
await chmod(home, 0o700);
await chown(home, uid, uid);
const tokenFile =
profile.token === undefined || profile.token === "" ? null : join(home, "token");
if (tokenFile !== null && profile.token !== undefined) {
await writePrivate(tokenFile, profile.token, uid, uid);
}
return { index, type: profile.type, model: profile.model, uid, gid: uid, home, tokenFile };
}
export async function prepareRuntime(
actionPath: string,
runtimeRoot: string,
@@ -74,10 +56,29 @@ export async function prepareRuntime(
// One profile at a time, in order, as each claims the next UID.
const profiles = await credentials.profiles.reduce<Promise<RuntimeProfile[]>>(
async (previous, profile, index) => [
...(await previous),
await prepareProfile(runtimeDir, profile, index),
],
async (previous, profile, index) => {
const prepared = await previous;
const uid = FIRST_PROFILE_UID + index;
const home = join(runtimeDir, `profile-${index}`);
await mkdir(home, { mode: 0o700 });
await chmod(home, 0o700);
await chown(home, uid, uid);
const tokenFile =
profile.token === undefined || profile.token === "" ? null : join(home, "token");
if (tokenFile !== null && profile.token !== undefined) {
await writePrivate(tokenFile, profile.token, uid, uid);
}
prepared.push({
index,
type: profile.type,
model: profile.model,
uid,
gid: uid,
home,
tokenFile,
});
return prepared;
},
Promise.resolve([]),
);