← Files Biological Sequence & Alignment ViewerARCHIVED FILE
src/workspace-browser-protocol.ts
4.81 KB · Sep 30, 2026 · 23:01 UTC
import { z } from "zod";
import { SEQUENCE_VIEWER_LIMITS } from "./runtime-contract";
import { sequenceViewerExportInputSchema } from "./viewer-operations";
export const SEQUENCE_LIST_WORKSPACE_DIRECTORY_TOOL_NAME =
"sequence.list_workspace_export_directory";
export const SEQUENCE_CREATE_WORKSPACE_DIRECTORY_TOOL_NAME =
"sequence.create_workspace_export_directory";
const sessionIdSchema = z.string().uuid();
const windowsReservedName = /^(?:AUX|CON|NUL|PRN|COM[1-9]|LPT[1-9])$/u;
export function isSafeWorkspaceBrowserChildName(value: string): boolean {
const windowsBase = value.split(".")[0]?.toUpperCase() ?? "";
return (
value.length > 0 &&
value !== "." &&
value !== ".." &&
!value.endsWith(".") &&
!value.endsWith(" ") &&
!/[\\/:\0\r\n]/u.test(value) &&
!windowsReservedName.test(windowsBase) &&
new TextEncoder().encode(value).byteLength <= 255
);
}
const workspaceDirectorySchema = z
.string()
.trim()
.min(1)
.max(4_096)
.refine(
(value) =>
!value.startsWith("/") &&
!value.includes("\\") &&
!/[\0\r\n]/u.test(value) &&
!/^[A-Za-z]:/u.test(value) &&
!/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(value) &&
(value === "." ||
value
.split("/")
.every(
(segment) =>
segment === ".." || isSafeWorkspaceBrowserChildName(segment),
)),
"directory must be relative to the opened source directory.",
);
const childNameSchema = z
.string()
.min(1)
.max(255)
.refine(
isSafeWorkspaceBrowserChildName,
"name must be one safe child segment.",
);
const candidateSchema = z
.object({
format: sequenceViewerExportInputSchema.shape.format,
name: childNameSchema,
})
.strict();
export const sequenceListWorkspaceDirectoryInputSchema = z
.object({
candidate: candidateSchema.optional(),
cursor: z.string().trim().min(1).max(1_024).optional(),
directory: workspaceDirectorySchema.default("."),
limit: z
.number()
.int()
.min(1)
.max(SEQUENCE_VIEWER_LIMITS.workspace.maxDirectoryPageSize)
.default(50),
sessionId: sessionIdSchema,
})
.strict();
const safeWorkspacePathSchema = z
.string()
.min(1)
.max(4_096)
.refine(
(value) =>
!value.startsWith("/") &&
!value.includes("\\") &&
!value.includes(":") &&
!/[\0\r\n]/u.test(value) &&
!/^[A-Za-z]:/u.test(value),
"workspace path must be relative.",
);
export const sequenceWorkspaceDirectoryCandidateSchema = z
.object({
exactAvailable: z.boolean(),
exactWorkspacePath: safeWorkspacePathSchema,
name: childNameSchema,
nextVersionName: childNameSchema.optional(),
nextVersionWorkspacePath: safeWorkspacePathSchema.optional(),
})
.strict();
export const sequenceListWorkspaceDirectoryResultSchema = z
.object({
candidate: sequenceWorkspaceDirectoryCandidateSchema.optional(),
directory: z
.object({
breadcrumbs: z
.array(
z
.object({
label: childNameSchema.or(z.literal("Workspace")),
relativePath: workspaceDirectorySchema,
workspacePath: safeWorkspacePathSchema,
})
.strict(),
)
.max(256),
parentRelativePath: workspaceDirectorySchema.optional(),
relativePath: workspaceDirectorySchema,
sourceDirectoryWorkspacePath: safeWorkspacePathSchema,
workspacePath: safeWorkspacePathSchema,
})
.strict(),
entries: z
.array(
z
.object({
kind: z.enum(["directory", "file"]),
name: childNameSchema,
relativePath: workspaceDirectorySchema,
size: z.number().int().nonnegative().optional(),
})
.strict(),
)
.max(SEQUENCE_VIEWER_LIMITS.workspace.maxDirectoryPageSize),
nextCursor: z.string().min(1).max(1_024).optional(),
omittedEntries: z.number().int().nonnegative(),
})
.strict();
export const sequenceCreateWorkspaceDirectoryInputSchema = z
.object({
name: childNameSchema,
parentDirectory: workspaceDirectorySchema.default("."),
sessionId: sessionIdSchema,
})
.strict();
export const sequenceCreateWorkspaceDirectoryResultSchema = z
.object({
name: childNameSchema,
relativePath: workspaceDirectorySchema,
workspacePath: safeWorkspacePathSchema,
})
.strict();
export type SequenceListWorkspaceDirectoryInput = z.infer<
typeof sequenceListWorkspaceDirectoryInputSchema
>;
export type SequenceListWorkspaceDirectoryResult = z.infer<
typeof sequenceListWorkspaceDirectoryResultSchema
>;
export type SequenceCreateWorkspaceDirectoryInput = z.infer<
typeof sequenceCreateWorkspaceDirectoryInputSchema
>;
export type SequenceCreateWorkspaceDirectoryResult = z.infer<
typeof sequenceCreateWorkspaceDirectoryResultSchema
>;
SHA-256: ea451e58999e1d68f12b9245f5503166694048c40d1e3d8e5da30748d3568eb2