@agentuity/server 0.0.35 → 0.0.36

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 (44) hide show
  1. package/dist/api/api.d.ts +53 -0
  2. package/dist/api/api.d.ts.map +1 -0
  3. package/dist/api/api.js +178 -0
  4. package/dist/api/api.js.map +1 -0
  5. package/dist/api/index.d.ts +4 -0
  6. package/dist/api/index.d.ts.map +1 -0
  7. package/dist/api/index.js +4 -0
  8. package/dist/api/index.js.map +1 -0
  9. package/dist/api/org/index.d.ts +2 -0
  10. package/dist/api/org/index.d.ts.map +1 -0
  11. package/dist/api/org/index.js +2 -0
  12. package/dist/api/org/index.js.map +1 -0
  13. package/dist/api/org/list.d.ts +21 -0
  14. package/dist/api/org/list.d.ts.map +1 -0
  15. package/dist/api/org/list.js +16 -0
  16. package/dist/api/org/list.js.map +1 -0
  17. package/dist/api/project/create.d.ts +28 -0
  18. package/dist/api/project/create.d.ts.map +1 -0
  19. package/dist/api/project/create.js +27 -0
  20. package/dist/api/project/create.js.map +1 -0
  21. package/dist/api/project/exists.d.ts +23 -0
  22. package/dist/api/project/exists.d.ts.map +1 -0
  23. package/dist/api/project/exists.js +61 -0
  24. package/dist/api/project/exists.js.map +1 -0
  25. package/dist/api/project/index.d.ts +3 -0
  26. package/dist/api/project/index.d.ts.map +1 -0
  27. package/dist/api/project/index.js +3 -0
  28. package/dist/api/project/index.js.map +1 -0
  29. package/dist/index.d.ts +2 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +2 -0
  32. package/dist/index.js.map +1 -1
  33. package/package.json +4 -2
  34. package/src/api/api-example.md +155 -0
  35. package/src/api/api.ts +241 -0
  36. package/src/api/index.ts +3 -0
  37. package/src/api/org/index.ts +1 -0
  38. package/src/api/org/list.ts +28 -0
  39. package/src/api/project/create.ts +43 -0
  40. package/src/api/project/exists.ts +76 -0
  41. package/src/api/project/index.ts +2 -0
  42. package/src/config.ts +20 -0
  43. package/src/index.ts +14 -0
  44. package/src/server.ts +133 -0
