@persistmemory/sdk 0.1.1 → 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;
@@ -995,6 +1142,44 @@ var Health = class {
995
1142
  }
996
1143
  };
997
1144
 
1145
+ // src/resources/agent.ts
1146
+ var Agent = class {
1147
+ #http;
1148
+ constructor(http) {
1149
+ this.#http = http;
1150
+ }
1151
+ /** The row, including whether it finished and how large the result is. */
1152
+ async request(id, options) {
1153
+ return this.#http.get(
1154
+ `/api/v1/agent/request/${encodeURIComponent(id)}`,
1155
+ void 0,
1156
+ options
1157
+ );
1158
+ }
1159
+ /**
1160
+ * A short-lived link to the bytes of a finished request.
1161
+ *
1162
+ * Returns the URL rather than the file, and that is a deliberate limit of
1163
+ * this package rather than an oversight. The transport under every other
1164
+ * method parses JSON, retries, and attaches the API key; none of those is
1165
+ * right for a hundred-megabyte binary body, and building a second request
1166
+ * path inside the SDK to serve one method is how a client ends up with two
1167
+ * retry policies that differ only during an outage. Fetch the URL with
1168
+ * whatever already streams in your runtime - it needs no credential, which
1169
+ * is the whole reason it is signed.
1170
+ *
1171
+ * Treat the URL as the file. It is a bearer credential for exactly one
1172
+ * object, it expires in minutes, and it should not be logged or stored.
1173
+ */
1174
+ async downloadLink(id, options) {
1175
+ return this.#http.get(
1176
+ `/api/v1/agent/request/${encodeURIComponent(id)}/download`,
1177
+ void 0,
1178
+ options
1179
+ );
1180
+ }
1181
+ };
1182
+
998
1183
  // src/client.ts
999
1184
  var PersistMemory = class {
1000
1185
  memories;
@@ -1008,7 +1193,10 @@ var PersistMemory = class {
1008
1193
  conflicts;
1009
1194
  conversations;
1010
1195
  integrations;
1196
+ /** Drive, mail and contacts on the user's connected Google account. */
1197
+ google;
1011
1198
  health;
1199
+ agent;
1012
1200
  #http;
1013
1201
  constructor(options) {
1014
1202
  this.#http = new HttpClient(options);
@@ -1023,7 +1211,9 @@ var PersistMemory = class {
1023
1211
  this.conflicts = new Conflicts(this.#http);
1024
1212
  this.conversations = new Conversations(this.#http);
1025
1213
  this.integrations = new Integrations(this.#http);
1214
+ this.google = new Google(this.#http);
1026
1215
  this.health = new Health(this.#http);
1216
+ this.agent = new Agent(this.#http);
1027
1217
  }
1028
1218
  /**
1029
1219
  * An escape hatch for an endpoint this package has not caught up with.