@aiassist-secure/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +327 -0
- package/dist/index.d.mts +223 -0
- package/dist/index.d.ts +223 -0
- package/dist/index.js +345 -0
- package/dist/index.mjs +314 -0
- package/package.json +50 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var AiAssistError = class extends Error {
|
|
3
|
+
constructor(message, status, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "AiAssistError";
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var AuthenticationError = class extends AiAssistError {
|
|
11
|
+
constructor(message = "Invalid API key") {
|
|
12
|
+
super(message, 401, "invalid_api_key");
|
|
13
|
+
this.name = "AuthenticationError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var RateLimitError = class extends AiAssistError {
|
|
17
|
+
constructor(message = "Rate limit exceeded", retryAfter) {
|
|
18
|
+
super(message, 429, "rate_limit_exceeded");
|
|
19
|
+
this.name = "RateLimitError";
|
|
20
|
+
this.retryAfter = retryAfter;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var APIError = class extends AiAssistError {
|
|
24
|
+
constructor(message, status, code) {
|
|
25
|
+
super(message, status, code);
|
|
26
|
+
this.name = "APIError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/client.ts
|
|
31
|
+
var DEFAULT_BASE_URL = "https://api.aiassist.net";
|
|
32
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
33
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
34
|
+
var VERSION = "1.0.0";
|
|
35
|
+
function normalizeRole(role) {
|
|
36
|
+
if (role === "assistant" || role === "ai") return "ai";
|
|
37
|
+
if (role === "human") return "human";
|
|
38
|
+
if (role === "system") return "system";
|
|
39
|
+
return "user";
|
|
40
|
+
}
|
|
41
|
+
var AiAssistClient = class {
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.apiKey = options.apiKey;
|
|
44
|
+
this.baseURL = options.baseURL ?? DEFAULT_BASE_URL;
|
|
45
|
+
this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
46
|
+
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
47
|
+
this.defaultHeaders = options.defaultHeaders ?? {};
|
|
48
|
+
this.chat = new ChatAPI(this);
|
|
49
|
+
this.models = new ModelsAPI(this);
|
|
50
|
+
this.workspaces = new WorkspacesAPI(this);
|
|
51
|
+
}
|
|
52
|
+
getHeaders() {
|
|
53
|
+
return {
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
"X-API-Key": this.apiKey,
|
|
56
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
57
|
+
"User-Agent": `aiassist-js/${VERSION}`,
|
|
58
|
+
...this.defaultHeaders
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async request(method, path, options) {
|
|
62
|
+
const url = `${this.baseURL}${path}`;
|
|
63
|
+
const headers = { ...this.getHeaders(), ...options?.headers };
|
|
64
|
+
let lastError = null;
|
|
65
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
66
|
+
try {
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
69
|
+
const response = await fetch(url, {
|
|
70
|
+
method,
|
|
71
|
+
headers,
|
|
72
|
+
body: options?.body ? JSON.stringify(options.body) : void 0,
|
|
73
|
+
signal: controller.signal
|
|
74
|
+
});
|
|
75
|
+
clearTimeout(timeoutId);
|
|
76
|
+
if (response.status === 401) {
|
|
77
|
+
throw new AuthenticationError();
|
|
78
|
+
}
|
|
79
|
+
if (response.status === 429) {
|
|
80
|
+
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "5", 10);
|
|
81
|
+
if (attempt < this.maxRetries - 1) {
|
|
82
|
+
await this.sleep(retryAfter * 1e3);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
throw new RateLimitError("Rate limit exceeded", retryAfter);
|
|
86
|
+
}
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
const errorData = await response.json().catch(() => ({}));
|
|
89
|
+
throw new APIError(
|
|
90
|
+
errorData.detail ?? `HTTP ${response.status}`,
|
|
91
|
+
response.status,
|
|
92
|
+
errorData.code
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return await response.json();
|
|
96
|
+
} catch (error) {
|
|
97
|
+
lastError = error;
|
|
98
|
+
if (error instanceof AiAssistError) {
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
if (attempt < this.maxRetries - 1) {
|
|
102
|
+
await this.sleep(Math.pow(2, attempt) * 1e3);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
throw new APIError(`Request failed: ${lastError?.message}`);
|
|
108
|
+
}
|
|
109
|
+
async requestSilent(method, path, options) {
|
|
110
|
+
try {
|
|
111
|
+
await this.request(method, path, options);
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async *stream(method, path, body) {
|
|
116
|
+
const url = `${this.baseURL}${path}`;
|
|
117
|
+
const headers = this.getHeaders();
|
|
118
|
+
const response = await fetch(url, {
|
|
119
|
+
method,
|
|
120
|
+
headers,
|
|
121
|
+
body: JSON.stringify(body)
|
|
122
|
+
});
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
if (response.status === 401) throw new AuthenticationError();
|
|
125
|
+
if (response.status === 429) throw new RateLimitError();
|
|
126
|
+
const errorData = await response.json().catch(() => ({}));
|
|
127
|
+
throw new APIError(errorData.detail ?? `HTTP ${response.status}`, response.status);
|
|
128
|
+
}
|
|
129
|
+
const reader = response.body?.getReader();
|
|
130
|
+
if (!reader) throw new APIError("Response body is not readable");
|
|
131
|
+
const decoder = new TextDecoder();
|
|
132
|
+
let buffer = "";
|
|
133
|
+
while (true) {
|
|
134
|
+
const { done, value } = await reader.read();
|
|
135
|
+
if (done) break;
|
|
136
|
+
buffer += decoder.decode(value, { stream: true });
|
|
137
|
+
const lines = buffer.split("\n");
|
|
138
|
+
buffer = lines.pop() ?? "";
|
|
139
|
+
for (const line of lines) {
|
|
140
|
+
if (line.startsWith("data: ")) {
|
|
141
|
+
const data = line.slice(6);
|
|
142
|
+
if (data === "[DONE]") return;
|
|
143
|
+
try {
|
|
144
|
+
yield JSON.parse(data);
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
sleep(ms) {
|
|
152
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var ChatAPI = class {
|
|
156
|
+
constructor(client) {
|
|
157
|
+
this.completions = new ChatCompletionsAPI(client);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
var ChatCompletionsAPI = class {
|
|
161
|
+
constructor(client) {
|
|
162
|
+
this.client = client;
|
|
163
|
+
}
|
|
164
|
+
async create(params) {
|
|
165
|
+
if (params.stream) {
|
|
166
|
+
return this.client.stream("POST", "/v1/chat/completions", params);
|
|
167
|
+
}
|
|
168
|
+
return this.client.request("POST", "/v1/chat/completions", {
|
|
169
|
+
body: params
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
var ModelsAPI = class {
|
|
174
|
+
constructor(client) {
|
|
175
|
+
this.client = client;
|
|
176
|
+
}
|
|
177
|
+
async list() {
|
|
178
|
+
const response = await this.client.request("GET", "/v1/models");
|
|
179
|
+
return response.data;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var WorkspacesAPI = class {
|
|
183
|
+
constructor(client) {
|
|
184
|
+
this.client = client;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Create a new workspace with optional system prompt and context.
|
|
188
|
+
*
|
|
189
|
+
* @param params - Workspace creation options
|
|
190
|
+
* @returns Workspace and initial messages
|
|
191
|
+
*/
|
|
192
|
+
async create(params) {
|
|
193
|
+
const body = {};
|
|
194
|
+
if (params?.initial_message) body.initial_message = params.initial_message;
|
|
195
|
+
if (params?.client_id) body.client_id = params.client_id;
|
|
196
|
+
if (params?.system_prompt) body.system_prompt = params.system_prompt;
|
|
197
|
+
if (params?.context) body.context = params.context;
|
|
198
|
+
if (params?.metadata) body.metadata = params.metadata;
|
|
199
|
+
const data = await this.client.request("POST", "/api/workspaces", { body });
|
|
200
|
+
return {
|
|
201
|
+
workspace: {
|
|
202
|
+
id: data.workspace.id,
|
|
203
|
+
mode: data.workspace.mode,
|
|
204
|
+
status: data.workspace.status || "active"
|
|
205
|
+
},
|
|
206
|
+
messages: (data.messages || []).map((m) => ({
|
|
207
|
+
id: m.id,
|
|
208
|
+
role: normalizeRole(m.role),
|
|
209
|
+
content: m.content,
|
|
210
|
+
created_at: m.created_at
|
|
211
|
+
}))
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Get workspace by ID.
|
|
216
|
+
*/
|
|
217
|
+
async get(workspaceId) {
|
|
218
|
+
const data = await this.client.request("GET", `/api/workspaces/${workspaceId}`);
|
|
219
|
+
const ws = data.workspace || data;
|
|
220
|
+
return {
|
|
221
|
+
id: ws.id,
|
|
222
|
+
mode: ws.mode || "ai",
|
|
223
|
+
status: ws.status || "active"
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get workspace by client ID.
|
|
228
|
+
* Returns null if not found.
|
|
229
|
+
*/
|
|
230
|
+
async getByClientId(clientId) {
|
|
231
|
+
try {
|
|
232
|
+
const data = await this.client.request("GET", `/api/workspaces/by-client/${clientId}`);
|
|
233
|
+
return {
|
|
234
|
+
workspace: data.workspace ? {
|
|
235
|
+
id: data.workspace.id,
|
|
236
|
+
mode: data.workspace.mode,
|
|
237
|
+
status: "active"
|
|
238
|
+
} : null,
|
|
239
|
+
messages: (data.messages || []).map((m) => ({
|
|
240
|
+
id: m.id,
|
|
241
|
+
role: normalizeRole(m.role),
|
|
242
|
+
content: m.content,
|
|
243
|
+
created_at: m.created_at
|
|
244
|
+
})),
|
|
245
|
+
exists: data.exists
|
|
246
|
+
};
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (error instanceof APIError && error.status === 404) {
|
|
249
|
+
return { workspace: null, messages: [], exists: false };
|
|
250
|
+
}
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Send a message to a workspace.
|
|
256
|
+
* Returns user message, AI/human responses, mode, and shadow mode approval status.
|
|
257
|
+
*/
|
|
258
|
+
async sendMessage(workspaceId, content) {
|
|
259
|
+
const data = await this.client.request("POST", `/api/workspaces/${workspaceId}/messages`, {
|
|
260
|
+
body: { content }
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
user_message: {
|
|
264
|
+
id: data.user_message.id,
|
|
265
|
+
role: "user",
|
|
266
|
+
content: data.user_message.content,
|
|
267
|
+
created_at: data.user_message.created_at
|
|
268
|
+
},
|
|
269
|
+
responses: data.responses.map((r) => ({
|
|
270
|
+
id: r.id,
|
|
271
|
+
role: normalizeRole(r.role),
|
|
272
|
+
content: r.content,
|
|
273
|
+
created_at: r.created_at
|
|
274
|
+
})),
|
|
275
|
+
mode: data.mode,
|
|
276
|
+
pending_approval: data.pending_approval
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get all messages in a workspace.
|
|
281
|
+
*/
|
|
282
|
+
async getMessages(workspaceId) {
|
|
283
|
+
const data = await this.client.request("GET", `/api/workspaces/${workspaceId}/messages`);
|
|
284
|
+
return (data.messages || []).map((m) => ({
|
|
285
|
+
id: m.id,
|
|
286
|
+
role: normalizeRole(m.role),
|
|
287
|
+
content: m.content,
|
|
288
|
+
created_at: m.created_at
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Send typing preview to workspace (for real-time typing indicators).
|
|
293
|
+
* Fails silently - non-critical operation.
|
|
294
|
+
*/
|
|
295
|
+
async sendTypingPreview(workspaceId, text) {
|
|
296
|
+
await this.client.requestSilent("POST", `/api/workspaces/${workspaceId}/typing`, {
|
|
297
|
+
body: { text }
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* End a conversation/workspace.
|
|
302
|
+
* Fails silently - non-critical operation.
|
|
303
|
+
*/
|
|
304
|
+
async endConversation(workspaceId) {
|
|
305
|
+
await this.client.requestSilent("POST", `/api/workspaces/${workspaceId}/end`, {});
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
export {
|
|
309
|
+
APIError,
|
|
310
|
+
AiAssistClient,
|
|
311
|
+
AiAssistError,
|
|
312
|
+
AuthenticationError,
|
|
313
|
+
RateLimitError
|
|
314
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aiassist-secure/core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AiAssist Secure TypeScript SDK - Framework-agnostic API client",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
20
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
21
|
+
"typecheck": "tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"tsup": "^8.0.0",
|
|
25
|
+
"typescript": "^5.0.0"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"aiassist",
|
|
29
|
+
"aiassist-secure",
|
|
30
|
+
"ai",
|
|
31
|
+
"llm",
|
|
32
|
+
"openai",
|
|
33
|
+
"groq",
|
|
34
|
+
"chat",
|
|
35
|
+
"api",
|
|
36
|
+
"sdk"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/aiassistsecure/aiassist-js"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://aiassist.net",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/aiassistsecure/aiassist-js/issues"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|