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
52 changes: 40 additions & 12 deletions spec/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -30376,7 +30376,8 @@
},
"examples": [
"wspc todo add \"Buy milk\"",
"wspc todo add \"Buy milk\" --project prj_xxx"
"wspc todo add \"Buy milk\" --project prj_xxx",
"wspc todo add \"Fix styling issue\" --type-id typ_xxx --custom-fields '{\"severity\":\"high\",\"tags\":[\"ui\"]}'"
],
"display": {
"shape": "object",
Expand All @@ -30395,7 +30396,7 @@
}
},
"summary": "Create a todo",
"description": "### 🎯 Overview & Purpose\nCreate a new todo item under a specified project. This can either be a standalone root-level todo or a nested subtask attached to an existing root todo.\n\n### 🔍 When to Use\n* Use this to capture a fresh work item, document an ongoing task, or break a larger root todo into subtasks by creating child todos under it.\n\n### 💡 Key Features & Constraints\n* **One-Level Nesting Limit**: WSPC supports a maximum of one level of task nesting (Root ➔ Child). A root-level todo can have children, but a child todo cannot have further subtasks. Setting a child todo as a parent will fail and trigger a `PARENT_IS_CHILD` error.\n* **Description Handling**: Passing a non-empty string stores the description; passing `\"\"` explicitly stores an empty string. Passing `null` is strictly rejected.\n* **Due Date Format**: Accepts an ISO-8601 date-only format (`YYYY-MM-DD`). Pass `\"\"` or omit the field to skip setting a due date.\n* **Project Binding**: Every todo must belong to a valid active project (`project_id`).\n\n### ⚠️ Common Errors & Troubleshooting\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if required fields are missing, if `due_at` violates the `YYYY-MM-DD` format, or if `title` exceeds 500 characters.\n* **`PARENT_IS_CHILD` (HTTP 400)**: Thrown if the target `parent_id` refers to a todo that is itself already a child todo.\n* **`WOULD_CREATE_CYCLE` (HTTP 400)**: Thrown if `parent_id` points to the todo's own ID.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the specified `project_id` or `parent_id` does not exist or has been soft-deleted.",
"description": "### 🎯 Overview & Purpose\nCreate a new todo item under a specified project. This can either be a standalone root-level todo or a nested subtask attached to an existing root todo.\n\n### 🔍 When to Use\n* Use this to capture a fresh work item, document an ongoing task, or break a larger root todo into subtasks by creating child todos under it.\n\n### 💡 Key Features & Constraints\n* **One-Level Nesting Limit**: WSPC supports a maximum of one level of task nesting (Root ➔ Child). A root-level todo can have children, but a child todo cannot have further subtasks. Setting a child todo as a parent will fail and trigger a `PARENT_IS_CHILD` error.\n* **Description Handling**: Passing a non-empty string stores the description; passing `\"\"` explicitly stores an empty string. Passing `null` is strictly rejected.\n* **Due Date Format**: Accepts an ISO-8601 date-only format (`YYYY-MM-DD`). Pass `\"\"` or omit the field to skip setting a due date.\n* **Project Binding**: Every todo must belong to a valid active project (`project_id`).\n\n### 💡 Best Practices & Guidelines\n* **Descriptive Descriptions**: Always provide a detailed description explaining the task's context, goal, or definition of done. Avoid leaving it empty.\n* **Task Breakdown**: For complex items, split the task into 2-3 logical subtasks (by passing a parent todo ID in `parent_id`). Remember WSPC supports only one level of nesting (Root -> Child).\n* **Integration with Drive**: If a task requires storing large text blocks, research logs, checklists, or files, do not overload the description. Use the Drive feature to store a markdown file (e.g., `research/notes.md`) and place a clean reference link in the description, such as `[Notes](drive://<library>/<path>)`.\n* **Structured Properties (Custom Fields)**: Avoid putting structured meta-attributes (like priority, severity, story points, tags) directly in the description. Instead, use custom todo types (`todo_type`). Create a custom type schema first, then assign it with `type_id` and pass values via `custom_fields`.\n\n### ⚠️ Common Errors & Troubleshooting\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if required fields are missing, if `due_at` violates the `YYYY-MM-DD` format, or if `title` exceeds 500 characters.\n* **`PARENT_IS_CHILD` (HTTP 400)**: Thrown if the target `parent_id` refers to a todo that is itself already a child todo.\n* **`WOULD_CREATE_CYCLE` (HTTP 400)**: Thrown if `parent_id` points to the todo's own ID.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the specified `project_id` or `parent_id` does not exist or has been soft-deleted.",
"security": [
{
"bearerAuth": []
Expand Down Expand Up @@ -31358,8 +31359,18 @@
"TodoTypes"
],
"x-cli": {
"command": "_internal",
"hidden": true
"command": "todo type add",
"positional": [
"label"
],
"aliases": {
"project": "p"
},
"examples": [
"wspc todo type add \"Bug Report\"",
"wspc todo type add \"Bug Report\" --project prj_xxx",
"wspc todo type add \"Sprint Task\" --custom-fields '[{\"key\":\"story_points\",\"label\":\"Story Points\",\"type\":\"string\"}]'"
]
},
"summary": "Create a todo type",
"description": "### 🎯 Overview & Purpose\nCreate a new custom todo type. This allows you to define specialized category schemas (e.g. \"Bug Report\") and configure custom field constraints.\n\n### 🔍 When to Use\n* Use this to set up customized task behaviors (e.g. tracking choices, additional metadata, or enforcing hidden fields) tailored to a project.\n\n### 💡 Key Features & Constraints\n* **Automatic Seeding**: The first project initialization will lazily seed a `Default Project` and a `Default` todo type if they do not already exist.\n* **Metadata Schema**: Custom field keys mapped here are evaluated during task creation/update.\n\n### ⚠️ Common Errors & Troubleshooting\n* **`VALIDATION_ERROR` (HTTP 400)**: Thrown if required fields are missing or schema constraints are violated.",
Expand Down Expand Up @@ -36169,7 +36180,8 @@
],
"examples": [
"wspc todo update tod_xxx --status done",
"wspc todo update tod_xxx --title \"New title\""
"wspc todo update tod_xxx --title \"New title\"",
"wspc todo update tod_xxx --custom-fields '{\"severity\":\"critical\"}'"
],
"display": {
"shape": "object",
Expand All @@ -36188,7 +36200,7 @@
}
},
"summary": "Update a todo",
"description": "### 🎯 Overview & Purpose\nUpdate one or more fields of an existing todo item, such as its title, status, parent todo, due date, or description.\n\n### 🔍 When to Use\n* Use this to log progress by changing the status (e.g., to `in_progress` or `done`), reschedule due dates, edit title/description, or reassign/move a task by changing its `parent_id`.\n\n### 💡 Key Features & Constraints\n* **Optimistic Locking (`expected_version`)**: An optional integer representing the version you expect to update. If provided, the server matches it with the current database version. If they match, the update succeeds and increments the version; if they mismatch, a `VERSION_CONFLICT` error is thrown. Omit this field to skip version checking (Last-Write-Wins behavior).\n* **Parent Re-assignment**: Set `parent_id: null` to move a child todo back to the root level.\n* **Status Transitions**: Transitioning the `status` to `done` automatically emits a `captureTodoCompleted` analytics event.\n* **Clearing Fields**: To clear an existing description or due date, explicitly pass `\"\"`. Passing `null` is rejected.\n\n### ⚠️ Common Errors & Troubleshooting\n* **`VERSION_CONFLICT` (HTTP 409)**: Thrown if `expected_version` does not match the current database row version.\n* **`PARENT_IS_CHILD` (HTTP 400)**: Thrown if the new `parent_id` refers to a todo that is itself already a child todo.\n* **`WOULD_CREATE_CYCLE` (HTTP 400)**: Thrown if the update attempts to make a parent todo a child of its own descendant.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the todo `id` or the new `parent_id` does not exist or has been soft-deleted.",
"description": "### 🎯 Overview & Purpose\nUpdate one or more fields of an existing todo item, such as its title, status, parent todo, due date, or description.\n\n### 🔍 When to Use\n* Use this to log progress by changing the status (e.g., to `in_progress` or `done`), reschedule due dates, edit title/description, or reassign/move a task by changing its `parent_id`.\n\n### 💡 Key Features & Constraints\n* **Optimistic Locking (`expected_version`)**: An optional integer representing the version you expect to update. If provided, the server matches it with the current database version. If they match, the update succeeds and increments the version; if they mismatch, a `VERSION_CONFLICT` error is thrown. Omit this field to skip version checking (Last-Write-Wins behavior).\n* **Parent Re-assignment**: Set `parent_id: null` to move a child todo back to the root level.\n* **Status Transitions**: Transitioning the `status` to `done` automatically emits a `captureTodoCompleted` analytics event.\n* **Clearing Fields**: To clear an existing description or due date, explicitly pass `\"\"`. Passing `null` is rejected.\n\n### 💡 Best Practices & Guidelines\n* **Enriched Context**: Keep the description updated with definition of done, relevant progress notes, or resolution summaries.\n* **Keep Descriptions Clean**: Delegate heavy logs/documents to a Markdown file on Drive, and put a markdown link inside the description.\n* **Structured Properties**: Use custom fields (`custom_fields`) matching the todo's type to record properties like tags, priority, or severity.\n\n### ⚠️ Common Errors & Troubleshooting\n* **`VERSION_CONFLICT` (HTTP 409)**: Thrown if `expected_version` does not match the current database row version.\n* **`PARENT_IS_CHILD` (HTTP 400)**: Thrown if the new `parent_id` refers to a todo that is itself already a child todo.\n* **`WOULD_CREATE_CYCLE` (HTTP 400)**: Thrown if the update attempts to make a parent todo a child of its own descendant.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the todo `id` or the new `parent_id` does not exist or has been soft-deleted.",
"security": [
{
"bearerAuth": []
Expand Down Expand Up @@ -36595,8 +36607,13 @@
"TodoTypes"
],
"x-cli": {
"command": "_internal",
"hidden": true
"command": "todo type rm",
"positional": [
"id"
],
"examples": [
"wspc todo type rm typ_xxx"
]
},
"summary": "Soft-delete a todo type",
"description": "### 🎯 Overview & Purpose\nSoft-delete/archive a custom todo type.\n\n### 🔍 When to Use\n* Use this to retire a custom task category workspace that is no longer needed.\n\n### 💡 Key Features & Constraints\n* **Default Type Protection**: The current active default type of a project cannot be deleted. You must assign another type as default first; otherwise the call fails with `CANNOT_DELETE_DEFAULT_TYPE`.\n\n### ⚠️ Common Errors & Troubleshooting\n* **`CANNOT_DELETE_DEFAULT_TYPE` (HTTP 409)**: Thrown if the target todo type is currently the project's default type.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target ID does not exist.",
Expand Down Expand Up @@ -37307,8 +37324,14 @@
"TodoTypes"
],
"x-cli": {
"command": "_internal",
"hidden": true
"command": "todo type set",
"positional": [
"id"
],
"examples": [
"wspc todo type set typ_xxx --label \"Feature Request\"",
"wspc todo type set typ_xxx --custom-fields '[{\"key\":\"severity\",\"label\":\"Severity\",\"type\":\"string\"}]'"
]
},
"summary": "Update a todo type",
"description": "### 🎯 Overview & Purpose\nUpdate a custom todo type's label, core field overrides, or custom field schema definitions.\n\n### 🔍 When to Use\n* Use this to rename a task category category, hide native todo attributes, or adjust custom data schemas.\n\n### 💡 Key Features & Constraints\n* **Type Modification Constraints**: Changing the data `type` of an existing custom field key (e.g. converting a string field to a boolean field) is strictly rejected with `CANNOT_CHANGE_FIELD_TYPE`. To migrate, remove the key and re-add it under a brand new name.\n\n### ⚠️ Common Errors & Troubleshooting\n* **`CANNOT_CHANGE_FIELD_TYPE` (HTTP 422)**: Thrown if you attempt to modify the declared data type of an existing custom field key.\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target ID does not exist.",
Expand Down Expand Up @@ -38464,8 +38487,13 @@
"TodoTypes"
],
"x-cli": {
"command": "_internal",
"hidden": true
"command": "todo type restore",
"positional": [
"id"
],
"examples": [
"wspc todo type restore typ_xxx"
]
},
"summary": "Restore a soft-deleted todo type",
"description": "### 🎯 Overview & Purpose\nRestore a previously archived/soft-deleted custom todo type.\n\n### 🔍 When to Use\n* Use this to bring a retired task category back into active status.\n\n### 💡 Key Features & Constraints\n* **Task Re-Attachment**: Restoring a type clears its `deleted_at` timestamp. Todo items previously assigned to this type immediately become active and validated under this recovered category schema.\n\n### ⚠️ Common Errors & Troubleshooting\n* **`NOT_FOUND` (HTTP 404)**: Thrown if the target ID does not exist.",
Expand Down
1 change: 1 addition & 0 deletions src/generated/cli/alias/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { render } from "../../../handwritten/output/render.js"

