The earlier sweep cleaned the hot path (worker/runtime/automation/api-
server/ipc core — 59 sites). This pass finishes the deferred long tail:
main/* services, database/* CRUD modules, api-server/* routes, ipc-*
handlers, thread-integration, automation helpers, browser, skills.
Default strategy: catch (error) { logger.warn("[module] context:", ...) }
— preserve current behavior, make the failure visible instead of
silently hidden. Widened 3 logger interface signatures to include warn
where callers only passed info/error previously (thread-helpers,
host-environment-service, ipc-domain-common).
Legitimate boundaries left intact: JSON.parse on untrusted input, DNS
resolver no-records paths, explicit safe* helpers (safeJson, parseJson,
parseTriggerDetail), URL-parse predicates, and `<unserialisable>`
fallbacks inside the prompt-builder summarisers. Each kept catch now
either has a reason in context or is a documented safe* helper.
Tests adjusted only where test-constructed no-op loggers needed a warn
stub (message-dispatch, company-mode-switch, ipc-agent-graph-handlers).
With the earlier pass, AgentCompany no longer has silently-swallowing
catch blocks in main-process or renderer business code.
95 lines
3.6 KiB
TypeScript
95 lines
3.6 KiB
TypeScript
import {
|
|
IPC_CHANNELS,
|
|
budgetPolicyInputSchema,
|
|
listBudgetPoliciesSchema,
|
|
deleteBudgetPolicySchema,
|
|
listBudgetIncidentsSchema,
|
|
resolveBudgetIncidentSchema,
|
|
getBudgetStatusSchema,
|
|
} from "@shared/contracts";
|
|
import { fail, ok, type RegisterDomainHandlersDependencies } from "./ipc-domain-common";
|
|
import type { createBudgetEnforcementService } from "./budget-enforcement-service";
|
|
import type { AppDatabase } from "./database";
|
|
|
|
interface BudgetHandlerDependencies extends Pick<
|
|
RegisterDomainHandlersDependencies,
|
|
"registerHandle" | "ensureCompanyExists" | "publishDomainChanged" | "logger"
|
|
> {
|
|
budgetService: ReturnType<typeof createBudgetEnforcementService> | null;
|
|
db: AppDatabase;
|
|
}
|
|
|
|
export function registerBudgetHandlers({
|
|
registerHandle,
|
|
ensureCompanyExists,
|
|
publishDomainChanged,
|
|
budgetService,
|
|
db,
|
|
logger,
|
|
}: BudgetHandlerDependencies) {
|
|
// ---- Save (create/update) a budget policy ----
|
|
registerHandle(IPC_CHANNELS.saveBudgetPolicy, async (_event, payload) => {
|
|
const parsed = budgetPolicyInputSchema.parse(payload);
|
|
const companyError = ensureCompanyExists(parsed.companyId);
|
|
if (companyError) return companyError;
|
|
const id = db.saveBudgetPolicy(parsed);
|
|
// D-06: Auto-resolve incidents if limit was increased above current spend
|
|
if (budgetService) {
|
|
try {
|
|
budgetService.checkAutoResolve(parsed.companyId, id);
|
|
} catch (err) {
|
|
logger.warn(`[ipc/budget] checkAutoResolve failed for policy ${id} in company ${parsed.companyId}:`, err instanceof Error ? err.message : String(err));
|
|
}
|
|
}
|
|
publishDomainChanged();
|
|
return ok(id);
|
|
});
|
|
|
|
// ---- Delete a budget policy ----
|
|
registerHandle(IPC_CHANNELS.deleteBudgetPolicy, async (_event, payload) => {
|
|
const parsed = deleteBudgetPolicySchema.parse(payload);
|
|
const companyError = ensureCompanyExists(parsed.companyId);
|
|
if (companyError) return companyError;
|
|
db.deleteBudgetPolicy(parsed.companyId, parsed.policyId);
|
|
publishDomainChanged();
|
|
return ok(true);
|
|
});
|
|
|
|
// ---- List all budget policies for a company ----
|
|
registerHandle(IPC_CHANNELS.listBudgetPolicies, async (_event, payload) => {
|
|
const parsed = listBudgetPoliciesSchema.parse(payload);
|
|
const companyError = ensureCompanyExists(parsed.companyId);
|
|
if (companyError) return companyError;
|
|
return ok(db.listBudgetPolicies(parsed.companyId));
|
|
});
|
|
|
|
// ---- List budget incidents ----
|
|
registerHandle(IPC_CHANNELS.listBudgetIncidents, async (_event, payload) => {
|
|
const parsed = listBudgetIncidentsSchema.parse(payload);
|
|
const companyError = ensureCompanyExists(parsed.companyId);
|
|
if (companyError) return companyError;
|
|
return ok(db.listBudgetIncidents(parsed.companyId, parsed.status));
|
|
});
|
|
|
|
// ---- Resolve a budget incident manually ----
|
|
registerHandle(IPC_CHANNELS.resolveBudgetIncident, async (_event, payload) => {
|
|
const parsed = resolveBudgetIncidentSchema.parse(payload);
|
|
const companyError = ensureCompanyExists(parsed.companyId);
|
|
if (companyError) return companyError;
|
|
db.resolveBudgetIncident(parsed.incidentId, parsed.companyId);
|
|
publishDomainChanged();
|
|
return ok(true);
|
|
});
|
|
|
|
// ---- Get budget status summary ----
|
|
registerHandle(IPC_CHANNELS.getBudgetStatus, async (_event, payload) => {
|
|
const parsed = getBudgetStatusSchema.parse(payload);
|
|
const companyError = ensureCompanyExists(parsed.companyId);
|
|
if (companyError) return companyError;
|
|
if (!budgetService) {
|
|
return fail("SERVICE_NOT_READY", "Budget enforcement service is not initialized.");
|
|
}
|
|
return ok(budgetService.getBudgetStatus(parsed.companyId));
|
|
});
|
|
}
|