attachments.ts
tools/BriefTool/attachments.ts
111
Lines
3889
Bytes
3
Exports
8
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is part of the tool layer, which means it describes actions the system can perform for the user or model.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with tool-system. It contains 111 lines, 8 detected imports, and 3 detected exports.
Important relationships
Detected exports
ResolvedAttachmentvalidateAttachmentPathsresolveAttachments
Keywords
utilsrawpathuploadsizefullpathattachmentstatpathstatedfeature
Detected imports
bun:bundlefs/promises../../Tool.js../../utils/cwd.js../../utils/envUtils.js../../utils/errors.js../../utils/imagePaste.js../../utils/path.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
/**
* Shared attachment validation + resolution for SendUserMessage and
* SendUserFile. Lives in BriefTool/ so the dynamic `./upload.js` import
* inside the feature('BRIDGE_MODE') guard stays relative and upload.ts
* (axios, crypto, auth utils) remains tree-shakeable from non-bridge builds.
*/
import { feature } from 'bun:bundle'
import { stat } from 'fs/promises'
import type { ValidationResult } from '../../Tool.js'
import { getCwd } from '../../utils/cwd.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
import { getErrnoCode } from '../../utils/errors.js'
import { IMAGE_EXTENSION_REGEX } from '../../utils/imagePaste.js'
import { expandPath } from '../../utils/path.js'
export type ResolvedAttachment = {
path: string
size: number
isImage: boolean
file_uuid?: string
}
export async function validateAttachmentPaths(
rawPaths: string[],
): Promise<ValidationResult> {
const cwd = getCwd()
for (const rawPath of rawPaths) {
const fullPath = expandPath(rawPath)
try {
const stats = await stat(fullPath)
if (!stats.isFile()) {
return {
result: false,
message: `Attachment "${rawPath}" is not a regular file.`,
errorCode: 1,
}
}
} catch (e) {
const code = getErrnoCode(e)
if (code === 'ENOENT') {
return {
result: false,
message: `Attachment "${rawPath}" does not exist. Current working directory: ${cwd}.`,
errorCode: 1,
}
}
if (code === 'EACCES' || code === 'EPERM') {
return {
result: false,
message: `Attachment "${rawPath}" is not accessible (permission denied).`,
errorCode: 1,
}
}
throw e
}
}
return { result: true }
}
export async function resolveAttachments(
rawPaths: string[],
uploadCtx: { replBridgeEnabled: boolean; signal?: AbortSignal },
): Promise<ResolvedAttachment[]> {
// Stat serially (local, fast) to keep ordering deterministic, then upload
// in parallel (network, slow). Upload failures resolve undefined — the
// attachment still carries {path, size, isImage} for local renderers.
const stated: ResolvedAttachment[] = []
for (const rawPath of rawPaths) {
const fullPath = expandPath(rawPath)
// Single stat — we need size, so this is the operation, not a guard.
// validateInput ran before us, but the file could have moved since
// (TOCTOU); if it did, let the error propagate so the model sees it.
const stats = await stat(fullPath)
stated.push({
path: fullPath,
size: stats.size,
isImage: IMAGE_EXTENSION_REGEX.test(fullPath),
})
}
// Dynamic import inside the feature() guard so upload.ts (axios, crypto,
// zod, auth utils, MIME map) is fully eliminated from non-BRIDGE_MODE
// builds. A static import would force module-scope evaluation regardless
// of the guard inside uploadBriefAttachment — CLAUDE.md: "helpers defined
// outside remain in the build even if never called".
if (feature('BRIDGE_MODE')) {
// Headless/SDK callers never set appState.replBridgeEnabled (only the TTY
// REPL does, at main.tsx init). CLAUDE_CODE_BRIEF_UPLOAD lets a host that
// runs the CLI as a subprocess opt in — e.g. the cowork desktop bridge,
// which already passes CLAUDE_CODE_OAUTH_TOKEN for auth.
const shouldUpload =
uploadCtx.replBridgeEnabled ||
isEnvTruthy(process.env.CLAUDE_CODE_BRIEF_UPLOAD)
const { uploadBriefAttachment } = await import('./upload.js')
const uuids = await Promise.all(
stated.map(a =>
uploadBriefAttachment(a.path, a.size, {
replBridgeEnabled: shouldUpload,
signal: uploadCtx.signal,
}),
),
)
return stated.map((a, i) =>
uuids[i] === undefined ? a : { ...a, file_uuid: uuids[i] },
)
}
return stated
}