@nurama/sdk 0.0.0-stage → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +1080 -2
  4. package/dist/BotClient.d.ts +66 -0
  5. package/dist/BotClient.d.ts.map +1 -0
  6. package/dist/BotClient.js +68 -0
  7. package/dist/BotClient.js.map +1 -0
  8. package/dist/NuramaClient.d.ts +448 -0
  9. package/dist/NuramaClient.d.ts.map +1 -0
  10. package/dist/NuramaClient.js +864 -0
  11. package/dist/NuramaClient.js.map +1 -0
  12. package/dist/browser/nurama-bot-sdk.js +11780 -0
  13. package/dist/browser/nurama-bot-sdk.min.js +1 -0
  14. package/dist/browser/nurama-sdk.js +11732 -0
  15. package/dist/browser/nurama-sdk.min.js +1 -0
  16. package/dist/routes/ai.d.ts +280 -0
  17. package/dist/routes/ai.d.ts.map +1 -0
  18. package/dist/routes/ai.js +173 -0
  19. package/dist/routes/ai.js.map +1 -0
  20. package/dist/routes/asset.d.ts +493 -0
  21. package/dist/routes/asset.d.ts.map +1 -0
  22. package/dist/routes/asset.js +848 -0
  23. package/dist/routes/asset.js.map +1 -0
  24. package/dist/routes/auth.d.ts +218 -0
  25. package/dist/routes/auth.d.ts.map +1 -0
  26. package/dist/routes/auth.js +454 -0
  27. package/dist/routes/auth.js.map +1 -0
  28. package/dist/routes/blogPosts.d.ts +17 -0
  29. package/dist/routes/blogPosts.d.ts.map +1 -0
  30. package/dist/routes/blogPosts.js +29 -0
  31. package/dist/routes/blogPosts.js.map +1 -0
  32. package/dist/routes/board.d.ts +187 -0
  33. package/dist/routes/board.d.ts.map +1 -0
  34. package/dist/routes/board.js +270 -0
  35. package/dist/routes/board.js.map +1 -0
  36. package/dist/routes/bot.d.ts +147 -0
  37. package/dist/routes/bot.d.ts.map +1 -0
  38. package/dist/routes/bot.js +157 -0
  39. package/dist/routes/bot.js.map +1 -0
  40. package/dist/routes/chat.d.ts +842 -0
  41. package/dist/routes/chat.d.ts.map +1 -0
  42. package/dist/routes/chat.js +863 -0
  43. package/dist/routes/chat.js.map +1 -0
  44. package/dist/routes/chatAi.d.ts +51 -0
  45. package/dist/routes/chatAi.d.ts.map +1 -0
  46. package/dist/routes/chatAi.js +109 -0
  47. package/dist/routes/chatAi.js.map +1 -0
  48. package/dist/routes/config.d.ts +11 -0
  49. package/dist/routes/config.d.ts.map +1 -0
  50. package/dist/routes/config.js +24 -0
  51. package/dist/routes/config.js.map +1 -0
  52. package/dist/routes/convo.d.ts +169 -0
  53. package/dist/routes/convo.d.ts.map +1 -0
  54. package/dist/routes/convo.js +284 -0
  55. package/dist/routes/convo.js.map +1 -0
  56. package/dist/routes/credits.d.ts +82 -0
  57. package/dist/routes/credits.d.ts.map +1 -0
  58. package/dist/routes/credits.js +49 -0
  59. package/dist/routes/credits.js.map +1 -0
  60. package/dist/routes/device.d.ts +74 -0
  61. package/dist/routes/device.d.ts.map +1 -0
  62. package/dist/routes/device.js +122 -0
  63. package/dist/routes/device.js.map +1 -0
  64. package/dist/routes/folder.d.ts +75 -0
  65. package/dist/routes/folder.d.ts.map +1 -0
  66. package/dist/routes/folder.js +99 -0
  67. package/dist/routes/folder.js.map +1 -0
  68. package/dist/routes/invite.d.ts +61 -0
  69. package/dist/routes/invite.d.ts.map +1 -0
  70. package/dist/routes/invite.js +86 -0
  71. package/dist/routes/invite.js.map +1 -0
  72. package/dist/routes/joinLink.d.ts +38 -0
  73. package/dist/routes/joinLink.d.ts.map +1 -0
  74. package/dist/routes/joinLink.js +81 -0
  75. package/dist/routes/joinLink.js.map +1 -0
  76. package/dist/routes/membership.d.ts +116 -0
  77. package/dist/routes/membership.d.ts.map +1 -0
  78. package/dist/routes/membership.js +183 -0
  79. package/dist/routes/membership.js.map +1 -0
  80. package/dist/routes/notification.d.ts +103 -0
  81. package/dist/routes/notification.d.ts.map +1 -0
  82. package/dist/routes/notification.js +89 -0
  83. package/dist/routes/notification.js.map +1 -0
  84. package/dist/routes/payment.d.ts +56 -0
  85. package/dist/routes/payment.d.ts.map +1 -0
  86. package/dist/routes/payment.js +78 -0
  87. package/dist/routes/payment.js.map +1 -0
  88. package/dist/routes/product.d.ts +43 -0
  89. package/dist/routes/product.d.ts.map +1 -0
  90. package/dist/routes/product.js +53 -0
  91. package/dist/routes/product.js.map +1 -0
  92. package/dist/routes/project.d.ts +821 -0
  93. package/dist/routes/project.d.ts.map +1 -0
  94. package/dist/routes/project.js +1153 -0
  95. package/dist/routes/project.js.map +1 -0
  96. package/dist/routes/public.d.ts +269 -0
  97. package/dist/routes/public.d.ts.map +1 -0
  98. package/dist/routes/public.js +412 -0
  99. package/dist/routes/public.js.map +1 -0
  100. package/dist/routes/scratch.d.ts +70 -0
  101. package/dist/routes/scratch.d.ts.map +1 -0
  102. package/dist/routes/scratch.js +67 -0
  103. package/dist/routes/scratch.js.map +1 -0
  104. package/dist/routes/settings.d.ts +102 -0
  105. package/dist/routes/settings.d.ts.map +1 -0
  106. package/dist/routes/settings.js +94 -0
  107. package/dist/routes/settings.js.map +1 -0
  108. package/dist/routes/shortlink.d.ts +79 -0
  109. package/dist/routes/shortlink.d.ts.map +1 -0
  110. package/dist/routes/shortlink.js +25 -0
  111. package/dist/routes/shortlink.js.map +1 -0
  112. package/dist/routes/socket.d.ts +108 -0
  113. package/dist/routes/socket.d.ts.map +1 -0
  114. package/dist/routes/socket.js +555 -0
  115. package/dist/routes/socket.js.map +1 -0
  116. package/dist/routes/storage.d.ts +44 -0
  117. package/dist/routes/storage.d.ts.map +1 -0
  118. package/dist/routes/storage.js +49 -0
  119. package/dist/routes/storage.js.map +1 -0
  120. package/dist/routes/subscription.d.ts +184 -0
  121. package/dist/routes/subscription.d.ts.map +1 -0
  122. package/dist/routes/subscription.js +219 -0
  123. package/dist/routes/subscription.js.map +1 -0
  124. package/dist/routes/supportChat.d.ts +40 -0
  125. package/dist/routes/supportChat.d.ts.map +1 -0
  126. package/dist/routes/supportChat.js +53 -0
  127. package/dist/routes/supportChat.js.map +1 -0
  128. package/dist/routes/supportTicket.d.ts +89 -0
  129. package/dist/routes/supportTicket.d.ts.map +1 -0
  130. package/dist/routes/supportTicket.js +54 -0
  131. package/dist/routes/supportTicket.js.map +1 -0
  132. package/dist/routes/tag.d.ts +72 -0
  133. package/dist/routes/tag.d.ts.map +1 -0
  134. package/dist/routes/tag.js +81 -0
  135. package/dist/routes/tag.js.map +1 -0
  136. package/dist/routes/task.d.ts +252 -0
  137. package/dist/routes/task.d.ts.map +1 -0
  138. package/dist/routes/task.js +284 -0
  139. package/dist/routes/task.js.map +1 -0
  140. package/dist/routes/taskRelation.d.ts +80 -0
  141. package/dist/routes/taskRelation.d.ts.map +1 -0
  142. package/dist/routes/taskRelation.js +71 -0
  143. package/dist/routes/taskRelation.js.map +1 -0
  144. package/dist/routes/token.d.ts +75 -0
  145. package/dist/routes/token.d.ts.map +1 -0
  146. package/dist/routes/token.js +51 -0
  147. package/dist/routes/token.js.map +1 -0
  148. package/dist/routes/user.d.ts +112 -0
  149. package/dist/routes/user.d.ts.map +1 -0
  150. package/dist/routes/user.js +151 -0
  151. package/dist/routes/user.js.map +1 -0
  152. package/dist/routes/version.d.ts +42 -0
  153. package/dist/routes/version.d.ts.map +1 -0
  154. package/dist/routes/version.js +38 -0
  155. package/dist/routes/version.js.map +1 -0
  156. package/dist/routes/webhook.d.ts +170 -0
  157. package/dist/routes/webhook.d.ts.map +1 -0
  158. package/dist/routes/webhook.js +173 -0
  159. package/dist/routes/webhook.js.map +1 -0
  160. package/dist/routes/workspace.d.ts +120 -0
  161. package/dist/routes/workspace.d.ts.map +1 -0
  162. package/dist/routes/workspace.js +199 -0
  163. package/dist/routes/workspace.js.map +1 -0
  164. package/dist/utils/uploadSessionManager.d.ts +133 -0
  165. package/dist/utils/uploadSessionManager.d.ts.map +1 -0
  166. package/dist/utils/uploadSessionManager.js +321 -0
  167. package/dist/utils/uploadSessionManager.js.map +1 -0
  168. package/dist/utils/urlParams.d.ts +35 -0
  169. package/dist/utils/urlParams.d.ts.map +1 -0
  170. package/dist/utils/urlParams.js +146 -0
  171. package/dist/utils/urlParams.js.map +1 -0
  172. package/dist/version.d.ts +15 -0
  173. package/dist/version.d.ts.map +1 -0
  174. package/dist/version.js +12 -0
  175. package/dist/version.js.map +1 -0
  176. package/package.json +87 -3
  177. package/src/BotClient.ts +113 -0
  178. package/src/NuramaClient.ts +1193 -0
  179. package/src/bot-browser-entry.js +15 -0
  180. package/src/browser-entry.js +20 -0
  181. package/src/routes/ai.ts +378 -0
  182. package/src/routes/asset.ts +1104 -0
  183. package/src/routes/auth.ts +587 -0
  184. package/src/routes/blogPosts.ts +29 -0
  185. package/src/routes/board.ts +403 -0
  186. package/src/routes/bot.ts +257 -0
  187. package/src/routes/chat.ts +1292 -0
  188. package/src/routes/chatAi.ts +125 -0
  189. package/src/routes/config.ts +31 -0
  190. package/src/routes/convo.ts +321 -0
  191. package/src/routes/credits.ts +112 -0
  192. package/src/routes/device.ts +133 -0
  193. package/src/routes/folder.ts +154 -0
  194. package/src/routes/invite.ts +133 -0
  195. package/src/routes/joinLink.ts +100 -0
  196. package/src/routes/membership.ts +237 -0
  197. package/src/routes/notification.ts +166 -0
  198. package/src/routes/payment.ts +104 -0
  199. package/src/routes/product.ts +67 -0
  200. package/src/routes/project.ts +1528 -0
  201. package/src/routes/public.ts +496 -0
  202. package/src/routes/scratch.ts +94 -0
  203. package/src/routes/settings.ts +152 -0
  204. package/src/routes/shortlink.ts +90 -0
  205. package/src/routes/socket.ts +739 -0
  206. package/src/routes/storage.ts +83 -0
  207. package/src/routes/subscription.ts +307 -0
  208. package/src/routes/supportChat.ts +62 -0
  209. package/src/routes/supportTicket.ts +114 -0
  210. package/src/routes/tag.ts +131 -0
  211. package/src/routes/task.ts +431 -0
  212. package/src/routes/taskRelation.ts +125 -0
  213. package/src/routes/token.ts +113 -0
  214. package/src/routes/user.ts +214 -0
  215. package/src/routes/version.ts +62 -0
  216. package/src/routes/webhook.ts +295 -0
  217. package/src/routes/workspace.ts +223 -0
  218. package/src/utils/uploadSessionManager.ts +407 -0
  219. package/src/utils/urlParams.ts +181 -0
  220. package/src/version.ts +22 -0
