Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/writeback-spec-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Contract-backed means the endpoint uses `contractEndpoint(...)`, loads its reque
| gmail | None | 0 | 3 | Inline JS schemas. |
| google-calendar | None | 0 | 1 | Inline JS schemas. |
| google-drive | None | 0 | 2 | Inline JS schemas. |
| granola | None | 0 | 2 | Inline JS schemas. |
| hubspot | None | 0 | 4 | Inline JS schemas. |
| intercom | None | 0 | 3 | Inline JS schemas. |
| jira | None | 0 | 4 | Inline JS schemas. |
Expand All @@ -31,6 +32,7 @@ Contract-backed means the endpoint uses `contractEndpoint(...)`, loads its reque
| pipedrive | None | 0 | 4 | Inline JS schemas. |
| postgres | None | 0 | 2 | Inline JS schemas; database/table shape is runtime-native rather than provider OpenAPI. |
| redis | None | 0 | 2 | Inline JS schemas; key/value shape is runtime-native rather than provider OpenAPI. |
| reddit | None | 0 | 2 | Inline JS schemas. |
| s3 | None | 0 | 2 | Inline JS schemas. |
| salesforce | None | 0 | 5 | Inline JS schemas. |
| sharepoint | None | 0 | 2 | Inline JS schemas. |
Expand Down
63 changes: 63 additions & 0 deletions packages/granola/discovery/granola/.adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Granola adapter

The Granola adapter exposes meeting notes and folders under `/granola`, with writeback routes for creating notes and folders.

Read-only mounts:
- `/granola/notes/<noteId>.json` - Note records.
- `/granola/notes/by-id/<noteId>.json` - Note lookup aliases.
- `/granola/folders/<folderId>.json` - Folder records.
- `/granola/folders/by-id/<folderId>.json` - Folder lookup aliases.

Resources:

| Resource | Schema | Create example | ID pattern | What it does |
|---|---|---|---|---|
| `/granola/notes/<id>.json` | `/granola/notes/.schema.json` | `/granola/notes/.create.example.json` | `^not_[A-Za-z0-9]{14}$` | Creates a Granola note. |
| `/granola/folders/<id>.json` | `/granola/folders/.schema.json` | `/granola/folders/.create.example.json` | `^fol_[A-Za-z0-9]{14}$` | Creates a Granola folder. |

## Operations

| To... | Do... |
|---|---|
| Read | `cat <canonical-resource-path>` after listing the resource directory or following an alias when one is available. Use the resource table and ID patterns below to determine whether a resource uses a bare id, an adapter-specific slug/id filename, or an exact sidecar path such as `content.md`. |
| Edit | Write the resource update payload to the canonical resource path. For JSON resources, included mutable fields PATCH; fields marked `readOnly` in `.schema.json` are rejected. |
| Create | Write JSON to any non-canonical filename such as `create request.json`. The adapter creates the record at its canonical resource path and rewrites the draft as `{ "created": "<real-id>", "path": "<canonical-resource-path>", "url": "<provider-url>" }`. |
| Ignore | Editor scratch files named `partial.json`, `.tmp.json`, `.partial.json`, `*.tmp.json`, or `*.partial.json` are ignored and never treated as create drafts. |
| Delete | `rm <canonical-resource-path>` for canonical records. |

## ID Patterns
- `/granola/notes/<id>.json`: `^not_[A-Za-z0-9]{14}$`. Filenames that do not match this pattern are treated as create drafts.
- `/granola/folders/<id>.json`: `^fol_[A-Za-z0-9]{14}$`. Filenames that do not match this pattern are treated as create drafts.

## Write field contracts

### Create Granola note

Resource: `/granola/notes/<id>.json`
Schema: `/granola/notes/.schema.json`
Create example: `/granola/notes/.create.example.json`
Required fields: `title`.
Optional fields: `summary_markdown`, `summary_text`, `folder_membership`.

Fields:

- `title` (required, string) - Note title.
- `summary_markdown` (optional, string) - Markdown note body.
- `summary_text` (optional, string) - Plain-text note summary.
- `folder_membership` (optional, array) - Folders the note belongs to.

### Create Granola folder

Resource: `/granola/folders/<id>.json`
Schema: `/granola/folders/.schema.json`
Create example: `/granola/folders/.create.example.json`
Required fields: `name`.
Optional fields: `parent_folder_id`.

