@agentuity/server 0.0.42 → 0.0.44
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/dist/api/api.d.ts +28 -12
- package/dist/api/api.d.ts.map +1 -1
- package/dist/api/api.js +208 -84
- package/dist/api/api.js.map +1 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/org/list.d.ts +14 -8
- package/dist/api/org/list.d.ts.map +1 -1
- package/dist/api/org/list.js +8 -3
- package/dist/api/org/list.js.map +1 -1
- package/dist/api/project/create.d.ts +15 -7
- package/dist/api/project/create.d.ts.map +1 -1
- package/dist/api/project/create.js +8 -3
- package/dist/api/project/create.js.map +1 -1
- package/dist/api/project/delete.d.ts.map +1 -1
- package/dist/api/project/delete.js +1 -1
- package/dist/api/project/delete.js.map +1 -1
- package/dist/api/project/deploy.d.ts +184 -0
- package/dist/api/project/deploy.d.ts.map +1 -0
- package/dist/api/project/deploy.js +160 -0
- package/dist/api/project/deploy.js.map +1 -0
- package/dist/api/project/env-delete.d.ts +15 -0
- package/dist/api/project/env-delete.d.ts.map +1 -0
- package/dist/api/project/env-delete.js +24 -0
- package/dist/api/project/env-delete.js.map +1 -0
- package/dist/api/project/env-update.d.ts +17 -0
- package/dist/api/project/env-update.d.ts.map +1 -0
- package/dist/api/project/env-update.js +39 -0
- package/dist/api/project/env-update.js.map +1 -0
- package/dist/api/project/exists.d.ts +7 -5
- package/dist/api/project/exists.d.ts.map +1 -1
- package/dist/api/project/exists.js +1 -1
- package/dist/api/project/exists.js.map +1 -1
- package/dist/api/project/get.d.ts +7 -12
- package/dist/api/project/get.d.ts.map +1 -1
- package/dist/api/project/get.js +5 -4
- package/dist/api/project/get.js.map +1 -1
- package/dist/api/project/index.d.ts +3 -0
- package/dist/api/project/index.d.ts.map +1 -1
- package/dist/api/project/index.js +3 -0
- package/dist/api/project/index.js.map +1 -1
- package/dist/api/project/list.d.ts +20 -8
- package/dist/api/project/list.d.ts.map +1 -1
- package/dist/api/project/list.js +9 -6
- package/dist/api/project/list.js.map +1 -1
- package/dist/api/region/create.d.ts +46 -0
- package/dist/api/region/create.d.ts.map +1 -0
- package/dist/api/region/create.js +35 -0
- package/dist/api/region/create.js.map +1 -0
- package/dist/api/region/delete.d.ts +38 -0
- package/dist/api/region/delete.d.ts.map +1 -0
- package/dist/api/region/delete.js +31 -0
- package/dist/api/region/delete.js.map +1 -0
- package/dist/api/region/index.d.ts +5 -0
- package/dist/api/region/index.d.ts.map +1 -0
- package/dist/api/region/index.js +5 -0
- package/dist/api/region/index.js.map +1 -0
- package/dist/api/region/list.d.ts +27 -0
- package/dist/api/region/list.d.ts.map +1 -0
- package/dist/api/region/list.js +21 -0
- package/dist/api/region/list.js.map +1 -0
- package/dist/api/region/resources.d.ts +53 -0
- package/dist/api/region/resources.d.ts.map +1 -0
- package/dist/api/region/resources.js +35 -0
- package/dist/api/region/resources.js.map +1 -0
- package/dist/api/user/index.d.ts +2 -0
- package/dist/api/user/index.d.ts.map +1 -0
- package/dist/api/user/index.js +2 -0
- package/dist/api/user/index.js.map +1 -0
- package/dist/api/user/whoami.d.ts +35 -0
- package/dist/api/user/whoami.d.ts.map +1 -0
- package/dist/api/user/whoami.js +26 -0
- package/dist/api/user/whoami.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +39 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +241 -0
- package/dist/logger.js.map +1 -0
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/package.json +4 -2
- package/src/api/api-example.md +16 -11
- package/src/api/api.ts +254 -93
- package/src/api/index.ts +2 -0
- package/src/api/org/list.ts +13 -10
- package/src/api/project/create.ts +14 -9
- package/src/api/project/delete.ts +2 -2
- package/src/api/project/deploy.ts +224 -0
- package/src/api/project/env-delete.ts +40 -0
- package/src/api/project/env-update.ts +59 -0
- package/src/api/project/exists.ts +1 -1
- package/src/api/project/get.ts +12 -12
- package/src/api/project/index.ts +3 -0
- package/src/api/project/list.ts +20 -15
- package/src/api/region/create.ts +55 -0
- package/src/api/region/delete.ts +49 -0
- package/src/api/region/index.ts +4 -0
- package/src/api/region/list.ts +31 -0
- package/src/api/region/resources.ts +51 -0
- package/src/api/user/index.ts +1 -0
- package/src/api/user/whoami.ts +31 -0
- package/src/config.ts +2 -0
- package/src/index.ts +1 -0
- package/src/logger.ts +291 -0
- package/src/server.ts +2 -2
package/src/api/api.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { z } from 'zod';
|
|
12
|
+
import type { Logger } from '@agentuity/core';
|
|
12
13
|
|
|
13
14
|
export interface APIErrorResponse {
|
|
14
15
|
success: boolean;
|
|
@@ -24,14 +25,19 @@ export interface APIErrorResponse {
|
|
|
24
25
|
export interface APIClientConfig {
|
|
25
26
|
skipVersionCheck?: boolean;
|
|
26
27
|
userAgent?: string;
|
|
28
|
+
maxRetries?: number;
|
|
29
|
+
retryDelayMs?: number;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export class ValidationError extends Error {
|
|
33
|
+
public url: string;
|
|
30
34
|
constructor(
|
|
35
|
+
url: string,
|
|
31
36
|
message: string,
|
|
32
37
|
public issues: z.ZodIssue[]
|
|
33
38
|
) {
|
|
34
39
|
super(message);
|
|
40
|
+
this.url = url;
|
|
35
41
|
this.name = 'ValidationError';
|
|
36
42
|
}
|
|
37
43
|
}
|
|
@@ -55,33 +61,39 @@ export class APIError extends Error {
|
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
export class APIClient {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
#baseUrl: string;
|
|
65
|
+
#apiKey?: string;
|
|
66
|
+
#config?: APIClientConfig;
|
|
67
|
+
#logger: Logger;
|
|
61
68
|
|
|
62
|
-
constructor(baseUrl: string, config?: APIClientConfig);
|
|
63
|
-
constructor(baseUrl: string, apiKey: string, config?: APIClientConfig);
|
|
69
|
+
constructor(baseUrl: string, logger: Logger, config?: APIClientConfig);
|
|
70
|
+
constructor(baseUrl: string, logger: Logger, apiKey: string, config?: APIClientConfig);
|
|
64
71
|
constructor(
|
|
65
72
|
baseUrl: string,
|
|
73
|
+
logger: Logger,
|
|
66
74
|
apiKeyOrConfig?: string | APIClientConfig,
|
|
67
75
|
config?: APIClientConfig
|
|
68
76
|
) {
|
|
69
|
-
this
|
|
77
|
+
this.#baseUrl = baseUrl;
|
|
78
|
+
this.#logger = logger;
|
|
70
79
|
|
|
71
|
-
// Detect if
|
|
80
|
+
// Detect if third parameter is apiKey (string) or config (object)
|
|
72
81
|
if (typeof apiKeyOrConfig === 'string') {
|
|
73
|
-
this
|
|
74
|
-
this
|
|
82
|
+
this.#apiKey = apiKeyOrConfig;
|
|
83
|
+
this.#config = config;
|
|
75
84
|
} else {
|
|
76
|
-
this
|
|
77
|
-
this
|
|
85
|
+
this.#apiKey = undefined;
|
|
86
|
+
this.#config = apiKeyOrConfig;
|
|
87
|
+
}
|
|
88
|
+
if (!this.#apiKey && process.env.AGENTUITY_SDK_KEY) {
|
|
89
|
+
this.#apiKey = process.env.AGENTUITY_SDK_KEY;
|
|
78
90
|
}
|
|
79
91
|
}
|
|
80
92
|
|
|
81
|
-
async request<TResponse, TBody = unknown>(
|
|
93
|
+
async request<TResponse = void, TBody = unknown>(
|
|
82
94
|
method: string,
|
|
83
95
|
endpoint: string,
|
|
84
|
-
responseSchema
|
|
96
|
+
responseSchema?: z.ZodType<TResponse>,
|
|
85
97
|
body?: TBody,
|
|
86
98
|
bodySchema?: z.ZodType<TBody>
|
|
87
99
|
): Promise<TResponse> {
|
|
@@ -90,13 +102,14 @@ export class APIClient {
|
|
|
90
102
|
const validationResult = bodySchema.safeParse(body);
|
|
91
103
|
if (!validationResult.success) {
|
|
92
104
|
throw new ValidationError(
|
|
105
|
+
endpoint,
|
|
93
106
|
'Request body validation failed',
|
|
94
107
|
validationResult.error.issues
|
|
95
108
|
);
|
|
96
109
|
}
|
|
97
110
|
}
|
|
98
111
|
|
|
99
|
-
const response = await this
|
|
112
|
+
const response = await this.#makeRequest(method, endpoint, body);
|
|
100
113
|
|
|
101
114
|
// Handle empty responses (204 or zero-length body)
|
|
102
115
|
let data: unknown;
|
|
@@ -108,7 +121,7 @@ export class APIClient {
|
|
|
108
121
|
data = null;
|
|
109
122
|
} else {
|
|
110
123
|
const contentType = response.headers.get('content-type');
|
|
111
|
-
if (contentType
|
|
124
|
+
if (contentType?.includes('application/json')) {
|
|
112
125
|
data = JSON.parse(text);
|
|
113
126
|
} else {
|
|
114
127
|
data = text;
|
|
@@ -116,105 +129,225 @@ export class APIClient {
|
|
|
116
129
|
}
|
|
117
130
|
}
|
|
118
131
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
132
|
+
if (responseSchema) {
|
|
133
|
+
// Validate response
|
|
134
|
+
const validationResult = responseSchema.safeParse(data);
|
|
135
|
+
if (!validationResult.success) {
|
|
136
|
+
throw new ValidationError(
|
|
137
|
+
endpoint,
|
|
138
|
+
'Response validation failed',
|
|
139
|
+
validationResult.error.issues
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return validationResult.data;
|
|
123
144
|
}
|
|
124
145
|
|
|
125
|
-
return
|
|
146
|
+
return undefined as TResponse;
|
|
126
147
|
}
|
|
127
148
|
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
'Content-Type': 'application/json',
|
|
132
|
-
};
|
|
149
|
+
async #makeRequest(method: string, endpoint: string, body?: unknown): Promise<Response> {
|
|
150
|
+
const maxRetries = this.#config?.maxRetries ?? 3;
|
|
151
|
+
const baseDelayMs = this.#config?.retryDelayMs ?? 100;
|
|
133
152
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
153
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
154
|
+
try {
|
|
155
|
+
const url = `${this.#baseUrl}${endpoint}`;
|
|
156
|
+
const headers: Record<string, string> = {
|
|
157
|
+
'Content-Type': 'application/json',
|
|
158
|
+
};
|
|
137
159
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
160
|
+
if (this.#config?.userAgent) {
|
|
161
|
+
headers['User-Agent'] = this.#config.userAgent;
|
|
162
|
+
}
|
|
141
163
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
146
|
-
});
|
|
164
|
+
if (this.#apiKey) {
|
|
165
|
+
headers['Authorization'] = `Bearer ${this.#apiKey}`;
|
|
166
|
+
}
|
|
147
167
|
|
|
148
|
-
|
|
149
|
-
|
|
168
|
+
const response = await fetch(url, {
|
|
169
|
+
method,
|
|
170
|
+
headers,
|
|
171
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Check if we should retry on specific status codes (409, 501, 503)
|
|
175
|
+
const retryableStatuses = [409, 501, 503];
|
|
176
|
+
if (retryableStatuses.includes(response.status) && attempt < maxRetries) {
|
|
177
|
+
let delayMs = this.#getRetryDelay(attempt, baseDelayMs);
|
|
178
|
+
|
|
179
|
+
// For 409, check for rate limit headers
|
|
180
|
+
if (response.status === 409) {
|
|
181
|
+
const rateLimitDelay = this.#getRateLimitDelay(response);
|
|
182
|
+
if (rateLimitDelay !== null) {
|
|
183
|
+
delayMs = rateLimitDelay;
|
|
184
|
+
this.#logger.debug(
|
|
185
|
+
`Got 409 with rate limit headers, waiting ${delayMs}ms (attempt ${attempt + 1}/${maxRetries + 1})`
|
|
186
|
+
);
|
|
187
|
+
} else {
|
|
188
|
+
this.#logger.debug(
|
|
189
|
+
`Got 409, retrying with backoff ${delayMs}ms (attempt ${attempt + 1}/${maxRetries + 1})`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
this.#logger.debug(
|
|
194
|
+
`Got ${response.status}, retrying (attempt ${attempt + 1}/${maxRetries + 1})`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
150
197
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
errorData = JSON.parse(responseBody) as APIErrorResponse;
|
|
155
|
-
} catch {
|
|
156
|
-
// Not JSON, ignore
|
|
157
|
-
}
|
|
198
|
+
await this.#sleep(delayMs);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
158
201
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
202
|
+
// Handle error responses
|
|
203
|
+
if (!response.ok) {
|
|
204
|
+
const responseBody = await response.text();
|
|
205
|
+
|
|
206
|
+
// Try to parse error response
|
|
207
|
+
let errorData: APIErrorResponse | null = null;
|
|
208
|
+
try {
|
|
209
|
+
errorData = JSON.parse(responseBody) as APIErrorResponse;
|
|
210
|
+
} catch {
|
|
211
|
+
// Not JSON, ignore
|
|
165
212
|
}
|
|
166
|
-
}
|
|
167
213
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
214
|
+
// Sanitize headers to avoid leaking API keys
|
|
215
|
+
const sanitizedHeaders = { ...headers };
|
|
216
|
+
for (const key in sanitizedHeaders) {
|
|
217
|
+
if (key.toLowerCase() === 'authorization') {
|
|
218
|
+
sanitizedHeaders[key] = 'REDACTED';
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.#logger.debug('API Error Details:');
|
|
223
|
+
this.#logger.debug(' URL:', url);
|
|
224
|
+
this.#logger.debug(' Method:', method);
|
|
225
|
+
this.#logger.debug(' Status:', response.status, response.statusText);
|
|
226
|
+
this.#logger.debug(' Headers:', JSON.stringify(sanitizedHeaders, null, 2));
|
|
227
|
+
this.#logger.debug(' Response:', responseBody);
|
|
228
|
+
|
|
229
|
+
// Check for UPGRADE_REQUIRED error
|
|
230
|
+
if (errorData?.code === 'UPGRADE_REQUIRED') {
|
|
231
|
+
// Skip version check if configured
|
|
232
|
+
if (this.#config?.skipVersionCheck) {
|
|
233
|
+
this.#logger.debug('Skipping version check (configured to skip)');
|
|
234
|
+
// Request is still rejected, but throw UpgradeRequiredError so callers
|
|
235
|
+
// can detect it and handle UI behavior (e.g., suppress banner) based on skip flag
|
|
236
|
+
throw new UpgradeRequiredError(
|
|
237
|
+
errorData.message ||
|
|
238
|
+
'Version check skipped, but request failed. Try upgrading the client.'
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
throw new UpgradeRequiredError(
|
|
243
|
+
errorData.message || 'Please upgrade to the latest version'
|
|
244
|
+
);
|
|
245
|
+
}
|
|
175
246
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (this.config?.skipVersionCheck) {
|
|
180
|
-
if (process.env.DEBUG) {
|
|
181
|
-
console.error('[DEBUG] Skipping version check (configured to skip)');
|
|
247
|
+
// Handle Zod validation errors from the API
|
|
248
|
+
if (errorData?.error?.name === 'ZodError' && errorData.error.issues) {
|
|
249
|
+
throw new ValidationError(url, 'API validation failed', errorData.error.issues);
|
|
182
250
|
}
|
|
183
|
-
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
errorData.message
|
|
187
|
-
|
|
251
|
+
|
|
252
|
+
// Throw with message from API if available
|
|
253
|
+
if (errorData?.message) {
|
|
254
|
+
throw new APIError(errorData.message, response.status, errorData.code);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
throw new APIError(
|
|
258
|
+
`API error: ${response.status} ${response.statusText}`,
|
|
259
|
+
response.status
|
|
188
260
|
);
|
|
189
261
|
}
|
|
190
262
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
263
|
+
// Successful response; handle empty bodies (e.g., 204 No Content)
|
|
264
|
+
if (response.status === 204 || response.headers.get('content-length') === '0') {
|
|
265
|
+
return new Response(null, { status: 204 });
|
|
266
|
+
}
|
|
195
267
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
268
|
+
return response;
|
|
269
|
+
} catch (error) {
|
|
270
|
+
// Check if it's a retryable connection error
|
|
271
|
+
const isRetryable = this.#isRetryableError(error);
|
|
272
|
+
|
|
273
|
+
if (isRetryable && attempt < maxRetries) {
|
|
274
|
+
this.#logger.debug(
|
|
275
|
+
`Connection error, retrying (attempt ${attempt + 1}/${maxRetries + 1}):`,
|
|
276
|
+
error
|
|
277
|
+
);
|
|
278
|
+
await this.#sleep(this.#getRetryDelay(attempt, baseDelayMs));
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
200
281
|
|
|
201
|
-
|
|
202
|
-
if (errorData?.message) {
|
|
203
|
-
throw new APIError(errorData.message, response.status, errorData.code);
|
|
282
|
+
throw error;
|
|
204
283
|
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
throw new Error('Max retries exceeded');
|
|
287
|
+
}
|
|
205
288
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
289
|
+
#isRetryableError(error: unknown): boolean {
|
|
290
|
+
if (error && typeof error === 'object') {
|
|
291
|
+
const err = error as { code?: string; errno?: number };
|
|
292
|
+
// Retryable connection errors
|
|
293
|
+
return (
|
|
294
|
+
err.code === 'ECONNRESET' ||
|
|
295
|
+
err.code === 'ETIMEDOUT' ||
|
|
296
|
+
err.code === 'ECONNREFUSED' ||
|
|
297
|
+
err.code === 'ENOTFOUND'
|
|
209
298
|
);
|
|
210
299
|
}
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
211
302
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
303
|
+
#getRetryDelay(attempt: number, baseDelayMs: number): number {
|
|
304
|
+
// Exponential backoff with jitter: delay = base * 2^attempt * (0.5 + random(0, 0.5))
|
|
305
|
+
const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
|
|
306
|
+
const jitter = 0.5 + Math.random() * 0.5;
|
|
307
|
+
return Math.floor(exponentialDelay * jitter);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
#sleep(ms: number): Promise<void> {
|
|
311
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
#getRateLimitDelay(response: Response): number | null {
|
|
315
|
+
// Check for Retry-After header (standard HTTP)
|
|
316
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
317
|
+
if (retryAfter) {
|
|
318
|
+
// Can be either seconds or HTTP date
|
|
319
|
+
const seconds = parseInt(retryAfter, 10);
|
|
320
|
+
if (!isNaN(seconds)) {
|
|
321
|
+
return seconds * 1000; // Convert to milliseconds
|
|
322
|
+
}
|
|
323
|
+
// Try parsing as HTTP date
|
|
324
|
+
const retryDate = new Date(retryAfter);
|
|
325
|
+
if (!isNaN(retryDate.getTime())) {
|
|
326
|
+
const delayMs = retryDate.getTime() - Date.now();
|
|
327
|
+
return Math.max(0, delayMs);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check for X-RateLimit-Reset (Unix timestamp in seconds)
|
|
332
|
+
const rateLimitReset = response.headers.get('X-RateLimit-Reset');
|
|
333
|
+
if (rateLimitReset) {
|
|
334
|
+
const resetTime = parseInt(rateLimitReset, 10);
|
|
335
|
+
if (!isNaN(resetTime)) {
|
|
336
|
+
const delayMs = resetTime * 1000 - Date.now();
|
|
337
|
+
return Math.max(0, delayMs);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Check for X-RateLimit-Retry-After (seconds)
|
|
342
|
+
const rateLimitRetryAfter = response.headers.get('X-RateLimit-Retry-After');
|
|
343
|
+
if (rateLimitRetryAfter) {
|
|
344
|
+
const seconds = parseInt(rateLimitRetryAfter, 10);
|
|
345
|
+
if (!isNaN(seconds)) {
|
|
346
|
+
return seconds * 1000;
|
|
347
|
+
}
|
|
215
348
|
}
|
|
216
349
|
|
|
217
|
-
return
|
|
350
|
+
return null;
|
|
218
351
|
}
|
|
219
352
|
}
|
|
220
353
|
|
|
@@ -243,8 +376,36 @@ export function getAppBaseURL(overrides?: { app_url?: string }): string {
|
|
|
243
376
|
}
|
|
244
377
|
|
|
245
378
|
export const APIResponseSchema = <T extends z.ZodType>(dataSchema: T) =>
|
|
246
|
-
z.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
379
|
+
z.discriminatedUnion('success', [
|
|
380
|
+
z.object({
|
|
381
|
+
success: z.literal<false>(false),
|
|
382
|
+
message: z.string().describe('the error message'),
|
|
383
|
+
}),
|
|
384
|
+
z.object({
|
|
385
|
+
success: z.literal<true>(true),
|
|
386
|
+
data: dataSchema,
|
|
387
|
+
}),
|
|
388
|
+
]);
|
|
389
|
+
|
|
390
|
+
export const APIResponseSchemaOptionalData = <T extends z.ZodType>(dataSchema: T) =>
|
|
391
|
+
z.discriminatedUnion('success', [
|
|
392
|
+
z.object({
|
|
393
|
+
success: z.literal<false>(false),
|
|
394
|
+
message: z.string().describe('the error message'),
|
|
395
|
+
}),
|
|
396
|
+
z.object({
|
|
397
|
+
success: z.literal<true>(true),
|
|
398
|
+
data: dataSchema.optional(),
|
|
399
|
+
}),
|
|
400
|
+
]);
|
|
401
|
+
|
|
402
|
+
export const APIResponseSchemaNoData = () =>
|
|
403
|
+
z.discriminatedUnion('success', [
|
|
404
|
+
z.object({
|
|
405
|
+
success: z.literal<false>(false),
|
|
406
|
+
message: z.string().describe('the error message'),
|
|
407
|
+
}),
|
|
408
|
+
z.object({
|
|
409
|
+
success: z.literal<true>(true),
|
|
410
|
+
}),
|
|
411
|
+
]);
|
package/src/api/index.ts
CHANGED
package/src/api/org/list.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { APIResponseSchema, APIClient } from '../api';
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
z.
|
|
6
|
-
z.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
})
|
|
10
|
-
)
|
|
4
|
+
const ListOrganizationsResponse = z.array(
|
|
5
|
+
z.object({
|
|
6
|
+
id: z.string().describe('the unique id for the organization'),
|
|
7
|
+
name: z.string().describe('the name of the organization'),
|
|
8
|
+
})
|
|
11
9
|
);
|
|
10
|
+
const ListOrganizationsResponseSchema = APIResponseSchema(ListOrganizationsResponse);
|
|
12
11
|
|
|
13
12
|
export type ListOrganizationsResponse = z.infer<typeof ListOrganizationsResponseSchema>;
|
|
14
|
-
export type OrganizationList =
|
|
13
|
+
export type OrganizationList = z.infer<typeof ListOrganizationsResponse>;
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* List all organizations
|
|
@@ -19,10 +18,14 @@ export type OrganizationList = NonNullable<ListOrganizationsResponse['data']>;
|
|
|
19
18
|
* @param client
|
|
20
19
|
* @returns
|
|
21
20
|
*/
|
|
22
|
-
export async function listOrganizations(client: APIClient): Promise<
|
|
23
|
-
|
|
21
|
+
export async function listOrganizations(client: APIClient): Promise<OrganizationList> {
|
|
22
|
+
const resp = await client.request<ListOrganizationsResponse>(
|
|
24
23
|
'GET',
|
|
25
24
|
'/cli/organization',
|
|
26
25
|
ListOrganizationsResponseSchema
|
|
27
26
|
);
|
|
27
|
+
if (resp.success) {
|
|
28
|
+
return resp.data;
|
|
29
|
+
}
|
|
30
|
+
throw new Error(resp.message);
|
|
28
31
|
}
|
|
@@ -11,16 +11,17 @@ const CreateProjectRequestSchema = z.object({
|
|
|
11
11
|
provider: z.string().max(255).min(1),
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
z.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
);
|
|
14
|
+
const CreateProjectResponse = z.object({
|
|
15
|
+
id: z.string().describe('the unique id for the project'),
|
|
16
|
+
api_key: z.string().describe('the SDK api key for the project'),
|
|
17
|
+
projectKey: z.string().describe('the Project api key for the project'),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const CreateProjectResponseSchema = APIResponseSchema(CreateProjectResponse);
|
|
21
21
|
|
|
22
22
|
export type CreateProjectRequest = z.infer<typeof CreateProjectRequestSchema>;
|
|
23
23
|
export type CreateProjectResponse = z.infer<typeof CreateProjectResponseSchema>;
|
|
24
|
+
export type NewProject = Omit<z.infer<typeof CreateProjectResponse>, 'projectKey'>;
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Create a new Project
|
|
@@ -32,12 +33,16 @@ export type CreateProjectResponse = z.infer<typeof CreateProjectResponseSchema>;
|
|
|
32
33
|
export async function projectCreate(
|
|
33
34
|
client: APIClient,
|
|
34
35
|
body: CreateProjectRequest
|
|
35
|
-
): Promise<
|
|
36
|
-
|
|
36
|
+
): Promise<NewProject> {
|
|
37
|
+
const resp = await client.request<CreateProjectResponse, CreateProjectRequest>(
|
|
37
38
|
'POST',
|
|
38
39
|
'/cli/project',
|
|
39
40
|
CreateProjectResponseSchema,
|
|
40
41
|
body,
|
|
41
42
|
CreateProjectRequestSchema
|
|
42
43
|
);
|
|
44
|
+
if (resp.success) {
|
|
45
|
+
return resp.data;
|
|
46
|
+
}
|
|
47
|
+
throw new Error(resp.message);
|
|
43
48
|
}
|
|
@@ -7,7 +7,7 @@ const ProjectDeleteResponseSchema = APIResponseSchema(z.array(z.string()));
|
|
|
7
7
|
type ProjectDeleteRequest = z.infer<typeof ProjectDeleteRequestSchema>;
|
|
8
8
|
type ProjectDeleteResponse = z.infer<typeof ProjectDeleteResponseSchema>;
|
|
9
9
|
|
|
10
|
-
export async function projectDelete(client: APIClient, ...ids: string[]) {
|
|
10
|
+
export async function projectDelete(client: APIClient, ...ids: string[]): Promise<string[]> {
|
|
11
11
|
const resp = await client.request<ProjectDeleteResponse, ProjectDeleteRequest>(
|
|
12
12
|
'DELETE',
|
|
13
13
|
'/cli/project',
|
|
@@ -16,7 +16,7 @@ export async function projectDelete(client: APIClient, ...ids: string[]) {
|
|
|
16
16
|
ProjectDeleteRequestSchema
|
|
17
17
|
);
|
|
18
18
|
|
|
19
|
-
if (resp.
|
|
19
|
+
if (resp.success) {
|
|
20
20
|
return resp.data;
|
|
21
21
|
}
|
|
22
22
|
|