Skip to Content
Card SDKСправочник APIФайлы, буфер обмена и расход

Файлы, буфер обмена и расход

Файлы в папке воркспейса

Для чтения нужно fs.read, для изменений — fs.write. Каждый путь задаётся относительно папки воркспейса — папки проекта, которую пользователь выбрал для воркспейса, — через / или \; '' или '.' — сама папка.

// Read const pkg = JSON.parse(await card.fs.readText('package.json')) as { name: string } const logo = await card.fs.readBytes('public/logo.png') const info = await card.fs.stat('src/index.ts') // { path, type, size, mtimeMs } const { entries, truncated } = await card.fs.list('src', { recursive: true, maxEntries: 2000 }) render(pkg.name, logo.byteLength, info.size, entries.length, truncated) // Write (atomically: a temporary file, then a rename) await card.fs.writeText('reports/latest.md', '# Report\n', { createDirs: true }) await card.fs.mkdir('reports/archive') await card.fs.trash('reports/old.md') // to the OS trash; there is no hard delete // Write only if nobody changed it since you read it const before = await card.fs.stat('TODO.md') await card.fs.writeText('TODO.md', '- [ ] ship it\n', { ifMtimeMs: before.mtimeMs })
МетодРезультат
stat(path){ path, type: 'file' or 'directory', size, mtimeMs }
list(path?, { recursive?, maxEntries? }){ entries: { path, type, size? }[], truncated } — до 5 000 записей; рекурсивный список пропускает .git и node_modules, если только вы не просите содержимое внутри них
readText(path, { maxBytes? })текст UTF-8
readBytes(path, { maxBytes? })Uint8Array
read(path, { encoding?, maxBytes? }){ data, encoding, size, truncated } — результат как есть
writeText(path, text, { createDirs?, ifMtimeMs? })новый FsStat
writeBytes(path, bytes, { createDirs?, ifMtimeMs? })новый FsStat
mkdir(path)FsStat
trash(path)переносит файл или папку в корзину ОС (60 в минуту)
watch(path, handler, { recursive? })возвращает функцию, которая прекращает слежение

Чтение и запись — до 10 МБ за раз.

Слежение:

const stop = await card.fs.watch('reports', ({ path, type }) => { render(`${path} ${type === 'rename' ? 'was added or removed' : 'changed'}`) }, { recursive: true }) // later await stop()

Изменения объединяются с задержкой 100 мс; не больше 20 наблюдателей на карточку; наблюдатели останавливаются, когда страница карточки выгружается (подпишитесь снова после launch === 'resumed').

Ограда. Абсолютные пути и любые .. отклоняются сразу. Дальше приложение находит настоящий путь цели (а для нового файла — ближайшей существующей папки) и отказывает, если он вне папки воркспейса, — так что символическая ссылка или junction наружу не помогут. Всё это падает с FS_DENIED; прочие проблемы файловой системы (не найдено, не папка…) — с FS_ERROR, а неудачная проверка ifMtimeMs — с FS_ERROR и data.conflict.

Доступа к файлам нет вовсе — любой вызов fs.* падает с FS_DENIED, — если папка воркспейса — это домашняя папка пользователя, корень диска или в ней лежит папка данных самого NeuroSquad.

Защищённые пути

Запись, mkdir и trash для них отклоняются (FS_DENIED) на любой глубине; проверяется и путь, который вы передали, и настоящий путь за любой ссылкой — иначе карточка могла бы выполнить код через git, агентов пользователя или их инструменты:

  • всё внутри папки с именем .git (включая файл .git подмодуля или worktree), .gitmodules, .gitattributes;
  • папки .claude, .codex, .cursor, .gemini, .qwen, .opencode, .kilocode, .windsurf, .continue, .vscode, .idea, .husky, .devcontainer и .github/workflows;
  • файлы .mcp.json, CLAUDE.md, CLAUDE.local.md, AGENTS.md, GEMINI.md, QWEN.md, .cursorrules, .windsurfrules, opencode.json, opencode.jsonc, .envrc, .npmrc, .yarnrc, .yarnrc.yml, .pnpmfile.cjs.

Читать их с fs.read можно.

Выбора файла в версии 1 нет: карточка работает только с папкой воркспейса. Чтобы указать карточке на файл, дайте пользователю ввести путь в настройке или получайте ns:file-ref через порт.

Буфер обмена

Нужно clipboard.write — хороший кандидат на необязательное разрешение.

await card.copyText('npm test -- --grep checkout')

Не чаще раза в секунду, до 1 МБ. Собственный navigator.clipboard браузера в карточке не работает, а читать буфер обмена нельзя вовсе.

Расход токенов и стоимость

Нужно usage.read.

const summary = await card.usage('7d') // 'today' (default), '7d' or '30d' const dollars = (summary.totalCostMicroUsd / 1_000_000).toFixed(2) render(`$${dollars}${summary.partial ? ' + unpriced models' : ''}`) for (const row of summary.rows) { render(row.name, row.harness, row.inputTokens, row.outputTokens, row.costMicroUsd ?? 'no price') }

Те же числа, что на странице Расход приложения, для воркспейса карточки: period, from и to (локальные сутки, конец не включается) и по каждому агенту inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens и costMicroUsd. Деньги — в целых микродолларах (1 000 000 = $1). У модели без известной цены costMicroUsd: null — никогда не 0, — она не входит в totalCostMicroUsd и выставляет partial: true.