Fields:

- `name` (required, string) - Folder name.
- `parent_folder_id` (optional, string) - Parent Granola folder id (`fol_…`). Omit for a top-level folder.

## Create Examples
Read the resource `.schema.json` first, then use the sibling `.create.example.json` as a minimal create document. The example intentionally omits read-only fields.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "Replace example folder name"
}
86 changes: 86 additions & 0 deletions packages/granola/discovery/granola/folders/.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Granola folder",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Folder name.",
"minLength": 1
},
"parent_folder_id": {
"type": "string",
"description": "Parent Granola folder id (`fol_…`). Omit for a top-level folder."
},
"id": {
"type": "string",
"description": "Provider canonical record id.",
"readOnly": true
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "Provider creation timestamp.",
"readOnly": true
},
"updatedAt": {
"type": "string",
"format": "date-time",
"description": "Provider last update timestamp.",
"readOnly": true
},
"url": {
"type": "string",
"format": "uri",
"description": "Provider URL for the record.",
"readOnly": true
},
"identifier": {
"type": "string",
"description": "Provider human-readable identifier or key.",
"readOnly": true
},
"provider": {
"type": "string",
"description": "Relayfile provider name.",
"readOnly": true
},
"objectType": {
"type": "string",
"description": "Relayfile object type.",
"readOnly": true
},
"objectId": {
"type": "string",
"description": "Relayfile object id.",
"readOnly": true
},
"workspaceId": {
"type": "string",
"description": "Relayfile workspace id.",
"readOnly": true
},
"connectionId": {
"type": "string",
"description": "Relayfile connection id.",
"readOnly": true
},
"_webhook": {
"type": "object",
"description": "Provider webhook metadata captured during sync.",
"readOnly": true,
"additionalProperties": true
},
"_connection": {
"type": "object",
"description": "Relayfile connection metadata captured during sync.",
"readOnly": true,
"additionalProperties": true
}
},
"additionalProperties": false,
"description": "Full resource record schema. Fields marked readOnly are synced from the provider and cannot be written by agents."
}
4 changes: 4 additions & 0 deletions packages/granola/discovery/granola/notes/.create.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Replace example note title",
"summary_markdown": "# Notes\n\nReplace example note body."
}
99 changes: 99 additions & 0 deletions packages/granola/discovery/granola/notes/.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Granola note",
"type": "object",
"required": [
"title"
],
"properties": {
"title": {
"type": "string",
"description": "Note title.",
"minLength": 1
},
"summary_markdown": {
"type": "string",
"description": "Markdown note body."
},
"summary_text": {
"type": "string",
"description": "Plain-text note summary."
},
"folder_membership": {
"type": "array",
"description": "Folders the note belongs to.",
"items": {
"type": "object",
"description": "Folder membership entry. Provide the Granola folder `id` (`fol_…`). List `/granola/folders/` to find available folders.",
"additionalProperties": true
}
},
"id": {
"type": "string",
"description": "Provider canonical record id.",
"readOnly": true
},
"createdAt": {
"type": "string",
"format": "date-time",
"description": "Provider creation timestamp.",
"readOnly": true
},
"updatedAt": {
"type": "string",
"format": "date-time",
"description": "Provider last update timestamp.",
"readOnly": true
},
"url": {
"type": "string",
"format": "uri",
"description": "Provider URL for the record.",
"readOnly": true
},
"identifier": {
"type": "string",
"description": "Provider human-readable identifier or key.",
"readOnly": true
},
"provider": {
"type": "string",
"description": "Relayfile provider name.",
"readOnly": true
},
"objectType": {
"type": "string",
"description": "Relayfile object type.",
"readOnly": true
},
"objectId": {
"type": "string",
"description": "Relayfile object id.",
"readOnly": true
},
"workspaceId": {
"type": "string",
"description": "Relayfile workspace id.",
"readOnly": true
},
"connectionId": {
"type": "string",
"description": "Relayfile connection id.",
"readOnly": true
},
"_webhook": {
"type": "object",
"description": "Provider webhook metadata captured during sync.",
"readOnly": true,
"additionalProperties": true
},
"_connection": {
"type": "object",
"description": "Relayfile connection metadata captured during sync.",
"readOnly": true,
"additionalProperties": true
}
},
"additionalProperties": false,
"description": "Full resource record schema. Fields marked readOnly are synced from the provider and cannot be written by agents."
}
65 changes: 65 additions & 0 deletions packages/reddit/discovery/reddit/.adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Reddit adapter

