Read in:
Русский

updateNotes: программное редактирование заметок

updateNotes — GraphQL-мутация для программного изменения содержимого заметок. За один вызов выполняет три типа операций: создание или полная замена (upsert), атомарный find-and-replace (patch), скрытие заметки (hide). Изменения применяются сразу — отдельный вызов commitNotes не нужен.

Используйте updateNotes, когда точно знаете, что именно нужно изменить. Для полной синхронизации хранилища из плагина Obsidian подходит pushNotes + commitNotes.


Сигнатура мутации

mutation UpdateNotes($input: UpdateNotesInput!) {
  updateNotes(input: $input) {
    ... on UpdateNotesSuccessPayload { paths }
    ... on UpdateNotesHashMismatchPayload { path actualHash }
    ... on UpdateNotesPatchNotFoundPayload { path find }
    ... on ErrorPayload { message }
  }
}

Переменные:

{
  "input": {
    "changes": [ /* один или несколько объектов NoteChangeInput */ ]
  }
}

Типы операций

Каждый элемент changes — объект NoteChangeInput с одним из трёх полей.

upsert — создать или полностью заменить заметку

{
  "upsert": {
    "path": "inbox.md",
    "content": "# Inbox\n\n- item"
  }
}

Если заметка не существует — создаёт её. Если существует — полностью заменяет содержимое.

Опциональный expectedHash: sha256 текущего содержимого заметки, в кодировке base64url. Если заметка изменилась с момента чтения — мутация вернёт UpdateNotesHashMismatchPayload вместо записи.

patch — атомарный find-and-replace внутри заметки

{
  "patch": {
    "path": "todo.md",
    "find": "- [ ] купить молоко",
    "replace": "- [x] купить молоко"
  }
}

Сервер читает текущее содержимое, заменяет единственное вхождение find на replace и сохраняет — всё в одной транзакции.

Правила:

  • find должна встречаться в заметке ровно один раз. Если строка не найдена или встречается несколько раз — мутация вернёт UpdateNotesPatchNotFoundPayload с path и find.
  • find — точная строка, не регулярное выражение.
  • Если find совпадает с replace — заметка не изменяется и новая версия не создаётся.
  • Пустой replace допустим — удаляет найденную строку.

Опциональный expectedHash работает так же, как для upsert.

hide — скрыть заметку

{
  "hide": {
    "path": "draft.md"
  }
}

Помечает заметку как скрытую, не затрагивая содержимое. Заметка остаётся в хранилище, но перестаёт отдаваться читателям.


Переключение чекбоксов (patch-сценарий)

Patch — стандартный механизм для переключения задач прямо со страницы.

Как это работает:

  1. В frontmatter заметки добавьте allow_task_toggle: true. Рендерер добавит атрибут data-line к элементам чеклиста — в нём хранится исходная строка markdown.
  2. Читатель кликает на чекбокс. Фронтенд читает data-line элемента и вызывает updateNotes:
    • find: исходная строка, например - [ ] купить молоко
    • replace: строка с изменённым статусом, например - [x] купить молоко (или обратное для снятия отметки)
  3. При успехе интерфейс обновляется оптимистично.

Требование уникальности. Если одинаковый текст задачи встречается в заметке несколько раз — patch вернёт UpdateNotesPatchNotFoundPayload. Фронтенд должен показать сообщение «нельзя переключить — повторяющийся текст задачи», а не молча упасть.

Включите переключение чекбоксов через frontmatter:

allow_task_toggle: true

Чтобы включить для всех заметок сразу, используйте frontmatter-патч:

* → { allow_task_toggle: true }

Защита от конфликтов: expectedHash

expectedHash — sha256-хеш текущего содержимого заметки в кодировке base64url.

При передаче хеша:

  • Сервер проверяет хеш перед применением изменения.
  • Если содержимое изменилось с момента чтения — мутация возвращает UpdateNotesHashMismatchPayload с path и актуальным actualHash.
  • Повторите операцию: перечитайте заметку, используйте новый хеш.

