@baobox/sdk 0.1.2 → 0.3.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/dist/index.js CHANGED
@@ -1,60 +1,121 @@
1
- // @baobox/sdk — thin HTTP client for the BaoBox runtime.
2
- // No business logic here — everything smart lives server-side. This file
3
- // is ~300 lines on purpose; if it grows past ~500, something that belongs
4
- // in the runtime has leaked out.
5
1
  import { BaoBoxError } from "./errors.js";
6
2
  export { BaoBoxError } from "./errors.js";
7
3
  export class BaoBoxClient {
8
4
  endpoint;
9
5
  apiKey;
6
+ adminSecret;
10
7
  fetch;
11
8
  timeoutMs;
12
- // `admin` and `events` are pseudo-namespaces — just method bundles so
13
- // call sites read as `bb.admin.skills.upsert(...)` rather than a flat
14
- // `bb.upsertSkill(...)`. Matches Tech Arch §4.2.
9
+ health;
15
10
  admin;
16
11
  sessions;
12
+ skills;
13
+ tools;
14
+ eval;
17
15
  events;
18
16
  constructor(opts) {
19
17
  if (!opts.endpoint)
20
18
  throw new Error("BaoBoxClient: endpoint required");
21
- if (!opts.apiKey)
22
- throw new Error("BaoBoxClient: apiKey required");
19
+ if (!opts.apiKey && !opts.adminSecret) {
20
+ throw new Error("BaoBoxClient: apiKey or adminSecret required");
21
+ }
23
22
  this.endpoint = opts.endpoint.replace(/\/+$/, "");
24
- this.apiKey = opts.apiKey;
25
- // Bind the fetch to globalThis. Cloudflare Workers' native fetch is
26
- // a runtime intrinsic that must be invoked with this === globalThis;
27
- // stashing it on the client instance and calling it via this.fetch(...)
28
- // throws "Illegal invocation" because Workers sees the client as
29
- // the receiver. Binding here keeps call sites clean and covers the
30
- // case where a caller injects a method-bound custom fetch too.
23
+ this.apiKey = opts.apiKey ?? null;
24
+ this.adminSecret = opts.adminSecret ?? null;
31
25
  const rawFetch = opts.fetch ?? globalThis.fetch;
32
26
  this.fetch = rawFetch.bind(globalThis);
33
27
  this.timeoutMs = opts.timeoutMs ?? 30_000;
28
+ this.health = {
29
+ get: () => this.getHealth(),
30
+ };
34
31
  this.admin = {
32
+ keys: {
33
+ list: () => this.listApiKeys(),
34
+ create: (req) => this.createApiKey(req),
35
+ delete: (id) => this.deleteApiKey(id),
36
+ },
37
+ stats: {
38
+ get: (req) => this.getAdminStats(req),
39
+ },
40
+ logs: {
41
+ list: (req) => this.listAdminLogs(req),
42
+ },
43
+ tasks: {
44
+ list: () => this.listScheduledTasks(),
45
+ create: (req) => this.createScheduledTask(req),
46
+ update: (id, req) => this.updateScheduledTask(id, req),
47
+ delete: (id) => this.deleteScheduledTask(id),
48
+ },
35
49
  skills: {
36
- upsert: (req) => this.upsertSkill(req),
50
+ upsert: (req) => this.saveSkill(req),
37
51
  },
38
52
  tools: {
39
- upsert: (req) => this.upsertTool(req),
53
+ upsert: (req) => this.createTool(req),
40
54
  },
41
55
  };
42
56
  this.sessions = {
43
57
  create: (req) => this.createSession(req),
58
+ get: (id) => this.getSession(id),
44
59
  messages: (id) => this.listMessages(id),
60
+ timeline: (id) => this.getSessionTimeline(id),
61
+ delete: (id) => this.deleteSession(id),
62
+ };
63
+ this.skills = {
64
+ list: () => this.listSkills(),
65
+ get: (id) => this.getSkill(id),
66
+ create: (req) => this.createSkill(req),
67
+ update: (id, req) => this.updateSkill(id, req),
68
+ save: (req) => this.saveSkill(req),
69
+ import: (req) => this.importSkill(req),
70
+ delete: (id) => this.deleteSkill(id),
71
+ files: {
72
+ list: (id) => this.listSkillFiles(id),
73
+ get: (id, path) => this.getSkillFile(id, path),
74
+ set: (id, path, req) => this.setSkillFile(id, path, req),
75
+ delete: (id, path) => this.deleteSkillFile(id, path),
76
+ },
77
+ };
78
+ this.tools = {
79
+ list: () => this.listTools(),
80
+ get: (id) => this.getTool(id),
81
+ create: (req) => this.createTool(req),
82
+ delete: (id) => this.deleteTool(id),
83
+ skills: {
84
+ list: (skillId) => this.listSkillTools(skillId),
85
+ attach: (skillId, toolId) => this.attachToolToSkill(skillId, toolId),
86
+ detach: (skillId, toolId) => this.detachToolFromSkill(skillId, toolId),
87
+ },
88
+ secrets: {
89
+ list: (skillId) => this.listSkillSecrets(skillId),
90
+ set: (skillId, req) => this.setSkillSecret(skillId, req),
91
+ delete: (skillId, key) => this.deleteSkillSecret(skillId, key),
92
+ },
93
+ };
94
+ this.eval = {
95
+ tests: {
96
+ list: (skillId) => this.listEvalTests(skillId),
97
+ create: (skillId, req) => this.createEvalTest(skillId, req),
98
+ delete: (skillId, testId) => this.deleteEvalTest(skillId, testId),
99
+ },
100
+ run: (req) => this.runEval(req),
101
+ runs: {
102
+ get: (runId) => this.getEvalRun(runId),
103
+ },
104
+ stats: (req) => this.getEvalStats(req),
105
+ failures: (req) => this.listEvalFailures(req),
106
+ compare: (req) => this.compareEvalVersions(req),
45
107
  };
46
108
  this.events = {
47
109
  list: (req) => this.listEvents(req),
48
110
  };
49
111
  }
50
- // --- Chat (core) ---
51
112
  async chat(req) {
52
- const body = await this.request("POST", "/api/v1/chat", {
113
+ const body = await this.requestApi("POST", "/api/v1/chat", compactObject({
53
114
  skill_id: req.skillId,
54
115
  message: req.message,
55
116
  session_id: req.sessionId,
56
117
  metadata: req.metadata,
57
- });
118
+ }));
58
119
  return {
59
120
  response: body.data.response,
60
121
  usage: {
@@ -65,97 +126,324 @@ export class BaoBoxClient {
65
126
  meta: body.meta,
66
127
  };
67
128
  }
68
- // --- Sessions ---
69
- async createSession(req) {
70
- const body = await this.request("POST", "/api/v1/sessions", { skill_id: req.skillId });
129
+ // Single-turn, stateless skill execution. Caller passes the full
130
+ // history every call; BaoBox writes events under the returned runId
131
+ // and tags the call_logs row with run_type='workflow' + the tenant
132
+ // correlators. See `WorkflowRequest`/`WorkflowResponse` for the shape.
133
+ async workflow(req) {
134
+ const body = await this.requestApi("POST", "/api/v1/workflow", compactObject({
135
+ skill: req.skill,
136
+ client_id: req.clientId,
137
+ request_id: req.requestId,
138
+ input: req.input,
139
+ history: req.history,
140
+ }));
71
141
  return {
72
- id: body.data.id,
73
- skillId: body.data.skill_id,
74
- tenantId: body.data.tenant_id,
75
- createdAt: body.data.created_at,
76
- updatedAt: body.data.updated_at,
142
+ response: body.data.response,
143
+ runId: body.data.run_id,
144
+ usage: {
145
+ inputTokens: body.data.usage.input_tokens,
146
+ outputTokens: body.data.usage.output_tokens,
147
+ },
148
+ meta: body.meta,
149
+ };
150
+ }
151
+ async getHealth() {
152
+ const body = await this.requestNoAuth("GET", "/api/v1/health");
153
+ return {
154
+ status: body.data.status,
155
+ version: body.data.version,
156
+ meta: body.meta,
77
157
  };
78
158
  }
159
+ async createSession(req = {}) {
160
+ const body = await this.requestAdmin("POST", "/api/v1/sessions", compactObject({ skill_id: req.skillId }));
161
+ return mapSession(body.data);
162
+ }
163
+ async getSession(sessionId) {
164
+ const body = await this.requestAdmin("GET", `/api/v1/sessions/${encodeURIComponent(sessionId)}`);
165
+ return mapSession(body.data);
166
+ }
79
167
  async listMessages(sessionId) {
80
- const body = await this.request("GET", `/api/v1/sessions/${encodeURIComponent(sessionId)}/messages`);
81
- return body.data.map((m) => ({
82
- id: m.id,
83
- sessionId: m.session_id,
84
- role: m.role,
85
- content: m.content,
86
- tokenCount: m.token_count,
87
- createdAt: m.created_at,
88
- }));
168
+ const body = await this.requestAdmin("GET", `/api/v1/sessions/${encodeURIComponent(sessionId)}/messages`);
169
+ return body.data.map(mapSessionMessage);
89
170
  }
90
- // --- Admin: skills ---
91
- async upsertSkill(req) {
92
- const body = await this.request("POST", "/api/v1/admin/skills", {
93
- id: req.id,
94
- name: req.name,
95
- description: req.description,
96
- system_prompt: req.systemPrompt,
97
- model: req.model,
98
- temperature: req.temperature,
99
- max_tokens: req.maxTokens,
100
- tools: req.tools,
101
- });
171
+ async getSessionTimeline(sessionId) {
172
+ const body = await this.requestAdmin("GET", `/api/v1/sessions/${encodeURIComponent(sessionId)}/timeline`);
102
173
  return {
103
- id: body.data.id,
104
- name: body.data.name,
105
- description: body.data.description,
106
- systemPrompt: body.data.system_prompt,
107
- model: body.data.model,
108
- temperature: body.data.temperature,
109
- maxTokens: body.data.max_tokens,
110
- tenantId: body.data.tenant_id,
111
- createdAt: body.data.created_at,
112
- updatedAt: body.data.updated_at,
174
+ sessionId: body.data.session_id,
175
+ events: body.data.events.map(mapEvent),
113
176
  };
114
177
  }
115
- // --- Admin: tools ---
116
- async upsertTool(req) {
117
- const body = await this.request("POST", "/api/v1/admin/tools", {
178
+ async deleteSession(sessionId) {
179
+ const body = await this.requestAdmin("DELETE", `/api/v1/sessions/${encodeURIComponent(sessionId)}`);
180
+ return body.data;
181
+ }
182
+ async listSkills() {
183
+ const body = await this.requestAdmin("GET", "/api/v1/skills");
184
+ return body.data.map(mapSkill);
185
+ }
186
+ async getSkill(skillId) {
187
+ const body = await this.requestAdmin("GET", `/api/v1/skills/${encodeURIComponent(skillId)}`);
188
+ return mapSkillWithFiles(body.data);
189
+ }
190
+ async createSkill(req) {
191
+ const body = await this.requestAdmin("POST", "/api/v1/skills", buildSkillWriteBody(req));
192
+ const skill = mapSkill(body.data);
193
+ if (req.tools)
194
+ await this.syncSkillTools(skill.id, req.tools);
195
+ return skill;
196
+ }
197
+ async updateSkill(skillId, req) {
198
+ const writeBody = buildSkillWriteBody(req);
199
+ const hasFieldUpdates = Object.keys(writeBody).length > 0;
200
+ const skill = hasFieldUpdates
201
+ ? mapSkill((await this.requestAdmin("PUT", `/api/v1/skills/${encodeURIComponent(skillId)}`, writeBody)).data)
202
+ : skillWithoutFiles(await this.getSkill(skillId));
203
+ if (req.tools)
204
+ await this.syncSkillTools(skillId, req.tools);
205
+ return skill;
206
+ }
207
+ async saveSkill(req) {
208
+ return req.id ? this.updateSkill(req.id, req) : this.createSkill(req);
209
+ }
210
+ async importSkill(req) {
211
+ const body = await this.requestAdmin("POST", "/api/v1/skills/import", {
212
+ url: req.url,
213
+ name: req.name,
214
+ });
215
+ return mapSkill(body.data);
216
+ }
217
+ async deleteSkill(skillId) {
218
+ const body = await this.requestAdmin("DELETE", `/api/v1/skills/${encodeURIComponent(skillId)}`);
219
+ return body.data;
220
+ }
221
+ async listSkillFiles(skillId) {
222
+ const body = await this.requestAdmin("GET", `/api/v1/skills/${encodeURIComponent(skillId)}/files`);
223
+ return body.data.map(mapSkillFileSummary);
224
+ }
225
+ async getSkillFile(skillId, path) {
226
+ const body = await this.requestAdmin("GET", `/api/v1/skills/${encodeURIComponent(skillId)}/files/${encodePath(path)}`);
227
+ return mapSkillFile(body.data);
228
+ }
229
+ async setSkillFile(skillId, path, req) {
230
+ const body = await this.requestAdmin("PUT", `/api/v1/skills/${encodeURIComponent(skillId)}/files/${encodePath(path)}`, { content: req.content });
231
+ return body.data;
232
+ }
233
+ async deleteSkillFile(skillId, path) {
234
+ const body = await this.requestAdmin("DELETE", `/api/v1/skills/${encodeURIComponent(skillId)}/files/${encodePath(path)}`);
235
+ return body.data;
236
+ }
237
+ async listTools() {
238
+ const body = await this.requestAdmin("GET", "/api/v1/tools");
239
+ return body.data.map(mapTool);
240
+ }
241
+ async getTool(toolId) {
242
+ const body = await this.requestAdmin("GET", `/api/v1/tools/${encodeURIComponent(toolId)}`);
243
+ return mapTool(body.data);
244
+ }
245
+ async createTool(req) {
246
+ const body = await this.requestAdmin("POST", "/api/v1/tools", {
118
247
  name: req.name,
119
248
  description: req.description,
120
249
  input_schema: req.inputSchema,
121
250
  handler_type: req.handlerType,
122
251
  handler_config: req.handlerConfig,
123
252
  });
253
+ return mapTool(body.data);
254
+ }
255
+ async deleteTool(toolId) {
256
+ const body = await this.requestAdmin("DELETE", `/api/v1/tools/${encodeURIComponent(toolId)}`);
257
+ return body.data;
258
+ }
259
+ async listSkillTools(skillId) {
260
+ const body = await this.requestAdmin("GET", `/api/v1/tools/skills/${encodeURIComponent(skillId)}/tools`);
261
+ return body.data.map(mapTool);
262
+ }
263
+ async attachToolToSkill(skillId, toolId) {
264
+ const body = await this.requestAdmin("POST", `/api/v1/tools/skills/${encodeURIComponent(skillId)}/tools/${encodeURIComponent(toolId)}`);
265
+ return body.data;
266
+ }
267
+ async detachToolFromSkill(skillId, toolId) {
268
+ const body = await this.requestAdmin("DELETE", `/api/v1/tools/skills/${encodeURIComponent(skillId)}/tools/${encodeURIComponent(toolId)}`);
269
+ return body.data;
270
+ }
271
+ async listSkillSecrets(skillId) {
272
+ const body = await this.requestAdmin("GET", `/api/v1/tools/skills/${encodeURIComponent(skillId)}/secrets`);
273
+ return body.data.map(mapSkillSecretSummary);
274
+ }
275
+ async setSkillSecret(skillId, req) {
276
+ const body = await this.requestAdmin("PUT", `/api/v1/tools/skills/${encodeURIComponent(skillId)}/secrets`, { key: req.key, value: req.value });
277
+ return body.data;
278
+ }
279
+ async deleteSkillSecret(skillId, key) {
280
+ const body = await this.requestAdmin("DELETE", `/api/v1/tools/skills/${encodeURIComponent(skillId)}/secrets/${encodeURIComponent(key)}`);
281
+ return body.data;
282
+ }
283
+ async listApiKeys() {
284
+ const body = await this.requestAdmin("GET", "/api/v1/admin/keys");
285
+ return body.data.map(mapApiKey);
286
+ }
287
+ async createApiKey(req) {
288
+ const body = await this.requestAdmin("POST", "/api/v1/admin/keys", compactObject({
289
+ name: req.name,
290
+ permissions: req.permissions,
291
+ rate_limit: req.rateLimit,
292
+ expires_at: req.expiresAt,
293
+ }));
124
294
  return {
125
295
  id: body.data.id,
296
+ key: body.data.key,
126
297
  name: body.data.name,
127
- description: body.data.description,
128
- inputSchema: body.data.input_schema,
129
- handlerType: body.data.handler_type,
130
- handlerConfig: body.data.handler_config,
131
- createdAt: body.data.created_at,
298
+ tenantId: body.data.tenant_id,
132
299
  };
133
300
  }
134
- // --- Events ---
135
- async listEvents(req) {
136
- const qs = new URLSearchParams({ session_id: req.sessionId });
137
- if (req.limit !== undefined)
138
- qs.set("limit", String(req.limit));
139
- if (req.after !== undefined)
140
- qs.set("after", req.after);
141
- const body = await this.request("GET", `/api/v1/events?${qs.toString()}`);
142
- return body.data.map((e) => ({
143
- id: e.id,
144
- sessionId: e.session_id,
145
- requestId: e.request_id,
146
- eventType: e.event_type,
147
- content: e.content,
148
- metadata: safeParseJson(e.metadata),
149
- tokenCount: e.token_count,
150
- latencyMs: e.latency_ms,
151
- parentEventId: e.parent_event_id,
152
- createdAt: e.created_at,
301
+ async deleteApiKey(id) {
302
+ const body = await this.requestAdmin("DELETE", `/api/v1/admin/keys/${encodeURIComponent(id)}`);
303
+ return body.data;
304
+ }
305
+ async getAdminStats(req) {
306
+ const body = await this.requestAdmin("GET", appendQuery("/api/v1/admin/stats", { since: req?.since }));
307
+ return body.data;
308
+ }
309
+ async listAdminLogs(req) {
310
+ const body = await this.requestAdmin("GET", appendQuery("/api/v1/admin/logs", {
311
+ limit: req?.limit !== undefined ? String(req.limit) : undefined,
312
+ }));
313
+ return body.data;
314
+ }
315
+ async listScheduledTasks() {
316
+ const body = await this.requestAdmin("GET", "/api/v1/admin/tasks");
317
+ return body.data.map(mapScheduledTask);
318
+ }
319
+ async createScheduledTask(req) {
320
+ const body = await this.requestAdmin("POST", "/api/v1/admin/tasks", compactObject({
321
+ name: req.name,
322
+ skill_id: req.skillId,
323
+ prompt: req.prompt,
324
+ telegram_chat_id: req.telegramChatId,
325
+ schedule: req.schedule,
326
+ }));
327
+ return body.data ? mapScheduledTask(body.data) : null;
328
+ }
329
+ async updateScheduledTask(id, req) {
330
+ const body = await this.requestAdmin("PATCH", `/api/v1/admin/tasks/${encodeURIComponent(id)}`, compactObject({
331
+ enabled: req.enabled,
332
+ schedule: req.schedule,
333
+ prompt: req.prompt,
153
334
  }));
335
+ return body.data ? mapScheduledTask(body.data) : null;
336
+ }
337
+ async deleteScheduledTask(id) {
338
+ const body = await this.requestAdmin("DELETE", `/api/v1/admin/tasks/${encodeURIComponent(id)}`);
339
+ return body.data;
340
+ }
341
+ async listEvalTests(skillId) {
342
+ const body = await this.requestAdmin("GET", `/api/v1/eval/skills/${encodeURIComponent(skillId)}/tests`);
343
+ return body.data.map(mapEvalCase);
154
344
  }
155
- // --- core request plumbing ---
156
- async request(method, path, body) {
345
+ async createEvalTest(skillId, req) {
346
+ const body = await this.requestAdmin("POST", `/api/v1/eval/skills/${encodeURIComponent(skillId)}/tests`, compactObject({
347
+ name: req.name,
348
+ input: req.input,
349
+ expected_behavior: req.expectedBehavior,
350
+ dimensions: req.dimensions,
351
+ passing_threshold: req.passingThreshold,
352
+ }));
353
+ return mapEvalCase(body.data);
354
+ }
355
+ async deleteEvalTest(skillId, testId) {
356
+ const body = await this.requestAdmin("DELETE", `/api/v1/eval/skills/${encodeURIComponent(skillId)}/tests/${encodeURIComponent(testId)}`);
357
+ return body.data;
358
+ }
359
+ async runEval(req) {
360
+ const body = await this.requestAdmin("POST", "/api/v1/eval/run", compactObject({
361
+ skill_id: req.skillId,
362
+ test_case_ids: req.testCaseIds,
363
+ prompt_version: req.promptVersion,
364
+ }));
365
+ return mapEvalRunExecution(body.data);
366
+ }
367
+ async getEvalRun(runId) {
368
+ const body = await this.requestAdmin("GET", `/api/v1/eval/runs/${encodeURIComponent(runId)}`);
369
+ return {
370
+ ...mapEvalRun(body.data),
371
+ results: body.data.results.map(mapEvalRunResult),
372
+ };
373
+ }
374
+ async getEvalStats(req) {
375
+ const body = await this.requestAdmin("GET", appendQuery("/api/v1/eval/stats", {
376
+ skill_id: req?.skillId,
377
+ since: req?.since,
378
+ }));
379
+ return {
380
+ skillId: body.data.skill_id,
381
+ period: body.data.period,
382
+ summary: body.data.summary
383
+ ? {
384
+ total: body.data.summary.total,
385
+ avgScore: body.data.summary.avg_score,
386
+ distribution: body.data.summary.distribution,
387
+ }
388
+ : null,
389
+ trend: body.data.trend,
390
+ };
391
+ }
392
+ async listEvalFailures(req) {
393
+ const body = await this.requestAdmin("GET", appendQuery("/api/v1/eval/failures", {
394
+ skill_id: req?.skillId,
395
+ threshold: req?.threshold !== undefined ? String(req.threshold) : undefined,
396
+ limit: req?.limit !== undefined ? String(req.limit) : undefined,
397
+ }));
398
+ return body.data;
399
+ }
400
+ async compareEvalVersions(req) {
401
+ const body = await this.requestAdmin("GET", appendQuery("/api/v1/eval/compare", {
402
+ skill_id: req.skillId,
403
+ a: req.a,
404
+ b: req.b,
405
+ }));
406
+ return {
407
+ skillId: body.data.skill_id,
408
+ versionA: body.data.version_a,
409
+ versionB: body.data.version_b,
410
+ };
411
+ }
412
+ async listEvents(req) {
413
+ const timeline = await this.getSessionTimeline(req.sessionId);
414
+ return timeline.events;
415
+ }
416
+ async syncSkillTools(skillId, desiredToolIds) {
417
+ const desired = new Set(desiredToolIds);
418
+ const current = await this.listSkillTools(skillId);
419
+ const currentIds = new Set(current.map((tool) => tool.id));
420
+ for (const toolId of currentIds) {
421
+ if (!desired.has(toolId)) {
422
+ await this.detachToolFromSkill(skillId, toolId);
423
+ }
424
+ }
425
+ for (const toolId of desired) {
426
+ if (!currentIds.has(toolId)) {
427
+ await this.attachToolToSkill(skillId, toolId);
428
+ }
429
+ }
430
+ }
431
+ async requestNoAuth(method, path, body) {
432
+ return this.request(method, path, "none", body);
433
+ }
434
+ async requestApi(method, path, body) {
435
+ return this.request(method, path, "apiKey", body);
436
+ }
437
+ async requestAdmin(method, path, body) {
438
+ return this.request(method, path, "adminSecret", body);
439
+ }
440
+ async request(method, path, authMode, body) {
157
441
  const url = `${this.endpoint}${path}`;
158
442
  const controller = new AbortController();
443
+ const headers = {
444
+ ...this.getAuthHeaders(authMode),
445
+ ...(body !== undefined ? { "content-type": "application/json" } : {}),
446
+ };
159
447
  const timer = this.timeoutMs > 0
160
448
  ? setTimeout(() => controller.abort(), this.timeoutMs)
161
449
  : null;
@@ -163,10 +451,7 @@ export class BaoBoxClient {
163
451
  try {
164
452
  res = await this.fetch(url, {
165
453
  method,
166
- headers: {
167
- authorization: `Bearer ${this.apiKey}`,
168
- ...(body !== undefined ? { "content-type": "application/json" } : {}),
169
- },
454
+ headers,
170
455
  body: body !== undefined ? JSON.stringify(body) : undefined,
171
456
  signal: controller.signal,
172
457
  });
@@ -184,27 +469,273 @@ export class BaoBoxClient {
184
469
  const text = await res.text();
185
470
  const parsed = text.length ? safeParseJson(text) : {};
186
471
  if (!res.ok) {
187
- const errObj = parsed.error;
472
+ const errObj = parsed
473
+ .error;
188
474
  throw new BaoBoxError(res.status, errObj?.code ?? "HTTP_ERROR", errObj?.message ?? res.statusText, errObj?.request_id ?? null, parsed);
189
475
  }
190
476
  const envelope = parsed;
191
477
  return {
192
478
  data: envelope.data,
193
- meta: envelope.metadata
194
- ? {
195
- requestId: envelope.metadata.request_id,
196
- latencyMs: envelope.metadata.latency_ms,
197
- model: envelope.metadata.model,
198
- trace: envelope.metadata.trace?.map((t) => ({
199
- toolName: t.tool_name,
200
- input: t.input,
201
- output: t.output,
202
- latencyMs: t.latency_ms,
203
- })),
204
- }
205
- : { requestId: "", latencyMs: 0 },
479
+ meta: mapResponseMeta(envelope.metadata),
206
480
  };
207
481
  }
482
+ getAuthHeaders(authMode) {
483
+ if (authMode === "none")
484
+ return {};
485
+ if (authMode === "apiKey") {
486
+ if (!this.apiKey) {
487
+ throw new Error("BaoBoxClient: apiKey required for chat methods");
488
+ }
489
+ return { authorization: `Bearer ${this.apiKey}` };
490
+ }
491
+ if (!this.adminSecret) {
492
+ throw new Error("BaoBoxClient: adminSecret required for admin methods");
493
+ }
494
+ return { authorization: `Bearer ${this.adminSecret}` };
495
+ }
496
+ }
497
+ function mapResponseMeta(metadata) {
498
+ if (!metadata)
499
+ return { requestId: "", latencyMs: 0 };
500
+ return {
501
+ requestId: metadata.request_id,
502
+ latencyMs: metadata.latency_ms,
503
+ model: metadata.model,
504
+ trace: metadata.trace?.map((trace) => ({
505
+ toolName: trace.tool_name,
506
+ input: trace.input,
507
+ output: trace.output,
508
+ latencyMs: trace.latency_ms,
509
+ })),
510
+ };
511
+ }
512
+ function mapSession(raw) {
513
+ return {
514
+ id: raw.id,
515
+ skillId: raw.skill_id,
516
+ tenantId: raw.tenant_id,
517
+ createdAt: raw.created_at,
518
+ updatedAt: raw.updated_at,
519
+ };
520
+ }
521
+ function mapSessionMessage(raw) {
522
+ return {
523
+ id: raw.id,
524
+ sessionId: raw.session_id,
525
+ role: raw.role,
526
+ content: raw.content,
527
+ tokenCount: raw.token_count,
528
+ createdAt: raw.created_at,
529
+ };
530
+ }
531
+ function mapEvent(raw) {
532
+ return {
533
+ id: raw.id,
534
+ sessionId: raw.session_id,
535
+ requestId: raw.request_id,
536
+ runId: raw.run_id ?? null,
537
+ eventType: raw.event_type,
538
+ content: raw.content,
539
+ metadata: toJsonObject(raw.metadata),
540
+ tokenCount: raw.token_count,
541
+ latencyMs: raw.latency_ms,
542
+ parentEventId: raw.parent_event_id,
543
+ createdAt: raw.created_at,
544
+ };
545
+ }
546
+ function mapSkill(raw) {
547
+ return {
548
+ id: raw.id,
549
+ name: raw.name,
550
+ description: raw.description,
551
+ systemPrompt: raw.system_prompt,
552
+ model: raw.model,
553
+ temperature: raw.temperature,
554
+ maxTokens: raw.max_tokens,
555
+ sourceUrl: raw.source_url,
556
+ tenantId: raw.tenant_id,
557
+ createdAt: raw.created_at,
558
+ updatedAt: raw.updated_at,
559
+ };
560
+ }
561
+ function mapSkillWithFiles(raw) {
562
+ return {
563
+ ...mapSkill(raw),
564
+ files: raw.files.map((file) => ({
565
+ path: file.path,
566
+ size: file.size,
567
+ })),
568
+ };
569
+ }
570
+ function skillWithoutFiles(skill) {
571
+ const { files: _files, ...rest } = skill;
572
+ return rest;
573
+ }
574
+ function mapSkillFileSummary(raw) {
575
+ return {
576
+ path: raw.path,
577
+ size: raw.size,
578
+ updatedAt: raw.updated_at,
579
+ };
580
+ }
581
+ function mapSkillFile(raw) {
582
+ return {
583
+ id: raw.id,
584
+ skillId: raw.skill_id,
585
+ path: raw.path,
586
+ content: raw.content,
587
+ createdAt: raw.created_at,
588
+ updatedAt: raw.updated_at,
589
+ };
590
+ }
591
+ function mapTool(raw) {
592
+ return {
593
+ id: raw.id,
594
+ name: raw.name,
595
+ description: raw.description,
596
+ inputSchema: raw.input_schema,
597
+ handlerType: raw.handler_type,
598
+ handlerConfig: raw.handler_config,
599
+ createdAt: raw.created_at,
600
+ };
601
+ }
602
+ function mapSkillSecretSummary(raw) {
603
+ return {
604
+ id: raw.id,
605
+ key: raw.key,
606
+ createdAt: raw.created_at,
607
+ };
608
+ }
609
+ function mapApiKey(raw) {
610
+ return {
611
+ apiKeyId: raw.apiKeyId,
612
+ name: raw.name,
613
+ permissions: raw.permissions,
614
+ rateLimit: raw.rateLimit,
615
+ tenantId: raw.tenantId,
616
+ createdAt: raw.createdAt,
617
+ expiresAt: raw.expiresAt,
618
+ };
619
+ }
620
+ function mapScheduledTask(raw) {
621
+ return {
622
+ id: raw.id,
623
+ name: raw.name,
624
+ skillId: raw.skill_id,
625
+ prompt: raw.prompt,
626
+ telegramChatId: raw.telegram_chat_id,
627
+ schedule: raw.schedule,
628
+ enabled: raw.enabled,
629
+ createdAt: raw.created_at,
630
+ lastRunAt: raw.last_run_at,
631
+ };
632
+ }
633
+ function mapEvalCase(raw) {
634
+ return {
635
+ id: raw.id,
636
+ skillId: raw.skill_id,
637
+ name: raw.name,
638
+ input: raw.input,
639
+ expectedBehavior: raw.expected_behavior,
640
+ dimensions: raw.dimensions,
641
+ passingThreshold: raw.passing_threshold,
642
+ createdAt: raw.created_at,
643
+ updatedAt: raw.updated_at,
644
+ };
645
+ }
646
+ function mapEvalRunExecution(raw) {
647
+ return {
648
+ runId: raw.run_id,
649
+ status: raw.status,
650
+ totalCases: raw.total_cases,
651
+ passed: raw.passed,
652
+ failed: raw.failed,
653
+ avgScore: raw.avg_score,
654
+ results: raw.results.map(mapEvalRunResultSummary),
655
+ durationMs: raw.duration_ms,
656
+ };
657
+ }
658
+ function mapEvalRunResultSummary(raw) {
659
+ return {
660
+ testCaseId: raw.test_case_id,
661
+ status: raw.status,
662
+ score: raw.score,
663
+ scores: raw.scores,
664
+ response: raw.response,
665
+ reasoning: raw.reasoning,
666
+ };
667
+ }
668
+ function mapEvalRun(raw) {
669
+ return {
670
+ id: raw.id,
671
+ skillId: raw.skill_id,
672
+ promptVersion: raw.prompt_version,
673
+ status: raw.status,
674
+ totalCases: raw.total_cases,
675
+ passed: raw.passed,
676
+ failed: raw.failed,
677
+ avgScore: raw.avg_score,
678
+ metadata: raw.metadata,
679
+ createdAt: raw.created_at,
680
+ completedAt: raw.completed_at,
681
+ };
682
+ }
683
+ function mapEvalRunResult(raw) {
684
+ return {
685
+ id: raw.id,
686
+ runId: raw.run_id,
687
+ testCaseId: raw.test_case_id,
688
+ sessionId: raw.session_id,
689
+ status: raw.status,
690
+ score: raw.score,
691
+ scoresJson: raw.scores_json,
692
+ response: raw.response,
693
+ reasoning: raw.reasoning,
694
+ latencyMs: raw.latency_ms,
695
+ llmInputJson: raw.llm_input_json ?? null,
696
+ createdAt: raw.created_at,
697
+ };
698
+ }
699
+ function buildSkillWriteBody(req) {
700
+ return compactObject({
701
+ name: req.name,
702
+ description: req.description,
703
+ system_prompt: req.systemPrompt,
704
+ model: req.model,
705
+ temperature: req.temperature,
706
+ max_tokens: req.maxTokens,
707
+ source_url: req.sourceUrl,
708
+ files: req.files,
709
+ });
710
+ }
711
+ function appendQuery(path, query) {
712
+ const qs = new URLSearchParams();
713
+ for (const [key, value] of Object.entries(query)) {
714
+ if (value !== undefined)
715
+ qs.set(key, value);
716
+ }
717
+ const suffix = qs.toString();
718
+ return suffix ? `${path}?${suffix}` : path;
719
+ }
720
+ function encodePath(path) {
721
+ return path
722
+ .split("/")
723
+ .filter((segment) => segment.length > 0)
724
+ .map((segment) => encodeURIComponent(segment))
725
+ .join("/");
726
+ }
727
+ function compactObject(obj) {
728
+ return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
729
+ }
730
+ function toJsonObject(input) {
731
+ if (typeof input === "string") {
732
+ const parsed = safeParseJson(input);
733
+ return isJsonObject(parsed) ? parsed : {};
734
+ }
735
+ return isJsonObject(input) ? input : {};
736
+ }
737
+ function isJsonObject(value) {
738
+ return typeof value === "object" && value !== null && !Array.isArray(value);
208
739
  }
209
740
  function safeParseJson(text) {
210
741
  try {