The Reddit adapter exposes tracked subreddits and posts under `/reddit`, with writeback routes for tracking subreddits and creating posts.

Read-only mounts:
- `/reddit/subreddits/<subreddit>.json` - Tracked subreddit records.
- `/reddit/subreddits/<subreddit>/posts/<title>__<postId>.json` - Post records scoped by subreddit.
- `/reddit/posts/by-id/<subreddit>__<postId>.json` - Post lookup aliases.
- `/reddit/posts/by-status/<status>/<subreddit>__<postId>.json` - Post status aliases.

Resources:

| Resource | Schema | Create example | ID pattern | What it does |
|---|---|---|---|---|
| `/reddit/subreddits/<id>.json` | `/reddit/subreddits/.schema.json` | `/reddit/subreddits/.create.example.json` | `^[A-Za-z0-9_][A-Za-z0-9_-]{1,63}$` | Starts tracking a subreddit so its posts sync under `/reddit`. |
| `/reddit/subreddits/{subreddit}/posts/<id>.json` | `/reddit/subreddits/{subreddit}/posts/.schema.json` | `/reddit/subreddits/{subreddit}/posts/.create.example.json` | `^[A-Za-z0-9_/-]+$` | Submits a post to the subreddit named by the path. |

## Operations

| To... | Do... |
|---|---|
| Read | `cat <canonical-resource-path>` after listing the resource directory or following an alias when one is available. Use the resource table and ID patterns below to determine whether a resource uses a bare id, an adapter-specific slug/id filename, or an exact sidecar path such as `content.md`. |
| Edit | Write the resource update payload to the canonical resource path. For JSON resources, included mutable fields PATCH; fields marked `readOnly` in `.schema.json` are rejected. |
| Create | Write JSON to any non-canonical filename such as `create request.json`. The adapter creates the record at its canonical resource path and rewrites the draft as `{ "created": "<real-id>", "path": "<canonical-resource-path>", "url": "<provider-url>" }`. |
| Ignore | Editor scratch files named `partial.json`, `.tmp.json`, `.partial.json`, `*.tmp.json`, or `*.partial.json` are ignored and never treated as create drafts. |
| Delete | `rm <canonical-resource-path>` for canonical records. |

## ID Patterns
- `/reddit/subreddits/<id>.json`: `^[A-Za-z0-9_][A-Za-z0-9_-]{1,63}$`. Filenames that do not match this pattern are treated as create drafts.
- `/reddit/subreddits/{subreddit}/posts/<id>.json`: `^[A-Za-z0-9_/-]+$`. Filenames that do not match this pattern are treated as create drafts.

## Write field contracts

### Track Reddit subreddit

Resource: `/reddit/subreddits/<id>.json`
Schema: `/reddit/subreddits/.schema.json`
Create example: `/reddit/subreddits/.create.example.json`
Required fields: `name`.
Optional fields: none.

Fields:

- `name` (required, string) - Subreddit name without the `r/` prefix.

### Create Reddit post

Resource: `/reddit/subreddits/{subreddit}/posts/<id>.json`
Schema: `/reddit/subreddits/{subreddit}/posts/.schema.json`
Create example: `/reddit/subreddits/{subreddit}/posts/.create.example.json`
Required fields: `title`.
Optional fields: `text`, `link_url`, `kind`, `flair_id`, `nsfw`, `spoiler`.

Fields:

- `title` (required, string) - Post title.
- `text` (optional, string) - Self-post body. Used when `kind` is `self`.
- `link_url` (optional, string, uri) - External URL for link posts. Used when `kind` is `link`.
- `kind` (optional, enum) - Post kind. Defaults to `link` when `link_url` is provided, otherwise `self`. Allowed values: `self`, `link`.
- `flair_id` (optional, string) - Subreddit flair template id.
- `nsfw` (optional, boolean) - Whether the post is marked NSFW.
- `spoiler` (optional, boolean) - Whether the post is marked as a spoiler.

## Create Examples
Read the resource `.schema.json` first, then use the sibling `.create.example.json` as a minimal create document. The example intentionally omits read-only fields.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "selfhosted"
}
Loading
Loading