framework
Version:
The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.
74 lines • 3.73 kB
JavaScript
import { contextAutoPm, contextAutoPmSweep, contextQuota } from './context.js';
// The usage panel's read surface (#533): where the account's subscription quota stands, and where
// it stands against the quota boundary (#879). The source is wired into the dashboard context by
// the daemon, and the panel polls it for its whole life.
/** An honest empty view: no reading, and so no boundary to measure against. */
function noReading() {
// No windows and no boundary rather than zeroes: an empty bar reads as
// "nothing used", which is the one thing this panel must never imply.
return { windows: [], unavailable: 'fetch-failed' };
}
/** Where the account's quota stands against its boundary. */
export async function onQuota() {
return contextQuota().read().catch(() => noReading());
}
/**
* What auto PM last decided (#1161), for the line under the panel's toggle. It sits beside
* `onQuota` because it is the same panel and the same gate: auto PM spends against exactly the
* boundary drawn above it.
*
* `undefined` when the loop has nothing to report yet, which the panel reads as "nothing to say"
* rather than as an idle sweep — the distinction this whole read exists to make.
*/
export async function onAutoPm() {
try {
return contextAutoPm()();
}
catch {
return undefined;
}
}
/**
* Sweep now rather than at the next interval (#1210). The loop already had this — it is what a
* write that switches the preference on triggers (#1167) — but until now nothing could ask for
* it directly, so the only way to fast-forward was to tick the box off and on again.
*
* The `autoPm` preference does not gate it: that preference is consent to spend quota *unasked*,
* and this call is asking. So with auto-run off the daemon still sweeps once — every other
* stand-down reason in force — and the schedule stays wherever the box says.
*
* Awaits the sweep and returns what it decided, one line per project (#1433): the click used to
* be fire-and-forget, so two presses could show literally nothing — no loading state, no
* outcome, and the stand-down reason recoverable only from the source. The outcomes are read
* off the loop's own report once the tick resolves, so the card can say them without a poll
* having to race the sweep. `false` means the sweep itself failed.
*
* `drainOnly` narrows the sweep to working the queue (#1204): the drain routine's Run now spins
* agents up on the queue's entries — the fan-out only the sweep can do — and an empty queue is
* reported rather than borrowed for a rotation job.
*/
export async function sendAutoPmSweep(opts) {
const sweep = contextAutoPmSweep();
// The reporter is captured BEFORE the sweep is awaited. It had to be: the context was
// request-scoped and did not survive an await, so a post-await `contextAutoPm()` found nothing
// on every real request and the card fell back to "The sweep ran." — the very fallback this RPC
// exists to avoid. The context is wired once at start-up now (F3), so the order is no longer
// load-bearing; the captured closure needs no context to be called later either way.
const reporter = contextAutoPm();
try {
await sweep(opts?.drainOnly ? { drainOnly: true } : undefined);
}
catch {
return { ok: false };
}
// The outcomes live on the loop's report — the same lines `onAutoPm` polls — read once the
// tick has resolved, so they describe the sweep this click fired.
try {
const report = reporter();
return { ok: true, ...(report ? { outcomes: report.outcomes } : {}) };
}
catch {
return { ok: true };
}
}
//# sourceMappingURL=quota.js.map