Для переключения чекбоксов прямо из интерфейса expectedHash обычно не нужен — строка find сама служит защитой. Если заметка изменилась и строки больше нет, patch вернёт PatchNotFound вместо затирания новых изменений.

Используйте expectedHash, когда агент читает заметку, трансформирует содержимое и записывает обратно — и нужна гарантия, что между чтением и записью никто ничего не изменил.


Пакетные операции

Отправьте несколько изменений за один вызов:

{
  "input": {
    "changes": [
      {
        "patch": {
          "path": "todo.md",
          "find": "- [ ] купить молоко",
          "replace": "- [x] купить молоко"
        }
      },
      {
        "upsert": {
          "path": "log/2026-05-18.md",
          "content": "# Лог\n\nЗадача выполнена."
        }
      }
    ]
  }
}

Каждое изменение обрабатывается независимо. На первой ошибке обработка останавливается и возвращается соответствующий payload — предыдущие изменения уже применены. Пустые объекты NoteChangeInput {} пропускаются без ошибки.


Типы ответов

Payload Значение Что делать
UpdateNotesSuccessPayload Все изменения применены Читайте paths — список затронутых заметок
UpdateNotesHashMismatchPayload Заметка изменилась с момента чтения Перечитайте заметку, повторите с actualHash
UpdateNotesPatchNotFoundPayload Строка find не найдена или встречается несколько раз Покажите ошибку пользователю, при необходимости перечитайте заметку
ErrorPayload Ошибка валидации или системная ошибка Залогируйте message, исправьте входные данные

Авторизация

updateNotes требует API-ключ. Передайте его в заголовке:

X-Api-Key: your-api-key

Или, для webhook-агентов, используйте короткоживущий токен из payload вебхука:

Authorization: Bearer eyJhbGc...

Доступ на запись проверяется по паттернам записи ключа. Если целевой путь не покрыт паттернами — мутация вернёт ErrorPayload.


Интеграция с вебхуками

Webhook-агент получает временный api_token в теле запроса (если включён Pass API key). Этот токен можно напрямую использовать с updateNotes для записи в хранилище.

Поле changes[] в ответе агента тоже поддерживает patch-операции — передайте find и replace вместо content в объекте изменения. Подробнее о формате ответа агента — в документации вебхуков.

Cron-вебхуки запускают агентов по расписанию без внешней инфраструктуры. Например, агент может каждую ночь проверять заметки todo/**, находить просроченные задачи и обновлять их статусы через patch.


Паттерн маркера-якоря

Для дозаписи (inbox-боты, лог-записи) добавьте в заметку маркер и используйте patch для вставки контента с сохранением маркера:

Содержимое заметки:

# Inbox

$INBOX$

Вызов агента:

{
  "patch": {
    "path": "inbox.md",
    "find": "$INBOX$",
    "replace": "## 2026-05-18 10:00\nНовое сообщение\n\n$INBOX$"
  }
}

Каждый вызов добавляет новую запись перед маркером. Маркер остаётся в заметке и служит точкой вставки для следующего вызова. Паттерн работает без expectedHash — строка маркера уникальна по конструкции.


Паттерн условного вызова

Агент должен вызывать updateNotes только при наличии конкретного изменения:

получить payload вебхука
если changes[] содержит заметку с чекбоксом для переключения:
    вызвать updateNotes с patch (find=исходная строка, replace=изменённая строка)
    обработать PatchNotFound (дублирующийся текст) → вернуть ошибку в UI
    обработать HashMismatch → перечитать заметку и повторить
иначе:
    вернуть {"status": "ok"}  # нет изменений, запись не нужна

Семантика patch обеспечивает это автоматически — если строка find отсутствует, мутация возвращает ошибку, а не пишет молча. Агент, вызывающий мутацию безусловно, тоже получит корректное поведение.