@persistmemory/sdk 0.1.2 → 0.2.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
@@ -238,6 +238,27 @@ var HttpClient = class {
238
238
  ...options ? { options } : {}
239
239
  });
240
240
  }
241
+ /** POST with a file as the body. The type describes the bytes, not JSON. */
242
+ async postBytes(path, bytes, contentType, query, options) {
243
+ return this.#request({
244
+ method: "POST",
245
+ path,
246
+ rawBody: bytes,
247
+ contentType,
248
+ ...query ? { query } : {},
249
+ ...options ? { options } : {}
250
+ });
251
+ }
252
+ /** GET that returns bytes rather than JSON, for downloading a file. */
253
+ async getBytes(path, query, options) {
254
+ return this.#request({
255
+ method: "GET",
256
+ path,
257
+ rawResponse: true,
258
+ ...query ? { query } : {},
259
+ ...options ? { options } : {}
260
+ });
261
+ }
241
262
  async patch(path, body, options) {
242
263
  return this.#request({
243
264
  method: "PATCH",
@@ -290,11 +311,20 @@ var HttpClient = class {
290
311
  this.#fetch(url, {
291
312
  method: request.method,
292
313
  headers: this.#headers(request),
293
- ...request.body !== void 0 ? { body: JSON.stringify(request.body) } : {},
314
+ ...request.rawBody !== void 0 ? { body: request.rawBody } : request.body !== void 0 ? { body: JSON.stringify(request.body) } : {},
294
315
  signal: deadline.signal
295
316
  }),
296
317
  deadline.signal
297
318
  );
319
+ if (request.rawResponse && response.ok) {
320
+ const disposition = response.headers.get("content-disposition") ?? "";
321
+ const named = /filename="([^"]+)"/.exec(disposition)?.[1];
322
+ return {
323
+ bytes: new Uint8Array(await response.arrayBuffer()),
324
+ contentType: response.headers.get("content-type") ?? "application/octet-stream",
325
+ ...named ? { filename: named } : {}
326
+ };
327
+ }
298
328
  const payload = await readBody(response);
299
329
  if (!response.ok) throw errorFromResponse(response.status, payload, response.headers);
300
330
  return payload;
@@ -315,9 +345,11 @@ var HttpClient = class {
315
345
  return {
316
346
  // The only place the key is ever read.
317
347
  authorization: `Bearer ${this.#apiKey}`,
318
- accept: "application/json",
348
+ // A download route answers with the file's own type, so `*/*` rather
349
+ // than a promise to accept only JSON that the server would have to break.
350
+ accept: request.rawResponse ? "*/*" : "application/json",
319
351
  "user-agent": this.#userAgent,
320
- ...request.body !== void 0 ? { "content-type": "application/json" } : {},
352
+ ...request.rawBody !== void 0 ? { "content-type": request.contentType ?? "application/octet-stream" } : request.body !== void 0 ? { "content-type": "application/json" } : {},
321
353
  ...request.options?.idempotencyKey ? { "idempotency-key": request.options.idempotencyKey } : {}
322
354
  };
323
355
  }
@@ -909,6 +941,121 @@ var Conversations = class {
909
941
  }
910
942
  };
911
943
 
