@codeguide/core 0.0.27 → 0.0.29
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.
- package/README.md +50 -41
- package/__tests__/services/codespace/codespace-v2.test.ts +29 -18
- package/__tests__/services/usage/usage-service.test.ts +597 -85
- package/codeguide.ts +6 -0
- package/dist/codeguide.d.ts +3 -1
- package/dist/codeguide.js +2 -0
- package/dist/index.d.ts +4 -3
- package/dist/services/base/base-service.d.ts +21 -0
- package/dist/services/base/base-service.js +114 -0
- package/dist/services/codespace/codespace-service.d.ts +55 -1
- package/dist/services/codespace/codespace-service.js +260 -5
- package/dist/services/codespace/codespace-types.d.ts +193 -13
- package/dist/services/codespace/index.d.ts +1 -1
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.js +7 -1
- package/dist/services/projects/project-types.d.ts +66 -32
- package/dist/services/repository-analysis/repository-types.d.ts +1 -0
- package/dist/services/starter-kits/index.d.ts +2 -0
- package/dist/services/starter-kits/index.js +20 -0
- package/dist/services/starter-kits/starter-kits-service.d.ts +13 -0
- package/dist/services/starter-kits/starter-kits-service.js +27 -0
- package/dist/services/starter-kits/starter-kits-types.d.ts +34 -0
- package/dist/services/starter-kits/starter-kits-types.js +2 -0
- package/dist/services/tasks/task-service.d.ts +2 -1
- package/dist/services/tasks/task-service.js +8 -0
- package/dist/services/tasks/task-types.d.ts +26 -7
- package/dist/services/usage/usage-service.d.ts +5 -2
- package/dist/services/usage/usage-service.js +58 -9
- package/dist/services/usage/usage-types.d.ts +207 -34
- package/dist/services/users/index.d.ts +2 -0
- package/dist/services/users/index.js +20 -0
- package/dist/services/users/user-service.d.ts +12 -0
- package/dist/services/users/user-service.js +17 -0
- package/dist/services/users/user-types.d.ts +55 -0
- package/dist/services/users/user-types.js +2 -0
- package/docs/.vitepress/README.md +51 -0
- package/docs/.vitepress/config.ts +139 -0
- package/docs/.vitepress/theme/custom.css +80 -0
- package/docs/.vitepress/theme/index.ts +13 -0
- package/docs/.vitepress/tsconfig.json +19 -0
- package/docs/QUICKSTART.md +77 -0
- package/docs/README.md +134 -0
- package/docs/README_SETUP.md +46 -0
- package/docs/authentication.md +351 -0
- package/docs/codeguide-client.md +350 -0
- package/docs/codespace-models.md +1004 -0
- package/docs/codespace-service.md +558 -81
- package/docs/index.md +135 -0
- package/docs/package.json +14 -0
- package/docs/projects-service.md +688 -0
- package/docs/security-keys-service.md +773 -0
- package/docs/starter-kits-service.md +249 -0
- package/docs/task-service.md +955 -0
- package/docs/testsprite_tests/TC001_Homepage_Load_and_Hero_Section_Display.py +70 -0
- package/docs/testsprite_tests/TC002_Sidebar_Navigation_ExpandCollapse_Functionality.py +73 -0
- package/docs/testsprite_tests/TC003_Full_Text_Local_Search_with_Keyboard_Shortcut.py +90 -0
- package/docs/testsprite_tests/TC004_Dark_Mode_Toggle_and_Persistence.py +73 -0
- package/docs/testsprite_tests/TC005_Mobile_Responsiveness_and_Touch_Navigation.py +113 -0
- package/docs/testsprite_tests/TC006_GitHub_Integration_Edit_this_page_Links.py +73 -0
- package/docs/testsprite_tests/TC007_Syntax_Highlighting_and_Code_Copy_Functionality.py +73 -0
- package/docs/testsprite_tests/TC008_Auto_Generated_Table_of_Contents_Accuracy.py +73 -0
- package/docs/testsprite_tests/TC009_SEO_and_Content_Discoverability_Verification.py +73 -0
- package/docs/testsprite_tests/TC010_Accessibility_Compliance_WCAG_AA.py +73 -0
- package/docs/testsprite_tests/TC011_Local_Development_Workflow_Build_and_Hot_Reload.py +74 -0
- package/docs/testsprite_tests/TC012_Performance_Metrics_Compliance.py +73 -0
- package/docs/testsprite_tests/standard_prd.json +122 -0
- package/docs/testsprite_tests/testsprite-mcp-test-report.html +2508 -0
- package/docs/testsprite_tests/testsprite-mcp-test-report.md +273 -0
- package/docs/testsprite_tests/testsprite_frontend_test_plan.json +390 -0
- package/docs/usage-service.md +616 -0
- package/index.ts +11 -3
- package/package.json +16 -2
- package/plans/CODESPACE_LOGS_STREAMING_GUIDE.md +320 -0
- package/plans/CODESPACE_TASK_LOGS_API_COMPLETE_GUIDE.md +821 -0
- package/services/base/base-service.ts +130 -0
- package/services/codespace/codespace-service.ts +347 -8
- package/services/codespace/codespace-types.ts +263 -14
- package/services/codespace/index.ts +16 -1
- package/services/index.ts +4 -0
- package/services/projects/README.md +107 -34
- package/services/projects/project-types.ts +69 -32
- package/services/repository-analysis/repository-types.ts +1 -0
- package/services/starter-kits/index.ts +2 -0
- package/services/starter-kits/starter-kits-service.ts +33 -0
- package/services/starter-kits/starter-kits-types.ts +38 -0
- package/services/tasks/task-service.ts +10 -0
- package/services/tasks/task-types.ts +29 -7
- package/services/usage/usage-service.ts +59 -10
- package/services/usage/usage-types.ts +239 -34
- package/services/users/index.ts +2 -0
- package/services/users/user-service.ts +15 -0
- package/services/users/user-types.ts +59 -0
|
@@ -238,6 +238,11 @@ export abstract class BaseService {
|
|
|
238
238
|
return response.data
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
protected async patch<T>(url: string, data?: any, config?: any): Promise<T> {
|
|
242
|
+
const response = await this.client.patch<T>(url, data, config)
|
|
243
|
+
return response.data
|
|
244
|
+
}
|
|
245
|
+
|
|
241
246
|
protected async delete<T>(url: string, config?: any): Promise<T> {
|
|
242
247
|
const response = await this.client.delete<T>(url, config)
|
|
243
248
|
return response.data
|
|
@@ -246,4 +251,129 @@ export abstract class BaseService {
|
|
|
246
251
|
protected buildUrl(endpoint: string): string {
|
|
247
252
|
return endpoint.startsWith('/') ? endpoint : `/${endpoint}`
|
|
248
253
|
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Create a streaming connection for Server-Sent Events (SSE)
|
|
257
|
+
*
|
|
258
|
+
* @param url - The endpoint URL for streaming
|
|
259
|
+
* @param config - Optional configuration for the stream request
|
|
260
|
+
* @returns Promise that resolves to EventSource for SSE streaming
|
|
261
|
+
*/
|
|
262
|
+
protected async createStream(url: string, config?: any): Promise<EventSource> {
|
|
263
|
+
// Build full URL
|
|
264
|
+
const fullUrl = `${this.client.defaults.baseURL}${this.buildUrl(url)}`
|
|
265
|
+
|
|
266
|
+
// For SSE, we need to use EventSource API directly
|
|
267
|
+
// However, we need to handle authentication differently since EventSource doesn't support custom headers
|
|
268
|
+
// We'll pass auth info as query parameters for SSE endpoints
|
|
269
|
+
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
try {
|
|
272
|
+
const eventSource = new EventSource(fullUrl)
|
|
273
|
+
resolve(eventSource)
|
|
274
|
+
} catch (error) {
|
|
275
|
+
reject(error)
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Alternative streaming method using fetch for environments where EventSource is not available
|
|
282
|
+
* or when custom headers are needed for authentication
|
|
283
|
+
*
|
|
284
|
+
* @param url - The endpoint URL for streaming
|
|
285
|
+
* @param onMessage - Callback function for handling stream messages
|
|
286
|
+
* @param onError - Callback function for handling stream errors
|
|
287
|
+
* @param onComplete - Callback function for when stream completes
|
|
288
|
+
* @param config - Optional configuration for the stream request
|
|
289
|
+
* @returns Promise that resolves to a cleanup function
|
|
290
|
+
*/
|
|
291
|
+
protected async createStreamWithFetch(
|
|
292
|
+
url: string,
|
|
293
|
+
onMessage: (data: any) => void,
|
|
294
|
+
onError?: (error: any) => void,
|
|
295
|
+
onComplete?: () => void,
|
|
296
|
+
config?: any
|
|
297
|
+
): Promise<() => void> {
|
|
298
|
+
const streamConfig = {
|
|
299
|
+
...config,
|
|
300
|
+
headers: {
|
|
301
|
+
...this.client.defaults.headers,
|
|
302
|
+
'Accept': 'text/event-stream',
|
|
303
|
+
'Cache-Control': 'no-cache',
|
|
304
|
+
...config?.headers,
|
|
305
|
+
},
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const fullUrl = `${this.client.defaults.baseURL}${this.buildUrl(url)}`
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const response = await fetch(fullUrl, streamConfig)
|
|
312
|
+
|
|
313
|
+
if (!response.ok) {
|
|
314
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const reader = response.body?.getReader()
|
|
318
|
+
if (!reader) {
|
|
319
|
+
throw new Error('Response body is not readable')
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const decoder = new TextDecoder()
|
|
323
|
+
let buffer = ''
|
|
324
|
+
|
|
325
|
+
const processStream = async () => {
|
|
326
|
+
try {
|
|
327
|
+
while (true) {
|
|
328
|
+
const { done, value } = await reader.read()
|
|
329
|
+
|
|
330
|
+
if (done) {
|
|
331
|
+
onComplete?.()
|
|
332
|
+
break
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
buffer += decoder.decode(value, { stream: true })
|
|
336
|
+
const lines = buffer.split('\n')
|
|
337
|
+
buffer = lines.pop() || '' // Keep incomplete line in buffer
|
|
338
|
+
|
|
339
|
+
for (const line of lines) {
|
|
340
|
+
if (line.startsWith('data: ')) {
|
|
341
|
+
const data = line.slice(6) // Remove 'data: ' prefix
|
|
342
|
+
if (data.trim()) {
|
|
343
|
+
try {
|
|
344
|
+
const parsedData = JSON.parse(data)
|
|
345
|
+
onMessage(parsedData)
|
|
346
|
+
} catch (parseError) {
|
|
347
|
+
console.warn('Failed to parse SSE data:', data, parseError)
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
} else if (line.startsWith('event: ')) {
|
|
351
|
+
const eventType = line.slice(7) // Remove 'event: ' prefix
|
|
352
|
+
// Handle different event types if needed
|
|
353
|
+
if (eventType === 'complete') {
|
|
354
|
+
onComplete?.()
|
|
355
|
+
return
|
|
356
|
+
} else if (eventType === 'error') {
|
|
357
|
+
onError?.(new Error('Stream error event received'))
|
|
358
|
+
return
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
} catch (error) {
|
|
364
|
+
onError?.(error)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
processStream()
|
|
369
|
+
|
|
370
|
+
// Return cleanup function
|
|
371
|
+
return () => {
|
|
372
|
+
reader.cancel().catch(console.warn)
|
|
373
|
+
}
|
|
374
|
+
} catch (error) {
|
|
375
|
+
onError?.(error)
|
|
376
|
+
return () => {} // Return empty cleanup function on error
|
|
377
|
+
}
|
|
378
|
+
}
|
|
249
379
|
}
|
|
@@ -22,6 +22,20 @@ import {
|
|
|
22
22
|
GetLLMModelProvidersResponse,
|
|
23
23
|
GetLLMModelProviderResponse,
|
|
24
24
|
GetModelsByProviderResponse,
|
|
25
|
+
// GET /codespace/tasks Types
|
|
26
|
+
GetCodespaceTasksRequest,
|
|
27
|
+
GetCodespaceTasksResponse,
|
|
28
|
+
// GET /tasks/by-codespace-id Types
|
|
29
|
+
GetTasksByCodespaceIdRequest,
|
|
30
|
+
GetTasksByCodespaceIdResponse,
|
|
31
|
+
// Final Report Popup State Types
|
|
32
|
+
UpdateFinalReportPopupStateRequest,
|
|
33
|
+
UpdateFinalReportPopupStateResponse,
|
|
34
|
+
// Codespace Task Logs Types
|
|
35
|
+
GetCodespaceTaskLogsRequest,
|
|
36
|
+
CodespaceTaskLogsResponse,
|
|
37
|
+
StreamCodespaceTaskLogsRequest,
|
|
38
|
+
CodespaceLogStreamEvent,
|
|
25
39
|
} from './codespace-types'
|
|
26
40
|
|
|
27
41
|
export class CodespaceService extends BaseService {
|
|
@@ -29,7 +43,9 @@ export class CodespaceService extends BaseService {
|
|
|
29
43
|
return this.post<GenerateTaskTitleResponse>('/codespace/generate-task-title', request)
|
|
30
44
|
}
|
|
31
45
|
|
|
32
|
-
async generateQuestionnaire(
|
|
46
|
+
async generateQuestionnaire(
|
|
47
|
+
request: CodespaceQuestionnaireRequest
|
|
48
|
+
): Promise<CodespaceQuestionnaireResponse> {
|
|
33
49
|
this.validateQuestionnaireRequest(request)
|
|
34
50
|
return this.post<CodespaceQuestionnaireResponse>('/codespace/generate-questionnaire', request)
|
|
35
51
|
}
|
|
@@ -98,6 +114,216 @@ export class CodespaceService extends BaseService {
|
|
|
98
114
|
return this.get<CodespaceTaskDetailedResponse>(`/codespace/task/${codespaceTaskId}/detailed`)
|
|
99
115
|
}
|
|
100
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Get tasks by codespace task ID with optional pagination and sorting
|
|
119
|
+
*
|
|
120
|
+
* GET /tasks/by-codespace-id/{codespace_task_id}
|
|
121
|
+
*
|
|
122
|
+
* @param params - Request parameters including codespace_task_id and optional pagination/sorting
|
|
123
|
+
* @returns Promise resolving to paginated list of tasks associated with the codespace task ID
|
|
124
|
+
*/
|
|
125
|
+
async getTasksByCodespaceId(
|
|
126
|
+
params: GetTasksByCodespaceIdRequest
|
|
127
|
+
): Promise<GetTasksByCodespaceIdResponse> {
|
|
128
|
+
this.validateGetTasksByCodespaceIdRequest(params)
|
|
129
|
+
|
|
130
|
+
const queryParams = new URLSearchParams()
|
|
131
|
+
|
|
132
|
+
// Add pagination parameters
|
|
133
|
+
if (params.limit !== undefined) {
|
|
134
|
+
queryParams.append('limit', params.limit.toString())
|
|
135
|
+
}
|
|
136
|
+
if (params.offset !== undefined) {
|
|
137
|
+
queryParams.append('offset', params.offset.toString())
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Add sorting parameters
|
|
141
|
+
if (params.sort_by) {
|
|
142
|
+
queryParams.append('sort_by', params.sort_by)
|
|
143
|
+
}
|
|
144
|
+
if (params.sort_order) {
|
|
145
|
+
queryParams.append('sort_order', params.sort_order)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const url = `/tasks/by-codespace-id/${params.codespace_task_id}${
|
|
149
|
+
queryParams.toString() ? `?${queryParams.toString()}` : ''
|
|
150
|
+
}`
|
|
151
|
+
|
|
152
|
+
return this.get<GetTasksByCodespaceIdResponse>(url)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get codespace tasks with optional filtering and pagination
|
|
157
|
+
*
|
|
158
|
+
* GET /codespace/tasks
|
|
159
|
+
*
|
|
160
|
+
* @param params - Optional query parameters for filtering, sorting, and pagination
|
|
161
|
+
* @returns Promise resolving to paginated list of codespace tasks with model and attachment info
|
|
162
|
+
*/
|
|
163
|
+
async getCodespaceTasks(params?: GetCodespaceTasksRequest): Promise<GetCodespaceTasksResponse> {
|
|
164
|
+
const queryParams = new URLSearchParams()
|
|
165
|
+
|
|
166
|
+
if (params?.task_status) queryParams.append('task_status', params.task_status)
|
|
167
|
+
if (params?.project_id) queryParams.append('project_id', params.project_id)
|
|
168
|
+
if (params?.limit !== undefined) {
|
|
169
|
+
// Validate limit is between 1 and 100
|
|
170
|
+
if (params.limit < 1 || params.limit > 100) {
|
|
171
|
+
throw new Error('limit must be between 1 and 100')
|
|
172
|
+
}
|
|
173
|
+
queryParams.append('limit', params.limit.toString())
|
|
174
|
+
}
|
|
175
|
+
if (params?.offset !== undefined) {
|
|
176
|
+
// Validate offset is non-negative
|
|
177
|
+
if (params.offset < 0) {
|
|
178
|
+
throw new Error('offset must be 0 or greater')
|
|
179
|
+
}
|
|
180
|
+
queryParams.append('offset', params.offset.toString())
|
|
181
|
+
}
|
|
182
|
+
if (params?.sort_by) queryParams.append('sort_by', params.sort_by)
|
|
183
|
+
if (params?.sort_order) queryParams.append('sort_order', params.sort_order)
|
|
184
|
+
|
|
185
|
+
const url = `/codespace/tasks${queryParams.toString() ? `?${queryParams.toString()}` : ''}`
|
|
186
|
+
return this.get<GetCodespaceTasksResponse>(url)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ============================================================================
|
|
190
|
+
// Task Status Update Methods
|
|
191
|
+
// ============================================================================
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Update the final report popup state for a codespace task
|
|
195
|
+
*
|
|
196
|
+
* PATCH /task/{codespace_task_id}/final-report-popup-state
|
|
197
|
+
*
|
|
198
|
+
* @param codespaceTaskId - The ID of the codespace task
|
|
199
|
+
* @param request - The request body containing the new popup state
|
|
200
|
+
* @returns Promise resolving to the updated popup state response
|
|
201
|
+
*/
|
|
202
|
+
async updateFinalReportPopupState(
|
|
203
|
+
codespaceTaskId: string,
|
|
204
|
+
request: UpdateFinalReportPopupStateRequest
|
|
205
|
+
): Promise<UpdateFinalReportPopupStateResponse> {
|
|
206
|
+
if (!codespaceTaskId) {
|
|
207
|
+
throw new Error('codespace_task_id is required')
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Validate the popup state value
|
|
211
|
+
if (!['not_ready', 'open', 'closed'].includes(request.final_report_popup_state)) {
|
|
212
|
+
throw new Error('final_report_popup_state must be "not_ready", "open", or "closed"')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return this.patch<UpdateFinalReportPopupStateResponse>(
|
|
216
|
+
`/task/${codespaceTaskId}/final-report-popup-state`,
|
|
217
|
+
request
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// Codespace Task Logs Methods
|
|
223
|
+
// ============================================================================
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get paginated logs for a codespace task with optional filtering and sorting
|
|
227
|
+
*
|
|
228
|
+
* GET /codespace/task/{codespace_task_id}/logs
|
|
229
|
+
*
|
|
230
|
+
* @param request - Request parameters including codespace_task_id and optional filters
|
|
231
|
+
* @returns Promise resolving to paginated logs response
|
|
232
|
+
*/
|
|
233
|
+
async getCodespaceTaskLogs(
|
|
234
|
+
request: GetCodespaceTaskLogsRequest
|
|
235
|
+
): Promise<CodespaceTaskLogsResponse> {
|
|
236
|
+
this.validateGetLogsRequest(request)
|
|
237
|
+
|
|
238
|
+
const queryParams = new URLSearchParams()
|
|
239
|
+
|
|
240
|
+
// Add pagination parameters
|
|
241
|
+
if (request.limit !== undefined) {
|
|
242
|
+
queryParams.append('limit', request.limit.toString())
|
|
243
|
+
}
|
|
244
|
+
if (request.offset !== undefined) {
|
|
245
|
+
queryParams.append('offset', request.offset.toString())
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Add filter parameters
|
|
249
|
+
if (request.log_type) {
|
|
250
|
+
queryParams.append('log_type', request.log_type)
|
|
251
|
+
}
|
|
252
|
+
if (request.step_name) {
|
|
253
|
+
queryParams.append('step_name', request.step_name)
|
|
254
|
+
}
|
|
255
|
+
if (request.search) {
|
|
256
|
+
queryParams.append('search', request.search)
|
|
257
|
+
}
|
|
258
|
+
if (request.since) {
|
|
259
|
+
queryParams.append('since', request.since)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Add sorting parameters
|
|
263
|
+
if (request.sort_by) {
|
|
264
|
+
queryParams.append('sort_by', request.sort_by)
|
|
265
|
+
}
|
|
266
|
+
if (request.sort_order) {
|
|
267
|
+
queryParams.append('sort_order', request.sort_order)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const url = `/codespace/task/${request.codespace_task_id}/logs${
|
|
271
|
+
queryParams.toString() ? `?${queryParams.toString()}` : ''
|
|
272
|
+
}`
|
|
273
|
+
|
|
274
|
+
return this.get<CodespaceTaskLogsResponse>(url)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Stream real-time logs from a codespace task using Server-Sent Events (SSE)
|
|
279
|
+
*
|
|
280
|
+
* GET /codespace/task/{codespace_task_id}/logs/stream
|
|
281
|
+
*
|
|
282
|
+
* @param request - Request parameters including codespace_task_id and optional streaming parameters
|
|
283
|
+
* @param onLog - Callback function for handling log events
|
|
284
|
+
* @param onHeartbeat - Callback function for handling heartbeat events
|
|
285
|
+
* @param onComplete - Callback function for handling completion events
|
|
286
|
+
* @param onError - Callback function for handling error events
|
|
287
|
+
* @param onTimeout - Callback function for handling timeout events
|
|
288
|
+
* @returns Promise resolving to a cleanup function to stop streaming
|
|
289
|
+
*/
|
|
290
|
+
async streamCodespaceTaskLogs(
|
|
291
|
+
request: StreamCodespaceTaskLogsRequest,
|
|
292
|
+
onLog: (log: CodespaceLogStreamEvent) => void,
|
|
293
|
+
onHeartbeat?: (data: any) => void,
|
|
294
|
+
onComplete?: (data: any) => void,
|
|
295
|
+
onError?: (error: any) => void,
|
|
296
|
+
onTimeout?: (data: any) => void
|
|
297
|
+
): Promise<() => void> {
|
|
298
|
+
this.validateStreamLogsRequest(request)
|
|
299
|
+
|
|
300
|
+
const queryParams = new URLSearchParams()
|
|
301
|
+
|
|
302
|
+
// Add streaming parameters
|
|
303
|
+
if (request.since) {
|
|
304
|
+
queryParams.append('since', request.since)
|
|
305
|
+
}
|
|
306
|
+
if (request.timeout !== undefined) {
|
|
307
|
+
queryParams.append('timeout', request.timeout.toString())
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const url = `/codespace/task/${request.codespace_task_id}/logs/stream${
|
|
311
|
+
queryParams.toString() ? `?${queryParams.toString()}` : ''
|
|
312
|
+
}`
|
|
313
|
+
|
|
314
|
+
// Use the fetch-based streaming method since it supports custom headers for authentication
|
|
315
|
+
return this.createStreamWithFetch(
|
|
316
|
+
url,
|
|
317
|
+
data => {
|
|
318
|
+
// Handle different types of events based on the context
|
|
319
|
+
// The server will send different event types through the same data channel
|
|
320
|
+
onLog(data as CodespaceLogStreamEvent)
|
|
321
|
+
},
|
|
322
|
+
onError,
|
|
323
|
+
() => onComplete?.({}) // Call onComplete with empty data when stream completes
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
|
|
101
327
|
// ============================================================================
|
|
102
328
|
// Codespace Models Methods
|
|
103
329
|
// ============================================================================
|
|
@@ -112,9 +338,7 @@ export class CodespaceService extends BaseService {
|
|
|
112
338
|
*/
|
|
113
339
|
async getCodespaceModels(query?: GetCodespaceModelsQuery): Promise<GetCodespaceModelsResponse> {
|
|
114
340
|
const params = this.buildQueryParams(query)
|
|
115
|
-
const url = params
|
|
116
|
-
? `/api/codespace-models/models?${params}`
|
|
117
|
-
: '/api/codespace-models/models'
|
|
341
|
+
const url = params ? `/api/codespace-models/models?${params}` : '/api/codespace-models/models'
|
|
118
342
|
|
|
119
343
|
return this.get<GetCodespaceModelsResponse>(url)
|
|
120
344
|
}
|
|
@@ -172,7 +396,9 @@ export class CodespaceService extends BaseService {
|
|
|
172
396
|
if (!providerId) {
|
|
173
397
|
throw new Error('provider_id is required')
|
|
174
398
|
}
|
|
175
|
-
return this.get<GetModelsByProviderResponse>(
|
|
399
|
+
return this.get<GetModelsByProviderResponse>(
|
|
400
|
+
`/api/codespace-models/providers/${providerId}/models`
|
|
401
|
+
)
|
|
176
402
|
}
|
|
177
403
|
|
|
178
404
|
/**
|
|
@@ -206,9 +432,9 @@ export class CodespaceService extends BaseService {
|
|
|
206
432
|
|
|
207
433
|
if (
|
|
208
434
|
request.execution_mode &&
|
|
209
|
-
!['implementation', 'docs-only'].includes(request.execution_mode)
|
|
435
|
+
!['implementation', 'docs-only', 'direct'].includes(request.execution_mode)
|
|
210
436
|
) {
|
|
211
|
-
throw new Error('execution_mode must be either "implementation"
|
|
437
|
+
throw new Error('execution_mode must be either "implementation", "docs-only", or "direct"')
|
|
212
438
|
}
|
|
213
439
|
|
|
214
440
|
// Validate model_api_keys if provided
|
|
@@ -237,7 +463,7 @@ export class CodespaceService extends BaseService {
|
|
|
237
463
|
if (typeof request.ai_questionnaire !== 'object' || request.ai_questionnaire === null) {
|
|
238
464
|
throw new Error('ai_questionnaire must be an object')
|
|
239
465
|
}
|
|
240
|
-
|
|
466
|
+
|
|
241
467
|
// Check if it's a plain object with string keys and string values
|
|
242
468
|
for (const [key, value] of Object.entries(request.ai_questionnaire)) {
|
|
243
469
|
if (typeof key !== 'string') {
|
|
@@ -283,4 +509,117 @@ export class CodespaceService extends BaseService {
|
|
|
283
509
|
}
|
|
284
510
|
}
|
|
285
511
|
}
|
|
512
|
+
|
|
513
|
+
private validateGetLogsRequest(request: GetCodespaceTaskLogsRequest): void {
|
|
514
|
+
if (!request.codespace_task_id) {
|
|
515
|
+
throw new Error('codespace_task_id is required')
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Validate limit
|
|
519
|
+
if (request.limit !== undefined) {
|
|
520
|
+
if (!Number.isInteger(request.limit)) {
|
|
521
|
+
throw new Error('limit must be an integer')
|
|
522
|
+
}
|
|
523
|
+
if (request.limit < 1 || request.limit > 500) {
|
|
524
|
+
throw new Error('limit must be between 1 and 500')
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Validate offset
|
|
529
|
+
if (request.offset !== undefined) {
|
|
530
|
+
if (!Number.isInteger(request.offset)) {
|
|
531
|
+
throw new Error('offset must be an integer')
|
|
532
|
+
}
|
|
533
|
+
if (request.offset < 0) {
|
|
534
|
+
throw new Error('offset must be 0 or greater')
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Validate sort_by
|
|
539
|
+
if (request.sort_by && !['created_at', 'step_name', 'log_type'].includes(request.sort_by)) {
|
|
540
|
+
throw new Error('sort_by must be one of: created_at, step_name, log_type')
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Validate sort_order
|
|
544
|
+
if (request.sort_order && !['asc', 'desc'].includes(request.sort_order)) {
|
|
545
|
+
throw new Error('sort_order must be either "asc" or "desc"')
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Validate log_type
|
|
549
|
+
const validLogTypes = ['thinking', 'coding', 'info', 'error', 'success']
|
|
550
|
+
if (request.log_type && !validLogTypes.includes(request.log_type)) {
|
|
551
|
+
throw new Error(`log_type must be one of: ${validLogTypes.join(', ')}`)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Validate since timestamp format (basic ISO format check)
|
|
555
|
+
if (request.since) {
|
|
556
|
+
const sinceDate = new Date(request.since)
|
|
557
|
+
if (isNaN(sinceDate.getTime())) {
|
|
558
|
+
throw new Error('since must be a valid ISO timestamp')
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
private validateStreamLogsRequest(request: StreamCodespaceTaskLogsRequest): void {
|
|
564
|
+
if (!request.codespace_task_id) {
|
|
565
|
+
throw new Error('codespace_task_id is required')
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Validate timeout
|
|
569
|
+
if (request.timeout !== undefined) {
|
|
570
|
+
if (!Number.isInteger(request.timeout)) {
|
|
571
|
+
throw new Error('timeout must be an integer')
|
|
572
|
+
}
|
|
573
|
+
if (request.timeout < 30 || request.timeout > 1800) {
|
|
574
|
+
throw new Error('timeout must be between 30 and 1800 seconds')
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Validate since timestamp format (basic ISO format check)
|
|
579
|
+
if (request.since) {
|
|
580
|
+
const sinceDate = new Date(request.since)
|
|
581
|
+
if (isNaN(sinceDate.getTime())) {
|
|
582
|
+
throw new Error('since must be a valid ISO timestamp')
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private validateGetTasksByCodespaceIdRequest(request: GetTasksByCodespaceIdRequest): void {
|
|
588
|
+
if (!request.codespace_task_id) {
|
|
589
|
+
throw new Error('codespace_task_id is required')
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Validate limit
|
|
593
|
+
if (request.limit !== undefined) {
|
|
594
|
+
if (!Number.isInteger(request.limit)) {
|
|
595
|
+
throw new Error('limit must be an integer')
|
|
596
|
+
}
|
|
597
|
+
if (request.limit < 1 || request.limit > 100) {
|
|
598
|
+
throw new Error('limit must be between 1 and 100')
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Validate offset
|
|
603
|
+
if (request.offset !== undefined) {
|
|
604
|
+
if (!Number.isInteger(request.offset)) {
|
|
605
|
+
throw new Error('offset must be an integer')
|
|
606
|
+
}
|
|
607
|
+
if (request.offset < 0) {
|
|
608
|
+
throw new Error('offset must be 0 or greater')
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Validate sort_by
|
|
613
|
+
if (
|
|
614
|
+
request.sort_by &&
|
|
615
|
+
!['created_at', 'updated_at', 'status', 'title'].includes(request.sort_by)
|
|
616
|
+
) {
|
|
617
|
+
throw new Error('sort_by must be one of: created_at, updated_at, status, title')
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Validate sort_order
|
|
621
|
+
if (request.sort_order && !['asc', 'desc'].includes(request.sort_order)) {
|
|
622
|
+
throw new Error('sort_order must be either "asc" or "desc"')
|
|
623
|
+
}
|
|
624
|
+
}
|
|
286
625
|
}
|