@@ -0,0 +1,155 @@
1
+ # APIClient Usage Examples
2
+
3
+ The `APIClient` has a friendly interface with built-in Zod schema validation.
4
+
5
+ ## Public API (no authentication)
6
+
7
+ ```typescript
8
+ import { APIClient } from '@agentuity/server';
9
+
10
+ // Simple - just URL and optional config
11
+ const client = new APIClient('https://api.agentuity.com');
12
+
13
+ // With config
14
+ const client = new APIClient('https://api.agentuity.com', {
15
+ skipVersionCheck: true,
16
+ userAgent: 'MyApp/1.0.0',
17
+ });
18
+ ```
19
+
20
+ ## Authenticated API
21
+
22
+ ```typescript
23
+ import { APIClient } from '@agentuity/server';
24
+
25
+ const apiKey = 'your-api-key';
26
+
27
+ // With API key
28
+ const client = new APIClient('https://api.agentuity.com', apiKey);
29
+
30
+ // With API key and config
31
+ const client = new APIClient('https://api.agentuity.com', apiKey, {
32
+ skipVersionCheck: true,
33
+ userAgent: 'MyApp/1.0.0',
34
+ });
35
+ ```
36
+
37
+ ## Making Requests
38
+
39
+ All requests require a response schema for validation:
40
+
41
+ ```typescript
42
+ import { APIClient, z, ValidationError } from '@agentuity/server';
43
+
44
+ // Define schemas
45
+ const UserSchema = z.object({
46
+ id: z.string(),
47
+ name: z.string(),
48
+ email: z.string().email(),
49
+ });
50
+
51
+ const CreateUserSchema = z.object({
52
+ name: z.string(),
53
+ email: z.string().email(),
54
+ });
55
+
56
+ const client = new APIClient('https://api.agentuity.com', apiKey);
57
+
58
+ // GET request with response validation
59
+ try {
60
+ const user = await client.request(
61
+ 'GET',
62
+ '/users/123',
63
+ UserSchema // response schema (required)
64
+ );
65
+ console.log(user.name); // Fully typed!
66
+ } catch (error) {
67
+ if (error instanceof ValidationError) {
68
+ console.error('Validation failed:', error.issues);
69
+ }
70
+ }
71
+
72
+ // POST request with request and response validation
73
+ const newUser = await client.request(
74
+ 'POST',
75
+ '/users',
76
+ UserSchema, // response schema (required)
77
+ {
78
+ // request body
79
+ name: 'John Doe',
80
+ email: 'john@example.com',
81
+ },
82
+ CreateUserSchema // request body schema (optional)
83
+ );
84
+ ```
85
+
86
+ ## API Response Wrapper Pattern
87
+
88
+ For APIs that wrap responses in a standard format:
89
+
90
+ ```typescript
91
+ import { z } from '@agentuity/server';
92
+
93
+ // Generic wrapper schema
94
+ const APIResponseSchema = <T extends z.ZodType>(dataSchema: T) =>
95
+ z.object({
96
+ success: z.boolean(),
97
+ message: z.string(),
98
+ data: dataSchema.optional(),
99
+ });
100
+
101
+ // Use it
102
+ const UserDataSchema = z.object({ id: z.string(), name: z.string() });
103
+
104
+ const response = await client.request('GET', '/users/123', APIResponseSchema(UserDataSchema));
105
+
106
+ if (response.success && response.data) {
107
+ console.log(response.data.name);
108
+ }
109
+ ```
110
+
111
+ ## CLI-specific Usage
112
+
113
+ The CLI wrapper automatically handles version checking and user agent:
114
+
115
+ ```typescript
116
+ import { APIClient, z } from '@agentuity/cli';
117
+ import type { Config } from '@agentuity/cli';
118
+
119
+ const config: Config = {
120
+ /* ... */
121
+ };
122
+
123
+ // Public API
124
+ const client = new APIClient('https://api.agentuity.com', config);
125
+
126
+ // Authenticated API
127
+ const client = new APIClient('https://api.agentuity.com', apiKey, config);
128
+
129
+ // Making requests (schema validation is built-in)
130
+ const ResponseSchema = z.object({
131
+ /* ... */
132
+ });
133
+ const data = await client.request('GET', '/endpoint', ResponseSchema);
134
+ ```
135
+
136
+ ## Error Handling
137
+
138
+ ```typescript
139
+ import { APIClient, APIError, ValidationError, UpgradeRequiredError } from '@agentuity/server';
140
+
141
+ try {
142
+ const data = await client.request('GET', '/endpoint', schema);
143
+ } catch (error) {
144
+ if (error instanceof ValidationError) {
145
+ // Response didn't match schema
146
+ console.error('Validation error:', error.issues);
147
+ } else if (error instanceof APIError) {
148
+ // HTTP error from API
149
+ console.error(`API error ${error.status}:`, error.message);
150
+ } else if (error instanceof UpgradeRequiredError) {
151
+ // Client needs upgrade
152
+ console.error('Please upgrade:', error.message);
153
+ }
154
+ }
155
+ ```
package/src/api/api.ts ADDED
@@ -0,0 +1,241 @@
1
+ /**
2
+ * API Client for Agentuity Platform
3
+ *
4
+ * Handles HTTP requests to the API with automatic error parsing and User-Agent headers.
5
+ *
6
+ * Error handling:
7
+ * - UPGRADE_REQUIRED (409): Throws UpgradeRequiredError
8
+ * - Other errors: Throws Error with API message or status text
9
+ */
10
+
11
+ import { z } from 'zod';
12
+
13
+ export interface APIErrorResponse {
14
+ success: boolean;
15
+ code?: string;
16
+ message: string;
17
+ details?: Record<string, unknown>;
18
+ }
19
+
20
+ export interface APIClientConfig {
21
+ skipVersionCheck?: boolean;
22
+ userAgent?: string;
23
+ }
24
+
25
+ export class ValidationError extends Error {
26
+ constructor(
27
+ message: string,
28
+ public issues: z.ZodIssue[]
29
+ ) {
30
+ super(message);
31
+ this.name = 'ValidationError';
32
+ }
33
+ }
34
+
35
+ export class UpgradeRequiredError extends Error {
36
+ constructor(message: string) {
37
+ super(message);
38
+ this.name = 'UpgradeRequiredError';
39
+ }
40
+ }
41
+
42
+ export class APIError extends Error {
43
+ constructor(
44
+ message: string,
45
+ public status: number,
46
+ public code?: string
47
+ ) {
48
+ super(message);
49
+ this.name = 'APIError';
50
+ }
51
+ }
52
+
53
+ export class APIClient {
54
+ private baseUrl: string;
55
+ private apiKey?: string;
56
+ private config?: APIClientConfig;
57
+
58
+ constructor(baseUrl: string, config?: APIClientConfig);
59
+ constructor(baseUrl: string, apiKey: string, config?: APIClientConfig);
60
+ constructor(
61
+ baseUrl: string,
62
+ apiKeyOrConfig?: string | APIClientConfig,
63
+ config?: APIClientConfig
64
+ ) {
65
+ this.baseUrl = baseUrl;
66
+
67
+ // Detect if second parameter is apiKey (string) or config (object)
68
+ if (typeof apiKeyOrConfig === 'string') {
69
+ this.apiKey = apiKeyOrConfig;
70
+ this.config = config;
71
+ } else {
72
+ this.apiKey = undefined;
73
+ this.config = apiKeyOrConfig;
74
+ }
75
+ }
76
+
77
+ async request<TResponse, TBody = unknown>(
78
+ method: string,
79
+ endpoint: string,
80
+ responseSchema: z.ZodType<TResponse>,
81
+ body?: TBody,
82
+ bodySchema?: z.ZodType<TBody>
83
+ ): Promise<TResponse> {
84
+ // Validate request body if schema provided
85
+ if (body !== undefined && bodySchema) {
86
+ const validationResult = bodySchema.safeParse(body);
87
+ if (!validationResult.success) {
88
+ throw new ValidationError(
89
+ 'Request body validation failed',
90
+ validationResult.error.issues
91
+ );
92
+ }
93
+ }
94
+
95
+ const response = await this.makeRequest(method, endpoint, body);
96
+
97
+ // Handle empty responses (204 or zero-length body)
98
+ let data: unknown;
99
+ if (response.status === 204 || response.headers.get('content-length') === '0') {
100
+ data = null;
101
+ } else {
102
+ const text = await response.text();
103
+ if (text === '') {
104
+ data = null;
105
+ } else {
106
+ const contentType = response.headers.get('content-type');
107
+ if (contentType && contentType.includes('application/json')) {
108
+ data = JSON.parse(text);
109
+ } else {
110
+ data = text;
111
+ }
112
+ }
113
+ }
114
+
115
+ // Validate response
116
+ const validationResult = responseSchema.safeParse(data);
117
+ if (!validationResult.success) {
118
+ throw new ValidationError('Response validation failed', validationResult.error.issues);
119
+ }
120
+
121
+ return validationResult.data;
122
+ }
123
+
124
+ private async makeRequest(method: string, endpoint: string, body?: unknown): Promise<Response> {
125
+ const url = `${this.baseUrl}${endpoint}`;
126
+ const headers: Record<string, string> = {
127
+ 'Content-Type': 'application/json',
128
+ };
129
+
130
+ if (this.config?.userAgent) {
131
+ headers['User-Agent'] = this.config.userAgent;
132
+ }
133
+
134
+ if (this.apiKey) {
135
+ headers['Authorization'] = `Bearer ${this.apiKey}`;
136
+ }
137
+
138
+ const response = await fetch(url, {
139
+ method,
140
+ headers,
141
+ body: body !== undefined ? JSON.stringify(body) : undefined,
142
+ });
143
+
144
+ if (!response.ok) {
145
+ const responseBody = await response.text();
146
+
147
+ // Try to parse error response
148
+ let errorData: APIErrorResponse | null = null;
149
+ try {
150
+ errorData = JSON.parse(responseBody) as APIErrorResponse;
151
+ } catch {
152
+ // Not JSON, ignore
153
+ }
154
+
155
+ if (process.env.DEBUG) {
156
+ // Sanitize headers to avoid leaking API keys
157
+ const sanitizedHeaders = { ...headers };
158
+ for (const key in sanitizedHeaders) {
159
+ if (key.toLowerCase() === 'authorization') {
160
+ sanitizedHeaders[key] = 'REDACTED';
161
+ }
162
+ }
163
+
164
+ console.error('API Error Details:');
165
+ console.error(' URL:', url);
166
+ console.error(' Method:', method);
167
+ console.error(' Status:', response.status, response.statusText);
168
+ console.error(' Headers:', JSON.stringify(sanitizedHeaders, null, 2));
169
+ console.error(' Response:', responseBody);
170
+ }
171
+
172
+ // Check for UPGRADE_REQUIRED error
173
+ if (errorData?.code === 'UPGRADE_REQUIRED') {
174
+ // Skip version check if configured
175
+ if (this.config?.skipVersionCheck) {
176
+ if (process.env.DEBUG) {
177
+ console.error('[DEBUG] Skipping version check (configured to skip)');
178
+ }
179
+ // Request is still rejected, but throw UpgradeRequiredError so callers
180
+ // can detect it and handle UI behavior (e.g., suppress banner) based on skip flag
181
+ throw new UpgradeRequiredError(
182
+ errorData.message ||
183
+ 'Version check skipped, but request failed. Try upgrading the client.'
184
+ );
185
+ }
186
+
187
+ throw new UpgradeRequiredError(
188
+ errorData.message || 'Please upgrade to the latest version'
189
+ );
190
+ }
191
+
192
+ // Throw with message from API if available
193
+ if (errorData?.message) {
194
+ throw new APIError(errorData.message, response.status, errorData.code);
195
+ }
196
+
197
+ throw new APIError(
198
+ `API error: ${response.status} ${response.statusText}`,
199
+ response.status
200
+ );
201
+ }
202
+
203
+ // Successful response; handle empty bodies (e.g., 204 No Content)
204
+ if (response.status === 204 || response.headers.get('content-length') === '0') {
205
+ return new Response(null, { status: 204 });
206
+ }
207
+
208
+ return response;
209
+ }
210
+ }
211
+
212
+ export function getAPIBaseURL(overrides?: { api_url?: string }): string {
213
+ if (process.env.AGENTUITY_API_URL) {
214
+ return process.env.AGENTUITY_API_URL;
215
+ }
216
+
217
+ if (overrides?.api_url) {
218
+ return overrides.api_url;
219
+ }
220
+
221
+ return 'https://api.agentuity.com';
222
+ }
223
+
224
+ export function getAppBaseURL(overrides?: { app_url?: string }): string {
225
+ if (process.env.AGENTUITY_APP_URL) {
226
+ return process.env.AGENTUITY_APP_URL;
227
+ }
228
+
229
+ if (overrides?.app_url) {
230
+ return overrides.app_url;
231
+ }
232
+
233
+ return 'https://app.agentuity.com';
234
+ }
235
+
236
+ export const APIResponseSchema = <T extends z.ZodType>(dataSchema: T) =>
237
+ z.object({
238
+ success: z.boolean(),
239
+ message: z.string().optional().describe('the error message if success=false'),
240
+ data: dataSchema.optional(),
241
+ });
@@ -0,0 +1,3 @@
1
+ export * from './api';
2
+ export * from './org';
3
+ export * from './project';
@@ -0,0 +1 @@
1
+ export * from './list';
@@ -0,0 +1,28 @@
1
+ import { z } from 'zod';
2
+ import { APIResponseSchema, APIClient } from '../api';
3
+
4
+ const ListOrganizationsResponseSchema = APIResponseSchema(
5
+ z.array(
6
+ z.object({
7
+ id: z.string().describe('the unique id for the organization'),
8
+ name: z.string().describe('the name of the organization'),
9
+ })
10
+ )
11
+ );
12
+
13
+ export type ListOrganizationsResponse = z.infer<typeof ListOrganizationsResponseSchema>;
14
+ export type OrganizationList = NonNullable<ListOrganizationsResponse['data']>;
15
+
16
+ /**
17
+ * List all organizations
18
+ *
19
+ * @param client
20
+ * @returns
21
+ */
22
+ export async function listOrganizations(client: APIClient): Promise<ListOrganizationsResponse> {
23
+ return client.request<ListOrganizationsResponse>(
24
+ 'GET',
25
+ '/cli/organization',
26
+ ListOrganizationsResponseSchema
27
+ );
28
+ }
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+ import { APIClient, APIResponseSchema } from '../api';
3
+
4
+ const CreateProjectRequestSchema = z.object({
5
+ name: z.string().max(255).min(1).describe('the name of the new project'),
6
+ organization_id: z
7
+ .string()
8
+ .max(255)
9
+ .min(1)
10
+ .describe('the organization id to create the project in'),
11
+ provider: z.string().max(255).min(1),
12
+ });
13
+
14
+ const CreateProjectResponseSchema = APIResponseSchema(
15
+ z.object({
16
+ id: z.string().describe('the unique id for the project'),
17
+ api_key: z.string().describe('the SDK api key for the project'),
18
+ projectKey: z.string().describe('the Project api key for the project'),
19
+ })
20
+ );
21
+
22
+ export type CreateProjectRequest = z.infer<typeof CreateProjectRequestSchema>;
23
+ export type CreateProjectResponse = z.infer<typeof CreateProjectResponseSchema>;
24
+
25
+ /**
26
+ * Create a new Project
27
+ *
28
+ * @param client
29
+ * @param body
30
+ * @returns
31
+ */
32
+ export async function createProject(
33
+ client: APIClient,
34
+ body: CreateProjectRequest
35
+ ): Promise<Omit<CreateProjectResponse, 'projectKey'>> {
36
+ return client.request<CreateProjectResponse, CreateProjectRequest>(
37
+ 'POST',
38
+ '/cli/project',
39
+ CreateProjectResponseSchema,
40
+ body,
41
+ CreateProjectRequestSchema
42
+ );
43
+ }
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod';
2
+ import { APIClient, APIResponseSchema, APIError } from '../api';
3
+
4
+ // func ProjectWithNameExists(ctx context.Context, logger logger.Logger, baseUrl string, token string, orgId string, name string) (bool, error) {
5
+ // client := util.NewAPIClient(ctx, logger, baseUrl, token)
6
+
7
+ // var resp Response[bool]
8
+ // if err := client.Do("GET", fmt.Sprintf("/cli/project/exists/%s?orgId=%s", url.PathEscape(name), url.PathEscape(orgId)), nil, &resp); err != nil {
9
+ // var apiErr *util.APIError
10
+ // if errors.As(err, &apiErr) {
11
+ // if apiErr.Status == http.StatusConflict {
12
+ // return true, nil
13
+ // }
14
+ // if apiErr.Status == http.StatusUnprocessableEntity {
15
+ // return false, apiErr
16
+ // }
17
+ // }
18
+ // return false, fmt.Errorf("error validating project name: %w", err)
19
+ // }
20
+ // return resp.Data, nil
21
+ // }
22
+
23
+ const _ProjectExistsRequestSchema = z.object({
24
+ name: z.string().max(255).min(1).describe('the name of the new project'),
25
+ organization_id: z
26
+ .string()
27
+ .max(255)
28
+ .min(1)
29
+ .describe('the organization id to create the project in'),
30
+ });
31
+
32
+ const ProjectExistsResponseSchema = APIResponseSchema(z.boolean());
33
+
34
+ export type ProjectExistsRequest = z.infer<typeof _ProjectExistsRequestSchema>;
35
+ export type ProjectExistsResponse = z.infer<typeof ProjectExistsResponseSchema>;
36
+
37
+ /**
38
+ * Create a new Project
39
+ *
40
+ * @param client
41
+ * @param body
42
+ * @returns
43
+ */
44
+ export async function projectExists(
45
+ client: APIClient,
46
+ body: ProjectExistsRequest
47
+ ): Promise<boolean> {
48
+ const qs = new URLSearchParams();
49
+ qs.set('orgId', body.organization_id);
50
+ try {
51
+ const resp = await client.request<ProjectExistsResponse, ProjectExistsRequest>(
52
+ 'GET',
53
+ `/cli/project/exists/${encodeURIComponent(body.name)}?${qs.toString()}`,
54
+ ProjectExistsResponseSchema
55
+ );
56
+
57
+ if (resp.data) {
58
+ return resp.data;
59
+ }
60
+
61
+ return false;
62
+ } catch (ex) {
63
+ const _ex = ex as Error;
64
+ if (_ex.name === 'APIError') {
65
+ const apiError = _ex as APIError;
66
+ switch (apiError.status) {
67
+ case 409:
68
+ return true;
69
+ case 422:
70
+ return false;
71
+ default:
72
+ }
73
+ }
74
+ throw ex;
75
+ }
76
+ }
@@ -0,0 +1,2 @@
1
+ export * from './create';
2
+ export * from './exists';
package/src/config.ts ADDED
@@ -0,0 +1,20 @@
1
+ export interface ServiceUrls {
2
+ keyvalue: string;
3
+ objectstore: string;
4
+ stream: string;
5
+ vector: string;
6
+ }
7
+
8
+ /**
9
+ * Get service URLs from environment variables with fallback defaults
10
+ */
11
+ export function getServiceUrls(): ServiceUrls {
12
+ const transportUrl = process.env.AGENTUITY_TRANSPORT_URL || 'https://agentuity.ai';
13
+
14
+ return {
15
+ keyvalue: process.env.AGENTUITY_KEYVALUE_URL || transportUrl,
16
+ objectstore: process.env.AGENTUITY_OBJECTSTORE_URL || transportUrl,
17
+ stream: process.env.AGENTUITY_STREAM_URL || 'https://streams.agentuity.cloud',
18
+ vector: process.env.AGENTUITY_VECTOR_URL || transportUrl,
19
+ };
20
+ }
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export * from './config';
2
+ export * from './server';
3
+ export * from './api';
4
+ export { z } from 'zod';
5
+ export type {
6
+ FetchAdapter,
7
+ FetchRequest,
8
+ FetchResponse,
9
+ FetchSuccessResponse,
10
+ FetchErrorResponse,
11
+ Body,
12
+ ServiceException,
13
+ } from '@agentuity/core';
14
+ export { buildUrl, toServiceException, toPayload, fromResponse } from '@agentuity/core';