← Files Biological Sequence & Alignment ViewerARCHIVED FILE
src/chat-file-resource.ts
44.4 KB · Sep 30, 2026 · 23:01 UTC
import {
createHash,
createHmac,
randomBytes,
randomUUID,
timingSafeEqual,
} from "node:crypto";
import { constants } from "node:fs";
import {
chmod,
lstat,
mkdir,
open,
readFile,
readdir,
realpath,
stat,
unlink,
writeFile,
} from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { createInterface } from "node:readline";
import { Transform } from "node:stream";
import { createGunzip, gunzip as gunzipCallback } from "node:zlib";
import {
McpServer,
ResourceTemplate,
} from "@modelcontextprotocol/sdk/server/mcp.js";
import { ListRootsResultSchema } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import {
isGzipCompressedBiologicalFileName,
stripBiologicalCompressionSuffix,
} from "./compressed-file-name";
import { createHistoricalViewerFileError } from "./resource-error";
import { serializeIndexedSequenceEnvelope } from "./indexed-sequence-envelope";
import { parseIndexedSequenceLines } from "./indexed-sequence-parser";
import { serializeBinarySequenceEnvelope } from "./binary-sequence-envelope";
import {
getBinarySequenceFormatHint,
sniffBinarySequenceFormat,
} from "./sequence/binary-parser";
import {
SEQUENCE_VIEWER_LIMITS,
assertTextWithinInputBudget,
formatBytes,
} from "./runtime-contract";
const gunzip = promisify(gunzipCallback);
const DEFAULT_MAX_PERSISTED_FILES = 256;
const DEFAULT_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1_000;
const MAX_RECORD_BYTES = 64 * 1_024;
const LEGACY_RECORD_VERSION = 1;
const RECORD_VERSION = 2;
const SIGNING_KEY_BYTES = 32;
const TOKEN_PATTERN =
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
export type ChatViewerFileInput = {
primaryFile: {
name: string;
uri: string;
};
};
export type RootsRequestExtra = {
signal?: AbortSignal;
sendRequest: (
request: { method: "roots/list"; params?: Record<string, never> },
schema: typeof ListRootsResultSchema,
) => Promise<{ roots: Array<{ uri: string }> }>;
};
export type OpenedChatViewerFile = {
discard?: () => Promise<void>;
rangeOnly?: true;
sessionId?: string;
sourcePath: string;
viewerFile: ChatViewerFileInput;
workspaceBacked: boolean;
};
export type ResolvedChatViewerResource = {
rangeOnly?: true;
sessionId: string;
sourcePath: string;
workspaceBacked: boolean;
workspaceRoot: string | null;
};
export type ExpectedChatFileSource = {
byteLength: number;
fileIdentity: {
device: string;
inode: string;
modifiedAtNanoseconds: string;
size: string;
};
sha256: string;
};
export type VerifiedOpenedChatViewerFile = OpenedChatViewerFile & {
discard: () => Promise<void>;
};
export async function readWorkspaceFileBytes({
extra,
filePath,
isSupportedFileName,
maxFileBytes,
viewerName,
}: {
extra: RootsRequestExtra;
filePath: string;
isSupportedFileName: (fileName: string) => boolean;
maxFileBytes: number;
viewerName: string;
}): Promise<{ bytes: Buffer; name: string }> {
const openedFile = await resolveWorkspaceFile({
extra,
filePath,
isSupportedFileName,
viewerName,
});
assertFileIdentityWithinBudget(openedFile.fileIdentity, maxFileBytes);
const fileHandle = await open(openedFile.absolutePath, "r");
try {
const beforeRead = fileIdentityFromStat(
await fileHandle.stat({ bigint: true }),
);
if (!sameFileIdentity(beforeRead, openedFile.fileIdentity)) {
throw new Error(
`${openedFile.name} changed before it could be read. Retry the operation.`,
);
}
const bytes = await fileHandle.readFile();
const afterRead = fileIdentityFromStat(
await fileHandle.stat({ bigint: true }),
);
const currentPathIdentity = await readFileIdentity(openedFile.absolutePath);
if (
currentPathIdentity == null ||
!sameFileIdentity(beforeRead, afterRead) ||
!sameFileIdentity(afterRead, currentPathIdentity)
) {
throw new Error(
`${openedFile.name} moved or changed while it was being read. Retry the operation.`,
);
}
return { bytes, name: openedFile.name };
} finally {
await fileHandle.close();
}
}
async function assertOpenedFileMatchesExpectedSource(
openedFile: OpenedFile,
expected: ExpectedChatFileSource,
): Promise<void> {
if (
expected.byteLength !== Number(openedFile.fileIdentity.size) ||
expected.fileIdentity.size !== String(expected.byteLength) ||
!sameFileIdentity(openedFile.fileIdentity, expected.fileIdentity) ||
!/^[a-f0-9]{64}$/u.test(expected.sha256)
) {
throw new Error(
"The acquired public example changed before it could be opened.",
);
}
const fileHandle = await open(
openedFile.absolutePath,
constants.O_RDONLY | constants.O_NOFOLLOW,
);
try {
const beforeRead = fileIdentityFromStat(
await fileHandle.stat({ bigint: true }),
);
if (!sameFileIdentity(beforeRead, expected.fileIdentity)) {
throw new Error(
"The acquired public example changed before it could be opened.",
);
}
const bytes = await fileHandle.readFile();
const afterRead = fileIdentityFromStat(
await fileHandle.stat({ bigint: true }),
);
const currentPathIdentity = await readFileIdentity(openedFile.absolutePath);
if (
bytes.byteLength !== expected.byteLength ||
createHash("sha256").update(bytes).digest("hex") !== expected.sha256 ||
currentPathIdentity == null ||
!sameFileIdentity(beforeRead, afterRead) ||
!sameFileIdentity(afterRead, currentPathIdentity) ||
!sameFileIdentity(afterRead, expected.fileIdentity)
) {
throw new Error(
"The acquired public example changed before it could be opened.",
);
}
} finally {
await fileHandle.close();
}
}
type LegacyFileIdentity = {
device: string;
inode: string;
modifiedAtNanoseconds: string;
size: string;
};
type FileIdentity = LegacyFileIdentity & {
changedAtNanoseconds: string;
};
type OpenedFile = {
absolutePath: string;
bundled?: true;
expectedSha256?: string;
fileIdentity: FileIdentity;
name: string;
rangeOnly?: true;
sessionId?: string;
workspaceRoot: string | null;
};
const legacyFileIdentitySchema = z
.object({
device: z.string().regex(/^\d+$/),
inode: z.string().regex(/^\d+$/),
modifiedAtNanoseconds: z.string().regex(/^-?\d+$/),
size: z.string().regex(/^\d+$/),
})
.strict();
const fileIdentitySchema = legacyFileIdentitySchema
.extend({ changedAtNanoseconds: z.string().regex(/^-?\d+$/) })
.strict();
const expectedSha256Schema = z.string().regex(/^[a-f0-9]{64}$/u);
const persistedOpenedFilePayloadSchema = z
.object({
absolutePath: z.string().max(32_768).refine(path.isAbsolute),
bundled: z.literal(true).optional(),
expectedSha256: expectedSha256Schema.optional(),
fileIdentity: fileIdentitySchema,
name: z.string().min(1).max(1_024),
openedAt: z.number().int().nonnegative(),
rangeOnly: z.literal(true).optional(),
sessionId: z.string().uuid().optional(),
token: z.string().regex(TOKEN_PATTERN),
version: z.literal(RECORD_VERSION),
workspaceRoot: z.string().max(32_768).refine(path.isAbsolute).nullable(),
})
.strict();
// A v1 record cannot prove that its source escaped a same-inode rewrite with a
// restored mtime. Preserve only public handles whose signed digest still
// authenticates the bytes; ordinary legacy handles must be opened again.
const legacyPersistedOpenedFilePayloadSchema = persistedOpenedFilePayloadSchema
.extend({
expectedSha256: expectedSha256Schema,
fileIdentity: legacyFileIdentitySchema,
version: z.literal(LEGACY_RECORD_VERSION),
})
.strict();
const persistedOpenedFileSchema = z.discriminatedUnion("version", [
persistedOpenedFilePayloadSchema
.extend({ signature: z.string().regex(/^[A-Za-z0-9_-]{43}$/) })
.strict(),
legacyPersistedOpenedFilePayloadSchema
.extend({ signature: z.string().regex(/^[A-Za-z0-9_-]{43}$/) })
.strict(),
]);
type PersistedOpenedFilePayload =
| z.infer<typeof persistedOpenedFilePayloadSchema>
| z.infer<typeof legacyPersistedOpenedFilePayloadSchema>;
// Codex mints host-managed file resources for native file entrypoints. Chat
// tools keep the same resources/read contract for workspace files while
// persisting only signed, opaque handles under the user's private Codex state
// directory.
export function createChatFileResourceStore({
isSupportedFileName,
maxAgeMs = DEFAULT_MAX_AGE_MS,
maxCompressedFileBytes = 100 * 1_024 * 1_024,
maxFileBytes = SEQUENCE_VIEWER_LIMITS.input.maxTextBytes,
maxIndexedFileBytes,
maxRangedFileBytes,
maxPersistedFiles = DEFAULT_MAX_PERSISTED_FILES,
now = Date.now,
resourceUriPrefix,
stateDirectory = defaultChatFileStateDirectory(),
viewerName,
}: {
isSupportedFileName: (fileName: string) => boolean;
maxAgeMs?: number;
maxCompressedFileBytes?: number;
maxFileBytes?: number;
maxIndexedFileBytes?: number;
maxRangedFileBytes?: number;
maxPersistedFiles?: number;
now?: () => number;
resourceUriPrefix: string;
stateDirectory?: string;
viewerName: string;
}): {
open: (
filePath: string,
extra: RootsRequestExtra,
) => Promise<ChatViewerFileInput>;
openWithSource: (
filePath: string,
extra: RootsRequestExtra,
sessionId?: string,
) => Promise<OpenedChatViewerFile>;
openVerifiedWithSource: (
filePath: string,
expected: ExpectedChatFileSource,
extra: RootsRequestExtra,
sessionId?: string,
) => Promise<VerifiedOpenedChatViewerFile>;
openIndexedWorkspaceFile: (
filePath: string,
extra: RootsRequestExtra,
minimumSourceBytes?: number,
allocateSessionId?: () => string,
) => Promise<OpenedChatViewerFile | null>;
resolveResource: (
resourceUri: string,
sessionId: string,
extra: RootsRequestExtra,
) => Promise<ResolvedChatViewerResource>;
register: (server: McpServer) => void;
} {
const resolvedMaxIndexedFileBytes =
maxIndexedFileBytes ??
(maxFileBytes === SEQUENCE_VIEWER_LIMITS.input.maxTextBytes
? 512 * 1_024 * 1_024
: maxFileBytes);
const resolvedMaxRangedFileBytes =
maxRangedFileBytes ?? resolvedMaxIndexedFileBytes;
async function rememberOpenedFileRecord(
openedFile: OpenedFile,
sessionOwner?: string | (() => string),
): Promise<{
discard: () => Promise<void>;
rangeOnly?: true;
sessionId?: string;
viewerFile: ChatViewerFileInput;
}> {
const rangeOnly =
maxRangedFileBytes != null &&
openedFile.workspaceRoot != null &&
isIndexableSequenceFileName(openedFile.name) &&
!isGzipCompressedBiologicalFileName(openedFile.name) &&
BigInt(openedFile.fileIdentity.size) >
BigInt(resolvedMaxIndexedFileBytes);
assertFileIdentityWithinBudget(
openedFile.fileIdentity,
rangeOnly
? resolvedMaxRangedFileBytes
: isIndexableSequenceFileName(openedFile.name)
? resolvedMaxIndexedFileBytes
: isGzipCompressedBiologicalFileName(openedFile.name)
? maxCompressedFileBytes
: maxFileBytes,
);
const sessionId =
typeof sessionOwner === "function" ? sessionOwner() : sessionOwner;
if (sessionId != null && !z.string().uuid().safeParse(sessionId).success) {
throw new Error("The biological viewer session identity is invalid.");
}
const token = randomUUID();
try {
await persistOpenedFile({
maxAgeMs,
maxPersistedFiles,
now: now(),
openedFile: {
...openedFile,
...(rangeOnly ? { rangeOnly: true as const } : {}),
...(sessionId == null ? {} : { sessionId }),
},
stateDirectory,
token,
});
} catch {
throw new Error(
"The viewer could not securely save this file handle. Check that Codex local state is writable and retry.",
);
}
return {
discard: async () => unlinkIfExists(recordPath(stateDirectory, token)),
...(rangeOnly ? { rangeOnly: true } : {}),
...(sessionId == null ? {} : { sessionId }),
viewerFile: {
primaryFile: {
name: openedFile.name,
uri: `${resourceUriPrefix}/${token}`,
},
},
};
}
async function openWithSource(
filePath: string,
extra: RootsRequestExtra,
sessionId?: string,
): Promise<OpenedChatViewerFile> {
const openedFile = await resolveWorkspaceFile({
extra,
filePath,
isSupportedFileName,
viewerName,
});
const remembered = await rememberOpenedFileRecord(openedFile, sessionId);
return {
discard: remembered.discard,
...(remembered.rangeOnly ? { rangeOnly: true } : {}),
...(remembered.sessionId == null
? {}
: { sessionId: remembered.sessionId }),
sourcePath: openedFile.absolutePath,
viewerFile: remembered.viewerFile,
workspaceBacked: openedFile.workspaceRoot != null,
};
}
async function openVerifiedWithSource(
filePath: string,
expected: ExpectedChatFileSource,
extra: RootsRequestExtra,
sessionId?: string,
): Promise<VerifiedOpenedChatViewerFile> {
const openedFile = await resolveWorkspaceFile({
extra,
filePath,
isSupportedFileName,
viewerName,
});
await assertOpenedFileMatchesExpectedSource(openedFile, expected);
const remembered = await rememberOpenedFileRecord(
{
...openedFile,
expectedSha256: expected.sha256,
},
sessionId,
);
return {
discard: remembered.discard,
...(remembered.sessionId == null
? {}
: { sessionId: remembered.sessionId }),
sourcePath: openedFile.absolutePath,
viewerFile: remembered.viewerFile,
workspaceBacked: openedFile.workspaceRoot != null,
};
}
async function openIndexedWorkspaceFile(
filePath: string,
extra: RootsRequestExtra,
minimumSourceBytes = SEQUENCE_VIEWER_LIMITS.input.maxTextBytes,
allocateSessionId?: () => string,
): Promise<OpenedChatViewerFile | null> {
if (!isIndexableSequenceFileName(path.basename(filePath))) {
return null;
}
let openedFile: OpenedFile;
try {
openedFile = await resolveWorkspaceFile({
extra,
filePath,
isSupportedFileName,
viewerName,
});
} catch {
// Trusted metadata that cannot be proven to identify a workspace file
// must leave the host-owned resource route unchanged.
return null;
}
const sourceBytes = Number(openedFile.fileIdentity.size);
if (
openedFile.workspaceRoot == null ||
!Number.isSafeInteger(sourceBytes) ||
sourceBytes <= minimumSourceBytes
) {
return null;
}
const remembered = await rememberOpenedFileRecord(
openedFile,
allocateSessionId,
);
return {
discard: remembered.discard,
...(remembered.rangeOnly ? { rangeOnly: true } : {}),
...(remembered.sessionId == null
? {}
: { sessionId: remembered.sessionId }),
sourcePath: openedFile.absolutePath,
viewerFile: remembered.viewerFile,
workspaceBacked: true,
};
}
return {
async open(filePath, extra) {
return (await openWithSource(filePath, extra)).viewerFile;
},
openIndexedWorkspaceFile,
openVerifiedWithSource,
openWithSource,
async resolveResource(resourceUri, sessionId, extra) {
const prefix = `${resourceUriPrefix}/`;
if (
!resourceUri.startsWith(prefix) ||
!TOKEN_PATTERN.test(resourceUri.slice(prefix.length)) ||
!z.string().uuid().safeParse(sessionId).success
) {
throw new Error("The saved biological viewer presentation is invalid.");
}
extra.signal?.throwIfAborted();
const record = await readPersistedOpenedFile({
isSupportedFileName,
maxAgeMs,
now: now(),
stateDirectory,
token: resourceUri.slice(prefix.length),
});
if (record.sessionId == null || record.sessionId !== sessionId) {
throw new Error(
"The saved biological viewer presentation does not own this session.",
);
}
if (record.bundled) {
throw new Error("The saved biological viewer source cannot be restored.");
}
const canonicalPath = await realpath(record.absolutePath).catch(() => null);
if (canonicalPath == null || canonicalPath !== record.absolutePath) {
throw new Error("The saved biological viewer source moved or changed.");
}
const roots = await tryListCanonicalWorkspaceRoots(extra);
const activeRoot = roots
?.filter((root) => isPathWithin(root, canonicalPath))
.sort((left, right) => right.length - left.length)[0];
if (
(record.workspaceRoot != null &&
(activeRoot !== record.workspaceRoot ||
(await realpath(record.workspaceRoot).catch(() => null)) !==
record.workspaceRoot)) ||
(record.workspaceRoot == null &&
roots != null &&
roots.length > 0 &&
activeRoot == null)
) {
throw new Error(
"The saved biological viewer source is outside its active workspace.",
);
}
extra.signal?.throwIfAborted();
const handle = await open(
canonicalPath,
constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),
).catch(() => null);
if (handle == null) {
throw new Error("The saved biological viewer source moved or changed.");
}
try {
const before = await handle.stat({ bigint: true });
const named = await lstat(canonicalPath, { bigint: true }).catch(
() => null,
);
const after = await handle.stat({ bigint: true });
if (
named == null ||
!before.isFile() ||
!named.isFile() ||
named.isSymbolicLink() ||
!sameFileIdentity(fileIdentityFromStat(before), record.fileIdentity) ||
!sameFileIdentity(fileIdentityFromStat(after), record.fileIdentity) ||
!sameFileIdentity(fileIdentityFromStat(named), record.fileIdentity) ||
before.nlink !== after.nlink ||
before.nlink !== named.nlink
) {
throw new Error("The saved biological viewer source moved or changed.");
}
if (record.version === LEGACY_RECORD_VERSION) {
const digest = createHash("sha256");
if (before.size > 0n) {
for await (const chunk of handle.createReadStream({
autoClose: false,
end: Number(before.size) - 1,
highWaterMark: 64 * 1_024,
start: 0,
})) {
extra.signal?.throwIfAborted();
digest.update(chunk);
}
}
const afterDigest = await handle.stat({ bigint: true });
const namedAfterDigest = await lstat(canonicalPath, {
bigint: true,
}).catch(() => null);
if (
digest.digest("hex") !== record.expectedSha256 ||
namedAfterDigest == null ||
!namedAfterDigest.isFile() ||
namedAfterDigest.isSymbolicLink() ||
!sameFileIdentity(
fileIdentityFromStat(afterDigest),
record.fileIdentity,
) ||
!sameFileIdentity(
fileIdentityFromStat(namedAfterDigest),
record.fileIdentity,
) ||
afterDigest.nlink !== namedAfterDigest.nlink
) {
throw new Error(
"The saved biological viewer source moved or changed.",
);
}
}
extra.signal?.throwIfAborted();
return {
...(record.rangeOnly ? { rangeOnly: true as const } : {}),
sessionId,
sourcePath: canonicalPath,
workspaceBacked: record.workspaceRoot != null,
workspaceRoot: record.workspaceRoot,
};
} finally {
await handle.close();
}
},
register(server) {
server.registerResource(
`${viewerName} chat file`,
new ResourceTemplate(`${resourceUriPrefix}/{token}`, {
list: undefined,
}),
{
description: `A local file opened in the ${viewerName}.`,
mimeType: "text/plain",
},
async (uri, variables, extra) => {
const token = getSingleTemplateVariable(variables.token);
if (token == null || !TOKEN_PATTERN.test(token)) {
throw createHistoricalViewerFileError(
"This historical viewer link has expired. Reopen the file from chat.",
);
}
const openedFile = await readPersistedOpenedFile({
isSupportedFileName,
maxAgeMs,
now: now(),
stateDirectory,
token,
});
return {
contents: [
{
mimeType: "text/plain",
text: await readOpenedFileContents(
openedFile,
extra,
maxFileBytes,
maxCompressedFileBytes,
resolvedMaxIndexedFileBytes,
),
uri: uri.href,
},
],
};
},
);
},
};
}
function defaultChatFileStateDirectory(): string {
const configuredCodexHome = process.env.CODEX_HOME?.trim();
return path.resolve(
configuredCodexHome || path.join(os.homedir(), ".codex"),
"state",
"plugins",
"sequence-viewer",
"opened-files-v1",
);
}
async function resolveWorkspaceFile({
extra,
filePath,
isSupportedFileName,
viewerName,
}: {
extra: RootsRequestExtra;
filePath: string;
isSupportedFileName: (fileName: string) => boolean;
viewerName: string;
}): Promise<OpenedFile> {
const requestedName = path.basename(filePath);
if (!isSupportedFileName(requestedName)) {
throw new Error(`${requestedName} is not supported by the ${viewerName}.`);
}
const roots = await tryListCanonicalWorkspaceRoots(extra);
if (!path.isAbsolute(filePath) && (roots == null || roots.length === 0)) {
throw new Error(
`Relative paths require active local workspace roots. Retry with the exact absolute path to ${requestedName}.`,
);
}
const candidates = path.isAbsolute(filePath)
? [path.resolve(filePath)]
: (roots ?? []).map((root) => path.resolve(root, filePath));
const resolvedFiles = new Map<string, OpenedFile>();
for (const candidate of candidates) {
let canonicalCandidate: string;
try {
canonicalCandidate = await realpath(candidate);
} catch {
continue;
}
const workspaceRoot =
roots
?.filter((root) => isPathWithin(root, canonicalCandidate))
.sort((left, right) => right.length - left.length)[0] ?? null;
if (roots != null && roots.length > 0 && workspaceRoot == null) {
continue;
}
try {
const fileIdentity = await readFileIdentity(canonicalCandidate);
if (fileIdentity == null) {
continue;
}
resolvedFiles.set(canonicalCandidate, {
absolutePath: canonicalCandidate,
fileIdentity,
name: requestedName,
workspaceRoot,
});
} catch {
continue;
}
}
if (resolvedFiles.size === 1) {
return [...resolvedFiles.values()][0] as OpenedFile;
}
if (resolvedFiles.size > 1) {
throw new Error(
`The relative path to ${requestedName} matches multiple active workspace roots. Retry with its exact absolute path.`,
);
}
throw new Error(
roots == null || roots.length === 0
? `${filePath} was not found as a readable local file.`
: `${filePath} was not found inside the active local workspace roots.`,
);
}
async function persistOpenedFile({
maxAgeMs,
maxPersistedFiles,
now,
openedFile,
stateDirectory,
token,
}: {
maxAgeMs: number;
maxPersistedFiles: number;
now: number;
openedFile: OpenedFile;
stateDirectory: string;
token: string;
}): Promise<void> {
await prepareStateDirectory(stateDirectory);
const payload: PersistedOpenedFilePayload = {
...openedFile,
openedAt: now,
token,
version: RECORD_VERSION,
};
const key = await readOrCreateSigningKey(stateDirectory);
const persistedPath = recordPath(stateDirectory, token);
let recordWritten = false;
try {
await writeFile(
persistedPath,
`${JSON.stringify({
...payload,
signature: signOpenedFile(payload, key),
})}\n`,
{ flag: "wx", mode: 0o600 },
);
recordWritten = true;
await prunePersistedFiles({
maxAgeMs,
maxPersistedFiles,
now,
stateDirectory,
});
} catch (error) {
if (recordWritten) {
await unlinkIfExists(persistedPath).catch(() => undefined);
}
throw error;
}
}
async function readPersistedOpenedFile({
isSupportedFileName,
maxAgeMs,
now,
stateDirectory,
token,
}: {
isSupportedFileName: (fileName: string) => boolean;
maxAgeMs: number;
now: number;
stateDirectory: string;
token: string;
}): Promise<PersistedOpenedFilePayload> {
try {
await prepareStateDirectory(stateDirectory);
const persistedPath = recordPath(stateDirectory, token);
const persistedStat = await lstat(persistedPath);
if (!persistedStat.isFile() || persistedStat.size > MAX_RECORD_BYTES) {
throw new Error("Invalid persisted viewer record.");
}
const parsed = persistedOpenedFileSchema.safeParse(
JSON.parse(await readFile(persistedPath, "utf8")),
);
if (!parsed.success || parsed.data.token !== token) {
throw new Error("Invalid persisted viewer record.");
}
const { signature, ...payload } = parsed.data;
const key = await readOrCreateSigningKey(stateDirectory);
const actualSignature = Buffer.from(signature, "base64url");
const expectedSignature = Buffer.from(
signOpenedFile(payload, key),
"base64url",
);
if (
actualSignature.length !== expectedSignature.length ||
!timingSafeEqual(actualSignature, expectedSignature)
) {
throw new Error("Invalid persisted viewer record signature.");
}
if (now - payload.openedAt > maxAgeMs) {
await unlinkIfExists(persistedPath);
throw new Error("Expired persisted viewer record.");
}
if (
!isSupportedFileName(payload.name) ||
payload.name.includes("/") ||
payload.name.includes("\\") ||
(payload.workspaceRoot != null &&
!isPathWithin(payload.workspaceRoot, payload.absolutePath))
) {
throw new Error("Expired persisted viewer record.");
}
return payload;
} catch {
throw createHistoricalViewerFileError(
"This historical viewer link has expired. Reopen the file from chat.",
);
}
}
async function readOpenedFileContents(
openedFile: PersistedOpenedFilePayload,
extra: RootsRequestExtra,
maxFileBytes: number,
maxCompressedFileBytes: number,
maxIndexedFileBytes: number,
): Promise<string> {
if (openedFile.rangeOnly) {
throw createHistoricalViewerFileError(
"This large workspace source is available only through the active Sequence viewer's bounded, app-only range tools. Reopen it from its workspace.",
);
}
if (openedFile.bundled) {
throw createHistoricalViewerFileError(
"Bundled example cards are retired. Choose a database-backed starter to acquire a current authoritative record.",
);
}
let canonicalPath: string;
try {
canonicalPath = await realpath(openedFile.absolutePath);
} catch {
throw createHistoricalViewerFileError(
"The original file was moved or deleted. Reopen it from its current location.",
);
}
if (canonicalPath !== openedFile.absolutePath) {
throw createHistoricalViewerFileError(
"The original file moved since this viewer card was created. Reopen it from its current location.",
);
}
if (openedFile.workspaceRoot != null) {
let canonicalOriginalRoot: string;
try {
canonicalOriginalRoot = await realpath(openedFile.workspaceRoot);
} catch {
throw createHistoricalViewerFileError(
"The original workspace is no longer available. Reopen the file from chat.",
);
}
if (
canonicalOriginalRoot !== openedFile.workspaceRoot ||
!isPathWithin(canonicalOriginalRoot, canonicalPath)
) {
throw createHistoricalViewerFileError(
"The original file is no longer available inside its workspace. Reopen it from chat.",
);
}
}
const currentRoots = await tryListCanonicalWorkspaceRoots(extra);
const currentWorkspaceRoot = currentRoots
?.filter((root) => isPathWithin(root, canonicalPath))
.sort((left, right) => right.length - left.length)[0];
if (
(openedFile.workspaceRoot != null &&
currentWorkspaceRoot !== openedFile.workspaceRoot) ||
(openedFile.workspaceRoot == null &&
currentRoots != null &&
currentRoots.length > 0 &&
currentWorkspaceRoot == null)
) {
throw createHistoricalViewerFileError(
"This viewer file is outside the active workspace. Reopen it from the correct workspace.",
);
}
let fileHandle;
try {
fileHandle = await open(
canonicalPath,
constants.O_RDONLY | constants.O_NOFOLLOW,
);
} catch {
throw createHistoricalViewerFileError(
"The original file can no longer be read. Reopen it from its current location.",
);
}
try {
const beforeRead = fileIdentityFromStat(
await fileHandle.stat({ bigint: true }),
);
assertFileIdentityWithinBudget(
beforeRead,
isIndexableSequenceFileName(openedFile.name)
? maxIndexedFileBytes
: isGzipCompressedBiologicalFileName(openedFile.name)
? maxCompressedFileBytes
: maxFileBytes,
);
if (!sameFileIdentity(beforeRead, openedFile.fileIdentity)) {
throw createHistoricalViewerFileError(
"The original file changed since this viewer card was created. Reopen it to view the current contents.",
);
}
const fileSize = Number(beforeRead.size);
const shouldIndex =
isIndexableSequenceFileName(openedFile.name) &&
(isGzipCompressedBiologicalFileName(openedFile.name) ||
fileSize > 8 * 1_024 * 1_024);
const contents = shouldIndex
? await readIndexedSequenceFile({
expectedSha256: openedFile.expectedSha256,
fileHandle,
fileName: openedFile.name,
maxDecodedBytes: maxIndexedFileBytes,
signal: extra.signal,
sourceBytes: fileSize,
sourceVersion: fileIdentityVersion(openedFile.fileIdentity),
})
: await readWholeTextFile({
compressed: isGzipCompressedBiologicalFileName(openedFile.name),
expectedSha256: openedFile.expectedSha256,
fileHandle,
fileName: openedFile.name,
maxTextBytes: maxFileBytes,
sourceBytes: fileSize,
});
const afterRead = fileIdentityFromStat(
await fileHandle.stat({ bigint: true }),
);
let currentPathIdentity: FileIdentity | null = null;
try {
currentPathIdentity = await readFileIdentity(canonicalPath);
} catch {
// The path may have moved or become unreadable after the handle opened.
}
if (
!sameFileIdentity(beforeRead, afterRead) ||
currentPathIdentity == null ||
!sameFileIdentity(afterRead, currentPathIdentity)
) {
throw createHistoricalViewerFileError(
"The original file moved or changed while it was being opened. Reopen it to view the current contents.",
);
}
assertTextWithinInputBudget(contents);
return contents;
} finally {
await fileHandle.close();
}
}
async function readWholeTextFile({
compressed,
expectedSha256,
fileHandle,
fileName,
maxTextBytes,
sourceBytes,
}: {
compressed: boolean;
expectedSha256?: string;
fileHandle: Awaited<ReturnType<typeof open>>;
fileName: string;
maxTextBytes: number;
sourceBytes: number;
}): Promise<string> {
const bytes =
expectedSha256 == null
? await fileHandle.readFile()
: await readExactFileBytes(fileHandle, sourceBytes);
assertExpectedFileDigest(bytes, expectedSha256);
const resourceText = (decoded: Uint8Array): string =>
getBinarySequenceFormatHint(fileName) != null ||
sniffBinarySequenceFormat(decoded) != null
? serializeBinarySequenceEnvelope(decoded)
: Buffer.from(
decoded.buffer,
decoded.byteOffset,
decoded.byteLength,
).toString("utf8");
if (!compressed) return resourceText(bytes);
try {
return resourceText(await gunzip(bytes, { maxOutputLength: maxTextBytes }));
} catch (error) {
if (
error instanceof RangeError ||
isNodeError(error, "ERR_BUFFER_TOO_LARGE")
) {
throw new Error(
`This compressed file expands beyond the bounded viewer limit of ${formatBytes(maxTextBytes)}. Create a smaller subset or summary and reopen it.`,
);
}
throw error;
}
}
async function readIndexedSequenceFile({
expectedSha256,
fileHandle,
fileName,
maxDecodedBytes,
signal,
sourceBytes,
sourceVersion,
}: {
expectedSha256?: string;
fileHandle: Awaited<ReturnType<typeof open>>;
fileName: string;
maxDecodedBytes: number;
signal?: AbortSignal;
sourceBytes: number;
sourceVersion: string;
}): Promise<string> {
const compressed = isGzipCompressedBiologicalFileName(fileName);
const rawStream = fileHandle.createReadStream({
autoClose: false,
...(expectedSha256 == null || sourceBytes === 0
? {}
: { end: sourceBytes - 1, start: 0 }),
});
const rawHash = expectedSha256 == null ? null : createHash("sha256");
const updateRawHash =
rawHash == null
? null
: (chunk: string | Buffer) => {
rawHash.update(chunk);
};
if (updateRawHash != null) rawStream.on("data", updateRawHash);
const decodedStream = compressed ? rawStream.pipe(createGunzip()) : rawStream;
const contentStream = decodedStream.pipe(
createDecodedByteLimitTransform(maxDecodedBytes),
);
const forwardDecodedError = (error: Error): void => {
contentStream.destroy(error);
};
const forwardRawError = (error: Error): void => {
if (decodedStream !== rawStream) decodedStream.destroy(error);
contentStream.destroy(error);
};
decodedStream.on("error", forwardDecodedError);
rawStream.on("error", forwardRawError);
contentStream.setEncoding("utf8");
const reader = createInterface({ crlfDelay: Infinity, input: contentStream });
const abort = (): void => {
contentStream.destroy(new Error("Indexed sequence loading was cancelled."));
};
signal?.addEventListener("abort", abort, { once: true });
try {
const envelope = await parseIndexedSequenceLines({
compressed,
fileName,
format: inferIndexedSequenceFormat(fileName),
lines: reader,
maxDecodedBytes,
signal,
sourceBytes,
sourceVersion,
});
if (expectedSha256 != null && rawHash?.digest("hex") !== expectedSha256) {
throw changedVerifiedSourceError();
}
return serializeIndexedSequenceEnvelope(envelope);
} finally {
signal?.removeEventListener("abort", abort);
decodedStream.off("error", forwardDecodedError);
rawStream.off("error", forwardRawError);
if (updateRawHash != null) rawStream.off("data", updateRawHash);
reader.close();
if (!contentStream.readableEnded) contentStream.destroy();
if (decodedStream !== rawStream && !decodedStream.readableEnded) {
decodedStream.destroy();
}
if (!rawStream.readableEnded) rawStream.destroy();
}
}
async function readExactFileBytes(
fileHandle: Awaited<ReturnType<typeof open>>,
byteLength: number,
): Promise<Buffer> {
const bytes = Buffer.allocUnsafe(byteLength);
let offset = 0;
while (offset < byteLength) {
const { bytesRead } = await fileHandle.read(
bytes,
offset,
byteLength - offset,
offset,
);
if (bytesRead === 0) throw changedVerifiedSourceError();
offset += bytesRead;
}
return bytes;
}
function assertExpectedFileDigest(
bytes: Uint8Array,
expectedSha256: string | undefined,
): void {
if (
expectedSha256 != null &&
createHash("sha256").update(bytes).digest("hex") !== expectedSha256
) {
throw changedVerifiedSourceError();
}
}
function changedVerifiedSourceError(): Error {
return createHistoricalViewerFileError(
"The original file changed since this viewer card was created. Reopen it to view the current contents.",
);
}
function createDecodedByteLimitTransform(maxDecodedBytes: number): Transform {
let decodedBytes = 0;
return new Transform({
transform(chunk, _encoding, callback) {
decodedBytes += Buffer.isBuffer(chunk)
? chunk.byteLength
: Buffer.byteLength(String(chunk));
if (decodedBytes > maxDecodedBytes) {
callback(indexedExpansionLimitError(maxDecodedBytes));
return;
}
callback(null, chunk);
},
});
}
function indexedExpansionLimitError(maxDecodedBytes: number): Error {
return new Error(
`This indexed file expands beyond the bounded viewer limit of ${formatBytes(maxDecodedBytes)}. Create a smaller subset or summary and reopen it.`,
);
}
function isIndexableSequenceFileName(fileName: string): boolean {
return /\.(?:fasta|fas|fa|fna|faa|fastq|fq)$/iu.test(
stripBiologicalCompressionSuffix(fileName),
);
}
function inferIndexedSequenceFormat(fileName: string): "fasta" | "fastq" {
return /\.(?:fastq|fq)$/iu.test(stripBiologicalCompressionSuffix(fileName))
? "fastq"
: "fasta";
}
function fileIdentityVersion(
identity: FileIdentity | LegacyFileIdentity,
): string {
return createHash("sha256")
.update(
[
identity.device,
identity.inode,
identity.modifiedAtNanoseconds,
identity.size,
...("changedAtNanoseconds" in identity
? [identity.changedAtNanoseconds]
: []),
].join(":"),
)
.digest("hex");
}
function assertFileIdentityWithinBudget(
identity: FileIdentity,
maxFileBytes: number,
): void {
const size = Number(identity.size);
if (!Number.isSafeInteger(size) || size > maxFileBytes) {
throw new Error(
`This file is ${Number.isSafeInteger(size) ? formatBytes(size) : "too large"}; the bounded viewer accepts at most ${formatBytes(maxFileBytes)} of text. Create a smaller subset or summary and reopen it.`,
);
}
}
async function prepareStateDirectory(stateDirectory: string): Promise<void> {
await mkdir(stateDirectory, { mode: 0o700, recursive: true });
const directoryStat = await lstat(stateDirectory);
if (!directoryStat.isDirectory() || directoryStat.isSymbolicLink()) {
throw new Error("Viewer state directory is not a private directory.");
}
if (
typeof process.getuid === "function" &&
directoryStat.uid !== process.getuid()
) {
throw new Error("Viewer state directory is owned by another user.");
}
if (process.platform !== "win32") {
await chmod(stateDirectory, 0o700);
}
}
async function readOrCreateSigningKey(stateDirectory: string): Promise<Buffer> {
const keyPath = path.join(stateDirectory, "signing-key");
try {
return await readSigningKey(keyPath);
} catch (error) {
if (!isNodeError(error, "ENOENT")) {
throw error;
}
}
try {
await writeFile(keyPath, randomBytes(SIGNING_KEY_BYTES), {
flag: "wx",
mode: 0o600,
});
} catch (error) {
if (!isNodeError(error, "EEXIST")) {
throw error;
}
}
return readSigningKey(keyPath);
}
async function readSigningKey(keyPath: string): Promise<Buffer> {
const keyStat = await lstat(keyPath);
if (!keyStat.isFile() || keyStat.isSymbolicLink()) {
throw new Error("Viewer state signing key is invalid.");
}
const key = await readFile(keyPath);
if (key.length !== SIGNING_KEY_BYTES) {
throw new Error("Viewer state signing key is invalid.");
}
if (process.platform !== "win32") {
await chmod(keyPath, 0o600);
}
return key;
}
async function prunePersistedFiles({
maxAgeMs,
maxPersistedFiles,
now,
stateDirectory,
}: {
maxAgeMs: number;
maxPersistedFiles: number;
now: number;
stateDirectory: string;
}): Promise<void> {
const entries = await readdir(stateDirectory, { withFileTypes: true });
const persistedFiles = (
await Promise.all(
entries
.filter(
(entry) =>
entry.isFile() &&
TOKEN_PATTERN.test(entry.name.replace(/\.json$/, "")) &&
entry.name.endsWith(".json"),
)
.map(async (entry) => {
const filePath = path.join(stateDirectory, entry.name);
try {
return { filePath, modifiedAt: (await lstat(filePath)).mtimeMs };
} catch (error) {
if (isNodeError(error, "ENOENT")) {
return null;
}
throw error;
}
}),
)
)
.filter((entry) => entry != null)
.sort((left, right) => right.modifiedAt - left.modifiedAt);
await Promise.all(
persistedFiles
.filter(
({ modifiedAt }, index) =>
index >= maxPersistedFiles || now - modifiedAt > maxAgeMs,
)
.map(({ filePath }) => unlinkIfExists(filePath)),
);
}
async function unlinkIfExists(filePath: string): Promise<void> {
try {
await unlink(filePath);
} catch (error) {
if (!isNodeError(error, "ENOENT")) {
throw error;
}
}
}
async function readFileIdentity(
filePath: string,
): Promise<FileIdentity | null> {
const fileStat = await stat(filePath, { bigint: true });
return fileStat.isFile() ? fileIdentityFromStat(fileStat) : null;
}
function fileIdentityFromStat(fileStat: {
ctimeNs: bigint;
dev: bigint;
ino: bigint;
mtimeNs: bigint;
size: bigint;
}): FileIdentity {
return {
device: fileStat.dev.toString(),
inode: fileStat.ino.toString(),
modifiedAtNanoseconds: fileStat.mtimeNs.toString(),
size: fileStat.size.toString(),
changedAtNanoseconds: fileStat.ctimeNs.toString(),
};
}
function sameFileIdentity(
left: FileIdentity | LegacyFileIdentity,
right: FileIdentity | LegacyFileIdentity,
): boolean {
return (
left.device === right.device &&
left.inode === right.inode &&
left.modifiedAtNanoseconds === right.modifiedAtNanoseconds &&
left.size === right.size &&
(!("changedAtNanoseconds" in left) ||
!("changedAtNanoseconds" in right) ||
left.changedAtNanoseconds === right.changedAtNanoseconds)
);
}
function signOpenedFile(
payload: PersistedOpenedFilePayload,
key: Buffer,
): string {
return createHmac("sha256", key)
.update(
JSON.stringify({
absolutePath: payload.absolutePath,
bundled: payload.bundled,
expectedSha256: payload.expectedSha256,
fileIdentity: {
device: payload.fileIdentity.device,
inode: payload.fileIdentity.inode,
modifiedAtNanoseconds: payload.fileIdentity.modifiedAtNanoseconds,
size: payload.fileIdentity.size,
...(payload.version === RECORD_VERSION
? {
changedAtNanoseconds:
payload.fileIdentity.changedAtNanoseconds,
}
: {}),
},
name: payload.name,
openedAt: payload.openedAt,
...(payload.rangeOnly ? { rangeOnly: true } : {}),
...(payload.sessionId == null ? {} : { sessionId: payload.sessionId }),
token: payload.token,
version: payload.version,
workspaceRoot: payload.workspaceRoot,
}),
)
.digest("base64url");
}
function recordPath(stateDirectory: string, token: string): string {
return path.join(stateDirectory, `${token}.json`);
}
async function tryListCanonicalWorkspaceRoots(
extra: RootsRequestExtra,
): Promise<Array<string> | null> {
let rootsResult: { roots: Array<{ uri: string }> };
try {
rootsResult = await extra.sendRequest(
{ method: "roots/list" },
ListRootsResultSchema,
);
} catch {
return null;
}
return (
await Promise.all(
rootsResult.roots.map(async ({ uri }) => {
if (!uri.startsWith("file://")) {
return null;
}
try {
return await realpath(fileURLToPath(uri));
} catch {
return null;
}
}),
)
).filter((root): root is string => root != null);
}
function isPathWithin(root: string, candidate: string): boolean {
const relativePath = path.relative(root, candidate);
return (
relativePath === "" ||
(!path.isAbsolute(relativePath) &&
relativePath !== ".." &&
!relativePath.startsWith(`..${path.sep}`))
);
}
function getSingleTemplateVariable(
value: string | Array<string> | undefined,
): string | null {
return typeof value === "string" ? value : null;
}
function isNodeError(
error: unknown,
code: string,
): error is NodeJS.ErrnoException {
return error instanceof Error && "code" in error && error.code === code;
}
SHA-256: 4aee442ec1b0c9fc7244190cf28390a0a7cb47c67fc7959d039ee62455bea704