Turn off no-await-in-loop, consistent-return and switch-exhaustiveness-check, turn on no-array-reduce, and restore the plain loops
This commit is contained in:
@@ -247,8 +247,6 @@ const rules: DummyRuleMap = {
|
||||
// Collides with explicit-types `no-undefined-type`: a `let` that `try`
|
||||
// assigns would need `T | undefined`.
|
||||
"init-declarations": "off",
|
||||
// Collides with `no-await-in-loop`: sequential steps chain through `reduce`.
|
||||
"unicorn/no-array-reduce": "off",
|
||||
// Collides with literal Gitea Actions `${{ }}` expressions in workflow strings.
|
||||
"no-template-curly-in-string": "off",
|
||||
// Node code, everywhere: this action runs on Node.
|
||||
@@ -264,6 +262,12 @@ const rules: DummyRuleMap = {
|
||||
// PENDING USER: a fitting `name` needs a constructor and changes logged
|
||||
// error names.
|
||||
"unicorn/custom-error-definition": "off",
|
||||
// Sequential awaits are intentional; workarounds cost more than they save.
|
||||
"no-await-in-loop": "off",
|
||||
// Not type-aware; TypeScript's `noImplicitReturns` covers it.
|
||||
"consistent-return": "off",
|
||||
// TypeScript's type checking covers exhaustive switches.
|
||||
"typescript/switch-exhaustiveness-check": "off",
|
||||
// Off, too much churn:
|
||||
// Literal numbers read better than a constant per use.
|
||||
"no-magic-numbers": "off",
|
||||
|
||||
+16
-27
@@ -53,37 +53,26 @@ export function failureFooter(failures: ProfileFailure[]): string {
|
||||
.join(", ")}`;
|
||||
}
|
||||
|
||||
// Tries the profiles from `index` on, moving on only after a sign-in failure
|
||||
// or exhausted usage, and records every profile it tried.
|
||||
async function attemptFrom<T, P extends ProfileIdentity>(
|
||||
profiles: P[],
|
||||
attempt: (profile: P) => Promise<T>,
|
||||
failures: ProfileFailure[],
|
||||
index: number,
|
||||
): Promise<T> {
|
||||
const profile = profiles[index];
|
||||
if (profile === undefined) {
|
||||
throw new Error("profiles must contain at least one profile");
|
||||
}
|
||||
try {
|
||||
return await attempt(profile);
|
||||
} catch (error) {
|
||||
const reason = failureReason(error);
|
||||
failures.push({ type: profile.type, model: profile.model, reason });
|
||||
if (reason === "failed" || index === profiles.length - 1) {
|
||||
throw error;
|
||||
}
|
||||
console.error(`${profile.type} ${reason}; trying next profile`);
|
||||
return attemptFrom(profiles, attempt, failures, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
export function runProfiles<T, P extends ProfileIdentity>(
|
||||
// Tries the profiles in order, moving on only after a sign-in failure or
|
||||
// exhausted usage, and records every profile it tried.
|
||||
export async function runProfiles<T, P extends ProfileIdentity>(
|
||||
profiles: P[],
|
||||
attempt: (profile: P) => Promise<T>,
|
||||
failures: ProfileFailure[] = [],
|
||||
): Promise<T> {
|
||||
return attemptFrom(profiles, attempt, failures, 0);
|
||||
for (const [index, profile] of profiles.entries()) {
|
||||
try {
|
||||
return await attempt(profile);
|
||||
} catch (error) {
|
||||
const reason = failureReason(error);
|
||||
failures.push({ type: profile.type, model: profile.model, reason });
|
||||
if (reason === "failed" || index === profiles.length - 1) {
|
||||
throw error;
|
||||
}
|
||||
console.error(`${profile.type} ${reason}; trying next profile`);
|
||||
}
|
||||
}
|
||||
throw new Error("profiles must contain at least one profile");
|
||||
}
|
||||
|
||||
// Environments are maps from variable name to value, converted to the object
|
||||
|
||||
+6
-16
@@ -283,21 +283,6 @@ async function runClaude(profile: RuntimeProfile, token: string, prompt: string)
|
||||
return result.result;
|
||||
}
|
||||
|
||||
// Polls every 100 ms until the login prints something, failing if it exits first.
|
||||
async function pollForInstructions(
|
||||
shownText: () => string,
|
||||
status: Promise<number>,
|
||||
): Promise<void> {
|
||||
if (shownText().trim() !== "") {
|
||||
return;
|
||||
}
|
||||
const exited = await Promise.race([status.then(() => true), sleep(100, false)]);
|
||||
if (exited) {
|
||||
throw new AgentAuthError("AI bot login produced no instructions");
|
||||
}
|
||||
await pollForInstructions(shownText, status);
|
||||
}
|
||||
|
||||
async function codexDeviceLogin(
|
||||
profile: RuntimeProfile,
|
||||
environment: ReadonlyMap<string, string>,
|
||||
@@ -319,7 +304,12 @@ async function codexDeviceLogin(
|
||||
};
|
||||
const drained = Promise.all([collect(login.stdout), collect(login.stderr)]);
|
||||
await sleep(3000);
|
||||
await pollForInstructions(() => shown, status);
|
||||
while (shown.trim() === "") {
|
||||
const exited = await Promise.race([status.then(() => true), sleep(100, false)]);
|
||||
if (exited) {
|
||||
throw new AgentAuthError("AI bot login produced no instructions");
|
||||
}
|
||||
}
|
||||
await postAuthorComment(withProfile(shown, profile));
|
||||
const [code] = await Promise.all([status, drained]);
|
||||
if (code !== 0) {
|
||||
|
||||
+22
-26
@@ -61,32 +61,28 @@ export async function prepareRuntime(
|
||||
await writePrivate(reviewerFile, credentials.reviewerToken, 0, 0);
|
||||
|
||||
// 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) => {
|
||||
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([]),
|
||||
);
|
||||
const profiles: RuntimeProfile[] = [];
|
||||
for (const [index, profile] of credentials.profiles.entries()) {
|
||||
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);
|
||||
}
|
||||
profiles.push({
|
||||
index,
|
||||
type: profile.type,
|
||||
model: profile.model,
|
||||
uid,
|
||||
gid: uid,
|
||||
home,
|
||||
tokenFile,
|
||||
});
|
||||
}
|
||||
|
||||
const manifestPath = join(runtimeDir, "manifest.json");
|
||||
const manifest: RuntimeManifest = {
|
||||
|
||||
Reference in New Issue
Block a user