export const emailAliasCreateCommand = new Command("add")
.description("Create a receiving alias")
.addHelpText("after", "\n### Overview\nReserves and provisions a new passwordless/disposable receiving email alias address under the configured WSPC domain or a fully verified organization custom domain. All inbound emails received on this alias will be forwarded into the caller's inbox.\n\n### When to Use\n- Use this endpoint to spin up a fresh, dedicated email address (e.g., `alice-shop@wspc.app`) for specific websites, newsletters, or contexts to prevent spam or categorize incoming mail.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **Alias Formatting**: The local part must be between 5 and 32 characters, start with an alphanumeric character, and only contain letters, numbers, dots, underscores, and hyphens.\n- **Custom Domains**: If the address uses a non-platform host, that domain must be registered to the caller's organization and have `status = verified`, `sending_status = verified`, and `receiving_status = verified`.\n- **Limit Check**: Each user is allowed a maximum of 10 active email aliases. Soft-deleted aliases do not count against this quota limit.\n\n### Troubleshooting\n- **401 Unauthorized**: Bearer token is missing, invalid, or expired.\n- **400 Bad Request / INVALID_CHARSET / RESERVED**: The alias local part contains invalid characters, is too short/long, or matches a reserved keyword.\n- **400 Bad Request / ALIAS_DOMAIN_NOT_FOUND**: The custom domain is not registered to the caller's organization.\n- **400 Bad Request / UNVERIFIED_DOMAIN**: The custom domain exists but is not verified yet.\n- **400 Bad Request / ALIAS_DOMAIN_NOT_READY**: The custom domain exists but has not completed sending or receiving verification.\n- **409 Conflict / ALIAS_CONFLICT**: An alias with the exact requested email address already exists globally (whether active or soft-deleted by any user).\n- **429 Too Many Requests / ALIAS_LIMIT_EXCEEDED**: The user has reached the active alias cap limit of 10. A previously deleted alias must be cleaned up or wait for quota availability.\n")
.argument("<email>", "email")
.action(async (email, opts) => {
const client = await loadSdkClient()
Expand Down
1 change: 1 addition & 0 deletions src/generated/cli/alias/ls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { render } from "../../../handwritten/output/render.js"

export const emailAliasListCommand = new Command("ls")
.description("List the caller's aliases")
.addHelpText("after", "\n### Overview\nRetrieves a list of all email receiving aliases owned by the authenticated user.\n\n### When to Use\n- Use this endpoint to render an alias directory in user profiles or to provide a selection list of verified sender aliases in an email compose interface.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- By default, only active receiving aliases are returned. Pass `include_deleted=true` in the query to also fetch soft-deleted aliases (which have `deleted_at` timestamps set).\n\n### Troubleshooting\n- **401 Unauthorized**: Active token is missing, expired, or invalid.\n")
.option("--include-deleted", "include_deleted")
.action(async (opts) => {
const client = await loadSdkClient()
Expand Down
1 change: 1 addition & 0 deletions src/generated/cli/alias/restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { render } from "../../../handwritten/output/render.js"

export const emailAliasRestoreCommand = new Command("restore")
.description("Restore a soft-deleted alias")
.addHelpText("after", "\n### Overview\nReactivates a previously soft-deleted email receiving alias, immediately resuming mail forwarding to the user's inbox.\n\n### When to Use\n- Use this endpoint to re-enable a temporarily disabled alias or to recover one that was deleted by mistake.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **Quota Check**: Reactivating an alias increases the active alias count towards the user's maximum quota of 10 active aliases. If the limit is exceeded, a `429 ALIAS_LIMIT_EXCEEDED` error is returned.\n- **Path Parameter**: The `@` character in the path parameter must be URL-encoded as `%40`.\n\n### Troubleshooting\n- **401 Unauthorized**: Missing or invalid token.\n- **404 Not Found**: No soft-deleted alias with this exact address was found for the authenticated user.\n- **429 Too Many Requests / ALIAS_LIMIT_EXCEEDED**: Reactivating this alias would exceed the per-user limit of 10 active aliases.\n")
.argument("<email>", "email")
.action(async (email, opts) => {
const client = await loadSdkClient()
Expand Down
1 change: 1 addition & 0 deletions src/generated/cli/alias/rm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { render } from "../../../handwritten/output/render.js"

export const emailAliasDeleteCommand = new Command("rm")
.description("Soft-delete an alias")
.addHelpText("after", "\n### Overview\nSoft-deletes a specific active email receiving alias owned by the caller. Once soft-deleted, the alias stops accepting and forwarding any new inbound emails.\n\n### When to Use\n- Use this endpoint when decommissioning a disposable alias address that is no longer needed or is receiving excessive spam.\n\n### Constraints\n- Requires a valid Bearer token in the `Authorization` header.\n- **Data Retention**: Soft-deletion is immediate. Inbound mail forwarding stops, but historical emails previously received on this alias remain fully readable in the inbox.\n- **Restoration**: The alias remains globally reserved and cannot be created fresh by anyone; use `POST /email/aliases/{email}/restore` to reactivate.\n- **Path Parameter**: The `@` character in the `{email}` path parameter must be URL-encoded as `%40`.\n\n### Troubleshooting\n- **401 Unauthorized**: Missing or invalid token.\n- **404 Not Found**: No active alias with this exact address was found for the authenticated user, or the alias is already deleted.\n")
.argument("<email>", "email")
.action(async (email, opts) => {
const client = await loadSdkClient()
Expand Down
Loading
Loading