944
+ // src/resources/google.ts
945
+ var Google = class {
946
+ #http;
947
+ constructor(http) {
948
+ this.#http = http;
949
+ }
950
+ /** Files by name, newest first. Omit the query for recently changed ones. */
951
+ async searchDrive(params = {}, options) {
952
+ return this.#http.get(
953
+ "/api/v1/google/drive/files",
954
+ {
955
+ ...params.query !== void 0 ? { query: params.query } : {},
956
+ ...params.limit !== void 0 ? { limit: params.limit } : {}
957
+ },
958
+ options
959
+ );
960
+ }
961
+ async getDriveFile(fileId, options) {
962
+ return this.#http.get(
963
+ `/api/v1/google/drive/files/${encodeURIComponent(fileId)}`,
964
+ void 0,
965
+ options
966
+ );
967
+ }
968
+ /**
969
+ * The bytes of a Drive file.
970
+ *
971
+ * A Google Doc, Sheet or Slide holds no bytes of its own and is exported on
972
+ * the way - a document as PDF, a spreadsheet as CSV - so `filename` comes
973
+ * back describing what it BECAME. Writing it under the id instead produces a
974
+ * file nothing will open.
975
+ */
976
+ async downloadDriveFile(fileId, options) {
977
+ return this.#http.getBytes(
978
+ `/api/v1/google/drive/files/${encodeURIComponent(fileId)}/content`,
979
+ void 0,
980
+ options
981
+ );
982
+ }
983
+ /**
984
+ * Writes a file into the user's Drive.
985
+ *
986
+ * Needs one of the Drive write permissions on their connection. A read-only
987
+ * grant is refused by Google, and the error names the missing permission
988
+ * rather than reporting a failed upload - one is fixed with a checkbox and
989
+ * the other sends somebody looking for a bug.
990
+ */
991
+ async saveToDrive(params, options) {
992
+ return this.#http.postBytes(
993
+ "/api/v1/google/drive/files",
994
+ params.bytes,
995
+ params.contentType ?? "application/octet-stream",
996
+ {
997
+ name: params.name,
998
+ ...params.folderId !== void 0 ? { folderId: params.folderId } : {}
999
+ },
1000
+ options
1001
+ );
1002
+ }
1003
+ /**
1004
+ * Recent messages - senders, subjects and a one-line preview, never bodies.
1005
+ *
1006
+ * `query` is Gmail's own syntax passed through as written: `from:priya`,
1007
+ * `has:attachment`, `newer_than:7d`. It selects within the connected mailbox
1008
+ * and cannot reach another one.
1009
+ */
1010
+ async searchMail(params = {}, options) {
1011
+ return this.#http.get(
1012
+ "/api/v1/google/mail",
1013
+ {
1014
+ ...params.query !== void 0 ? { query: params.query } : {},
1015
+ ...params.limit !== void 0 ? { limit: params.limit } : {}
1016
+ },
1017
+ options
1018
+ );
1019
+ }
1020
+ /** One message, with its body and the names of what is attached. */
1021
+ async readMail(messageId, options) {
1022
+ return this.#http.get(
1023
+ `/api/v1/google/mail/${encodeURIComponent(messageId)}`,
1024
+ void 0,
1025
+ options
1026
+ );
1027
+ }
1028
+ /**
1029
+ * The bytes of one attachment.
1030
+ *
1031
+ * Separate from `readMail` so listing a mailbox never drags attachments
1032
+ * across the network: a message with a 40 MB deck should not cost 40 MB to
1033
+ * summarise.
1034
+ */
1035
+ async downloadAttachment(messageId, attachmentId, options) {
1036
+ return this.#http.getBytes(
1037
+ `/api/v1/google/mail/${encodeURIComponent(messageId)}/attachments/${encodeURIComponent(attachmentId)}`,
1038
+ void 0,
1039
+ options
1040
+ );
1041
+ }
1042
+ /** Sends as the connected account. Needs the send permission. */
1043
+ async sendMail(params, options) {
1044
+ return this.#http.post("/api/v1/google/mail/send", params, options);
1045
+ }
1046
+ /** People in the user's contacts. Omit the query to list them. */
1047
+ async contacts(params = {}, options) {
1048
+ return this.#http.get(
1049
+ "/api/v1/google/contacts",
1050
+ {
1051
+ ...params.query !== void 0 ? { query: params.query } : {},
1052
+ ...params.limit !== void 0 ? { limit: params.limit } : {}
1053
+ },
1054
+ options
1055
+ );
1056
+ }
1057
+ };
1058
+
912
1059
  // src/resources/integrations.ts
913
1060
  var Integrations = class {
914
1061
  #http;
@@ -1046,6 +1193,8 @@ var PersistMemory = class {
1046
1193
  conflicts;
1047
1194
  conversations;
1048
1195
  integrations;
1196
+ /** Drive, mail and contacts on the user's connected Google account. */
1197
+ google;
1049
1198
  health;
1050
1199
  agent;
1051
1200
  #http;
@@ -1062,6 +1211,7 @@ var PersistMemory = class {
1062
1211
  this.conflicts = new Conflicts(this.#http);
1063
1212
  this.conversations = new Conversations(this.#http);
1064
1213
  this.integrations = new Integrations(this.#http);
1214
+ this.google = new Google(this.#http);
1065
1215
  this.health = new Health(this.#http);
1066
1216
  this.agent = new Agent(this.#http);
1067
1217
  }