memorysync-sdk 1.3.0 → 1.5.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
@@ -90,6 +90,9 @@ var V1 = "/api/v1/integrations";
90
90
  function seg(value) {
91
91
  return encodeURIComponent(String(value));
92
92
  }
93
+ function asItem(value, key) {
94
+ return typeof value === "string" ? { [key]: value } : value;
95
+ }
93
96
  var Namespace = class {
94
97
  constructor(request) {
95
98
  this.req = request;
@@ -109,10 +112,22 @@ var SlackNamespace = class extends Namespace {
109
112
  channels(connectionId) {
110
113
  return this.req("GET", `${V2}/connections/${seg(connectionId)}/slack/channels`);
111
114
  }
112
- /** Select channels for syncing. */
113
- addChannels(connectionId, channelIds) {
115
+ /**
116
+ * Select channels for syncing.
117
+ *
118
+ * Accepts bare channel ids, which is the common case, or objects carrying the
119
+ * name and type across so the server does not have to look them up again:
120
+ *
121
+ * ```ts
122
+ * await client.connections.slack.addChannels("c1", ["C0123", "C0456"]);
123
+ * await client.connections.slack.addChannels("c1", [
124
+ * { id: "C0123", name: "support", is_private: false },
125
+ * ]);
126
+ * ```
127
+ */
128
+ addChannels(connectionId, channels) {
114
129
  return this.req("POST", `${V2}/connections/${seg(connectionId)}/slack/channels`, {
115
- body: { channel_ids: channelIds }
130
+ body: { channels: channels.map((c) => asItem(c, "id")) }
116
131
  });
117
132
  }
118
133
  /** Stop syncing one channel. */
@@ -179,22 +194,42 @@ var S3Namespace = class extends Namespace {
179
194
  prefixes(connectionId) {
180
195
  return this.req("GET", `${V2}/connections/${seg(connectionId)}/s3/prefixes`);
181
196
  }
182
- /** Select prefixes for syncing. */
197
+ /**
198
+ * Select prefixes for syncing.
199
+ *
200
+ * Accepts bare prefixes, or objects carrying `bucket` and `label`:
201
+ *
202
+ * ```ts
203
+ * await client.connections.s3.addPrefixes("c1", ["handbook/", "policies/"]);
204
+ * await client.connections.s3.addPrefixes("c1", [
205
+ * { prefix: "handbook/", label: "Handbook" },
206
+ * ]);
207
+ * ```
208
+ *
209
+ * An empty string means the bucket root. The bucket defaults to the one the
210
+ * connection's credentials were validated against, and the API rejects any
211
+ * other bucket rather than indexing one nobody proved access to.
212
+ */
183
213
  addPrefixes(connectionId, prefixes) {
184
214
  return this.req("POST", `${V2}/connections/${seg(connectionId)}/s3/prefixes`, {
185
- body: { prefixes }
215
+ body: { prefixes: prefixes.map((p) => asItem(p, "prefix")) }
186
216
  });
187
217
  }
188
218
  /**
189
- * Stop syncing the given prefixes.
219
+ * Revoke one prefix approval, optionally purging what it produced.
220
+ *
221
+ * The prefix travels as a query parameter, not in the body and not as a path
222
+ * segment: prefixes contain slashes, which a path segment cannot carry
223
+ * unambiguously, and this endpoint reads no body at all.
190
224
  *
191
- * The prefixes travel in the body rather than the path because they contain
192
- * slashes, which is why this DELETE carries one.
225
+ * Pass `purge: true` to also delete the memories already derived from the
226
+ * prefix. The default leaves them in place, so revoking an approval does not
227
+ * silently destroy knowledge.
193
228
  */
194
- removePrefixes(connectionId, prefixes) {
195
- return this.req("DELETE", `${V2}/connections/${seg(connectionId)}/s3/prefixes`, {
196
- body: { prefixes }
197
- });
229
+ removePrefix(connectionId, prefix = "", options = {}) {
230
+ const query = { prefix, purge: options.purge ?? false };
231
+ if (options.bucket !== void 0) query.bucket = options.bucket;
232
+ return this.req("DELETE", `${V2}/connections/${seg(connectionId)}/s3/prefixes`, { query });
198
233
  }
199
234
  /** Keys and patterns this connection will never sync. */
200
235
  exclusionPolicy(connectionId) {
@@ -249,9 +284,18 @@ var GranolaNamespace = class extends Namespace {
249
284
  linkIdentity(connectionId, body) {
250
285
  return this.req("POST", `${V2}/connections/${seg(connectionId)}/granola/identities/link`, { body });
251
286
  }
252
- /** Move an existing mapping to a different end user. */
253
- relinkIdentity(connectionId, body) {
254
- return this.req("POST", `${V2}/connections/${seg(connectionId)}/granola/identities/relink`, { body });
287
+ /**
288
+ * Re-run identity matching for this connection.
289
+ *
290
+ * Takes no arguments: the route reads no body and re-matches the whole roster.
291
+ * `body` is kept optional only so a forward-compatible field can be passed
292
+ * once the route grows one.
293
+ */
294
+ relinkIdentity(connectionId, body = {}) {
295
+ const hasFields = Object.keys(body).length > 0;
296
+ return this.req("POST", `${V2}/connections/${seg(connectionId)}/granola/identities/relink`, {
297
+ body: hasFields ? body : void 0
298
+ });
255
299
  }
256
300
  /** Effective Granola settings for this connection. */
257
301
  settings(connectionId) {
@@ -272,7 +316,9 @@ var ConnectionOAuthNamespace = class extends Namespace {
272
316
  * back — not your backend. Poll {@link status} to find out how it went.
273
317
  */
274
318
  initiate(provider, body = {}) {
275
- return this.req("POST", `${V2}/oauth/initiate`, { body: { provider, ...body } });
319
+ return this.req("POST", `${V2}/oauth/initiate`, {
320
+ body: { provider_id: provider, ...body }
321
+ });
276
322
  }
277
323
  /** Where an in-flight OAuth connection got to. */
278
324
  status(query) {
@@ -297,15 +343,21 @@ var ConnectionsNamespace = class extends Namespace {
297
343
  return this.req("GET", `${V2}/connections/${seg(connectionId)}`);
298
344
  }
299
345
  /** Connect a provider that authenticates with an API key or bot token. */
346
+ /**
347
+ * Connect a provider that authenticates with an API key or bot token.
348
+ *
349
+ * The wire field is `provider_id`; the argument is named `provider` because
350
+ * that is what the rest of this namespace calls it.
351
+ */
300
352
  createWithApiKey(provider, apiKey, body = {}) {
301
353
  return this.req("POST", `${V2}/connections/api-key`, {
302
- body: { provider, api_key: apiKey, ...body }
354
+ body: { provider_id: provider, api_key: apiKey, ...body }
303
355
  });
304
356
  }
305
357
  /** Connect a provider that needs a credential bundle, such as S3 keys. */
306
358
  createWithCredentials(provider, credentials, body = {}) {
307
359
  return this.req("POST", `${V2}/connections/credentials`, {
308
- body: { provider, credentials, ...body }
360
+ body: { provider_id: provider, credentials, ...body }
309
361
  });
310
362
  }
311
363
  /** Change a connection's name, schedule or settings. */
@@ -524,7 +576,7 @@ var IntegrationsNamespace = class extends Namespace {
524
576
  };
525
577
 
526
578
  // src/control-plane.ts
527
- var SDK_VERSION = "1.1.1";
579
+ var SDK_VERSION = "1.5.0";
528
580
  function safeJson(text) {
529
581
  try {
530
582
  return JSON.parse(text);
@@ -601,17 +653,51 @@ function boundedInteger(value, name, minimum, maximum) {
601
653
  throw new ValidationError(`${name} must be an integer between ${minimum} and ${maximum}`);
602
654
  }
603
655
  }
604
- function nonNegativeInteger(value, name) {
605
- if (!Number.isInteger(value) || value < 0) {
606
- throw new ValidationError(`${name} must be a non-negative integer`);
607
- }
608
- }
609
656
  function nonEmptyStrings(values, name) {
610
657
  if (!Array.isArray(values) || values.length === 0 || values.some((value) => typeof value !== "string" || !value.trim())) {
611
658
  throw new ValidationError(`${name} must contain at least one non-empty string`);
612
659
  }
613
660
  }
661
+ function projectName(value) {
662
+ nonEmpty(value, "name");
663
+ if (value.length > 200) throw new ValidationError("name may contain at most 200 characters");
664
+ }
665
+ function pathId(value, name, minimum = 1, maximum) {
666
+ nonEmpty(value, name);
667
+ const trimmed = value.trim();
668
+ if (trimmed.length < minimum || maximum !== void 0 && trimmed.length > maximum) {
669
+ const range = maximum === void 0 ? `at least ${minimum}` : `between ${minimum} and ${maximum}`;
670
+ throw new ValidationError(`${name} must contain ${range} characters`);
671
+ }
672
+ return encodeURIComponent(trimmed);
673
+ }
674
+ function validateWebhookRetryConfig(config) {
675
+ if (config.maxRetries !== void 0) boundedInteger(config.maxRetries, "maxRetries", 1, 10);
676
+ if (config.initialDelaySeconds !== void 0) boundedInteger(config.initialDelaySeconds, "initialDelaySeconds", 1, 60);
677
+ if (config.maxDelaySeconds !== void 0) boundedInteger(config.maxDelaySeconds, "maxDelaySeconds", 60, 86400);
678
+ if (config.backoffMultiplier !== void 0 && (!Number.isFinite(config.backoffMultiplier) || config.backoffMultiplier < 1 || config.backoffMultiplier > 5)) {
679
+ throw new ValidationError("backoffMultiplier must be between 1 and 5");
680
+ }
681
+ if (config.retryStatusCodes !== void 0 && (!Array.isArray(config.retryStatusCodes) || config.retryStatusCodes.length === 0 || config.retryStatusCodes.some((value) => typeof value !== "string" || value.trim().length === 0))) {
682
+ throw new ValidationError("retryStatusCodes must contain at least one non-empty string");
683
+ }
684
+ }
685
+ function validateWebhookSignatureConfig(config) {
686
+ if (config.algorithm !== void 0 && config.algorithm !== "hmac-sha256" && config.algorithm !== "hmac-sha512") {
687
+ throw new ValidationError("algorithm must be 'hmac-sha256' or 'hmac-sha512'");
688
+ }
689
+ for (const [name, value] of [["headerName", config.headerName], ["timestampHeader", config.timestampHeader]]) {
690
+ if (value !== void 0 && value.trim().length === 0) {
691
+ throw new ValidationError(`${name} must be a non-empty string`);
692
+ }
693
+ if (value !== void 0 && value.length > 64) {
694
+ throw new ValidationError(`${name} may contain at most 64 characters`);
695
+ }
696
+ }
697
+ if (config.toleranceSeconds !== void 0) boundedInteger(config.toleranceSeconds, "toleranceSeconds", 60, 3600);
698
+ }
614
699
  function webhookRetryConfig(config) {
700
+ validateWebhookRetryConfig(config);
615
701
  const wire = {};
616
702
  if (config.enabled !== void 0) wire.enabled = config.enabled;
617
703
  if (config.maxRetries !== void 0) wire.max_retries = config.maxRetries;
@@ -622,6 +708,7 @@ function webhookRetryConfig(config) {
622
708
  return wire;
623
709
  }
624
710
  function webhookSignatureConfig(config) {
711
+ validateWebhookSignatureConfig(config);
625
712
  const wire = {};
626
713
  if (config.algorithm !== void 0) wire.algorithm = config.algorithm;
627
714
  if (config.headerName !== void 0) wire.header_name = config.headerName;
@@ -629,6 +716,19 @@ function webhookSignatureConfig(config) {
629
716
  if (config.toleranceSeconds !== void 0) wire.tolerance_seconds = config.toleranceSeconds;
630
717
  return wire;
631
718
  }
719
+ function exportFilters(filters) {
720
+ const wire = {};
721
+ if (filters.q !== void 0) wire.q = filters.q;
722
+ if (filters.userId !== void 0) wire.user_id = filters.userId;
723
+ if (filters.isSummary !== void 0) wire.is_summary = filters.isSummary;
724
+ if (filters.tier !== void 0) wire.tier = filters.tier;
725
+ if (filters.source !== void 0) wire.source = filters.source;
726
+ if (filters.timeRange !== void 0) wire.time_range = filters.timeRange;
727
+ if (filters.dateFrom !== void 0) wire.date_from = filters.dateFrom;
728
+ if (filters.dateTo !== void 0) wire.date_to = filters.dateTo;
729
+ if (filters.includeSoftDeleted !== void 0) wire.include_soft_deleted = filters.includeSoftDeleted;
730
+ return wire;
731
+ }
632
732
  function validateWebhook(name, url, events) {
633
733
  nonEmpty(name, "name");
634
734
  if (name.length > 128) throw new ValidationError("name may contain at most 128 characters");
@@ -768,20 +868,6 @@ var ControlPlaneClient = class {
768
868
  async getCurrentPlan(options = {}) {
769
869
  return this.request("GET", "/org/billing/current-plan", options);
770
870
  }
771
- async listTeamMembers(options = {}) {
772
- return this.request("GET", "/admin/team/members", options);
773
- }
774
- async suspendTeamMember(memberId, options = {}) {
775
- positiveId(memberId, "memberId");
776
- return this.request("PATCH", `/admin/team/members/${memberId}`, {
777
- ...options,
778
- body: { status: "suspended" }
779
- });
780
- }
781
- async removeTeamMember(memberId, options = {}) {
782
- positiveId(memberId, "memberId");
783
- return this.request("DELETE", `/admin/team/members/${memberId}`, options);
784
- }
785
871
  async listSessions(options = {}) {
786
872
  return this.request("GET", "/auth/sessions", options);
787
873
  }
@@ -789,43 +875,6 @@ var ControlPlaneClient = class {
789
875
  positiveId(sessionId, "sessionId");
790
876
  return this.request("POST", `/auth/sessions/${sessionId}/revoke`, options);
791
877
  }
792
- async listAuditEvents(query = {}, options = {}) {
793
- if (query.limit !== void 0) boundedInteger(query.limit, "limit", 1, 200);
794
- if (query.cursor !== void 0) nonNegativeInteger(query.cursor, "cursor");
795
- if (query.skip !== void 0) nonNegativeInteger(query.skip, "skip");
796
- if (query.sortDirection !== void 0 && query.sortDirection !== "asc" && query.sortDirection !== "desc") {
797
- throw new ValidationError("sortDirection must be 'asc' or 'desc'");
798
- }
799
- const path = "/admin/audit-logs" + queryString({
800
- limit: query.limit,
801
- cursor: query.cursor,
802
- skip: query.skip,
803
- sort: query.sortDirection,
804
- tenant_id: query.tenantId,
805
- actor: query.actor,
806
- actor_email: query.actorEmail,
807
- ip: query.ip,
808
- action: query.action,
809
- resource_type: query.resourceType,
810
- resource_id: query.resourceId,
811
- severity: query.severity,
812
- category: query.category,
813
- start: query.start,
814
- end: query.end,
815
- success: query.success,
816
- source: query.source,
817
- ingest_method: query.ingestMethod,
818
- search: query.search,
819
- include_stats: query.includeStats
820
- });
821
- const raw = await this.request("GET", path, options);
822
- return {
823
- events: raw.logs ?? [],
824
- nextCursor: raw.nextCursor ?? null,
825
- stats: raw.stats ?? null,
826
- sort: raw.sort ?? query.sortDirection ?? "desc"
827
- };
828
- }
829
878
  async listIntegrations(query = {}, options = {}) {
830
879
  if (query.category !== void 0) nonEmpty(query.category, "category");
831
880
  const path = "/api/v1/integrations/catalog" + queryString({ category: query.category });
@@ -841,23 +890,36 @@ var ControlPlaneClient = class {
841
890
  async listOrganizations(options = {}) {
842
891
  return this.request("GET", "/organizations", options);
843
892
  }
844
- async listOrganizationMembers(options = {}) {
845
- return this.listTeamMembers(options);
846
- }
847
- async getOrganizationSettings(query = {}, options = {}) {
848
- if (query.tenantId !== void 0) nonEmpty(query.tenantId, "tenantId");
849
- const path = "/admin/tenant-settings" + queryString({ tenant_id: query.tenantId });
850
- return this.request("GET", path, options);
851
- }
852
893
  async listProjects(options = {}) {
853
894
  return this.request("GET", "/org/projects", options);
854
895
  }
896
+ async createProject(request, options = {}) {
897
+ projectName(request.name);
898
+ return this.request("POST", "/org/projects", { ...options, body: { name: request.name } });
899
+ }
900
+ async renameProject(projectId, request, options = {}) {
901
+ const encodedId = pathId(projectId, "projectId");
902
+ projectName(request.name);
903
+ return this.request("PATCH", `/org/projects/${encodedId}`, { ...options, body: { name: request.name } });
904
+ }
905
+ async archiveProject(projectId, options = {}) {
906
+ return this.request("POST", `/org/projects/${pathId(projectId, "projectId")}/archive`, options);
907
+ }
908
+ async unarchiveProject(projectId, options = {}) {
909
+ return this.request("POST", `/org/projects/${pathId(projectId, "projectId")}/unarchive`, options);
910
+ }
911
+ async deleteProject(projectId, options = {}) {
912
+ return this.request("DELETE", `/org/projects/${pathId(projectId, "projectId")}`, options);
913
+ }
855
914
  async createWebhook(request, options = {}) {
856
915
  validateWebhook(request.name, request.url, request.events);
857
916
  if (request.description !== void 0 && request.description.length > 500) {
858
917
  throw new ValidationError("description may contain at most 500 characters");
859
918
  }
860
- if (request.projectId !== void 0) nonEmpty(request.projectId, "projectId");
919
+ if (request.projectId !== void 0) {
920
+ nonEmpty(request.projectId, "projectId");
921
+ if (request.projectId.length > 64) throw new ValidationError("projectId may contain at most 64 characters");
922
+ }
861
923
  if (options.projectId !== void 0) nonEmpty(options.projectId, "projectId override");
862
924
  if (request.projectId && options.projectId && request.projectId.trim() !== options.projectId.trim()) {
863
925
  throw new ValidationError("request projectId and options projectId must match");
@@ -876,6 +938,28 @@ var ControlPlaneClient = class {
876
938
  async listWebhooks(options = {}) {
877
939
  return this.request("GET", "/org/webhooks", options);
878
940
  }
941
+ async getWebhook(endpointId, options = {}) {
942
+ positiveId(endpointId, "endpointId");
943
+ return this.request("GET", `/org/webhooks/${endpointId}`, options);
944
+ }
945
+ async getWebhookEventTypes(options = {}) {
946
+ return this.request("GET", "/org/webhooks/event-types", options);
947
+ }
948
+ async getWebhookHealth(options = {}) {
949
+ return this.request("GET", "/org/webhooks/health", options);
950
+ }
951
+ async pauseWebhook(endpointId, options = {}) {
952
+ positiveId(endpointId, "endpointId");
953
+ return this.request("POST", `/org/webhooks/${endpointId}/pause`, options);
954
+ }
955
+ async resumeWebhook(endpointId, options = {}) {
956
+ positiveId(endpointId, "endpointId");
957
+ return this.request("POST", `/org/webhooks/${endpointId}/resume`, options);
958
+ }
959
+ async rotateWebhookSecret(endpointId, options = {}) {
960
+ positiveId(endpointId, "endpointId");
961
+ return this.request("POST", `/org/webhooks/${endpointId}/rotate-secret`, options);
962
+ }
879
963
  async updateWebhook(endpointId, request, options = {}) {
880
964
  positiveId(endpointId, "endpointId");
881
965
  const body = {};
@@ -936,9 +1020,7 @@ var ControlPlaneClient = class {
936
1020
  async listWebhookDeliveries(endpointId, query = {}, options = {}) {
937
1021
  positiveId(endpointId, "endpointId");
938
1022
  if (query.page !== void 0) positiveId(query.page, "page");
939
- if (query.pageSize !== void 0 && (!Number.isInteger(query.pageSize) || query.pageSize < 1 || query.pageSize > 100)) {
940
- throw new ValidationError("pageSize must be an integer between 1 and 100");
941
- }
1023
+ if (query.pageSize !== void 0) boundedInteger(query.pageSize, "pageSize", 1, 100);
942
1024
  if (query.status !== void 0) nonEmpty(query.status, "status");
943
1025
  const path = `/org/webhooks/${endpointId}/deliveries` + queryString({
944
1026
  page: query.page,
@@ -947,10 +1029,62 @@ var ControlPlaneClient = class {
947
1029
  });
948
1030
  return this.request("GET", path, options);
949
1031
  }
1032
+ async getLatestWebhookDelivery(endpointId, options = {}) {
1033
+ positiveId(endpointId, "endpointId");
1034
+ return this.request("GET", `/org/webhooks/${endpointId}/deliveries/latest`, options);
1035
+ }
1036
+ async listRecentWebhookDeliveries(query = {}, options = {}) {
1037
+ if (query.page !== void 0) positiveId(query.page, "page");
1038
+ if (query.pageSize !== void 0) boundedInteger(query.pageSize, "pageSize", 1, 100);
1039
+ if (query.endpointId !== void 0) positiveId(query.endpointId, "endpointId");
1040
+ if (query.status !== void 0) nonEmpty(query.status, "status");
1041
+ const path = "/org/webhooks/deliveries/recent" + queryString({
1042
+ page: query.page,
1043
+ page_size: query.pageSize,
1044
+ endpoint_id: query.endpointId,
1045
+ status_filter: query.status
1046
+ });
1047
+ return this.request("GET", path, options);
1048
+ }
1049
+ async getWebhookDelivery(deliveryId, options = {}) {
1050
+ positiveId(deliveryId, "deliveryId");
1051
+ return this.request("GET", `/org/webhooks/deliveries/${deliveryId}`, options);
1052
+ }
1053
+ async retryWebhookDelivery(deliveryId, options = {}) {
1054
+ positiveId(deliveryId, "deliveryId");
1055
+ return this.request("POST", `/org/webhooks/deliveries/${deliveryId}/retry`, options);
1056
+ }
1057
+ async createExport(request, options = {}) {
1058
+ const format = request.format ?? "csv";
1059
+ const scope = request.scope ?? "filtered";
1060
+ if (format !== "csv" && format !== "jsonl") throw new ValidationError("format must be 'csv' or 'jsonl'");
1061
+ if (scope !== "filtered" && scope !== "all" && scope !== "date_range") {
1062
+ throw new ValidationError("scope must be 'filtered', 'all', or 'date_range'");
1063
+ }
1064
+ const body = { format, scope };
1065
+ if (request.filters !== void 0) body.filters = request.filters === null ? null : exportFilters(request.filters);
1066
+ return this.request("POST", "/exports", { ...options, body });
1067
+ }
1068
+ async listExports(query = {}, options = {}) {
1069
+ if (query.limit !== void 0) boundedInteger(query.limit, "limit", 1, 100);
1070
+ return this.request("GET", "/exports" + queryString({ limit: query.limit }), options);
1071
+ }
1072
+ async getExport(jobId, options = {}) {
1073
+ return this.request("GET", `/exports/${pathId(jobId, "jobId", 8, 64)}`, options);
1074
+ }
1075
+ async cancelExport(jobId, options = {}) {
1076
+ return this.request("POST", `/exports/${pathId(jobId, "jobId", 8, 64)}/cancel`, options);
1077
+ }
1078
+ async retryExport(jobId, options = {}) {
1079
+ return this.request("POST", `/exports/${pathId(jobId, "jobId", 8, 64)}/retry`, options);
1080
+ }
1081
+ async getExportDownloadUrl(jobId, options = {}) {
1082
+ return this.request("GET", `/exports/${pathId(jobId, "jobId", 8, 64)}/download-url`, options);
1083
+ }
950
1084
  };
951
1085
 
952
1086
  // src/index.ts
953
- var SDK_VERSION2 = "1.3.0";
1087
+ var SDK_VERSION2 = "1.5.0";
954
1088
  function camelToSnakeKey(key) {
955
1089
  return key.replace(/([A-Z])/g, "_$1").toLowerCase();
956
1090
  }