@hexis-ai/engram-sdk 0.12.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
@@ -48,11 +48,36 @@ export interface CreateWorkspaceResult {
48
48
  /** Present when `issueKey !== false`. */
49
49
  key?: IssuedKey;
50
50
  }
51
+ export interface Org {
52
+ id: string;
53
+ name?: string | null;
54
+ metadata?: Record<string, unknown>;
55
+ createdAt: string;
56
+ }
57
+ export interface OrgMembership {
58
+ orgId: string;
59
+ userId: string;
60
+ role: string;
61
+ joinedAt: string;
62
+ }
63
+ export interface CreateOrgInput {
64
+ id?: string;
65
+ name?: string;
66
+ metadata?: Record<string, unknown>;
67
+ }
68
+ export interface UpsertMemberInput {
69
+ /** Provide one of these — userId wins when both are present. */
70
+ userId?: string;
71
+ email?: string;
72
+ role?: string;
73
+ }
74
+ export interface OrgWorkspace {
75
+ id: string;
76
+ name: string | null;
77
+ orgId: string;
78
+ }
51
79
  export declare class EngramAdmin {
52
- private readonly baseUrl;
53
- private readonly adminToken;
54
- private readonly fetchImpl;
55
- private readonly authHeaders?;
80
+ private readonly http;
56
81
  constructor(opts: AdminClientOptions);
57
82
  createWorkspace(input?: CreateWorkspaceInput): Promise<CreateWorkspaceResult>;
58
83
  listWorkspaces(): Promise<Workspace[]>;
@@ -63,6 +88,29 @@ export declare class EngramAdmin {
63
88
  }): Promise<IssuedKey>;
64
89
  listKeys(workspaceId: string): Promise<ApiKey[]>;
65
90
  revokeKey(workspaceId: string, keyId: string): Promise<void>;
91
+ createOrg(input?: CreateOrgInput): Promise<Org>;
92
+ listOrgs(): Promise<Org[]>;
93
+ getOrg(id: string): Promise<Org>;
94
+ deleteOrg(id: string): Promise<void>;
95
+ listOrgMembers(orgId: string): Promise<OrgMembership[]>;
96
+ upsertOrgMember(orgId: string, input: UpsertMemberInput): Promise<OrgMembership>;
97
+ removeOrgMember(orgId: string, userId: string): Promise<void>;
98
+ /** Create a workspace under an org and (by default) issue an api key. */
99
+ createWorkspaceUnderOrg(orgId: string, input?: CreateWorkspaceInput): Promise<CreateWorkspaceResult>;
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>;
66
114
  private request;
67
115
  }
68
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);
@@ -41,25 +42,61 @@ export class EngramAdmin {
41
42
  async revokeKey(workspaceId, keyId) {
42
43
  await this.request("DELETE", `/admin/v1/workspaces/${encodeURIComponent(workspaceId)}/keys/${encodeURIComponent(keyId)}`);
43
44
  }
44
- async request(method, path, body) {
45
- const headers = {
46
- "content-type": "application/json",
47
- "x-admin-token": this.adminToken,
48
- };
49
- if (this.authHeaders)
50
- Object.assign(headers, await this.authHeaders());
51
- const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
52
- method,
53
- headers,
54
- ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
55
- });
56
- if (!res.ok) {
57
- const text = await res.text().catch(() => "");
58
- throw new Error(`engram-admin ${method} ${path} ${res.status}: ${text}`);
59
- }
60
- if (res.status === 204)
61
- return undefined;
62
- return (await res.json());
45
+ // ---------- orgs (Wave G) ---------------------------------
46
+ async createOrg(input = {}) {
47
+ const r = await this.request("POST", "/admin/v1/orgs", input);
48
+ return r.org;
49
+ }
50
+ async listOrgs() {
51
+ const r = await this.request("GET", "/admin/v1/orgs");
52
+ return r.orgs;
53
+ }
54
+ async getOrg(id) {
55
+ const r = await this.request("GET", `/admin/v1/orgs/${encodeURIComponent(id)}`);
56
+ return r.org;
57
+ }
58
+ async deleteOrg(id) {
59
+ await this.request("DELETE", `/admin/v1/orgs/${encodeURIComponent(id)}`);
60
+ }
61
+ async listOrgMembers(orgId) {
62
+ const r = await this.request("GET", `/admin/v1/orgs/${encodeURIComponent(orgId)}/members`);
63
+ return r.members;
64
+ }
65
+ async upsertOrgMember(orgId, input) {
66
+ const r = await this.request("POST", `/admin/v1/orgs/${encodeURIComponent(orgId)}/members`, input);
67
+ return r.member;
68
+ }
69
+ async removeOrgMember(orgId, userId) {
70
+ await this.request("DELETE", `/admin/v1/orgs/${encodeURIComponent(orgId)}/members/${encodeURIComponent(userId)}`);
71
+ }
72
+ /** Create a workspace under an org and (by default) issue an api key. */
73
+ async createWorkspaceUnderOrg(orgId, input = {}) {
74
+ return this.request("POST", `/admin/v1/orgs/${encodeURIComponent(orgId)}/workspaces`, input);
75
+ }
76
+ async listOrgWorkspaces(orgId) {
77
+ const r = await this.request("GET", `/admin/v1/orgs/${encodeURIComponent(orgId)}/workspaces`);
78
+ return r.workspaces;
79
+ }
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);
63
100
  }
64
101
  }
65
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/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export { extractReferences, encodeResourceId, type RefCandidate, type ReferenceS
3
3
  export { parseToolName, type ParsedToolName } from "./tool-name";
4
4
  export { BufferedTelemetry, type TelemetrySession, type TelemetryMessage, type TelemetryEvent, type TelemetryPerson, type TelemetryIdentity, type TelemetryAlias, } from "./buffered";
5
5
  export { fetchIdToken, cloudRunIdTokenAuth } from "./id-token";
6
- export { EngramAdmin, createAdminClient, type AdminClientOptions, type CreateWorkspaceInput, type CreateWorkspaceResult, type Workspace as AdminWorkspace, type ApiKey as AdminApiKey, type IssuedKey as AdminIssuedKey, } from "./admin";
6
+ export { EngramAdmin, createAdminClient, type AdminClientOptions, type CreateWorkspaceInput, type CreateWorkspaceResult, type Workspace as AdminWorkspace, type ApiKey as AdminApiKey, type IssuedKey as AdminIssuedKey, type Org, type OrgMembership, type CreateOrgInput, type UpsertMemberInput, type OrgWorkspace, } from "./admin";
7
7
  export type { SessionInit, SessionUpdate, SessionAck, SessionEvent, StepEvent, ParticipantEvent, TitleEvent, EndEvent, MessageContentBlock, MessageEvent, EventBatch, PersonInfo, PersonCreate, PersonUpdate, PersonMap, AliasInfo, AliasUpsert, IdentityInfo, IdentityUpsert, } from "./types";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexis-ai/engram-sdk",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "author": "hexis ltd.",
5
5
  "repository": {
6
6
  "type": "git",