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`
|
// Collides with explicit-types `no-undefined-type`: a `let` that `try`
|
||||||
// assigns would need `T | undefined`.
|
// assigns would need `T | undefined`.
|
||||||
"init-declarations": "off",
|
"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.
|
// Collides with literal Gitea Actions `${{ }}` expressions in workflow strings.
|
||||||
"no-template-curly-in-string": "off",
|
"no-template-curly-in-string": "off",
|
||||||
// Node code, everywhere: this action runs on Node.
|
// 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
|
// PENDING USER: a fitting `name` needs a constructor and changes logged
|
||||||
// error names.
|
// error names.
|
||||||
"unicorn/custom-error-definition": "off",
|
"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:
|
// Off, too much churn:
|
||||||
// Literal numbers read better than a constant per use.
|
// Literal numbers read better than a constant per use.
|
||||||
"no-magic-numbers": "off",
|
"no-magic-numbers": "off",
|
||||||
|
|||||||
+16
-27
@@ -53,37 +53,26 @@ export function failureFooter(failures: ProfileFailure[]): string {
|
|||||||
.join(", ")}`;
|
.join(", ")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tries the profiles from `index` on, moving on only after a sign-in failure
|
// Tries the profiles in order, moving on only after a sign-in failure or
|
||||||
// or exhausted usage, and records every profile it tried.
|
// exhausted usage, and records every profile it tried.
|
||||||
async function attemptFrom<T, P extends ProfileIdentity>(
|
export async function runProfiles<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>(
|
|
||||||
profiles: P[],
|
profiles: P[],
|
||||||
attempt: (profile: P) => Promise<T>,
|
attempt: (profile: P) => Promise<T>,
|
||||||
failures: ProfileFailure[] = [],
|
failures: ProfileFailure[] = [],
|
||||||
): Promise<T> {
|
): 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
|
// 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;
|
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(
|
async function codexDeviceLogin(
|
||||||
profile: RuntimeProfile,
|
profile: RuntimeProfile,
|
||||||
environment: ReadonlyMap<string, string>,
|
environment: ReadonlyMap<string, string>,
|
||||||
@@ -319,7 +304,12 @@ async function codexDeviceLogin(
|
|||||||
};
|
};
|
||||||
const drained = Promise.all([collect(login.stdout), collect(login.stderr)]);
|
const drained = Promise.all([collect(login.stdout), collect(login.stderr)]);
|
||||||
await sleep(3000);
|
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));
|
await postAuthorComment(withProfile(shown, profile));
|
||||||
const [code] = await Promise.all([status, drained]);
|
const [code] = await Promise.all([status, drained]);
|
||||||
if (code !== 0) {
|
if (code !== 0) {
|
||||||
|
|||||||
+22
-26
@@ -61,32 +61,28 @@ export async function prepareRuntime(
|
|||||||
await writePrivate(reviewerFile, credentials.reviewerToken, 0, 0);
|
await writePrivate(reviewerFile, credentials.reviewerToken, 0, 0);
|
||||||
|
|
||||||
// One profile at a time, in order, as each claims the next UID.
|
// One profile at a time, in order, as each claims the next UID.
|
||||||
const profiles = await credentials.profiles.reduce<Promise<RuntimeProfile[]>>(
|
const profiles: RuntimeProfile[] = [];
|
||||||
async (previous, profile, index) => {
|
for (const [index, profile] of credentials.profiles.entries()) {
|
||||||
const prepared = await previous;
|
const uid = FIRST_PROFILE_UID + index;
|
||||||
const uid = FIRST_PROFILE_UID + index;
|
const home = join(runtimeDir, `profile-${index}`);
|
||||||
const home = join(runtimeDir, `profile-${index}`);
|
await mkdir(home, { mode: 0o700 });
|
||||||
await mkdir(home, { mode: 0o700 });
|
await chmod(home, 0o700);
|
||||||
await chmod(home, 0o700);
|
await chown(home, uid, uid);
|
||||||
await chown(home, uid, uid);
|
const tokenFile =
|
||||||
const tokenFile =
|
profile.token === undefined || profile.token === "" ? null : join(home, "token");
|
||||||
profile.token === undefined || profile.token === "" ? null : join(home, "token");
|
if (tokenFile !== null && profile.token !== undefined) {
|
||||||
if (tokenFile !== null && profile.token !== undefined) {
|
await writePrivate(tokenFile, profile.token, uid, uid);
|
||||||
await writePrivate(tokenFile, profile.token, uid, uid);
|
}
|
||||||
}
|
profiles.push({
|
||||||
prepared.push({
|
index,
|
||||||
index,
|
type: profile.type,
|
||||||
type: profile.type,
|
model: profile.model,
|
||||||
model: profile.model,
|
uid,
|
||||||
uid,
|
gid: uid,
|
||||||
gid: uid,
|
home,
|
||||||
home,
|
tokenFile,
|
||||||
tokenFile,
|
});
|
||||||
});
|
}
|
||||||
return prepared;
|
|
||||||
},
|
|
||||||
Promise.resolve([]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const manifestPath = join(runtimeDir, "manifest.json");
|
const manifestPath = join(runtimeDir, "manifest.json");
|
||||||
const manifest: RuntimeManifest = {
|
const manifest: RuntimeManifest = {
|
||||||
|
|||||||
Reference in New Issue
Block a user