@codeguide/core 0.0.23 → 0.0.25
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 +176 -292
- package/__tests__/services/codespace/codespace-models.test.ts +458 -0
- package/__tests__/services/security-keys.test.ts +587 -0
- package/codeguide.ts +3 -1
- package/dist/codeguide.d.ts +2 -1
- package/dist/codeguide.js +1 -0
- package/dist/services/codespace/codespace-service.d.ts +54 -1
- package/dist/services/codespace/codespace-service.js +136 -0
- package/dist/services/codespace/codespace-types.d.ts +60 -0
- package/dist/services/codespace/index.d.ts +1 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +4 -1
- package/dist/services/security-keys/index.d.ts +3 -0
- package/dist/services/security-keys/index.js +21 -0
- package/dist/services/security-keys/security-keys-service.d.ts +111 -0
- package/dist/services/security-keys/security-keys-service.js +190 -0
- package/dist/services/security-keys/security-keys-types.d.ts +105 -0
- package/dist/services/security-keys/security-keys-types.js +8 -0
- package/package.json +1 -1
- package/services/codespace/codespace-service.ts +162 -0
- package/services/codespace/codespace-types.ts +82 -1
- package/services/codespace/index.ts +14 -1
- package/services/index.ts +2 -0
- package/services/security-keys/index.ts +25 -0
- package/services/security-keys/security-keys-service.ts +229 -0
- package/services/security-keys/security-keys-types.ts +187 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Keys Service Types
|
|
3
|
+
*
|
|
4
|
+
* This file contains type definitions for the Security Keys service,
|
|
5
|
+
* which manages provider API keys and GitHub tokens.
|
|
6
|
+
*/
|
|
7
|
+
export interface SecurityKeyData {
|
|
8
|
+
id: string;
|
|
9
|
+
created_at: string;
|
|
10
|
+
user_id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
displayed_name: string;
|
|
13
|
+
value_masked: string;
|
|
14
|
+
value?: string;
|
|
15
|
+
object_value: Record<string, any>;
|
|
16
|
+
encryption: string;
|
|
17
|
+
key_version: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ProviderAPIKeyData extends SecurityKeyData {
|
|
20
|
+
provider_id: string;
|
|
21
|
+
provider_name: string;
|
|
22
|
+
provider_key: string;
|
|
23
|
+
provider_logo_src?: string;
|
|
24
|
+
object_value: {
|
|
25
|
+
provider_id: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface GitHubTokenData extends SecurityKeyData {
|
|
29
|
+
object_value: {
|
|
30
|
+
token_type: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface SuccessResponse<T> {
|
|
34
|
+
status: 'success';
|
|
35
|
+
data: T;
|
|
36
|
+
}
|
|
37
|
+
export interface ErrorResponse {
|
|
38
|
+
detail: string;
|
|
39
|
+
}
|
|
40
|
+
export interface CreateProviderAPIKeyRequest {
|
|
41
|
+
provider_key: string;
|
|
42
|
+
api_key: string;
|
|
43
|
+
}
|
|
44
|
+
export interface CreateProviderAPIKeyResponse extends SuccessResponse<ProviderAPIKeyData> {
|
|
45
|
+
}
|
|
46
|
+
export interface GetProviderAPIKeyResponse extends SuccessResponse<ProviderAPIKeyData> {
|
|
47
|
+
}
|
|
48
|
+
export interface ListProviderAPIKeysResponse extends SuccessResponse<ProviderAPIKeyData[]> {
|
|
49
|
+
}
|
|
50
|
+
export interface CreateGitHubTokenRequest {
|
|
51
|
+
github_token: string;
|
|
52
|
+
}
|
|
53
|
+
export interface CreateGitHubTokenResponse extends SuccessResponse<GitHubTokenData> {
|
|
54
|
+
}
|
|
55
|
+
export interface GetGitHubTokenResponse extends SuccessResponse<GitHubTokenData> {
|
|
56
|
+
}
|
|
57
|
+
export interface ProviderNotFoundError extends ErrorResponse {
|
|
58
|
+
detail: string;
|
|
59
|
+
}
|
|
60
|
+
export interface DuplicateKeyError extends ErrorResponse {
|
|
61
|
+
detail: string;
|
|
62
|
+
}
|
|
63
|
+
export interface KeyNotFoundError extends ErrorResponse {
|
|
64
|
+
detail: string;
|
|
65
|
+
}
|
|
66
|
+
export interface InvalidGitHubTokenFormatError extends ErrorResponse {
|
|
67
|
+
detail: string;
|
|
68
|
+
}
|
|
69
|
+
export interface GitHubTokenValidationError extends ErrorResponse {
|
|
70
|
+
detail: string;
|
|
71
|
+
}
|
|
72
|
+
export interface DuplicateGitHubTokenError extends ErrorResponse {
|
|
73
|
+
detail: string;
|
|
74
|
+
}
|
|
75
|
+
export interface GitHubTokenNotFoundError extends ErrorResponse {
|
|
76
|
+
detail: string;
|
|
77
|
+
}
|
|
78
|
+
export interface AuthenticationError extends ErrorResponse {
|
|
79
|
+
detail: string;
|
|
80
|
+
}
|
|
81
|
+
export interface InternalServerError extends ErrorResponse {
|
|
82
|
+
detail: string;
|
|
83
|
+
}
|
|
84
|
+
export type ProviderAPIKeyResponse = CreateProviderAPIKeyResponse | GetProviderAPIKeyResponse | ListProviderAPIKeysResponse | RevokeProviderAPIKeyResponse | SecurityKeysError;
|
|
85
|
+
export interface RevokeProviderAPIKeyResponse {
|
|
86
|
+
status: string;
|
|
87
|
+
message: string;
|
|
88
|
+
revoked_provider_id: string;
|
|
89
|
+
}
|
|
90
|
+
export interface RevokeGitHubTokenResponse {
|
|
91
|
+
status: string;
|
|
92
|
+
message: string;
|
|
93
|
+
revoked_at: string;
|
|
94
|
+
}
|
|
95
|
+
export interface ProviderAPIKeyNotFoundError extends ErrorResponse {
|
|
96
|
+
detail: string;
|
|
97
|
+
}
|
|
98
|
+
export interface ProviderAPIKeyDeletionError extends ErrorResponse {
|
|
99
|
+
detail: string;
|
|
100
|
+
}
|
|
101
|
+
export interface GitHubTokenDeletionError extends ErrorResponse {
|
|
102
|
+
detail: string;
|
|
103
|
+
}
|
|
104
|
+
export type SecurityKeysError = ProviderNotFoundError | DuplicateKeyError | KeyNotFoundError | InvalidGitHubTokenFormatError | GitHubTokenValidationError | DuplicateGitHubTokenError | GitHubTokenNotFoundError | ProviderAPIKeyNotFoundError | ProviderAPIKeyDeletionError | GitHubTokenDeletionError | AuthenticationError | InternalServerError | ErrorResponse;
|
|
105
|
+
export type GitHubTokenResponse = CreateGitHubTokenResponse | GetGitHubTokenResponse | RevokeGitHubTokenResponse | SecurityKeysError;
|
package/package.json
CHANGED
|
@@ -13,6 +13,15 @@ import {
|
|
|
13
13
|
GetCodespaceTasksByProjectRequest,
|
|
14
14
|
GetCodespaceTasksByProjectResponse,
|
|
15
15
|
CodespaceTaskDetailedResponse,
|
|
16
|
+
CodespaceQuestionnaireRequest,
|
|
17
|
+
CodespaceQuestionnaireResponse,
|
|
18
|
+
// Codespace Models Types
|
|
19
|
+
GetCodespaceModelsQuery,
|
|
20
|
+
GetCodespaceModelsResponse,
|
|
21
|
+
GetCodespaceModelResponse,
|
|
22
|
+
GetLLMModelProvidersResponse,
|
|
23
|
+
GetLLMModelProviderResponse,
|
|
24
|
+
GetModelsByProviderResponse,
|
|
16
25
|
} from './codespace-types'
|
|
17
26
|
|
|
18
27
|
export class CodespaceService extends BaseService {
|
|
@@ -20,6 +29,11 @@ export class CodespaceService extends BaseService {
|
|
|
20
29
|
return this.post<GenerateTaskTitleResponse>('/codespace/generate-task-title', request)
|
|
21
30
|
}
|
|
22
31
|
|
|
32
|
+
async generateQuestionnaire(request: CodespaceQuestionnaireRequest): Promise<CodespaceQuestionnaireResponse> {
|
|
33
|
+
this.validateQuestionnaireRequest(request)
|
|
34
|
+
return this.post<CodespaceQuestionnaireResponse>('/codespace/generate-questionnaire', request)
|
|
35
|
+
}
|
|
36
|
+
|
|
23
37
|
async createCodespaceTask(
|
|
24
38
|
request: CreateCodespaceTaskRequest
|
|
25
39
|
): Promise<CreateCodespaceTaskResponse> {
|
|
@@ -84,6 +98,103 @@ export class CodespaceService extends BaseService {
|
|
|
84
98
|
return this.get<CodespaceTaskDetailedResponse>(`/codespace/task/${codespaceTaskId}/detailed`)
|
|
85
99
|
}
|
|
86
100
|
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// Codespace Models Methods
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get all codespace models with optional filtering
|
|
107
|
+
*
|
|
108
|
+
* GET /api/codespace-models/models
|
|
109
|
+
*
|
|
110
|
+
* @param query - Optional query parameters for filtering
|
|
111
|
+
* @returns Promise resolving to array of codespace models with provider info
|
|
112
|
+
*/
|
|
113
|
+
async getCodespaceModels(query?: GetCodespaceModelsQuery): Promise<GetCodespaceModelsResponse> {
|
|
114
|
+
const params = this.buildQueryParams(query)
|
|
115
|
+
const url = params
|
|
116
|
+
? `/api/codespace-models/models?${params}`
|
|
117
|
+
: '/api/codespace-models/models'
|
|
118
|
+
|
|
119
|
+
return this.get<GetCodespaceModelsResponse>(url)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get a specific codespace model by ID
|
|
124
|
+
*
|
|
125
|
+
* GET /api/codespace-models/models/{model_id}
|
|
126
|
+
*
|
|
127
|
+
* @param modelId - The UUID of the model
|
|
128
|
+
* @returns Promise resolving to the codespace model with provider info
|
|
129
|
+
*/
|
|
130
|
+
async getCodespaceModel(modelId: string): Promise<GetCodespaceModelResponse> {
|
|
131
|
+
if (!modelId) {
|
|
132
|
+
throw new Error('model_id is required')
|
|
133
|
+
}
|
|
134
|
+
return this.get<GetCodespaceModelResponse>(`/api/codespace-models/models/${modelId}`)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get all LLM model providers
|
|
139
|
+
*
|
|
140
|
+
* GET /api/codespace-models/providers
|
|
141
|
+
*
|
|
142
|
+
* @returns Promise resolving to array of LLM model providers
|
|
143
|
+
*/
|
|
144
|
+
async getLLMModelProviders(): Promise<GetLLMModelProvidersResponse> {
|
|
145
|
+
return this.get<GetLLMModelProvidersResponse>('/api/codespace-models/providers')
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get a specific LLM model provider by ID
|
|
150
|
+
*
|
|
151
|
+
* GET /api/codespace-models/providers/{provider_id}
|
|
152
|
+
*
|
|
153
|
+
* @param providerId - The UUID of the provider
|
|
154
|
+
* @returns Promise resolving to the LLM model provider
|
|
155
|
+
*/
|
|
156
|
+
async getLLMModelProvider(providerId: string): Promise<GetLLMModelProviderResponse> {
|
|
157
|
+
if (!providerId) {
|
|
158
|
+
throw new Error('provider_id is required')
|
|
159
|
+
}
|
|
160
|
+
return this.get<GetLLMModelProviderResponse>(`/api/codespace-models/providers/${providerId}`)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get models by provider
|
|
165
|
+
*
|
|
166
|
+
* GET /api/codespace-models/providers/{provider_id}/models
|
|
167
|
+
*
|
|
168
|
+
* @param providerId - The UUID of the provider
|
|
169
|
+
* @returns Promise resolving to array of models from the specified provider
|
|
170
|
+
*/
|
|
171
|
+
async getModelsByProvider(providerId: string): Promise<GetModelsByProviderResponse> {
|
|
172
|
+
if (!providerId) {
|
|
173
|
+
throw new Error('provider_id is required')
|
|
174
|
+
}
|
|
175
|
+
return this.get<GetModelsByProviderResponse>(`/api/codespace-models/providers/${providerId}/models`)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Build URL query parameters from an object
|
|
180
|
+
*
|
|
181
|
+
* @param params - The parameters object
|
|
182
|
+
* @returns URL query string
|
|
183
|
+
*/
|
|
184
|
+
private buildQueryParams(params?: Record<string, any>): string {
|
|
185
|
+
if (!params) return ''
|
|
186
|
+
|
|
187
|
+
const searchParams = new URLSearchParams()
|
|
188
|
+
|
|
189
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
190
|
+
if (value !== undefined && value !== null) {
|
|
191
|
+
searchParams.append(key, value.toString())
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
return searchParams.toString()
|
|
196
|
+
}
|
|
197
|
+
|
|
87
198
|
private validateCodespaceTaskRequest(request: CreateCodespaceTaskRequestV2): void {
|
|
88
199
|
if (!request.project_id) {
|
|
89
200
|
throw new Error('project_id is required')
|
|
@@ -120,5 +231,56 @@ export class CodespaceService extends BaseService {
|
|
|
120
231
|
if (request.base_branch === undefined) {
|
|
121
232
|
request.base_branch = 'main'
|
|
122
233
|
}
|
|
234
|
+
|
|
235
|
+
// Validate ai_questionnaire if provided
|
|
236
|
+
if (request.ai_questionnaire) {
|
|
237
|
+
if (typeof request.ai_questionnaire !== 'object' || request.ai_questionnaire === null) {
|
|
238
|
+
throw new Error('ai_questionnaire must be an object')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Check if it's a plain object with string keys and string values
|
|
242
|
+
for (const [key, value] of Object.entries(request.ai_questionnaire)) {
|
|
243
|
+
if (typeof key !== 'string') {
|
|
244
|
+
throw new Error('All ai_questionnaire keys must be strings')
|
|
245
|
+
}
|
|
246
|
+
if (typeof value !== 'string') {
|
|
247
|
+
throw new Error('All ai_questionnaire values must be strings')
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private validateQuestionnaireRequest(request: CodespaceQuestionnaireRequest): void {
|
|
254
|
+
if (!request.task_description) {
|
|
255
|
+
throw new Error('task_description is required')
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Validate attachments if provided
|
|
259
|
+
if (request.attachments) {
|
|
260
|
+
if (!Array.isArray(request.attachments)) {
|
|
261
|
+
throw new Error('attachments must be an array')
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for (const attachment of request.attachments) {
|
|
265
|
+
// Check required fields
|
|
266
|
+
if (!attachment.filename || typeof attachment.filename !== 'string') {
|
|
267
|
+
throw new Error('Each attachment must have a valid filename string')
|
|
268
|
+
}
|
|
269
|
+
if (!attachment.file_data || typeof attachment.file_data !== 'string') {
|
|
270
|
+
throw new Error('Each attachment must have valid file_data string')
|
|
271
|
+
}
|
|
272
|
+
if (!attachment.mime_type || typeof attachment.mime_type !== 'string') {
|
|
273
|
+
throw new Error('Each attachment must have a valid mime_type string')
|
|
274
|
+
}
|
|
275
|
+
if (typeof attachment.file_size !== 'number' || attachment.file_size < 0) {
|
|
276
|
+
throw new Error('Each attachment must have a valid file_size number (>= 0)')
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Validate optional description field
|
|
280
|
+
if (attachment.description && typeof attachment.description !== 'string') {
|
|
281
|
+
throw new Error('Attachment description must be a string if provided')
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
123
285
|
}
|
|
124
286
|
}
|
|
@@ -50,6 +50,7 @@ export interface CreateCodespaceTaskRequestV2 {
|
|
|
50
50
|
starter_kit_repo?: string
|
|
51
51
|
use_enhanced_summary?: boolean
|
|
52
52
|
attachments?: Attachment[]
|
|
53
|
+
ai_questionnaire?: Record<string, string>
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export interface CreateCodespaceTaskResponseV2 {
|
|
@@ -164,4 +165,84 @@ export interface CodespaceTaskDetailedResponse {
|
|
|
164
165
|
usage_summary: any // Usage statistics
|
|
165
166
|
}
|
|
166
167
|
message: string
|
|
167
|
-
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface CodespaceQuestionnaireRequest {
|
|
171
|
+
task_description: string
|
|
172
|
+
project_context?: string
|
|
173
|
+
repository_info?: {
|
|
174
|
+
name?: string
|
|
175
|
+
description?: string
|
|
176
|
+
}
|
|
177
|
+
attachments?: Attachment[]
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface CodespaceQuestionnaireResponse {
|
|
181
|
+
success: boolean
|
|
182
|
+
questions: string[]
|
|
183
|
+
message: string
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Codespace Models Types
|
|
188
|
+
// ============================================================================
|
|
189
|
+
|
|
190
|
+
export interface LLMModelProviderInDB {
|
|
191
|
+
id: string
|
|
192
|
+
created_at: string
|
|
193
|
+
name?: string
|
|
194
|
+
key?: string
|
|
195
|
+
logo_src?: string
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface CodespaceModelInDB {
|
|
199
|
+
id: string
|
|
200
|
+
created_at: string
|
|
201
|
+
key?: string
|
|
202
|
+
name?: string
|
|
203
|
+
provider_id?: string
|
|
204
|
+
base_url?: string
|
|
205
|
+
completion_base_url?: string
|
|
206
|
+
execution_mode?: 'opencode' | 'claude-code' | 'docs-only' | 'implementation'
|
|
207
|
+
logo_src?: string
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface CodespaceModelWithProvider extends CodespaceModelInDB {
|
|
211
|
+
provider?: LLMModelProviderInDB
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface GetCodespaceModelsQuery {
|
|
215
|
+
provider_id?: string
|
|
216
|
+
execution_mode?: string
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface GetCodespaceModelsResponse extends Array<CodespaceModelWithProvider> {}
|
|
220
|
+
|
|
221
|
+
export interface GetCodespaceModelResponse extends CodespaceModelWithProvider {}
|
|
222
|
+
|
|
223
|
+
export interface GetLLMModelProvidersResponse extends Array<LLMModelProviderInDB> {}
|
|
224
|
+
|
|
225
|
+
export interface GetLLMModelProviderResponse extends LLMModelProviderInDB {}
|
|
226
|
+
|
|
227
|
+
export interface GetModelsByProviderResponse extends Array<CodespaceModelInDB> {}
|
|
228
|
+
|
|
229
|
+
// ============================================================================
|
|
230
|
+
// Codespace Models Error Types
|
|
231
|
+
// ============================================================================
|
|
232
|
+
|
|
233
|
+
export interface CodespaceModelNotFoundError {
|
|
234
|
+
detail: string // "Codespace model with ID {model_id} not found"
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface AuthenticationRequiredError {
|
|
238
|
+
detail: string // "Authentication required"
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface CodespaceModelsFetchError {
|
|
242
|
+
detail: string // "Failed to fetch codespace models"
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export type CodespaceModelsError =
|
|
246
|
+
| CodespaceModelNotFoundError
|
|
247
|
+
| AuthenticationRequiredError
|
|
248
|
+
| CodespaceModelsFetchError
|
|
@@ -12,5 +12,18 @@ export type {
|
|
|
12
12
|
GetCodespaceTaskResponse,
|
|
13
13
|
CodespaceTaskData,
|
|
14
14
|
TechnicalDocument,
|
|
15
|
-
GetProjectTasksByCodespaceResponse
|
|
15
|
+
GetProjectTasksByCodespaceResponse,
|
|
16
|
+
CodespaceQuestionnaireRequest,
|
|
17
|
+
CodespaceQuestionnaireResponse,
|
|
18
|
+
// Codespace Models Types
|
|
19
|
+
LLMModelProviderInDB,
|
|
20
|
+
CodespaceModelInDB,
|
|
21
|
+
CodespaceModelWithProvider,
|
|
22
|
+
GetCodespaceModelsQuery,
|
|
23
|
+
GetCodespaceModelsResponse,
|
|
24
|
+
GetCodespaceModelResponse,
|
|
25
|
+
GetLLMModelProvidersResponse,
|
|
26
|
+
GetLLMModelProviderResponse,
|
|
27
|
+
GetModelsByProviderResponse,
|
|
28
|
+
CodespaceModelsError
|
|
16
29
|
} from './codespace-types'
|
package/services/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export { SubscriptionService } from './subscriptions'
|
|
|
18
18
|
export { CancellationFunnelService } from './cancellation-funnel'
|
|
19
19
|
export { CodespaceService } from './codespace'
|
|
20
20
|
export { ExternalTokenService } from './external-tokens'
|
|
21
|
+
export { SecurityKeysService } from './security-keys'
|
|
21
22
|
|
|
22
23
|
// Re-export all types for convenience
|
|
23
24
|
export * from './generation'
|
|
@@ -30,3 +31,4 @@ export * from './subscriptions'
|
|
|
30
31
|
export * from './cancellation-funnel'
|
|
31
32
|
export * from './codespace'
|
|
32
33
|
export * from './external-tokens'
|
|
34
|
+
export * from './security-keys'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export { SecurityKeysService } from './security-keys-service'
|
|
2
|
+
|
|
3
|
+
// Export all types
|
|
4
|
+
export * from './security-keys-types'
|
|
5
|
+
|
|
6
|
+
// Re-export commonly used types for convenience
|
|
7
|
+
export type {
|
|
8
|
+
SecurityKeyData,
|
|
9
|
+
ProviderAPIKeyData,
|
|
10
|
+
GitHubTokenData,
|
|
11
|
+
CreateProviderAPIKeyRequest,
|
|
12
|
+
CreateProviderAPIKeyResponse,
|
|
13
|
+
GetProviderAPIKeyResponse,
|
|
14
|
+
ListProviderAPIKeysResponse,
|
|
15
|
+
RevokeProviderAPIKeyResponse,
|
|
16
|
+
CreateGitHubTokenRequest,
|
|
17
|
+
CreateGitHubTokenResponse,
|
|
18
|
+
GetGitHubTokenResponse,
|
|
19
|
+
RevokeGitHubTokenResponse,
|
|
20
|
+
SuccessResponse,
|
|
21
|
+
ErrorResponse,
|
|
22
|
+
SecurityKeysError,
|
|
23
|
+
ProviderAPIKeyResponse,
|
|
24
|
+
GitHubTokenResponse
|
|
25
|
+
} from './security-keys-types'
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { BaseService } from '../base/base-service'
|
|
2
|
+
import { APIServiceConfig } from '../../types'
|
|
3
|
+
import {
|
|
4
|
+
CreateProviderAPIKeyRequest,
|
|
5
|
+
CreateProviderAPIKeyResponse,
|
|
6
|
+
GetProviderAPIKeyResponse,
|
|
7
|
+
ListProviderAPIKeysResponse,
|
|
8
|
+
RevokeProviderAPIKeyResponse,
|
|
9
|
+
CreateGitHubTokenRequest,
|
|
10
|
+
CreateGitHubTokenResponse,
|
|
11
|
+
GetGitHubTokenResponse,
|
|
12
|
+
RevokeGitHubTokenResponse,
|
|
13
|
+
SecurityKeysError
|
|
14
|
+
} from './security-keys-types'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Security Keys Service
|
|
18
|
+
*
|
|
19
|
+
* This service provides methods for managing security keys including:
|
|
20
|
+
* - Provider API Keys (OpenAI, Anthropic, etc.)
|
|
21
|
+
* - GitHub Tokens
|
|
22
|
+
*
|
|
23
|
+
* API endpoints match the actual backend implementation:
|
|
24
|
+
* - POST /security-keys/provider-api-key
|
|
25
|
+
* - GET /security-keys/provider-api-key/{provider_key}
|
|
26
|
+
* - GET /security-keys/provider-api-keys
|
|
27
|
+
* - DELETE /security-keys/provider-api-key/{provider_key}
|
|
28
|
+
* - POST /security-keys/github-token
|
|
29
|
+
* - GET /security-keys/github-token
|
|
30
|
+
* - DELETE /security-keys/github-token
|
|
31
|
+
*/
|
|
32
|
+
export class SecurityKeysService extends BaseService {
|
|
33
|
+
constructor(config: APIServiceConfig) {
|
|
34
|
+
super(config)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Provider API Key Methods
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Save a new provider API key
|
|
43
|
+
*
|
|
44
|
+
* POST /security-keys/provider-api-key
|
|
45
|
+
*
|
|
46
|
+
* @param request - The provider API key creation request
|
|
47
|
+
* @returns Promise resolving to the created API key response
|
|
48
|
+
* @throws {Error} When provider_id or api_key is missing
|
|
49
|
+
*/
|
|
50
|
+
async createProviderAPIKey(request: CreateProviderAPIKeyRequest): Promise<CreateProviderAPIKeyResponse> {
|
|
51
|
+
this.validateProviderAPIKeyRequest(request)
|
|
52
|
+
|
|
53
|
+
return this.post<CreateProviderAPIKeyResponse>(
|
|
54
|
+
'/security-keys/provider-api-key',
|
|
55
|
+
request
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get a provider API key by provider key
|
|
61
|
+
*
|
|
62
|
+
* GET /security-keys/provider-api-key/{provider_key}
|
|
63
|
+
*
|
|
64
|
+
* @param providerKey - The provider key (e.g., 'openai', 'anthropic')
|
|
65
|
+
* @param reveal - Whether to reveal the actual API key (default: false)
|
|
66
|
+
* @returns Promise resolving to the API key response
|
|
67
|
+
* @throws {Error} When provider_key is missing
|
|
68
|
+
*/
|
|
69
|
+
async getProviderAPIKey(providerKey: string, reveal: boolean = false): Promise<GetProviderAPIKeyResponse> {
|
|
70
|
+
if (!providerKey) {
|
|
71
|
+
throw new Error('provider_key is required')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const url = reveal
|
|
75
|
+
? `/security-keys/provider-api-key/${providerKey}?reveal=true`
|
|
76
|
+
: `/security-keys/provider-api-key/${providerKey}`
|
|
77
|
+
|
|
78
|
+
return this.get<GetProviderAPIKeyResponse>(url)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* List all provider API keys
|
|
83
|
+
*
|
|
84
|
+
* GET /security-keys/provider-api-keys
|
|
85
|
+
*
|
|
86
|
+
* @param reveal - Whether to reveal the actual API keys (default: false)
|
|
87
|
+
* @returns Promise resolving to the list of all provider API keys
|
|
88
|
+
*/
|
|
89
|
+
async listProviderAPIKeys(reveal: boolean = false): Promise<ListProviderAPIKeysResponse> {
|
|
90
|
+
const url = reveal
|
|
91
|
+
? '/security-keys/provider-api-keys?reveal=true'
|
|
92
|
+
: '/security-keys/provider-api-keys'
|
|
93
|
+
|
|
94
|
+
return this.get<ListProviderAPIKeysResponse>(url)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Delete a provider API key by provider key
|
|
99
|
+
*
|
|
100
|
+
* DELETE /security-keys/provider-api-key/{provider_key}
|
|
101
|
+
*
|
|
102
|
+
* @param providerKey - The provider key (e.g., 'openai', 'anthropic')
|
|
103
|
+
* @returns Promise resolving to the deletion response
|
|
104
|
+
* @throws {Error} When provider_key is missing
|
|
105
|
+
*/
|
|
106
|
+
async revokeProviderAPIKey(providerKey: string): Promise<RevokeProviderAPIKeyResponse> {
|
|
107
|
+
if (!providerKey) {
|
|
108
|
+
throw new Error('provider_key is required')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return this.delete<RevokeProviderAPIKeyResponse>(`/security-keys/provider-api-key/${providerKey}`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// GitHub Token Methods
|
|
116
|
+
// ============================================================================
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Save a new GitHub token
|
|
120
|
+
*
|
|
121
|
+
* POST /security-keys/github-token
|
|
122
|
+
*
|
|
123
|
+
* @param request - The GitHub token creation request
|
|
124
|
+
* @returns Promise resolving to the created GitHub token response
|
|
125
|
+
* @throws {Error} When github_token is missing or invalid
|
|
126
|
+
*/
|
|
127
|
+
async createGitHubToken(request: CreateGitHubTokenRequest): Promise<CreateGitHubTokenResponse> {
|
|
128
|
+
this.validateGitHubTokenRequest(request)
|
|
129
|
+
|
|
130
|
+
return this.post<CreateGitHubTokenResponse>(
|
|
131
|
+
'/security-keys/github-token',
|
|
132
|
+
request
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get the GitHub token
|
|
138
|
+
*
|
|
139
|
+
* GET /security-keys/github-token
|
|
140
|
+
*
|
|
141
|
+
* @param reveal - Whether to reveal the actual token (default: false)
|
|
142
|
+
* @returns Promise resolving to the GitHub token response
|
|
143
|
+
*/
|
|
144
|
+
async getGitHubToken(reveal: boolean = false): Promise<GetGitHubTokenResponse> {
|
|
145
|
+
const url = reveal
|
|
146
|
+
? '/security-keys/github-token?reveal=true'
|
|
147
|
+
: '/security-keys/github-token'
|
|
148
|
+
|
|
149
|
+
return this.get<GetGitHubTokenResponse>(url)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Delete the GitHub token
|
|
154
|
+
*
|
|
155
|
+
* DELETE /security-keys/github-token
|
|
156
|
+
*
|
|
157
|
+
* @returns Promise resolving to the deletion response
|
|
158
|
+
*/
|
|
159
|
+
async revokeGitHubToken(): Promise<RevokeGitHubTokenResponse> {
|
|
160
|
+
return this.delete<RevokeGitHubTokenResponse>('/security-keys/github-token')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ============================================================================
|
|
164
|
+
// Utility Methods
|
|
165
|
+
// ============================================================================
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Validate provider API key creation request
|
|
169
|
+
*
|
|
170
|
+
* @param request - The request to validate
|
|
171
|
+
* @throws {Error} When validation fails
|
|
172
|
+
*/
|
|
173
|
+
private validateProviderAPIKeyRequest(request: CreateProviderAPIKeyRequest): void {
|
|
174
|
+
if (!request.provider_key) {
|
|
175
|
+
throw new Error('provider_key is required')
|
|
176
|
+
}
|
|
177
|
+
if (!request.api_key) {
|
|
178
|
+
throw new Error('api_key is required')
|
|
179
|
+
}
|
|
180
|
+
if (request.api_key.length < 10) {
|
|
181
|
+
throw new Error('api_key must be at least 10 characters long')
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Validate GitHub token creation request
|
|
187
|
+
* Matches the API validation for GitHub token formats
|
|
188
|
+
*
|
|
189
|
+
* @param request - The request to validate
|
|
190
|
+
* @throws {Error} When validation fails
|
|
191
|
+
*/
|
|
192
|
+
private validateGitHubTokenRequest(request: CreateGitHubTokenRequest): void {
|
|
193
|
+
if (!request.github_token) {
|
|
194
|
+
throw new Error('github_token is required')
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Valid GitHub token prefixes according to the API
|
|
198
|
+
const validPrefixes = ['ghp_', 'gho_', 'ghu_', 'ghs_', 'ghr_', 'github_pat_']
|
|
199
|
+
const hasValidPrefix = validPrefixes.some(prefix => request.github_token.startsWith(prefix))
|
|
200
|
+
|
|
201
|
+
if (!hasValidPrefix) {
|
|
202
|
+
throw new Error('Invalid GitHub token format. Expected format: ghp_*, gho_*, ghu_*, ghs_*, or ghr_* followed by 36 characters')
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// GitHub tokens should be at least 40 characters (prefix + 36 chars)
|
|
206
|
+
if (request.github_token.length < 40) {
|
|
207
|
+
throw new Error('GitHub token must be at least 40 characters long')
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Handle API errors consistently
|
|
213
|
+
*
|
|
214
|
+
* @param error - The error response from the API
|
|
215
|
+
* @throws {Error} With the API error message
|
|
216
|
+
*/
|
|
217
|
+
private handleAPIError(error: any): never {
|
|
218
|
+
if (error.response?.data?.detail) {
|
|
219
|
+
throw new Error(error.response.data.detail)
|
|
220
|
+
}
|
|
221
|
+
if (error.response?.data?.message) {
|
|
222
|
+
throw new Error(error.response.data.message)
|
|
223
|
+
}
|
|
224
|
+
if (error.message) {
|
|
225
|
+
throw new Error(error.message)
|
|
226
|
+
}
|
|
227
|
+
throw new Error('An unexpected error occurred')
|
|
228
|
+
}
|
|
229
|
+
}
|