Files
agentic-dev/packages/workspaces/src/application/use-cases/connect-workspace.use-case.ts
Danijel Martinek 8219c1fabb feat(workspaces): connectWorkspace use case with audit
Manifest-first: connectWorkspace declared mutates:true with the
workspace-connected audit event, requiredCores gains audit. Workspace
entity gains gitUrl + persisted status enum (created/connecting/ready/
error). Input takes name + git URL + PAT; the output schema is the
credential-free workspace entity, so the PAT can never round-trip.
Audit emission asserted with RecordingAuditLog; binders wire the use
case through wireUseCase with the __audited brand, and web-next
bindAll now binds core-audit (payload+stdout sinks in production,
stdout in dev-seed) so boot conformance passes in both modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
2026-07-12 22:36:18 +02:00

63 lines
2.7 KiB
TypeScript

import { z } from "zod";
import type { AuditLogProtocol } from "@repo/core-shared/di/bind-protocols";
import { gitUrlSchema, workspaceSchema } from "../../entities/models/workspace";
import type { IWorkspaceRepository } from "../repositories/workspace.repository.interface";
// ── Input ────────────────────────────────────────────────────────────────
export const connectWorkspaceInputSchema = z
.object({
name: z.string().min(1).max(128),
gitUrl: gitUrlSchema,
/** Repository personal access token. Write-only: encrypted at rest and never returned by any output. */
pat: z.string().min(1).max(4096),
})
.strict();
export type ConnectWorkspaceInput = z.infer<typeof connectWorkspaceInputSchema>;
// ── Output ───────────────────────────────────────────────────────────────
// The credential is deliberately absent: `workspaceSchema` has no credential
// field, and `.parse` strips any unknown keys a repository might leak.
export const connectWorkspaceOutputSchema = workspaceSchema;
export type ConnectWorkspaceOutput = z.infer<
typeof connectWorkspaceOutputSchema
>;
// ── Use case ─────────────────────────────────────────────────────────────
export type IConnectWorkspaceUseCase = ReturnType<
typeof connectWorkspaceUseCase
>;
export const connectWorkspaceUseCase =
(workspaceRepository: IWorkspaceRepository, auditLog?: AuditLogProtocol) =>
async (input: ConnectWorkspaceInput): Promise<ConnectWorkspaceOutput> => {
const workspace = await workspaceRepository.createWorkspace({
name: input.name,
gitUrl: input.gitUrl,
credential: input.pat,
});
// Audit event "workspace-connected" — declared in feature.manifest.ts
// (useCases.connectWorkspace.audits). No session context exists at this
// layer yet (auth wiring is a later story), so the system sentinels apply.
await auditLog?.record({
actorId: "system",
actorType: "system",
actorRoles: [],
action: "CREATE",
resource: { type: "workspaces", id: workspace.id },
at: new Date(),
scope: {
feature: "workspaces",
environment: process.env.NODE_ENV ?? "development",
tenant: "default",
},
from: { ipTruncated: "system", userAgent: "control-plane" },
containsPii: false,
outcome: "success",
reason: "workspace-connected",
});
return connectWorkspaceOutputSchema.parse(workspace);
};