@@ -0,0 +1,403 @@
1
+ import NuramaClient from '../NuramaClient.js';
2
+ import {
3
+ type Board,
4
+ type BoardColumn,
5
+ type BoardWithTasks,
6
+ type Task,
7
+ } from '@nurama/types';
8
+
9
+ // --- Request Body Interfaces ---
10
+
11
+ export interface CreateBoardData {
12
+ projectId: string;
13
+ name: string;
14
+ description?: string;
15
+ visibility?: ('creator' | 'reviewer')[];
16
+ columns?: {
17
+ name: string;
18
+ description?: string;
19
+ color?: string;
20
+ isDefault?: boolean;
21
+ taskStatus?: 'pending' | 'inProgress' | 'complete' | 'closed' | null;
22
+ sortOrder?: number;
23
+ }[];
24
+ }
25
+
26
+ export interface UpdateBoardData {
27
+ name?: string;
28
+ description?: string | null;
29
+ visibility?: ('creator' | 'reviewer')[];
30
+ status?: 'active' | 'archived';
31
+ sortOrder?: number;
32
+ /**
33
+ * When narrowing visibility, opt into automatic cleanup of dependent tasks,
34
+ * assignees, and relations. If omitted (or false) and dependents exist, the
35
+ * server returns 409 with a structured `errorData.blockers` payload.
36
+ */
37
+ cascade?: boolean;
38
+ }
39
+
40
+ export interface AddColumnData {
41
+ name: string;
42
+ description?: string;
43
+ color?: string;
44
+ isDefault?: boolean;
45
+ taskStatus?: 'pending' | 'inProgress' | 'complete' | 'closed' | null;
46
+ sortOrder?: number;
47
+ /** When true, reviewer-role users may create tasks in this column on a reviewer-visibility board. */
48
+ reviewersCanContribute?: boolean;
49
+ }
50
+
51
+ export interface UpdateColumnData {
52
+ name?: string;
53
+ description?: string | null;
54
+ color?: string | null;
55
+ isDefault?: boolean;
56
+ taskStatus?: 'pending' | 'inProgress' | 'complete' | 'closed' | null;
57
+ sortOrder?: number;
58
+ /** When true, reviewer-role users may create tasks in this column on a reviewer-visibility board. */
59
+ reviewersCanContribute?: boolean;
60
+ }
61
+
62
+ export interface ReorderColumnsData {
63
+ columns: { id: string; sortOrder: number }[];
64
+ }
65
+
66
+ export interface CreateBoardTaskData {
67
+ subject: string;
68
+ description?: string;
69
+ assignedToId?: string;
70
+ columnId?: string;
71
+ /** Optional announce flag. When supplied, after the task is created
72
+ * the server posts a Nu reply in `chatId` threaded under `messageId`
73
+ * with this task as a single taskCard and links them via TaskRelation.
74
+ * Mirrors the same flag on `tasks.bulkCreate`. */
75
+ announce?: {
76
+ chatId: string;
77
+ messageId: string;
78
+ };
79
+ }
80
+
81
+ export interface AddExistingTaskData {
82
+ taskId: string;
83
+ columnId?: string;
84
+ }
85
+
86
+ export interface MoveTaskData {
87
+ /** Required for same-board moves; optional when `targetBoardId` is
88
+ * set (server defaults to the target board's default column). */
89
+ columnId?: string;
90
+ sortOrder?: number;
91
+ /** When supplied AND different from the URL `boardId`, the server
92
+ * performs a cross-board move — `boardId` and `columnId` are
93
+ * updated together. Omit for the original same-board move. */
94
+ targetBoardId?: string;
95
+ }
96
+
97
+ export interface UpdateTaskDetailsData {
98
+ subject?: string;
99
+ description?: string | null;
100
+ assignedToId?: string | null;
101
+ }
102
+
103
+ export interface LinkTaskData {
104
+ linkedTaskId: string;
105
+ linkType?: 'related' | 'blocks' | 'blockedBy' | 'duplicate';
106
+ }
107
+
108
+ // --- Method Definitions ---
109
+
110
+ export default function createBoardMethods(client: NuramaClient) {
111
+ return {
112
+ // ========================================================================
113
+ // Board CRUD
114
+ // ========================================================================
115
+
116
+ /**
117
+ * Create a new board inside a project. If `columns` are omitted the
118
+ * server seeds the standard four-column layout.
119
+ */
120
+ async createBoard(data: CreateBoardData): Promise<Board> {
121
+ if (!data.projectId) throw new Error('projectId is required.');
122
+ if (!data.name) throw new Error('name is required.');
123
+ return client._request<Board>({
124
+ method: 'POST',
125
+ endpoint: '/v1/boards/',
126
+ body: data,
127
+ sendJWT: true,
128
+ });
129
+ },
130
+
131
+ /**
132
+ * List boards in a project. Filters by visibility, free-text search,
133
+ * and tag ids.
134
+ */
135
+ async getProjectBoards(projectId: string, params?: { visibility?: string; search?: string; tags?: string[] }): Promise<Board[]> {
136
+ if (!projectId) throw new Error('projectId is required.');
137
+ return client._request<Board[]>({
138
+ method: 'GET',
139
+ endpoint: `/v1/boards/project/${projectId}`,
140
+ params,
141
+ sendJWT: true,
142
+ });
143
+ },
144
+
145
+ /**
146
+ * List every task in a project across all boards. Use `boardId:
147
+ * 'unassigned'` to fetch only tasks not yet placed on a board.
148
+ */
149
+ async getProjectTasks(projectId: string, params?: {
150
+ search?: string;
151
+ status?: string;
152
+ boardId?: string | 'unassigned';
153
+ columnId?: string;
154
+ assignedToId?: string;
155
+ visibility?: string;
156
+ tags?: string[];
157
+ page?: number;
158
+ limit?: number;
159
+ sort?: Record<string, 1 | -1>;
160
+ }): Promise<any> {
161
+ if (!projectId) throw new Error('projectId is required.');
162
+ return client._request<any>({
163
+ method: 'GET',
164
+ endpoint: `/v1/boards/project/${projectId}/tasks`,
165
+ params,
166
+ sendJWT: true,
167
+ });
168
+ },
169
+
170
+ /** Fetch a board with its columns and tasks populated. */
171
+ async getBoard(boardId: string): Promise<BoardWithTasks> {
172
+ if (!boardId) throw new Error('boardId is required.');
173
+ return client._request<BoardWithTasks>({
174
+ method: 'GET',
175
+ endpoint: `/v1/boards/${boardId}`,
176
+ sendJWT: true,
177
+ });
178
+ },
179
+
180
+ /**
181
+ * Update a board's metadata. Set `cascade: true` to opt into
182
+ * automatic cleanup of dependent tasks when narrowing visibility;
183
+ * otherwise the server returns 409 with a `blockers` payload.
184
+ */
185
+ async updateBoard(boardId: string, data: UpdateBoardData): Promise<Board> {
186
+ if (!boardId) throw new Error('boardId is required.');
187
+ return client._request<Board>({
188
+ method: 'PUT',
189
+ endpoint: `/v1/boards/${boardId}`,
190
+ body: data,
191
+ sendJWT: true,
192
+ });
193
+ },
194
+
195
+ /**
196
+ * Delete a board. `disposition` controls what happens to its tasks:
197
+ * `unassign` (default — tasks survive boardless), `delete`, or
198
+ * `reassign` to another board/column.
199
+ */
200
+ async deleteBoard(
201
+ boardId: string,
202
+ body?: {
203
+ disposition?: 'unassign' | 'delete' | 'reassign';
204
+ targetBoardId?: string;
205
+ targetColumnId?: string;
206
+ }
207
+ ): Promise<{ message: string; disposition: string; deletedTaskIds: string[]; reassignedTaskIds: string[] }> {
208
+ if (!boardId) throw new Error('boardId is required.');
209
+ return client._request({
210
+ method: 'DELETE',
211
+ endpoint: `/v1/boards/${boardId}`,
212
+ body: body && Object.keys(body).length > 0 ? body : undefined,
213
+ sendJWT: true,
214
+ });
215
+ },
216
+
217
+ // ========================================================================
218
+ // Column Operations
219
+ // ========================================================================
220
+
221
+ /** Add a column to a board. Appended at the end unless `sortOrder` is provided. */
222
+ async addColumn(boardId: string, data: AddColumnData): Promise<BoardColumn> {
223
+ if (!boardId) throw new Error('boardId is required.');
224
+ if (!data.name) throw new Error('name is required.');
225
+ return client._request<BoardColumn>({
226
+ method: 'POST',
227
+ endpoint: `/v1/boards/${boardId}/columns`,
228
+ body: data,
229
+ sendJWT: true,
230
+ });
231
+ },
232
+
233
+ /** Reorder a board's columns. The request must include every column id. */
234
+ async reorderColumns(boardId: string, data: ReorderColumnsData): Promise<BoardColumn[]> {
235
+ if (!boardId) throw new Error('boardId is required.');
236
+ return client._request<BoardColumn[]>({
237
+ method: 'PUT',
238
+ endpoint: `/v1/boards/${boardId}/columns/reorder`,
239
+ body: data,
240
+ sendJWT: true,
241
+ });
242
+ },
243
+
244
+ /** Update column metadata (name, color, task-status mapping, etc.). */
245
+ async updateColumn(boardId: string, columnId: string, data: UpdateColumnData): Promise<BoardColumn> {
246
+ if (!boardId) throw new Error('boardId is required.');
247
+ if (!columnId) throw new Error('columnId is required.');
248
+ return client._request<BoardColumn>({
249
+ method: 'PUT',
250
+ endpoint: `/v1/boards/${boardId}/columns/${columnId}`,
251
+ body: data,
252
+ sendJWT: true,
253
+ });
254
+ },
255
+
256
+ /**
257
+ * Delete a column. If the column holds tasks, pass `targetColumnId`
258
+ * to move them; otherwise the call fails.
259
+ */
260
+ async deleteColumn(boardId: string, columnId: string, targetColumnId?: string): Promise<void> {
261
+ if (!boardId) throw new Error('boardId is required.');
262
+ if (!columnId) throw new Error('columnId is required.');
263
+ return client._request<void>({
264
+ method: 'DELETE',
265
+ endpoint: `/v1/boards/${boardId}/columns/${columnId}`,
266
+ body: targetColumnId ? { targetColumnId } : undefined,
267
+ sendJWT: true,
268
+ });
269
+ },
270
+
271
+ // ========================================================================
272
+ // Board Task Operations
273
+ // ========================================================================
274
+
275
+ /** List tasks on a board, optionally narrowed by column / assignee / tag / status / search. */
276
+ async getBoardTasks(
277
+ boardId: string,
278
+ params?: {
279
+ columnId?: string;
280
+ search?: string;
281
+ status?: string;
282
+ assignedToId?: string;
283
+ tags?: string[];
284
+ }
285
+ ): Promise<Task[]> {
286
+ if (!boardId) throw new Error('boardId is required.');
287
+ return client._request<Task[]>({
288
+ method: 'GET',
289
+ endpoint: `/v1/boards/${boardId}/tasks`,
290
+ params,
291
+ sendJWT: true,
292
+ });
293
+ },
294
+
295
+ /**
296
+ * Create a task directly on a board. When `announce` is supplied the
297
+ * server also posts a Nu reply in the named chat threaded under the
298
+ * given message, with this task linked via TaskRelation.
299
+ */
300
+ async createBoardTask(boardId: string, data: CreateBoardTaskData): Promise<Task> {
301
+ if (!boardId) throw new Error('boardId is required.');
302
+ if (!data.subject) throw new Error('subject is required.');
303
+ return client._request<Task>({
304
+ method: 'POST',
305
+ endpoint: `/v1/boards/${boardId}/tasks`,
306
+ body: data,
307
+ sendJWT: true,
308
+ });
309
+ },
310
+
311
+ /** Attach an existing project task to a board (no new task is created). */
312
+ async addExistingTaskToBoard(boardId: string, data: AddExistingTaskData): Promise<Task> {
313
+ if (!boardId) throw new Error('boardId is required.');
314
+ if (!data.taskId) throw new Error('taskId is required.');
315
+ return client._request<Task>({
316
+ method: 'POST',
317
+ endpoint: `/v1/boards/${boardId}/tasks/add`,
318
+ body: data,
319
+ sendJWT: true,
320
+ });
321
+ },
322
+
323
+ /** Move a task to a different column on the same board. */
324
+ async moveTask(boardId: string, taskId: string, data: MoveTaskData): Promise<Task> {
325
+ if (!boardId) throw new Error('boardId is required.');
326
+ if (!taskId) throw new Error('taskId is required.');
327
+ if (!data.columnId) throw new Error('columnId is required.');
328
+ return client._request<Task>({
329
+ method: 'PUT',
330
+ endpoint: `/v1/boards/${boardId}/tasks/${taskId}/move`,
331
+ body: data,
332
+ sendJWT: true,
333
+ });
334
+ },
335
+
336
+ /** Detach a task from a board. The task itself is preserved as boardless. */
337
+ async removeTaskFromBoard(boardId: string, taskId: string): Promise<Task> {
338
+ if (!boardId) throw new Error('boardId is required.');
339
+ if (!taskId) throw new Error('taskId is required.');
340
+ return client._request<Task>({
341
+ method: 'DELETE',
342
+ endpoint: `/v1/boards/${boardId}/tasks/${taskId}`,
343
+ sendJWT: true,
344
+ });
345
+ },
346
+
347
+ // ========================================================================
348
+ // Task Detail Operations
349
+ // ========================================================================
350
+
351
+ // ========================================================================
352
+ // Task Link Operations
353
+ // ========================================================================
354
+
355
+ // ========================================================================
356
+ // Tagging
357
+ // ========================================================================
358
+
359
+ /** Attach a tag to a board. */
360
+ async tagBoard(boardId: string, tagId: string): Promise<Board> {
361
+ return client._request<Board>({
362
+ method: 'PUT',
363
+ endpoint: `/v1/boards/${boardId}/tag`,
364
+ body: { tagId },
365
+ sendJWT: true,
366
+ });
367
+ },
368
+
369
+ /** Detach a tag from a board. */
370
+ async untagBoard(boardId: string, tagId: string): Promise<Board> {
371
+ return client._request<Board>({
372
+ method: 'PUT',
373
+ endpoint: `/v1/boards/${boardId}/untag`,
374
+ body: { tagId },
375
+ sendJWT: true,
376
+ });
377
+ },
378
+
379
+ // ========================================================================
380
+ // Following
381
+ // ========================================================================
382
+
383
+ /** Follow a board to receive notifications about its tasks. */
384
+ async followBoard(boardId: string): Promise<Board> {
385
+ if (!boardId) throw new Error('boardId is required.');
386
+ return client._request<Board>({
387
+ method: 'PUT',
388
+ endpoint: `/v1/boards/${boardId}/follow`,
389
+ sendJWT: true,
390
+ });
391
+ },
392
+
393
+ /** Stop following a board. */
394
+ async unfollowBoard(boardId: string): Promise<Board> {
395
+ if (!boardId) throw new Error('boardId is required.');
396
+ return client._request<Board>({
397
+ method: 'PUT',
398
+ endpoint: `/v1/boards/${boardId}/unfollow`,
399
+ sendJWT: true,
400
+ });
401
+ },
402
+ };
403
+ }
@@ -0,0 +1,257 @@
1
+ import NuramaClient from '../NuramaClient.js';
2
+
3
+ export interface BotApiKeySummary {
4
+ id: string;
5
+ keyPrefix: string;
6
+ name: string;
7
+ /** Discriminator. Bot keys mint as 'botAccess'. */
8
+ kind?: 'botAccess' | 'pat' | 'oauthAccess' | 'oauthRefresh' | null;
9
+ /** Granted action verbs. Empty array = unscoped (legacy). */
10
+ scopes?: string[];
11
+ lastUsed?: string | null;
12
+ expiresAt?: string | null;
13
+ status: 'active' | 'revoked';
14
+ createdAt: string;
15
+ }
16
+
17
+ export interface Bot {
18
+ id: string;
19
+ displayName: string;
20
+ color: string;
21
+ accountType: 'bot';
22
+ status: string;
23
+ avatarId: string | null;
24
+ /**
25
+ * Populated avatar Asset (joined server-side from `avatarId`). The web client
26
+ * resolves the image URL from this field via `getAvatarUrl()`.
27
+ */
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ avatar?: any | null;
30
+ roles?: string[];
31
+ membershipId?: string;
32
+ apiKeys?: BotApiKeySummary[];
33
+ apiKey?: BotApiKeySummary;
34
+ /**
35
+ * Project memberships within the workspace the bot belongs to. Populated by
36
+ * `listBots` and `getBot`. Empty array if the bot has no project assignments.
37
+ */
38
+ projectMemberships?: BotProjectMembership[];
39
+ createdAt: string;
40
+ updatedAt: string;
41
+ }
42
+
43
+ export interface CreateBotData {
44
+ /** Display name for the bot. 1–50 characters. */
45
+ name: string;
46
+ /** Optional hex color from the approved palette. Auto-assigned if omitted. */
47
+ color?: string;
48
+ /** Workspace roles to grant the bot. Must be one or more of:
49
+ * workspaceAdmin, workspaceMember, workspaceChatMember. */
50
+ roles: Array<'workspaceAdmin' | 'workspaceMember' | 'workspaceChatMember'>;
51
+ /**
52
+ * Optional API-key scopes. Defaults to [] (legacy — bot key can call
53
+ * any route that isn't yet scope-gated). Once a route is gated by
54
+ * `requireTokenScope` the bot needs the matching scope here for that
55
+ * route to work.
56
+ */
57
+ scopes?: string[];
58
+ }
59
+
60
+ export interface CreateBotResponse {
61
+ bot: Bot;
62
+ /** Raw API key, returned ONCE at creation time. Store it immediately — it
63
+ * cannot be retrieved again. */
64
+ rawKey: string;
65
+ }
66
+
67
+ export interface UpdateBotData {
68
+ name?: string;
69
+ color?: string;
70
+ }
71
+
72
+ export interface RotateBotKeyResponse {
73
+ apiKey: BotApiKeySummary;
74
+ /** New raw API key. Returned once. */
75
+ rawKey: string;
76
+ }
77
+
78
+ export interface UpdateBotAvatarFileData {
79
+ name: string;
80
+ sizeInMB: number;
81
+ checksum: string;
82
+ }
83
+
84
+ export interface UpdateBotAvatarResponse {
85
+ user: { id: string; avatarId: string | null };
86
+ asset?: { id: string; [k: string]: unknown };
87
+ signedUrlData?: {
88
+ key: string;
89
+ uploadId: string;
90
+ urls: string[];
91
+ };
92
+ [k: string]: unknown;
93
+ }
94
+
95
+ export type BotProjectRole = 'creator' | 'reviewer' | 'projectAdmin';
96
+
97
+ export interface BotProjectMembership {
98
+ membershipId: string;
99
+ projectId: string;
100
+ projectName: string;
101
+ roles: BotProjectRole[];
102
+ }
103
+
104
+ /**
105
+ * Bot user administration methods. Used by workspace owners/admins (callers
106
+ * holding a JWT) to manage bot users in a workspace. Bots themselves cannot
107
+ * call these endpoints — bot administration is restricted server-side.
108
+ */
109
+ export default function createBotMethods(client: NuramaClient) {
110
+ return {
111
+ /**
112
+ * Create a bot user in a workspace.
113
+ * Returns the new bot and the raw API key (shown only once).
114
+ */
115
+ async createBot(workspaceId: string, data: CreateBotData): Promise<CreateBotResponse> {
116
+ if (!workspaceId) throw new Error('workspaceId is required.');
117
+ return client._request<CreateBotResponse>({
118
+ method: 'POST',
119
+ endpoint: `/v1/workspaces/${workspaceId}/bots`,
120
+ body: data,
121
+ sendJWT: true,
122
+ });
123
+ },
124
+
125
+ /** List bot users in a workspace. */
126
+ async listBots(workspaceId: string): Promise<Bot[]> {
127
+ if (!workspaceId) throw new Error('workspaceId is required.');
128
+ return client._request<Bot[]>({
129
+ method: 'GET',
130
+ endpoint: `/v1/workspaces/${workspaceId}/bots`,
131
+ sendJWT: true,
132
+ bypassCache: true,
133
+ });
134
+ },
135
+
136
+ /** Get a single bot's details, including all of its API key metadata (no secrets). */
137
+ async getBot(workspaceId: string, botId: string): Promise<Bot> {
138
+ if (!workspaceId) throw new Error('workspaceId is required.');
139
+ if (!botId) throw new Error('botId is required.');
140
+ return client._request<Bot>({
141
+ method: 'GET',
142
+ endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}`,
143
+ sendJWT: true,
144
+ bypassCache: true,
145
+ });
146
+ },
147
+
148
+ /** Update a bot's display name and/or color. */
149
+ async updateBot(workspaceId: string, botId: string, data: UpdateBotData): Promise<Bot> {
150
+ if (!workspaceId) throw new Error('workspaceId is required.');
151
+ if (!botId) throw new Error('botId is required.');
152
+ return client._request<Bot>({
153
+ method: 'PUT',
154
+ endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}`,
155
+ body: data,
156
+ sendJWT: true,
157
+ });
158
+ },
159
+
160
+ /**
161
+ * Delete a bot user. Revokes all API keys, removes the workspace
162
+ * membership, and marks the user inactive.
163
+ */
164
+ async deleteBot(workspaceId: string, botId: string): Promise<void> {
165
+ if (!workspaceId) throw new Error('workspaceId is required.');
166
+ if (!botId) throw new Error('botId is required.');
167
+ return client._request<void>({
168
+ method: 'DELETE',
169
+ endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}`,
170
+ sendJWT: true,
171
+ });
172
+ },
173
+
174
+ /**
175
+ * Revoke the bot's current API key and issue a new one.
176
+ * The new raw key is returned once.
177
+ */
178
+ async rotateBotKey(workspaceId: string, botId: string): Promise<RotateBotKeyResponse> {
179
+ if (!workspaceId) throw new Error('workspaceId is required.');
180
+ if (!botId) throw new Error('botId is required.');
181
+ return client._request<RotateBotKeyResponse>({
182
+ method: 'POST',
183
+ endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/rotate-key`,
184
+ sendJWT: true,
185
+ });
186
+ },
187
+
188
+ /**
189
+ * Request signed URL data to upload a new avatar for the bot.
190
+ * Caller multipart-uploads the file to S3 using the returned `signedUrlData`,
191
+ * then calls `client.asset.completeUpload(...)` to finalize.
192
+ */
193
+ async updateBotAvatar(
194
+ workspaceId: string,
195
+ botId: string,
196
+ fileData: UpdateBotAvatarFileData,
197
+ ): Promise<UpdateBotAvatarResponse> {
198
+ if (!workspaceId) throw new Error('workspaceId is required.');
199
+ if (!botId) throw new Error('botId is required.');
200
+ return client._request<UpdateBotAvatarResponse>({
201
+ method: 'POST',
202
+ endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/avatar`,
203
+ body: fileData,
204
+ sendJWT: true,
205
+ });
206
+ },
207
+
208
+ /**
209
+ * List the bot's project memberships within the workspace.
210
+ */
211
+ async listProjectMemberships(workspaceId: string, botId: string): Promise<BotProjectMembership[]> {
212
+ if (!workspaceId) throw new Error('workspaceId is required.');
213
+ if (!botId) throw new Error('botId is required.');
214
+ return client._request<BotProjectMembership[]>({
215
+ method: 'GET',
216
+ endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/memberships`,
217
+ sendJWT: true,
218
+ bypassCache: true,
219
+ });
220
+ },
221
+
222
+ /**
223
+ * Upsert the bot's membership on a project. Replaces roles if a membership
224
+ * already exists; creates one otherwise. Idempotent.
225
+ */
226
+ async setProjectMembership(
227
+ workspaceId: string,
228
+ botId: string,
229
+ projectId: string,
230
+ data: { roles: BotProjectRole[] },
231
+ ): Promise<BotProjectMembership> {
232
+ if (!workspaceId) throw new Error('workspaceId is required.');
233
+ if (!botId) throw new Error('botId is required.');
234
+ if (!projectId) throw new Error('projectId is required.');
235
+ return client._request<BotProjectMembership>({
236
+ method: 'PUT',
237
+ endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/memberships/project/${projectId}`,
238
+ body: data,
239
+ sendJWT: true,
240
+ });
241
+ },
242
+
243
+ /**
244
+ * Remove the bot's membership on a project. Idempotent.
245
+ */
246
+ async removeProjectMembership(workspaceId: string, botId: string, projectId: string): Promise<void> {
247
+ if (!workspaceId) throw new Error('workspaceId is required.');
248
+ if (!botId) throw new Error('botId is required.');
249
+ if (!projectId) throw new Error('projectId is required.');
250
+ return client._request<void>({
251
+ method: 'DELETE',
252
+ endpoint: `/v1/workspaces/${workspaceId}/bots/${botId}/memberships/project/${projectId}`,
253
+ sendJWT: true,
254
+ });
255
+ },
256
+ };
257
+ }