mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-27 11:20:42 +01:00
Add sandbox work-folder persistence and shared lifecycle
Introduce company-scoped task, agent, user and project files, recoverable trash, object-backed repository checkpoints, and a shared 180-second lifecycle for sandbox execution. Wire file browsing and save status into the owner surfaces. This is an implementation checkpoint; staging acceptance and the complete merge gate remain outstanding.
This commit is contained in:
1 parent
392ab26b1e
commit
a13bb6b395
51 files changed
+176079
-37
No files matched your search
@@ -2728,3 +2728,4 @@ export {
|
||||
rewriteUrlHostToLoopback,
|
||||
} from "./runtime-exposure/loopback-bind.js";
|
||||
export { ACCOUNT_HANDLE_MAX_LENGTH, toAccountHandle } from "./account-handle.js";
|
||||
export * from "./work-folders.js";
|
||||
@@ -0,0 +1,63 @@
|
||||
/** Durable Paperclip files; CLI homes and caches are deliberately not scopes. */
|
||||
export const WORK_FOLDER_SCOPES = ["task", "agent", "user", "project"] as const;
|
||||
export type WorkFolderScope = (typeof WORK_FOLDER_SCOPES)[number];
|
||||
export const WORK_FOLDER_SYNC_INTERVAL_MS = 180_000;
|
||||
|
||||
export interface WorkFolderOwner {
|
||||
companyId: string;
|
||||
scope: WorkFolderScope;
|
||||
ownerId: string;
|
||||
}
|
||||
|
||||
export interface WorkFile {
|
||||
id: string;
|
||||
path: string;
|
||||
kind: "file" | "directory";
|
||||
byteSize: number;
|
||||
sha256: string | null;
|
||||
executable: boolean;
|
||||
contentType: string;
|
||||
deletedAt: string | null;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface WorkFolderListing {
|
||||
id: string;
|
||||
owner: WorkFolderOwner;
|
||||
files: WorkFile[];
|
||||
nextCursor: string | null;
|
||||
}
|
||||
|
||||
export interface WorkFolderSyncStatus {
|
||||
runId: string;
|
||||
state: "starting" | "saved" | "saving" | "failed";
|
||||
lastSavedAt: string | null;
|
||||
error: string | null;
|
||||
refreshRequested: boolean;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface SandboxWorkFolderManifest {
|
||||
version: 1;
|
||||
companyId: string;
|
||||
runId: string;
|
||||
taskId: string | null;
|
||||
agentId: string;
|
||||
responsibleUserId: string | null;
|
||||
projectId: string | null;
|
||||
leaseId: string;
|
||||
sandboxKey: string;
|
||||
home: string;
|
||||
finalCheckpointAt?: string;
|
||||
folders: Record<WorkFolderScope, string | null>;
|
||||
repositories: Array<{ bindingId: string; workspaceId: string; name: string; primary: boolean }>;
|
||||
}
|
||||
|
||||
/** Reject ambiguous paths instead of normalizing traversal into a valid path. */
|
||||
export function validateWorkFilePath(value: string): string {
|
||||
if (!value || value.length > 1024 || /[\\\x00-\x1f\x7f]/.test(value)
|
||||
|| value.split("/").some((part) => !part || part === "." || part === ".." || part === ".paperclip-runtime")) {
|
||||
throw new Error("Invalid work file path");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
Reference in new issue
Block a user