2026-03-14 13:44:26 -07:00
|
|
|
import { randomUUID } from "node:crypto";
|
2026-03-03 08:45:26 -06:00
|
|
|
import type { Db } from "@paperclipai/db";
|
|
|
|
|
import { activityLog } from "@paperclipai/db";
|
2026-03-14 13:44:26 -07:00
|
|
|
import { PLUGIN_EVENT_TYPES, type PluginEventType } from "@paperclipai/shared";
|
|
|
|
|
import type { PluginEvent } from "@paperclipai/plugin-sdk";
|
2026-02-17 12:24:43 -06:00
|
|
|
import { publishLiveEvent } from "./live-events.js";
|
2026-03-11 22:17:21 -05:00
|
|
|
import { redactCurrentUserValue } from "../log-redaction.js";
|
2026-02-19 15:43:52 -06:00
|
|
|
import { sanitizeRecord } from "../redaction.js";
|
2026-03-14 13:51:41 -07:00
|
|
|
import { logger } from "../middleware/logger.js";
|
2026-03-14 13:44:26 -07:00
|
|
|
import type { PluginEventBus } from "./plugin-event-bus.js";
|
|
|
|
|
|
|
|
|
|
const PLUGIN_EVENT_SET: ReadonlySet<string> = new Set(PLUGIN_EVENT_TYPES);
|
|
|
|
|
|
|
|
|
|
let _pluginEventBus: PluginEventBus | null = null;
|
|
|
|
|
|
|
|
|
|
/** Wire the plugin event bus so domain events are forwarded to plugins. */
|
|
|
|
|
export function setPluginEventBus(bus: PluginEventBus): void {
|
2026-03-14 13:51:41 -07:00
|
|
|
if (_pluginEventBus) {
|
|
|
|
|
logger.warn("setPluginEventBus called more than once, replacing existing bus");
|
|
|
|
|
}
|
2026-03-14 13:44:26 -07:00
|
|
|
_pluginEventBus = bus;
|
|
|
|
|
}
|
Add server routes for companies, approvals, costs, and dashboard
New routes: companies, approvals, costs, dashboard, authz. New
services: companies, approvals, costs, dashboard, heartbeat,
activity-log. Add auth middleware and structured error handling.
Expand existing agent and issue routes with richer CRUD operations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:07:27 -06:00
|
|
|
|
|
|
|
|
export interface LogActivityInput {
|
|
|
|
|
companyId: string;
|
|
|
|
|
actorType: "agent" | "user" | "system";
|
|
|
|
|
actorId: string;
|
|
|
|
|
action: string;
|
|
|
|
|
entityType: string;
|
|
|
|
|
entityId: string;
|
|
|
|
|
agentId?: string | null;
|
2026-02-19 09:09:40 -06:00
|
|
|
runId?: string | null;
|
Add server routes for companies, approvals, costs, and dashboard
New routes: companies, approvals, costs, dashboard, authz. New
services: companies, approvals, costs, dashboard, heartbeat,
activity-log. Add auth middleware and structured error handling.
Expand existing agent and issue routes with richer CRUD operations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:07:27 -06:00
|
|
|
details?: Record<string, unknown> | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function logActivity(db: Db, input: LogActivityInput) {
|
2026-02-19 15:43:52 -06:00
|
|
|
const sanitizedDetails = input.details ? sanitizeRecord(input.details) : null;
|
2026-03-11 22:17:21 -05:00
|
|
|
const redactedDetails = sanitizedDetails ? redactCurrentUserValue(sanitizedDetails) : null;
|
Add server routes for companies, approvals, costs, and dashboard
New routes: companies, approvals, costs, dashboard, authz. New
services: companies, approvals, costs, dashboard, heartbeat,
activity-log. Add auth middleware and structured error handling.
Expand existing agent and issue routes with richer CRUD operations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:07:27 -06:00
|
|
|
await db.insert(activityLog).values({
|
|
|
|
|
companyId: input.companyId,
|
|
|
|
|
actorType: input.actorType,
|
|
|
|
|
actorId: input.actorId,
|
|
|
|
|
action: input.action,
|
|
|
|
|
entityType: input.entityType,
|
|
|
|
|
entityId: input.entityId,
|
|
|
|
|
agentId: input.agentId ?? null,
|
2026-02-19 09:09:40 -06:00
|
|
|
runId: input.runId ?? null,
|
2026-03-11 22:17:21 -05:00
|
|
|
details: redactedDetails,
|
Add server routes for companies, approvals, costs, and dashboard
New routes: companies, approvals, costs, dashboard, authz. New
services: companies, approvals, costs, dashboard, heartbeat,
activity-log. Add auth middleware and structured error handling.
Expand existing agent and issue routes with richer CRUD operations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:07:27 -06:00
|
|
|
});
|
2026-02-17 12:24:43 -06:00
|
|
|
|
|
|
|
|
publishLiveEvent({
|
|
|
|
|
companyId: input.companyId,
|
|
|
|
|
type: "activity.logged",
|
|
|
|
|
payload: {
|
|
|
|
|
actorType: input.actorType,
|
|
|
|
|
actorId: input.actorId,
|
|
|
|
|
action: input.action,
|
|
|
|
|
entityType: input.entityType,
|
|
|
|
|
entityId: input.entityId,
|
|
|
|
|
agentId: input.agentId ?? null,
|
2026-02-19 09:09:40 -06:00
|
|
|
runId: input.runId ?? null,
|
2026-03-11 22:17:21 -05:00
|
|
|
details: redactedDetails,
|
2026-02-17 12:24:43 -06:00
|
|
|
},
|
|
|
|
|
});
|
2026-03-14 13:44:26 -07:00
|
|
|
|
|
|
|
|
if (_pluginEventBus && PLUGIN_EVENT_SET.has(input.action)) {
|
|
|
|
|
const event: PluginEvent = {
|
|
|
|
|
eventId: randomUUID(),
|
|
|
|
|
eventType: input.action as PluginEventType,
|
|
|
|
|
occurredAt: new Date().toISOString(),
|
|
|
|
|
actorId: input.actorId,
|
|
|
|
|
actorType: input.actorType,
|
|
|
|
|
entityId: input.entityId,
|
|
|
|
|
entityType: input.entityType,
|
|
|
|
|
companyId: input.companyId,
|
|
|
|
|
payload: {
|
|
|
|
|
...redactedDetails,
|
|
|
|
|
agentId: input.agentId ?? null,
|
|
|
|
|
runId: input.runId ?? null,
|
|
|
|
|
},
|
|
|
|
|
};
|
2026-03-14 13:51:41 -07:00
|
|
|
void _pluginEventBus.emit(event).then(({ errors }) => {
|
|
|
|
|
for (const { pluginId, error } of errors) {
|
|
|
|
|
logger.warn({ pluginId, eventType: event.eventType, err: error }, "plugin event handler failed");
|
|
|
|
|
}
|
|
|
|
|
}).catch(() => {});
|
2026-03-14 13:44:26 -07:00
|
|
|
}
|
Add server routes for companies, approvals, costs, and dashboard
New routes: companies, approvals, costs, dashboard, authz. New
services: companies, approvals, costs, dashboard, heartbeat,
activity-log. Add auth middleware and structured error handling.
Expand existing agent and issue routes with richer CRUD operations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 09:07:27 -06:00
|
|
|
}
|