Files
AgentCompany/src/main/ipc-core-admin-handlers.ts
T
2026-04-16 14:48:51 -04:00

178 lines
6.3 KiB
TypeScript

import {
IPC_CHANNELS,
companyInputSchema,
connectorDisabledModelsSchema,
connectorInputSchema,
deleteEntitySchema,
openTerminalCommandSchema,
secretInputSchema,
} from "@shared/contracts";
import { validateConnectorConfigurationModel, validateConnectorModelSelection } from "./connector-model-policy";
import { reconcileExecutionModeChange } from "./execution-mode-reconciler";
import { fail, ok, type RegisterCoreHandlersDependencies } from "./ipc-core-common";
export function registerCoreAdminHandlers({
registerHandle,
db,
ensureCompanyExists,
publishDomainChanged,
handleApprovalResolved,
checkConnector,
openConnectorAuthTerminal,
openGenericTerminal,
}: Pick<
RegisterCoreHandlersDependencies,
"registerHandle" | "db" | "ensureCompanyExists" | "publishDomainChanged" | "handleApprovalResolved" | "checkConnector" | "openConnectorAuthTerminal" | "openGenericTerminal"
>) {
registerHandle(IPC_CHANNELS.saveCompany, async (_event, payload) => {
const parsed = companyInputSchema.parse(payload);
const previousMode = parsed.id ? db.getCompanyExecutionMode(parsed.id) : parsed.executionMode;
const previousApprovalSettings = parsed.id
? db.getCompanyApprovalSettings(parsed.id)
: { autoApproveHires: parsed.autoApproveHires, reviewDeliverables: parsed.reviewDeliverables };
const id = db.saveCompany(parsed);
reconcileExecutionModeChange({
db,
companyId: id,
previousExecutionMode: previousMode,
nextExecutionMode: parsed.executionMode,
previousReviewDeliverables: previousApprovalSettings?.reviewDeliverables ?? false,
nextReviewDeliverables: parsed.reviewDeliverables,
handleApprovalResolved,
});
db.addActivity({
companyId: id,
actor: "board",
action: parsed.id ? "company.updated" : "company.created",
entityType: "company",
entityId: id,
detail: parsed.name,
});
publishDomainChanged();
return ok(id);
});
registerHandle(IPC_CHANNELS.setCurrentCompany, async (_event, companyId) => {
if (typeof companyId !== "string" || !companyId) {
return fail("INVALID_COMPANY_ID", "A valid company id is required.");
}
if (!db.hasCompany(companyId)) {
return fail("COMPANY_NOT_FOUND", "The selected company does not exist.");
}
db.setCurrentCompany(companyId);
publishDomainChanged();
return ok(true);
});
registerHandle(IPC_CHANNELS.saveConnector, async (_event, payload) => {
const parsed = connectorInputSchema.parse(payload);
const existingConnector = db.getConnector(parsed.id);
if (parsed.command !== existingConnector.command) {
return fail("CONNECTOR_COMMAND_LOCKED", `Connector command is managed by AgentCompany and must remain "${existingConnector.command}".`);
}
const connectorConfigValidation = validateConnectorConfigurationModel(parsed.id, parsed.model ?? null);
if (!connectorConfigValidation.ok) {
return fail("CONNECTOR_MODEL_NOT_ALLOWED", connectorConfigValidation.error);
}
if (!parsed.id.startsWith("claude_compat")) {
const modelSelection = validateConnectorModelSelection(db, parsed.id, parsed.model ?? null);
if (!modelSelection.ok) {
return fail("CONNECTOR_MODEL_NOT_ALLOWED", modelSelection.error);
}
}
db.saveConnector({
...parsed,
command: existingConnector.command,
model: parsed.model ?? null,
});
db.addActivity({
companyId: db.getCurrentCompanyId() ?? "00000000-0000-0000-0000-000000000000",
actor: "board",
action: "connector.updated",
entityType: "connector",
entityId: parsed.id,
detail: existingConnector.command,
});
publishDomainChanged();
return ok(true);
});
registerHandle(IPC_CHANNELS.testConnector, async (_event, id) => {
const parsed = connectorInputSchema.shape.id.parse(id);
await checkConnector(parsed);
return ok(true);
});
registerHandle(IPC_CHANNELS.openConnectorAuthTerminal, async (_event, id) => {
const parsed = connectorInputSchema.shape.id.parse(id);
await openConnectorAuthTerminal(parsed);
return ok(true);
});
registerHandle(IPC_CHANNELS.checkNodeInstalled, async () => {
return ok({ installed: true, version: process.version });
});
registerHandle(IPC_CHANNELS.openTerminalCommand, async (_event, payload) => {
const parsed = openTerminalCommandSchema.parse(payload);
await openGenericTerminal(parsed);
return ok(true);
});
registerHandle(IPC_CHANNELS.setConnectorDisabledModels, async (_event, payload) => {
const parsed = connectorDisabledModelsSchema.parse(payload);
db.setDisabledModels(parsed.connectorId, parsed.disabledModels);
publishDomainChanged();
return ok(true);
});
registerHandle(IPC_CHANNELS.saveSecret, async (_event, payload) => {
const parsed = secretInputSchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
const id = db.saveSecret(parsed);
db.addActivity({
companyId: parsed.companyId,
actor: "board",
action: parsed.id ? "secret.updated" : "secret.created",
entityType: "secret",
entityId: id,
detail: parsed.name,
});
publishDomainChanged();
return ok(id);
});
registerHandle(IPC_CHANNELS.deleteSecret, async (_event, payload) => {
const parsed = deleteEntitySchema.parse(payload);
const companyError = ensureCompanyExists(parsed.companyId);
if (companyError) return companyError;
if (!db.belongsToCompany("secrets", parsed.id, parsed.companyId)) {
return fail("SECRET_NOT_FOUND", "Secret not found in the current company.");
}
db.deleteSecret(parsed.id, parsed.companyId);
db.addActivity({
companyId: parsed.companyId,
actor: "board",
action: "secret.deleted",
entityType: "secret",
entityId: parsed.id,
detail: "",
});
publishDomainChanged();
return ok(true);
});
registerHandle(IPC_CHANNELS.deleteCompany, async (_event, companyId) => {
if (typeof companyId !== "string" || companyId.length === 0) {
return fail("INVALID_INPUT", "Company ID is required.");
}
if (!db.hasCompany(companyId)) {
return fail("COMPANY_NOT_FOUND", "Company not found.");
}
db.deleteCompany(companyId);
publishDomainChanged();
return ok(true);
});
}