@babav/knowledge-core-client 0.4.0 → 0.6.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.d.ts CHANGED
@@ -11,6 +11,14 @@
11
11
  * Auth: every call carries an X-API-Key. Use KnowledgeCoreClient with a TENANT
12
12
  * key for all data ops; use AdminClient with the ADMIN key for tenant / API-key /
13
13
  * agent management. The key is server-side only — never ship it to a browser.
14
+ *
15
+ * Configuration — environment variables (Node `process.env` or Deno `Deno.env`):
16
+ * `new KnowledgeCoreClient()` reads BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY
17
+ * `new AdminClient()` reads BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY
18
+ * Passing { baseUrl } / { apiKey } explicitly overrides the env var for that field
19
+ * (the app is then responsible for supplying it). A missing value (no param AND no
20
+ * env var) throws at construction with a clear message. In Deno, env access needs
21
+ * `--allow-env` (without it, pass the values explicitly).
14
22
  */
15
23
  export type UUID = string;
16
24
  export interface Page<T> {
@@ -116,6 +124,14 @@ export interface DocumentCount {
116
124
  in_flight: number;
117
125
  failed: number;
118
126
  }
127
+ /** A signed PUT URL for uploading a large file straight to GCS + the pre-allocated
128
+ * document id to reference on from-upload. */
129
+ export interface UploadUrl {
130
+ upload_url: string;
131
+ document_id: UUID;
132
+ gcs_uri: string;
133
+ expires_in: number;
134
+ }
119
135
  export interface Conversation {
120
136
  id: UUID;
121
137
  title: string | null;
@@ -196,10 +212,12 @@ export declare class KnowledgeCoreError extends Error {
196
212
  get code(): string | undefined;
197
213
  }
198
214
  export interface ClientOptions {
199
- baseUrl: string;
200
- apiKey: string;
201
- /** Optional default fetch timeout (ms). Streaming ignores this. Note:
202
- * synchronous ingest can take a while — set this generously or omit. */
215
+ /** API base URL. Defaults to env BABAV_KC_BASE_URL when omitted. */
216
+ baseUrl?: string;
217
+ /** Tenant key (KnowledgeCoreClient) or admin key (AdminClient). Defaults to env
218
+ * BABAV_KC_TENANT_KEY / BABAV_KC_ADMIN_KEY respectively when omitted. */
219
+ apiKey?: string;
220
+ /** Optional default fetch timeout (ms). Streaming ignores this. */
203
221
  timeoutMs?: number;
204
222
  fetch?: typeof fetch;
205
223
  }
@@ -215,7 +233,7 @@ declare class HttpBase {
215
233
  protected readonly apiKey: string;
216
234
  protected readonly timeoutMs?: number;
217
235
  protected readonly _fetch: typeof fetch;
218
- constructor(opts: ClientOptions);
236
+ constructor(opts?: ClientOptions, keyEnvVar?: string);
219
237
  protected url(path: string, query?: RequestOpts["query"]): string;
220
238
  protected raw(method: string, path: string, opts?: RequestOpts): Promise<Response>;
221
239
  protected request<T>(method: string, path: string, opts?: RequestOpts): Promise<T>;
@@ -240,6 +258,8 @@ export interface StreamHandlers {
240
258
  onEvent?: (event: string, data: unknown) => void;
241
259
  }
242
260
  export declare class KnowledgeCoreClient extends HttpBase {
261
+ /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY when not passed. */
262
+ constructor(opts?: ClientOptions);
243
263
  query(agentId: UUID, body: QueryRequest): Promise<QueryResponse>;
244
264
  /** Streaming query (SSE). Resolves when the stream ends. */
245
265
  queryStream(agentId: UUID, body: QueryRequest, handlers: StreamHandlers, signal?: AbortSignal): Promise<void>;
@@ -292,6 +312,32 @@ export declare class KnowledgeCoreClient extends HttpBase {
292
312
  folder_id?: UUID;
293
313
  custom_metadata?: Record<string, unknown>;
294
314
  }) => Promise<Document>;
315
+ /** Mint a signed PUT URL to upload a large file straight to GCS (bypasses the
316
+ * ~32 MB request limit). PUT the bytes to upload_url, then ingestFromUpload. */
317
+ uploadUrl: (id: UUID, a: {
318
+ filename: string;
319
+ content_type?: string;
320
+ }) => Promise<UploadUrl>;
321
+ /** Ingest a file already PUT to GCS via uploadUrl. Resolves with the `pending`
322
+ * document (202); track via documents.get() / webhook. */
323
+ ingestFromUpload: (id: UUID, a: {
324
+ document_id: UUID;
325
+ filename: string;
326
+ content_type?: string;
327
+ visibility?: Visibility;
328
+ folder_id?: UUID;
329
+ custom_metadata?: Record<string, unknown>;
330
+ }) => Promise<Document>;
331
+ /** Convenience for LARGE files: uploadUrl → PUT the bytes to GCS → ingestFromUpload.
332
+ * Use this instead of ingestDocument when the file may exceed ~32 MB. */
333
+ uploadDocument: (id: UUID, a: {
334
+ file: FileData;
335
+ filename: string;
336
+ content_type?: string;
337
+ visibility?: Visibility;
338
+ folder_id?: UUID;
339
+ custom_metadata?: Record<string, unknown>;
340
+ }) => Promise<Document>;
295
341
  };
296
342
  parse(a: {
297
343
  file: FileData;
@@ -396,6 +442,8 @@ export declare class KnowledgeCoreClient extends HttpBase {
396
442
  };
397
443
  }
398
444
  export declare class AdminClient extends HttpBase {
445
+ /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY when not passed. */
446
+ constructor(opts?: ClientOptions);
399
447
  tenants: {
400
448
  create: (b: Partial<Tenant> & {
401
449
  name: string;
package/dist/index.js CHANGED
@@ -11,6 +11,14 @@
11
11
  * Auth: every call carries an X-API-Key. Use KnowledgeCoreClient with a TENANT
12
12
  * key for all data ops; use AdminClient with the ADMIN key for tenant / API-key /
13
13
  * agent management. The key is server-side only — never ship it to a browser.
14
+ *
15
+ * Configuration — environment variables (Node `process.env` or Deno `Deno.env`):
16
+ * `new KnowledgeCoreClient()` reads BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY
17
+ * `new AdminClient()` reads BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY
18
+ * Passing { baseUrl } / { apiKey } explicitly overrides the env var for that field
19
+ * (the app is then responsible for supplying it). A missing value (no param AND no
20
+ * env var) throws at construction with a clear message. In Deno, env access needs
21
+ * `--allow-env` (without it, pass the values explicitly).
14
22
  */
15
23
  // ---------------------------------------------------------------------------
16
24
  // Errors
@@ -35,14 +43,37 @@ export class KnowledgeCoreError extends Error {
35
43
  return d?.detail?.error ?? d?.error;
36
44
  }
37
45
  }
46
+ /** Read an env var under Node (process.env) or Deno (Deno.env); undefined if unset
47
+ * or inaccessible (e.g. Deno without --allow-env). */
48
+ function readEnv(name) {
49
+ const g = globalThis;
50
+ const fromNode = g.process?.env?.[name];
51
+ if (fromNode)
52
+ return fromNode;
53
+ try {
54
+ const fromDeno = g.Deno?.env?.get?.(name);
55
+ if (fromDeno)
56
+ return fromDeno;
57
+ }
58
+ catch {
59
+ /* Deno env access denied (no --allow-env) — treat as unset */
60
+ }
61
+ return undefined;
62
+ }
38
63
  class HttpBase {
39
64
  baseUrl;
40
65
  apiKey;
41
66
  timeoutMs;
42
67
  _fetch;
43
- constructor(opts) {
44
- this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
45
- this.apiKey = opts.apiKey;
68
+ constructor(opts = {}, keyEnvVar = "BABAV_KC_TENANT_KEY") {
69
+ const baseUrl = opts.baseUrl ?? readEnv("BABAV_KC_BASE_URL");
70
+ const apiKey = opts.apiKey ?? readEnv(keyEnvVar);
71
+ if (!baseUrl)
72
+ throw new Error("KnowledgeCore: no base URL — pass { baseUrl } or set BABAV_KC_BASE_URL");
73
+ if (!apiKey)
74
+ throw new Error(`KnowledgeCore: no API key — pass { apiKey } or set ${keyEnvVar}`);
75
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
76
+ this.apiKey = apiKey;
46
77
  this.timeoutMs = opts.timeoutMs;
47
78
  this._fetch = opts.fetch ?? fetch;
48
79
  }
@@ -121,6 +152,10 @@ function toBlob(data, contentType) {
121
152
  // Tenant client (data ops) — use a TENANT key
122
153
  // ---------------------------------------------------------------------------
123
154
  export class KnowledgeCoreClient extends HttpBase {
155
+ /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY when not passed. */
156
+ constructor(opts = {}) {
157
+ super(opts, "BABAV_KC_TENANT_KEY");
158
+ }
124
159
  // --- query (agent-anchored) ---
125
160
  query(agentId, body) {
126
161
  return this.request("POST", `/v1/agents/${agentId}/query`, { json: body });
@@ -182,6 +217,29 @@ export class KnowledgeCoreClient extends HttpBase {
182
217
  fd.append("folder_id", a.folder_id);
183
218
  return this.request("POST", `/v1/corpora/${id}/documents`, { body: fd });
184
219
  },
220
+ /** Mint a signed PUT URL to upload a large file straight to GCS (bypasses the
221
+ * ~32 MB request limit). PUT the bytes to upload_url, then ingestFromUpload. */
222
+ uploadUrl: (id, a) => this.request("POST", `/v1/corpora/${id}/documents/upload-url`, { json: a }),
223
+ /** Ingest a file already PUT to GCS via uploadUrl. Resolves with the `pending`
224
+ * document (202); track via documents.get() / webhook. */
225
+ ingestFromUpload: (id, a) => this.request("POST", `/v1/corpora/${id}/documents/from-upload`, { json: a }),
226
+ /** Convenience for LARGE files: uploadUrl → PUT the bytes to GCS → ingestFromUpload.
227
+ * Use this instead of ingestDocument when the file may exceed ~32 MB. */
228
+ uploadDocument: async (id, a) => {
229
+ const u = await this.corpora.uploadUrl(id, { filename: a.filename, content_type: a.content_type });
230
+ const put = await this._fetch(u.upload_url, {
231
+ method: "PUT",
232
+ body: toBlob(a.file, a.content_type),
233
+ headers: a.content_type ? { "content-type": a.content_type } : {},
234
+ });
235
+ if (!put.ok) {
236
+ throw new KnowledgeCoreError(put.status, safeJson(await put.text()), u.upload_url);
237
+ }
238
+ return this.corpora.ingestFromUpload(id, {
239
+ document_id: u.document_id, filename: a.filename, content_type: a.content_type,
240
+ visibility: a.visibility, folder_id: a.folder_id, custom_metadata: a.custom_metadata,
241
+ });
242
+ },
185
243
  };
186
244
  // --- parsing (utility: file -> text, stores nothing) ---
187
245
  parse(a) {
@@ -257,6 +315,10 @@ export class KnowledgeCoreClient extends HttpBase {
257
315
  // Admin client (tenant + key + agent management) — use the ADMIN key
258
316
  // ---------------------------------------------------------------------------
259
317
  export class AdminClient extends HttpBase {
318
+ /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY when not passed. */
319
+ constructor(opts = {}) {
320
+ super(opts, "BABAV_KC_ADMIN_KEY");
321
+ }
260
322
  tenants = {
261
323
  create: (b) => this.request("POST", "/v1/tenants", { json: b }),
262
324
  list: (q) => this.request("GET", "/v1/tenants", { query: q }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babav/knowledge-core-client",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "TypeScript client for the Babav Knowledge Core API (Deno + Node 18+, zero deps).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -11,6 +11,14 @@
11
11
  * Auth: every call carries an X-API-Key. Use KnowledgeCoreClient with a TENANT
12
12
  * key for all data ops; use AdminClient with the ADMIN key for tenant / API-key /
13
13
  * agent management. The key is server-side only — never ship it to a browser.
14
+ *
15
+ * Configuration — environment variables (Node `process.env` or Deno `Deno.env`):
16
+ * `new KnowledgeCoreClient()` reads BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY
17
+ * `new AdminClient()` reads BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY
18
+ * Passing { baseUrl } / { apiKey } explicitly overrides the env var for that field
19
+ * (the app is then responsible for supplying it). A missing value (no param AND no
20
+ * env var) throws at construction with a clear message. In Deno, env access needs
21
+ * `--allow-env` (without it, pass the values explicitly).
14
22
  */
15
23
 
16
24
  // ---------------------------------------------------------------------------
@@ -130,6 +138,14 @@ export interface DocumentCount {
130
138
  in_flight: number;
131
139
  failed: number;
132
140
  }
141
+ /** A signed PUT URL for uploading a large file straight to GCS + the pre-allocated
142
+ * document id to reference on from-upload. */
143
+ export interface UploadUrl {
144
+ upload_url: string;
145
+ document_id: UUID;
146
+ gcs_uri: string;
147
+ expires_in: number;
148
+ }
133
149
  export interface Conversation {
134
150
  id: UUID;
135
151
  title: string | null;
@@ -225,14 +241,34 @@ export class KnowledgeCoreError extends Error {
225
241
  // Base HTTP
226
242
  // ---------------------------------------------------------------------------
227
243
  export interface ClientOptions {
228
- baseUrl: string;
229
- apiKey: string;
230
- /** Optional default fetch timeout (ms). Streaming ignores this. Note:
231
- * synchronous ingest can take a while — set this generously or omit. */
244
+ /** API base URL. Defaults to env BABAV_KC_BASE_URL when omitted. */
245
+ baseUrl?: string;
246
+ /** Tenant key (KnowledgeCoreClient) or admin key (AdminClient). Defaults to env
247
+ * BABAV_KC_TENANT_KEY / BABAV_KC_ADMIN_KEY respectively when omitted. */
248
+ apiKey?: string;
249
+ /** Optional default fetch timeout (ms). Streaming ignores this. */
232
250
  timeoutMs?: number;
233
251
  fetch?: typeof fetch; // override for tests
234
252
  }
235
253
 
254
+ /** Read an env var under Node (process.env) or Deno (Deno.env); undefined if unset
255
+ * or inaccessible (e.g. Deno without --allow-env). */
256
+ function readEnv(name: string): string | undefined {
257
+ const g = globalThis as {
258
+ process?: { env?: Record<string, string | undefined> };
259
+ Deno?: { env?: { get?: (n: string) => string | undefined } };
260
+ };
261
+ const fromNode = g.process?.env?.[name];
262
+ if (fromNode) return fromNode;
263
+ try {
264
+ const fromDeno = g.Deno?.env?.get?.(name);
265
+ if (fromDeno) return fromDeno;
266
+ } catch {
267
+ /* Deno env access denied (no --allow-env) — treat as unset */
268
+ }
269
+ return undefined;
270
+ }
271
+
236
272
  interface RequestOpts {
237
273
  query?: Record<string, string | number | boolean | undefined>;
238
274
  json?: unknown;
@@ -247,9 +283,13 @@ class HttpBase {
247
283
  protected readonly timeoutMs?: number;
248
284
  protected readonly _fetch: typeof fetch;
249
285
 
250
- constructor(opts: ClientOptions) {
251
- this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
252
- this.apiKey = opts.apiKey;
286
+ constructor(opts: ClientOptions = {}, keyEnvVar = "BABAV_KC_TENANT_KEY") {
287
+ const baseUrl = opts.baseUrl ?? readEnv("BABAV_KC_BASE_URL");
288
+ const apiKey = opts.apiKey ?? readEnv(keyEnvVar);
289
+ if (!baseUrl) throw new Error("KnowledgeCore: no base URL — pass { baseUrl } or set BABAV_KC_BASE_URL");
290
+ if (!apiKey) throw new Error(`KnowledgeCore: no API key — pass { apiKey } or set ${keyEnvVar}`);
291
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
292
+ this.apiKey = apiKey;
253
293
  this.timeoutMs = opts.timeoutMs;
254
294
  this._fetch = opts.fetch ?? fetch;
255
295
  }
@@ -342,6 +382,11 @@ export interface StreamHandlers {
342
382
  // Tenant client (data ops) — use a TENANT key
343
383
  // ---------------------------------------------------------------------------
344
384
  export class KnowledgeCoreClient extends HttpBase {
385
+ /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_TENANT_KEY when not passed. */
386
+ constructor(opts: ClientOptions = {}) {
387
+ super(opts, "BABAV_KC_TENANT_KEY");
388
+ }
389
+
345
390
  // --- query (agent-anchored) ---
346
391
  query(agentId: UUID, body: QueryRequest): Promise<QueryResponse> {
347
392
  return this.request("POST", `/v1/agents/${agentId}/query`, { json: body });
@@ -406,6 +451,31 @@ export class KnowledgeCoreClient extends HttpBase {
406
451
  if (a.folder_id) fd.append("folder_id", a.folder_id);
407
452
  return this.request<Document>("POST", `/v1/corpora/${id}/documents`, { body: fd });
408
453
  },
454
+ /** Mint a signed PUT URL to upload a large file straight to GCS (bypasses the
455
+ * ~32 MB request limit). PUT the bytes to upload_url, then ingestFromUpload. */
456
+ uploadUrl: (id: UUID, a: { filename: string; content_type?: string }) =>
457
+ this.request<UploadUrl>("POST", `/v1/corpora/${id}/documents/upload-url`, { json: a }),
458
+ /** Ingest a file already PUT to GCS via uploadUrl. Resolves with the `pending`
459
+ * document (202); track via documents.get() / webhook. */
460
+ ingestFromUpload: (id: UUID, a: { document_id: UUID; filename: string; content_type?: string; visibility?: Visibility; folder_id?: UUID; custom_metadata?: Record<string, unknown> }) =>
461
+ this.request<Document>("POST", `/v1/corpora/${id}/documents/from-upload`, { json: a }),
462
+ /** Convenience for LARGE files: uploadUrl → PUT the bytes to GCS → ingestFromUpload.
463
+ * Use this instead of ingestDocument when the file may exceed ~32 MB. */
464
+ uploadDocument: async (id: UUID, a: { file: FileData; filename: string; content_type?: string; visibility?: Visibility; folder_id?: UUID; custom_metadata?: Record<string, unknown> }): Promise<Document> => {
465
+ const u = await this.corpora.uploadUrl(id, { filename: a.filename, content_type: a.content_type });
466
+ const put = await this._fetch(u.upload_url, {
467
+ method: "PUT",
468
+ body: toBlob(a.file, a.content_type),
469
+ headers: a.content_type ? { "content-type": a.content_type } : {},
470
+ });
471
+ if (!put.ok) {
472
+ throw new KnowledgeCoreError(put.status, safeJson(await put.text()), u.upload_url);
473
+ }
474
+ return this.corpora.ingestFromUpload(id, {
475
+ document_id: u.document_id, filename: a.filename, content_type: a.content_type,
476
+ visibility: a.visibility, folder_id: a.folder_id, custom_metadata: a.custom_metadata,
477
+ });
478
+ },
409
479
  };
410
480
 
411
481
  // --- parsing (utility: file -> text, stores nothing) ---
@@ -501,6 +571,11 @@ export class KnowledgeCoreClient extends HttpBase {
501
571
  // Admin client (tenant + key + agent management) — use the ADMIN key
502
572
  // ---------------------------------------------------------------------------
503
573
  export class AdminClient extends HttpBase {
574
+ /** Defaults to env BABAV_KC_BASE_URL + BABAV_KC_ADMIN_KEY when not passed. */
575
+ constructor(opts: ClientOptions = {}) {
576
+ super(opts, "BABAV_KC_ADMIN_KEY");
577
+ }
578
+
504
579
  tenants = {
505
580
  create: (b: Partial<Tenant> & { name: string }) => this.request<Tenant>("POST", "/v1/tenants", { json: b }),
506
581
  list: (q?: { limit?: number; cursor?: string }) => this.request<Page<Tenant>>("GET", "/v1/tenants", { query: q }),