mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 18:10:39 +09:00
Add workspace operation tracking and fix project properties JSX
This commit is contained in:
parent
e39ae5a400
commit
4da13984e2
19 changed files with 11537 additions and 30 deletions
|
|
@ -29,6 +29,7 @@ import {
|
|||
issueService,
|
||||
logActivity,
|
||||
secretService,
|
||||
workspaceOperationService,
|
||||
} from "../services/index.js";
|
||||
import { conflict, forbidden, notFound, unprocessable } from "../errors.js";
|
||||
import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js";
|
||||
|
|
@ -62,6 +63,7 @@ export function agentRoutes(db: Db) {
|
|||
const heartbeat = heartbeatService(db);
|
||||
const issueApprovalsSvc = issueApprovalService(db);
|
||||
const secretsSvc = secretService(db);
|
||||
const workspaceOperations = workspaceOperationService(db);
|
||||
const strictSecretsMode = process.env.PAPERCLIP_SECRETS_STRICT_MODE === "true";
|
||||
|
||||
function canCreateAgents(agent: { role: string; permissions: Record<string, unknown> | null | undefined }) {
|
||||
|
|
@ -1560,6 +1562,40 @@ export function agentRoutes(db: Db) {
|
|||
res.json(result);
|
||||
});
|
||||
|
||||
router.get("/heartbeat-runs/:runId/workspace-operations", async (req, res) => {
|
||||
const runId = req.params.runId as string;
|
||||
const run = await heartbeat.getRun(runId);
|
||||
if (!run) {
|
||||
res.status(404).json({ error: "Heartbeat run not found" });
|
||||
return;
|
||||
}
|
||||
assertCompanyAccess(req, run.companyId);
|
||||
|
||||
const context = asRecord(run.contextSnapshot);
|
||||
const executionWorkspaceId = asNonEmptyString(context?.executionWorkspaceId);
|
||||
const operations = await workspaceOperations.listForRun(runId, executionWorkspaceId);
|
||||
res.json(redactCurrentUserValue(operations));
|
||||
});
|
||||
|
||||
router.get("/workspace-operations/:operationId/log", async (req, res) => {
|
||||
const operationId = req.params.operationId as string;
|
||||
const operation = await workspaceOperations.getById(operationId);
|
||||
if (!operation) {
|
||||
res.status(404).json({ error: "Workspace operation not found" });
|
||||
return;
|
||||
}
|
||||
assertCompanyAccess(req, operation.companyId);
|
||||
|
||||
const offset = Number(req.query.offset ?? 0);
|
||||
const limitBytes = Number(req.query.limitBytes ?? 256000);
|
||||
const result = await workspaceOperations.readLog(operationId, {
|
||||
offset: Number.isFinite(offset) ? offset : 0,
|
||||
limitBytes: Number.isFinite(limitBytes) ? limitBytes : 256000,
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
router.get("/issues/:issueId/live-runs", async (req, res) => {
|
||||
const rawId = req.params.issueId as string;
|
||||
const issueSvc = issueService(db);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type { Db } from "@paperclipai/db";
|
|||
import { issues, projects, projectWorkspaces } from "@paperclipai/db";
|
||||
import { updateExecutionWorkspaceSchema } from "@paperclipai/shared";
|
||||
import { validate } from "../middleware/validate.js";
|
||||
import { executionWorkspaceService, logActivity } from "../services/index.js";
|
||||
import { executionWorkspaceService, logActivity, workspaceOperationService } from "../services/index.js";
|
||||
import { parseProjectExecutionWorkspacePolicy } from "../services/execution-workspace-policy.js";
|
||||
import {
|
||||
cleanupExecutionWorkspaceArtifacts,
|
||||
|
|
@ -17,6 +17,7 @@ const TERMINAL_ISSUE_STATUSES = new Set(["done", "cancelled"]);
|
|||
export function executionWorkspaceRoutes(db: Db) {
|
||||
const router = Router();
|
||||
const svc = executionWorkspaceService(db);
|
||||
const workspaceOperationsSvc = workspaceOperationService(db);
|
||||
|
||||
router.get("/companies/:companyId/execution-workspaces", async (req, res) => {
|
||||
const companyId = req.params.companyId as string;
|
||||
|
|
@ -121,6 +122,10 @@ export function executionWorkspaceRoutes(db: Db) {
|
|||
workspace: existing,
|
||||
projectWorkspace,
|
||||
teardownCommand: projectPolicy?.workspaceStrategy?.teardownCommand ?? null,
|
||||
recorder: workspaceOperationsSvc.createRecorder({
|
||||
companyId: existing.companyId,
|
||||
executionWorkspaceId: existing.id,
|
||||
}),
|
||||
});
|
||||
cleanupWarnings = cleanupResult.warnings;
|
||||
const cleanupPatch: Record<string, unknown> = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue