// ── SDK v2 — CardsClient ─────────────────────────────────────────────────────── // // Covers: // GET /v2/cards // POST /v2/cards // GET /v2/cards/:id // PATCH /v2/cards/:id // DELETE /v2/cards/:id // POST /v2/cards/bulk // PATCH /v2/cards/bulk // DELETE /v2/cards/bulk // POST /v2/cards/bulk/move // POST /v2/cards/:id/restore // POST /v2/cards/:id/clone // POST /v2/cards/:id/agent // POST /v2/cards/:id/members // DELETE /v2/cards/:id/members/:uid // POST /v2/cards/:id/attachments // DELETE /v2/cards/:id/attachments // POST /v2/cards/:id/sessions // GET /v2/cards/:id/sessions // GET /v2/cards/:id/sessions/:sid // PATCH /v2/cards/:id/sessions/:sid // DELETE /v2/cards/:id/sessions/:sid // POST /v2/cards/:id/sessions/:sid/data // GET /v2/cards/:id/sessions/:sid/data // GET /v2/cards/:id/sessions/:sid/data/:did // PATCH /v2/cards/:id/sessions/:sid/data/:did // DELETE /v2/cards/:id/sessions/:sid/data/:did // POST /v2/cards/:id/comments // GET /v2/cards/:id/comments // PATCH /v2/cards/:id/comments/:cid // DELETE /v2/cards/:id/comments/:cid // POST /v2/cards/:id/comments/:cid/replies // GET /v2/cards/:id/comments/:cid/replies import type { Fetcher } from "../http/fetcher"; import type { RequestOptions } from "../http/types"; import type { Card, CardCreate, CardUpdate, CardBulkUpdateItem, CardBulkMoveItem, CardBulkDeleteItem, Session, SessionCreate, SessionUpdate, SessionData, SessionDataCreate, SessionDataUpdate, CardComment, CardCommentCreate, CardCommentUpdate, ListParams, Paginated, } from "../types/index"; export class CardsClient { constructor(private readonly fetcher: Fetcher) {} // ── Cards CRUD ──────────────────────────────────────────────────────────────── /** * Lists cards with cursor pagination and optional DSL filtering. * Pass `deleted: true` to list soft-deleted cards (recycle bin). * * @example * ```ts * // Active cards on a board * const page = await client.cards.list({ q: "board_id.eq:'board-abc'", limit: 50 }); * * // Recycle bin * const recycled = await client.cards.list({ deleted: true }); * ``` */ list( params?: ListParams & { deleted?: boolean }, opts?: RequestOptions, ): Promise> { return this.fetcher.list("/cards", params, opts); } /** * Creates a new card. * * @example * ```ts * const card = await client.cards.create({ * team_id: "team-abc", * board_id: "board-xyz", * owner_id: "user-1", * title: "Implement auth", * status: "todo", * column_key: "todo", * }); * ``` */ create(data: CardCreate, opts?: RequestOptions): Promise { return this.fetcher.post("/cards", data, opts); } /** * Gets a single card by ID. * Includes `related_cards_resolved` with board name and lifecycle status. * * @throws {NotFoundError} when no card with `id` exists. */ get(id: string, opts?: RequestOptions): Promise { return this.fetcher.get(`/cards/${id}`, undefined, opts); } /** * Partially updates a card. Only provided fields are changed. * * @throws {NotFoundError} when no card with `id` exists. */ update(id: string, data: CardUpdate, opts?: RequestOptions): Promise { return this.fetcher.patch(`/cards/${id}`, data, opts); } /** * Soft-deletes a card (moves it to the recycle bin). * Pass `permanent: true` to hard-delete immediately. * * @throws {NotFoundError} when no card with `id` exists. */ delete( id: string, options?: { permanent?: boolean }, opts?: RequestOptions, ): Promise { const query = options?.permanent ? { permanent: "true" } : undefined; return this.fetcher.request("DELETE", `/cards/${id}`, { query, request: opts, }); } // ── Bulk operations ─────────────────────────────────────────────────────────── /** * Creates multiple cards in a single request. * Returns the created cards in the same order as the input. * * @example * ```ts * const cards = await client.cards.bulkCreate([ * { team_id: "t", board_id: "b", owner_id: "u", title: "Task 1", status: "todo", column_key: "todo" }, * { team_id: "t", board_id: "b", owner_id: "u", title: "Task 2", status: "todo", column_key: "todo" }, * ]); * ``` */ bulkCreate(items: CardCreate[], opts?: RequestOptions): Promise { return this.fetcher.post("/cards/bulk", items, opts); } /** * Partially updates multiple cards in a single request. * Each item must include `card_id` plus the fields to update. * * @example * ```ts * await client.cards.bulkUpdate([ * { card_id: "card-1", status: "done" }, * { card_id: "card-2", status: "doing" }, * ]); * ``` */ bulkUpdate(items: CardBulkUpdateItem[], opts?: RequestOptions): Promise { return this.fetcher.patch("/cards/bulk", items, opts); } /** * Deletes multiple cards in a single request. * Performs soft-delete by default; pass `permanent: true` for hard-delete. */ bulkDelete( items: CardBulkDeleteItem[], options?: { permanent?: boolean }, opts?: RequestOptions, ): Promise { const query = options?.permanent ? { permanent: "true" } : undefined; return this.fetcher.request("DELETE", "/cards/bulk", { body: items, query, request: opts, }); } /** * Moves multiple cards to a target column in a single request. * * @example * ```ts * await client.cards.bulkMove([ * { card_id: "card-1", column_key: "done" }, * { card_id: "card-2", column_key: "done" }, * ]); * ``` */ bulkMove(items: CardBulkMoveItem[], opts?: RequestOptions): Promise { return this.fetcher.post("/cards/bulk/move", items, opts); } // ── Card operations ─────────────────────────────────────────────────────────── /** * Restores a soft-deleted card from the recycle bin. * * @throws {NotFoundError} when no recycled card with `id` exists. */ restore(id: string, opts?: RequestOptions): Promise { return this.fetcher.post(`/cards/${id}/restore`, undefined, opts); } /** * Creates a full copy of a card (including parts and metadata). * The clone lands in the same board and column as the original. * * @throws {NotFoundError} when no card with `id` exists. */ clone(id: string, opts?: RequestOptions): Promise { return this.fetcher.post(`/cards/${id}/clone`, undefined, opts); } // ── Agent assignment ────────────────────────────────────────────────────────── /** * Assigns (or clears) the agent on a card. * Pass `agent_id: null` to remove the current agent assignment. * * @throws {NotFoundError} when no card with `id` exists. */ assignAgent( id: string, data: { agent_id: string | null }, opts?: RequestOptions, ): Promise { return this.fetcher.post(`/cards/${id}/agent`, data, opts); } // ── Members ─────────────────────────────────────────────────────────────────── /** * Adds a user as a member of a card. * * @throws {NotFoundError} when no card with `id` exists. */ addMember( id: string, data: { user_id: string }, opts?: RequestOptions, ): Promise { return this.fetcher.post(`/cards/${id}/members`, data, opts); } /** * Removes a member from a card. * * @throws {NotFoundError} when no card with `id` exists. */ removeMember(id: string, userId: string, opts?: RequestOptions): Promise { return this.fetcher.delete(`/cards/${id}/members/${userId}`, opts); } // ── Attachments ─────────────────────────────────────────────────────────────── /** * Attaches an already-uploaded file to a card. * The file must have been uploaded via the Storage API first. * * @example * ```ts * await client.cards.addAttachment(cardId, { * file_id: "file-abc", * file_name: "report.pdf", * mime_type: "application/pdf", * }); * ``` */ addAttachment( id: string, data: { file_id: string; file_name: string; mime_type: string }, opts?: RequestOptions, ): Promise { return this.fetcher.post(`/cards/${id}/attachments`, data, opts); } /** * Removes an attachment from a card. * * @example * ```ts * await client.cards.removeAttachment(cardId, { file_id: "file-abc" }); * ``` */ removeAttachment( id: string, data: { file_id: string }, opts?: RequestOptions, ): Promise { return this.fetcher.request("DELETE", `/cards/${id}/attachments`, { body: data, request: opts, }); } // ── Sessions ────────────────────────────────────────────────────────────────── /** * Creates a new session on a card. * * @example * ```ts * const session = await client.cards.createSession(cardId, { metadata: { run_id: "r1" } }); * ``` */ createSession( cardId: string, data?: SessionCreate, opts?: RequestOptions, ): Promise { return this.fetcher.post(`/cards/${cardId}/sessions`, data, opts); } /** * Lists sessions for a card. */ listSessions( cardId: string, params?: ListParams, opts?: RequestOptions, ): Promise> { return this.fetcher.list(`/cards/${cardId}/sessions`, params, opts); } /** * Gets a single session by ID. * * @throws {NotFoundError} when the session doesn't exist. */ getSession(cardId: string, sessionId: string, opts?: RequestOptions): Promise { return this.fetcher.get( `/cards/${cardId}/sessions/${sessionId}`, undefined, opts, ); } /** * Updates a session's metadata. * * @throws {NotFoundError} when the session doesn't exist. */ updateSession( cardId: string, sessionId: string, data: SessionUpdate, opts?: RequestOptions, ): Promise { return this.fetcher.patch( `/cards/${cardId}/sessions/${sessionId}`, data, opts, ); } /** * Deletes a session and all its data entries. * * @throws {NotFoundError} when the session doesn't exist. */ deleteSession(cardId: string, sessionId: string, opts?: RequestOptions): Promise { return this.fetcher.delete(`/cards/${cardId}/sessions/${sessionId}`, opts); } // ── Session Data ────────────────────────────────────────────────────────────── /** * Creates a session data entry (or batch of entries). * * @example Single entry: * ```ts * const entry = await client.cards.createSessionData(cardId, sessionId, { * label: "result", * data: { score: 0.95, tokens: 1200 }, * }); * ``` * * @example Batch: * ```ts * const entries = await client.cards.createSessionData(cardId, sessionId, { * items: [ * { label: "step-1", data: { output: "..." } }, * { label: "step-2", data: { output: "..." } }, * ], * }); * ``` */ createSessionData( cardId: string, sessionId: string, data: SessionDataCreate | { items: SessionDataCreate[] }, opts?: RequestOptions, ): Promise { return this.fetcher.post( `/cards/${cardId}/sessions/${sessionId}/data`, data, opts, ); } /** * Lists all data entries for a session. */ listSessionData( cardId: string, sessionId: string, params?: ListParams, opts?: RequestOptions, ): Promise> { return this.fetcher.list( `/cards/${cardId}/sessions/${sessionId}/data`, params, opts, ); } /** * Gets a single session data entry by ID. * * @throws {NotFoundError} when the entry doesn't exist. */ getSessionData( cardId: string, sessionId: string, dataId: string, opts?: RequestOptions, ): Promise { return this.fetcher.get( `/cards/${cardId}/sessions/${sessionId}/data/${dataId}`, undefined, opts, ); } /** * Updates a session data entry. * * @throws {NotFoundError} when the entry doesn't exist. */ updateSessionData( cardId: string, sessionId: string, dataId: string, data: SessionDataUpdate, opts?: RequestOptions, ): Promise { return this.fetcher.patch( `/cards/${cardId}/sessions/${sessionId}/data/${dataId}`, data, opts, ); } /** * Deletes a session data entry. * * @throws {NotFoundError} when the entry doesn't exist. */ deleteSessionData( cardId: string, sessionId: string, dataId: string, opts?: RequestOptions, ): Promise { return this.fetcher.delete( `/cards/${cardId}/sessions/${sessionId}/data/${dataId}`, opts, ); } // ── Comments ────────────────────────────────────────────────────────────────── /** * Adds a comment to a card. * Include `mention_ids` to trigger mention notifications for the listed users. * * @example * ```ts * const comment = await client.cards.createComment(cardId, { * author_id: "user-1", * text: "Looks good to me!", * mention_ids: ["user-2"], * }); * ``` */ createComment( cardId: string, data: CardCommentCreate, opts?: RequestOptions, ): Promise { return this.fetcher.post(`/cards/${cardId}/comments`, data, opts); } /** * Lists comments on a card. */ listComments( cardId: string, params?: ListParams, opts?: RequestOptions, ): Promise> { return this.fetcher.list(`/cards/${cardId}/comments`, params, opts); } /** * Updates a comment's text. * * @throws {NotFoundError} when the comment doesn't exist. */ updateComment( cardId: string, commentId: string, data: CardCommentUpdate, opts?: RequestOptions, ): Promise { return this.fetcher.patch( `/cards/${cardId}/comments/${commentId}`, data, opts, ); } /** * Deletes a comment. * * @throws {NotFoundError} when the comment doesn't exist. */ deleteComment(cardId: string, commentId: string, opts?: RequestOptions): Promise { return this.fetcher.delete(`/cards/${cardId}/comments/${commentId}`, opts); } /** * Adds a reply to a comment. * The reply author is identified by `author_id`. * * @example * ```ts * await client.cards.createReply(cardId, commentId, { * author_id: "user-2", * text: "Thanks for the review!", * }); * ``` */ createReply( cardId: string, commentId: string, data: CardCommentCreate, opts?: RequestOptions, ): Promise { return this.fetcher.post( `/cards/${cardId}/comments/${commentId}/replies`, data, opts, ); } /** * Lists replies on a comment. */ listReplies( cardId: string, commentId: string, params?: ListParams, opts?: RequestOptions, ): Promise> { return this.fetcher.list( `/cards/${cardId}/comments/${commentId}/replies`, params, opts, ); } }