吸收数字员工安全服务重启入口
This commit is contained in:
@@ -264,6 +264,59 @@ describe("digital employee state service", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("records managed service restart actions as syncable events", async () => {
|
||||
const { recordDigitalEmployeeManagedServiceAction } = await import("./stateService");
|
||||
|
||||
const success = recordDigitalEmployeeManagedServiceAction({
|
||||
serviceId: "fileServer",
|
||||
action: "restart",
|
||||
status: "success",
|
||||
reason: "operator_retry",
|
||||
actor: "operator",
|
||||
allowlisted: true,
|
||||
result: { success: true, message: "started" },
|
||||
recordedAt: "2026-06-07T08:30:00.000Z",
|
||||
});
|
||||
const rejected = recordDigitalEmployeeManagedServiceAction({
|
||||
serviceId: "computerServer",
|
||||
action: "restart",
|
||||
status: "rejected",
|
||||
allowlisted: false,
|
||||
result: { error: "service_restart_not_allowed" },
|
||||
recordedAt: "2026-06-07T08:31:00.000Z",
|
||||
});
|
||||
|
||||
expect(success?.eventId).toBe("managed-service:fileServer:restart:success:2026-06-07T08-30-00-000Z");
|
||||
expect(rejected?.eventId).toBe("managed-service:computerServer:restart:rejected:2026-06-07T08-31-00-000Z");
|
||||
expect(mockState.db?.events.get(success!.eventId)).toMatchObject({
|
||||
kind: "managed_service_restart",
|
||||
message: "服务重启成功:fileServer",
|
||||
});
|
||||
expect(JSON.parse(mockState.db?.events.get(success!.eventId)?.payload ?? "{}")).toMatchObject({
|
||||
service_id: "fileServer",
|
||||
action: "restart",
|
||||
status: "success",
|
||||
reason: "operator_retry",
|
||||
actor: "operator",
|
||||
allowlisted: true,
|
||||
result: { success: true, message: "started" },
|
||||
});
|
||||
expect(mockState.db?.events.get(rejected!.eventId)).toMatchObject({
|
||||
kind: "managed_service_restart_rejected",
|
||||
message: "服务重启已拒绝:computerServer",
|
||||
});
|
||||
expect(JSON.parse(mockState.db?.events.get(rejected!.eventId)?.payload ?? "{}")).toMatchObject({
|
||||
service_id: "computerServer",
|
||||
status: "rejected",
|
||||
allowlisted: false,
|
||||
result: { error: "service_restart_not_allowed" },
|
||||
});
|
||||
expect(mockState.db?.outbox.get(`event:insert:${success!.eventId}`)).toMatchObject({
|
||||
entity_type: "event",
|
||||
status: "pending",
|
||||
});
|
||||
});
|
||||
|
||||
it("records and lists route decisions as syncable events", async () => {
|
||||
const {
|
||||
listDigitalEmployeeRouteDecisions,
|
||||
|
||||
@@ -417,6 +417,21 @@ export interface DigitalEmployeeDailyReportRecordResult {
|
||||
eventId: string;
|
||||
}
|
||||
|
||||
export interface DigitalEmployeeManagedServiceActionInput {
|
||||
serviceId: string;
|
||||
action: "restart";
|
||||
status: "success" | "failed" | "rejected";
|
||||
reason?: string | null;
|
||||
actor?: string | null;
|
||||
result?: Record<string, unknown> | null;
|
||||
allowlisted: boolean;
|
||||
recordedAt?: string | null;
|
||||
}
|
||||
|
||||
export interface DigitalEmployeeManagedServiceActionResult {
|
||||
eventId: string;
|
||||
}
|
||||
|
||||
export interface DigitalEmployeeGovernanceCommandRecord {
|
||||
commandKind: string;
|
||||
commandId: string;
|
||||
@@ -1559,6 +1574,39 @@ export function recordDigitalEmployeeDailyReport(
|
||||
return { reportId, planId, taskId, runId, artifactId, eventId };
|
||||
}
|
||||
|
||||
export function recordDigitalEmployeeManagedServiceAction(
|
||||
input: DigitalEmployeeManagedServiceActionInput,
|
||||
): DigitalEmployeeManagedServiceActionResult | null {
|
||||
const db = getDigitalEmployeeDb();
|
||||
if (!db) return null;
|
||||
const now = input.recordedAt || new Date().toISOString();
|
||||
const normalizedServiceId = safeId(input.serviceId || "unknown-service");
|
||||
const eventId = `managed-service:${normalizedServiceId}:${input.action}:${input.status}:${safeId(now)}`;
|
||||
const kind = input.status === "rejected"
|
||||
? "managed_service_restart_rejected"
|
||||
: "managed_service_restart";
|
||||
const payload = {
|
||||
service_id: input.serviceId,
|
||||
action: input.action,
|
||||
status: input.status,
|
||||
reason: input.reason ?? null,
|
||||
actor: input.actor ?? null,
|
||||
result: input.result ?? null,
|
||||
allowlisted: input.allowlisted,
|
||||
};
|
||||
insertEvent({
|
||||
event_id: eventId,
|
||||
kind,
|
||||
occurred_at: now,
|
||||
message: input.status === "rejected"
|
||||
? `服务重启已拒绝:${input.serviceId}`
|
||||
: `服务重启${input.status === "success" ? "成功" : "失败"}:${input.serviceId}`,
|
||||
plan_id: null,
|
||||
task_id: null,
|
||||
}, null, payload);
|
||||
return { eventId };
|
||||
}
|
||||
|
||||
export function recordDigitalEmployeeGovernanceCommand(
|
||||
command: DigitalEmployeeGovernanceCommandRecord,
|
||||
): { planId: string; taskId: string; runId: string } {
|
||||
|
||||
Reference in New Issue
Block a user