@galda/cli 0.10.52 → 0.10.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/index.html +1 -0
- package/engine/server.mjs +21 -12
- package/engine/worker-availability.mjs +8 -0
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -7695,6 +7695,7 @@ const AUTH_REASON_TEXT = {
|
|
|
7695
7695
|
'logged-out': 'Not signed in to claude. Run `claude` in a terminal and /login, then relaunch the manager from that terminal — tasks are paused until then.',
|
|
7696
7696
|
'bad-credentials': 'Worker sign-in failed. Check ANTHROPIC_API_KEY, or run `claude` and /login, then relaunch the manager from that terminal.',
|
|
7697
7697
|
'probe-failed': 'Couldn’t verify claude sign-in. Ensure the `claude` CLI is installed and signed in, then relaunch the manager.',
|
|
7698
|
+
'rate-limited': 'Claude has reached its usage limit. Claude tasks are queued until access resets; Codex tasks can continue now.',
|
|
7698
7699
|
};
|
|
7699
7700
|
function renderAuthBanner(auth){
|
|
7700
7701
|
const bar = $('errbar');
|
package/engine/server.mjs
CHANGED
|
@@ -26,6 +26,7 @@ import { createSerialQueue } from './lib.mjs';
|
|
|
26
26
|
import { collectProjectRules } from './lib.mjs';
|
|
27
27
|
import { FALLBACK_CODEX_MODELS, loadCodexModels } from './codex-models.mjs';
|
|
28
28
|
import { CLAUDE_MODELS } from './claude-models.mjs';
|
|
29
|
+
import { classifyWorkerPreflight, workerCanStart } from './worker-availability.mjs';
|
|
29
30
|
import { openPR } from './pr.mjs';
|
|
30
31
|
import { runVerification, exerciseUi } from './verify.mjs';
|
|
31
32
|
|
|
@@ -1470,9 +1471,9 @@ function startNextStacked(projectId) {
|
|
|
1470
1471
|
// standalone `claude` (runClaude), inheriting the launching shell's login. If
|
|
1471
1472
|
// that shell has no usable credentials, every task fails late with "Not logged
|
|
1472
1473
|
// in". We probe once at boot and hold the queue + raise a banner instead of
|
|
1473
|
-
// failing task after task.
|
|
1474
|
-
//
|
|
1475
|
-
let workerAuth = { ok:
|
|
1474
|
+
// failing task after task. Claude waits for the short probe; Codex is gated
|
|
1475
|
+
// independently and may start immediately.
|
|
1476
|
+
let workerAuth = { ok: null, reason: null, checkedAt: null };
|
|
1476
1477
|
const publicAuth = () => ({ ok: workerAuth.ok, reason: workerAuth.reason });
|
|
1477
1478
|
|
|
1478
1479
|
// Boot preflight: one cheap Read-only `claude` call; classify the result (pure,
|
|
@@ -1481,14 +1482,18 @@ const publicAuth = () => ({ ok: workerAuth.ok, reason: workerAuth.reason });
|
|
|
1481
1482
|
async function probeWorkerAuth() {
|
|
1482
1483
|
try {
|
|
1483
1484
|
const out = await runClaude({ prompt: 'Reply with the single word: ok', cwd: ROOT, tools: 'Read', model: 'haiku' });
|
|
1484
|
-
workerAuth = {
|
|
1485
|
+
workerAuth = {
|
|
1486
|
+
...classifyWorkerPreflight(out, { classifyAuthProbe, isRateLimited }),
|
|
1487
|
+
checkedAt: Date.now(),
|
|
1488
|
+
};
|
|
1485
1489
|
} catch {
|
|
1486
1490
|
workerAuth = { ok: false, reason: 'probe-failed', checkedAt: Date.now() };
|
|
1487
1491
|
}
|
|
1488
1492
|
console.log(workerAuth.ok
|
|
1489
1493
|
? '[manager] worker auth preflight OK'
|
|
1490
|
-
: `[manager]
|
|
1494
|
+
: `[manager] Claude preflight unavailable (${workerAuth.reason}) — holding Claude tasks; Codex may continue.`);
|
|
1491
1495
|
send({ ev: 'auth', auth: publicAuth() });
|
|
1496
|
+
if (workerAuth.ok) for (const project of projects) pump(project.id);
|
|
1492
1497
|
return workerAuth;
|
|
1493
1498
|
}
|
|
1494
1499
|
|
|
@@ -1654,10 +1659,8 @@ async function authorVerify(task, goal, project) {
|
|
|
1654
1659
|
// `lastStartAt` check in pump() below.
|
|
1655
1660
|
const slowModeLastStart = new Map(); // projectId -> ms epoch of the last task start
|
|
1656
1661
|
function pump(projectId) {
|
|
1657
|
-
//
|
|
1658
|
-
//
|
|
1659
|
-
// with "Not logged in". Cleared automatically once a probe succeeds.
|
|
1660
|
-
if (!workerAuth.ok) return;
|
|
1662
|
+
// A Claude sign-in or usage hold must not stop an installed Codex worker.
|
|
1663
|
+
// Claude work stays queued until a later probe succeeds.
|
|
1661
1664
|
// Project paused by the user (Doing列の一時停止 / chat) → hold this project's
|
|
1662
1665
|
// queue exactly like the auth hold above. Resume flips the flag and pumps.
|
|
1663
1666
|
if (isProjectPaused(projectId)) return;
|
|
@@ -1684,7 +1687,8 @@ function pump(projectId) {
|
|
|
1684
1687
|
if (wait > 0) { setTimeout(() => pump(projectId), wait); return; }
|
|
1685
1688
|
}
|
|
1686
1689
|
const runningGoalIds = [...q.running].map((t) => t.goalId);
|
|
1687
|
-
const
|
|
1690
|
+
const eligible = q.waiting.filter((task) => workerCanStart({ agent: task.agent, auth: workerAuth }));
|
|
1691
|
+
const toStart = nextRunnableTasks(eligible, runningGoalIds, slow.enabled ? 1 : slots);
|
|
1688
1692
|
for (const task of toStart) {
|
|
1689
1693
|
const i = q.waiting.indexOf(task);
|
|
1690
1694
|
if (i !== -1) q.waiting.splice(i, 1);
|
|
@@ -1981,7 +1985,9 @@ function queueReplyTask(goal, text, { from = 'manager', via = 'composer' } = {})
|
|
|
1981
1985
|
const projectCap = slow.enabled ? Math.min(PROJECT_MAX_PARALLEL, slow.maxParallel) : PROJECT_MAX_PARALLEL;
|
|
1982
1986
|
const slots = Math.min(projectCap - q.running.size, GLOBAL_MAX_PARALLEL - globalRunningCount());
|
|
1983
1987
|
const runningGoalIds = [...q.running].map((t) => t.goalId);
|
|
1984
|
-
const
|
|
1988
|
+
const eligible = q.waiting.filter((candidate) => workerCanStart({ agent: candidate.agent, auth: workerAuth }));
|
|
1989
|
+
const startsNow = workerCanStart({ agent: task.agent, auth: workerAuth })
|
|
1990
|
+
&& nextRunnableTasks(eligible, runningGoalIds, slow.enabled ? 1 : slots).some((candidate) => candidate.id === task.id);
|
|
1985
1991
|
goal.startedAt = startsNow ? new Date().toISOString() : null;
|
|
1986
1992
|
saveGoal(goal);
|
|
1987
1993
|
}
|
|
@@ -4913,7 +4919,10 @@ server.listen(PORT, '127.0.0.1', () => {
|
|
|
4913
4919
|
spawn('open', ['-g', openUrl], { stdio: 'ignore' }).unref();
|
|
4914
4920
|
}
|
|
4915
4921
|
console.log(`[manager] projects: ${projects.map((p) => `${p.id}=${p.dir}`).join(' ')}`);
|
|
4916
|
-
probeWorkerAuth().catch(() => {}); // hold
|
|
4922
|
+
probeWorkerAuth().catch(() => {}); // hold Claude tasks + banner if Claude is unavailable
|
|
4923
|
+
setInterval(() => {
|
|
4924
|
+
if (!workerAuth.ok) probeWorkerAuth().catch(() => {});
|
|
4925
|
+
}, 5 * 60_000).unref();
|
|
4917
4926
|
syncAllExternal().catch(() => {});
|
|
4918
4927
|
syncGoalMerges().catch(() => {});
|
|
4919
4928
|
setInterval(() => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function classifyWorkerPreflight({ result = '', code = 0 } = {}, { classifyAuthProbe, isRateLimited }) {
|
|
2
|
+
if (isRateLimited(result)) return { ok: false, reason: 'rate-limited' };
|
|
3
|
+
return classifyAuthProbe({ result, code });
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function workerCanStart({ agent, auth } = {}) {
|
|
7
|
+
return auth?.ok === true || agent === 'codex';
|
|
8
|
+
}
|
package/package.json
CHANGED