@hexis-ai/engram-sdk 0.13.0 → 0.14.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/admin.d.ts CHANGED
@@ -77,10 +77,7 @@ export interface OrgWorkspace {
77
77
  orgId: string;
78
78
  }
79
79
  export declare class EngramAdmin {
80
- private readonly baseUrl;
81
- private readonly adminToken;
82
- private readonly fetchImpl;
83
- private readonly authHeaders?;
80
+ private readonly http;
84
81
  constructor(opts: AdminClientOptions);
85
82
  createWorkspace(input?: CreateWorkspaceInput): Promise<CreateWorkspaceResult>;
86
83
  listWorkspaces(): Promise<Workspace[]>;
@@ -101,6 +98,19 @@ export declare class EngramAdmin {
101
98
  /** Create a workspace under an org and (by default) issue an api key. */
102
99
  createWorkspaceUnderOrg(orgId: string, input?: CreateWorkspaceInput): Promise<CreateWorkspaceResult>;
103
100
  listOrgWorkspaces(orgId: string): Promise<OrgWorkspace[]>;
101
+ /**
102
+ * Issue a fresh API key for a workspace under an org. Org-scoped
103
+ * replacement for the legacy `issueKey(workspaceId, ...)` which
104
+ * targeted `/admin/v1/workspaces/:id/keys`. Use this for key
105
+ * rotation after the initial `createWorkspaceUnderOrg`.
106
+ */
107
+ issueWorkspaceKey(orgId: string, workspaceId: string, opts?: {
108
+ name?: string;
109
+ }): Promise<IssuedKey>;
110
+ /** List the API keys of an org-owned workspace. */
111
+ listWorkspaceKeys(orgId: string, workspaceId: string): Promise<ApiKey[]>;
112
+ /** Revoke an API key on an org-owned workspace. */
113
+ revokeWorkspaceKey(orgId: string, workspaceId: string, keyId: string): Promise<void>;
104
114
  private request;
105
115
  }
106
116
  export declare function createAdminClient(opts: AdminClientOptions): EngramAdmin;
package/dist/admin.js CHANGED
@@ -3,20 +3,21 @@
3
3
  * `/admin/v1/*` mount. Use from privileged contexts only — the admin token
4
4
  * is platform-root and must never reach end-user code paths.
5
5
  */
6
+ import { createHttpClient } from "./http";
6
7
  export class EngramAdmin {
7
- baseUrl;
8
- adminToken;
9
- fetchImpl;
10
- authHeaders;
8
+ http;
11
9
  constructor(opts) {
12
10
  if (!opts.baseUrl)
13
11
  throw new Error("EngramAdmin: baseUrl is required");
14
12
  if (!opts.adminToken)
15
13
  throw new Error("EngramAdmin: adminToken is required");
16
- this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
17
- this.adminToken = opts.adminToken;
18
- this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
19
- this.authHeaders = opts.authHeaders;
14
+ this.http = createHttpClient({
15
+ baseUrl: opts.baseUrl,
16
+ ...(opts.fetch ? { fetch: opts.fetch } : {}),
17
+ ...(opts.authHeaders ? { authHeaders: opts.authHeaders } : {}),
18
+ staticHeaders: { "x-admin-token": opts.adminToken },
19
+ errorPrefix: "engram-admin",
20
+ });
20
21
  }
21
22
  async createWorkspace(input = {}) {
22
23
  return this.request("POST", "/admin/v1/workspaces", input);
@@ -76,25 +77,26 @@ export class EngramAdmin {
76
77
  const r = await this.request("GET", `/admin/v1/orgs/${encodeURIComponent(orgId)}/workspaces`);
77
78
  return r.workspaces;
78
79
  }
79
- async request(method, path, body) {
80
- const headers = {
81
- "content-type": "application/json",
82
- "x-admin-token": this.adminToken,
83
- };
84
- if (this.authHeaders)
85
- Object.assign(headers, await this.authHeaders());
86
- const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
87
- method,
88
- headers,
89
- ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
90
- });
91
- if (!res.ok) {
92
- const text = await res.text().catch(() => "");
93
- throw new Error(`engram-admin ${method} ${path} ${res.status}: ${text}`);
94
- }
95
- if (res.status === 204)
96
- return undefined;
97
- return (await res.json());
80
+ /**
81
+ * Issue a fresh API key for a workspace under an org. Org-scoped
82
+ * replacement for the legacy `issueKey(workspaceId, ...)` which
83
+ * targeted `/admin/v1/workspaces/:id/keys`. Use this for key
84
+ * rotation after the initial `createWorkspaceUnderOrg`.
85
+ */
86
+ async issueWorkspaceKey(orgId, workspaceId, opts = {}) {
87
+ return this.request("POST", `/admin/v1/orgs/${encodeURIComponent(orgId)}/workspaces/${encodeURIComponent(workspaceId)}/keys`, opts);
88
+ }
89
+ /** List the API keys of an org-owned workspace. */
90
+ async listWorkspaceKeys(orgId, workspaceId) {
91
+ const r = await this.request("GET", `/admin/v1/orgs/${encodeURIComponent(orgId)}/workspaces/${encodeURIComponent(workspaceId)}/keys`);
92
+ return r.keys;
93
+ }
94
+ /** Revoke an API key on an org-owned workspace. */
95
+ async revokeWorkspaceKey(orgId, workspaceId, keyId) {
96
+ await this.request("DELETE", `/admin/v1/orgs/${encodeURIComponent(orgId)}/workspaces/${encodeURIComponent(workspaceId)}/keys/${encodeURIComponent(keyId)}`);
97
+ }
98
+ request(method, path, body) {
99
+ return this.http.request(method, path, body);
98
100
  }
99
101
  }
100
102
  export function createAdminClient(opts) {
package/dist/client.d.ts CHANGED
@@ -84,14 +84,10 @@ export interface SearchRequest {
84
84
  /** @deprecated Use `SearchEnvelope` (carries `persons` map too). */
85
85
  export type SearchResponse = SearchEnvelope;
86
86
  export declare class Engram {
87
- private readonly apiKey;
88
- private readonly baseUrl;
89
- private readonly fetchImpl;
87
+ private readonly http;
90
88
  private readonly flushIntervalMs;
91
89
  private readonly batchSize;
92
90
  private readonly onError;
93
- private readonly apiKeyHeader;
94
- private readonly authHeaders?;
95
91
  readonly maxRetries: number;
96
92
  readonly retryBackoffMs: number;
97
93
  private telemetry;
package/dist/client.js CHANGED
@@ -1,15 +1,12 @@
1
1
  import { encodeResourceId, extractReferences } from "./extract";
2
2
  import { parseToolName } from "./tool-name";
3
3
  import { BufferedTelemetry, } from "./buffered";
4
+ import { createHttpClient } from "./http";
4
5
  export class Engram {
5
- apiKey;
6
- baseUrl;
7
- fetchImpl;
6
+ http;
8
7
  flushIntervalMs;
9
8
  batchSize;
10
9
  onError;
11
- apiKeyHeader;
12
- authHeaders;
13
10
  maxRetries;
14
11
  retryBackoffMs;
15
12
  telemetry = null;
@@ -18,16 +15,21 @@ export class Engram {
18
15
  throw new Error("Engram: apiKey is required");
19
16
  if (!opts.baseUrl)
20
17
  throw new Error("Engram: baseUrl is required");
21
- this.apiKey = opts.apiKey;
22
- this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
23
- this.fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
24
18
  this.flushIntervalMs = opts.flushIntervalMs ?? 2000;
25
19
  this.batchSize = opts.batchSize ?? 32;
26
20
  this.onError = opts.onError ?? ((e) => console.error("[engram]", e));
27
- this.apiKeyHeader = opts.apiKeyHeader ?? "authorization";
28
- this.authHeaders = opts.authHeaders;
29
21
  this.maxRetries = opts.maxRetries ?? 4;
30
22
  this.retryBackoffMs = opts.retryBackoffMs ?? 500;
23
+ const apiKeyHeader = opts.apiKeyHeader ?? "authorization";
24
+ this.http = createHttpClient({
25
+ baseUrl: opts.baseUrl,
26
+ ...(opts.fetch ? { fetch: opts.fetch } : {}),
27
+ ...(opts.authHeaders ? { authHeaders: opts.authHeaders } : {}),
28
+ staticHeaders: apiKeyHeader === "authorization"
29
+ ? { authorization: `Bearer ${opts.apiKey}` }
30
+ : { "x-api-key": opts.apiKey },
31
+ errorPrefix: "engram",
32
+ });
31
33
  }
32
34
  /**
33
35
  * Langfuse-style fire-and-forget telemetry surface. Lazily initialised
@@ -254,31 +256,8 @@ export class Engram {
254
256
  get config() {
255
257
  return { flushIntervalMs: this.flushIntervalMs, batchSize: this.batchSize, onError: this.onError };
256
258
  }
257
- async request(method, path, body) {
258
- const headers = { "content-type": "application/json" };
259
- if (this.apiKeyHeader === "authorization") {
260
- headers["authorization"] = `Bearer ${this.apiKey}`;
261
- }
262
- else {
263
- headers["x-api-key"] = this.apiKey;
264
- }
265
- if (this.authHeaders)
266
- Object.assign(headers, await this.authHeaders());
267
- const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
268
- method,
269
- headers,
270
- ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
271
- });
272
- if (!res.ok) {
273
- const text = await res.text().catch(() => "");
274
- throw new Error(`engram ${method} ${path} ${res.status}: ${text}`);
275
- }
276
- if (res.status === 204)
277
- return undefined;
278
- const ct = res.headers.get("content-type") ?? "";
279
- if (!ct.includes("application/json"))
280
- return undefined;
281
- return (await res.json());
259
+ request(method, path, body) {
260
+ return this.http.request(method, path, body);
282
261
  }
283
262
  }
284
263
  export class EngramSession {
package/dist/http.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Minimal JSON HTTP client shared by `Engram` (workspace api-key) and
3
+ * `EngramAdmin` (platform admin token). Encapsulates baseUrl normalisation,
4
+ * header merging, request body serialisation, and the small JSON-or-empty
5
+ * response contract that both clients use.
6
+ */
7
+ export interface HttpClientOptions {
8
+ baseUrl: string;
9
+ fetch?: typeof fetch;
10
+ /** Static headers attached to every request before {@link authHeaders} overlay. */
11
+ staticHeaders?: Record<string, string>;
12
+ /**
13
+ * Async per-request hook returning headers to attach. Useful for short-lived
14
+ * credentials like Cloud Run ID tokens. Resolves before each request; values
15
+ * overwrite {@link staticHeaders} on conflict.
16
+ */
17
+ authHeaders?: () => Promise<Record<string, string>> | Record<string, string>;
18
+ /** Error-message prefix — distinguishes overlapping SDKs in stack traces. */
19
+ errorPrefix: string;
20
+ }
21
+ export interface HttpClient {
22
+ request<T>(method: string, path: string, body?: unknown): Promise<T>;
23
+ }
24
+ export declare function createHttpClient(opts: HttpClientOptions): HttpClient;
package/dist/http.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Minimal JSON HTTP client shared by `Engram` (workspace api-key) and
3
+ * `EngramAdmin` (platform admin token). Encapsulates baseUrl normalisation,
4
+ * header merging, request body serialisation, and the small JSON-or-empty
5
+ * response contract that both clients use.
6
+ */
7
+ export function createHttpClient(opts) {
8
+ const baseUrl = opts.baseUrl.replace(/\/+$/, "");
9
+ const fetchImpl = opts.fetch ?? globalThis.fetch.bind(globalThis);
10
+ const staticHeaders = opts.staticHeaders ?? {};
11
+ const { authHeaders, errorPrefix } = opts;
12
+ return {
13
+ async request(method, path, body) {
14
+ const headers = {
15
+ "content-type": "application/json",
16
+ ...staticHeaders,
17
+ };
18
+ if (authHeaders)
19
+ Object.assign(headers, await authHeaders());
20
+ const res = await fetchImpl(`${baseUrl}${path}`, {
21
+ method,
22
+ headers,
23
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
24
+ });
25
+ if (!res.ok) {
26
+ const text = await res.text().catch(() => "");
27
+ throw new Error(`${errorPrefix} ${method} ${path} ${res.status}: ${text}`);
28
+ }
29
+ if (res.status === 204)
30
+ return undefined;
31
+ const ct = res.headers.get("content-type") ?? "";
32
+ if (!ct.includes("application/json"))
33
+ return undefined;
34
+ return (await res.json());
35
+ },
36
+ };
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexis-ai/engram-sdk",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "author": "hexis ltd.",
5
5
  "repository": {
6
6
  "type": "git",