notte-sdk 0.0.18 → 0.0.20

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.mjs CHANGED
@@ -6,7 +6,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  });
7
7
 
8
8
  // src/version.ts
9
- var SDK_VERSION = true ? "0.0.18" : "dev";
9
+ var SDK_VERSION = true ? "0.0.20" : "dev";
10
10
  var SDK_PACKAGE_NAME = true ? "notte-sdk" : "notte-sdk";
11
11
 
12
12
  // src/version-check.ts
@@ -1068,6 +1068,15 @@ var functionDownloadUrl = (options) => (options.client ?? client).get({
1068
1068
  url: "/functions/{function_id}",
1069
1069
  ...options
1070
1070
  });
1071
+ var functionMetadataUpdate = (options) => (options.client ?? client).patch({
1072
+ security: [{ scheme: "bearer", type: "http" }],
1073
+ url: "/functions/{function_id}",
1074
+ ...options,
1075
+ headers: {
1076
+ "Content-Type": "application/json",
1077
+ ...options.headers
1078
+ }
1079
+ });
1071
1080
  var functionUpdate = (options) => (options.client ?? client).post({
1072
1081
  ...formDataBodySerializer,
1073
1082
  security: [{ scheme: "bearer", type: "http" }],
@@ -1267,6 +1276,15 @@ var personaGet = (options) => (options.client ?? client).get({
1267
1276
  url: "/personas/{persona_id}",
1268
1277
  ...options
1269
1278
  });
1279
+ var personaUpdate = (options) => (options.client ?? client).patch({
1280
+ security: [{ scheme: "bearer", type: "http" }],
1281
+ url: "/personas/{persona_id}",
1282
+ ...options,
1283
+ headers: {
1284
+ "Content-Type": "application/json",
1285
+ ...options.headers
1286
+ }
1287
+ });
1270
1288
  var personaEmailsList = (options) => (options.client ?? client).get({
1271
1289
  security: [{ scheme: "bearer", type: "http" }],
1272
1290
  url: "/personas/{persona_id}/emails",
@@ -1282,6 +1300,48 @@ var personaSmsList = (options) => (options.client ?? client).get({
1282
1300
  url: "/personas/{persona_id}/sms",
1283
1301
  ...options
1284
1302
  });
1303
+ var mailboxConnectToken = (options) => (options.client ?? client).post({
1304
+ security: [{ scheme: "bearer", type: "http" }],
1305
+ url: "/mailboxes/connect-token",
1306
+ ...options,
1307
+ headers: {
1308
+ "Content-Type": "application/json",
1309
+ ...options.headers
1310
+ }
1311
+ });
1312
+ var mailboxSync = (options) => (options?.client ?? client).post({
1313
+ security: [{ scheme: "bearer", type: "http" }],
1314
+ url: "/mailboxes/sync",
1315
+ ...options,
1316
+ headers: {
1317
+ "Content-Type": "application/json",
1318
+ ...options?.headers
1319
+ }
1320
+ });
1321
+ var mailboxList = (options) => (options?.client ?? client).get({
1322
+ security: [{ scheme: "bearer", type: "http" }],
1323
+ url: "/mailboxes",
1324
+ ...options
1325
+ });
1326
+ var mailboxDelete = (options) => (options.client ?? client).delete({
1327
+ security: [{ scheme: "bearer", type: "http" }],
1328
+ url: "/mailboxes/{mailbox_id}",
1329
+ ...options
1330
+ });
1331
+ var mailboxGet = (options) => (options.client ?? client).get({
1332
+ security: [{ scheme: "bearer", type: "http" }],
1333
+ url: "/mailboxes/{mailbox_id}",
1334
+ ...options
1335
+ });
1336
+ var mailboxUpdate = (options) => (options.client ?? client).patch({
1337
+ security: [{ scheme: "bearer", type: "http" }],
1338
+ url: "/mailboxes/{mailbox_id}",
1339
+ ...options,
1340
+ headers: {
1341
+ "Content-Type": "application/json",
1342
+ ...options.headers
1343
+ }
1344
+ });
1285
1345
  var improvePrompt = (options) => (options.client ?? client).post({
1286
1346
  url: "/prompts/improve",
1287
1347
  ...options,
@@ -1495,14 +1555,24 @@ var Session = class {
1495
1555
  this.isActive = false;
1496
1556
  this.response = null;
1497
1557
  this.client = client2;
1498
- const { open_viewer = false, ...sessionOptions } = options;
1558
+ const { open_viewer = false, headless, ...sessionOptions } = options;
1499
1559
  this.options = sessionOptions;
1500
1560
  this.openViewer = open_viewer;
1561
+ this.legacyHeadless = headless;
1501
1562
  }
1502
1563
  /**
1503
1564
  * Start the session - creates a new session on the server
1504
1565
  */
1505
1566
  async start() {
1567
+ if (this.legacyHeadless === false) {
1568
+ throw new Error(
1569
+ "Remote `headless: false` is no longer supported. `headless: true` sessions still include session replays and access to the live viewer."
1570
+ );
1571
+ }
1572
+ if (this.legacyHeadless === true) {
1573
+ console.warn("`headless: true` is deprecated and ignored; remote sessions always use the supported browser launch mode.");
1574
+ this.legacyHeadless = void 0;
1575
+ }
1506
1576
  if (this.isActive) {
1507
1577
  throw new Error("Session is already active");
1508
1578
  }
@@ -1530,7 +1600,7 @@ var Session = class {
1530
1600
  /**
1531
1601
  * Stop the session - terminates the session on the server
1532
1602
  */
1533
- async stop() {
1603
+ async stop(closeReason = "manual") {
1534
1604
  if (!this.isActive || !this.sessionId) {
1535
1605
  return;
1536
1606
  }
@@ -1538,6 +1608,9 @@ var Session = class {
1538
1608
  client: this.client.getClient(),
1539
1609
  path: {
1540
1610
  session_id: this.sessionId
1611
+ },
1612
+ query: {
1613
+ close_reason: closeReason
1541
1614
  }
1542
1615
  });
1543
1616
  if (response?.error) {
@@ -1756,10 +1829,21 @@ var Session = class {
1756
1829
  */
1757
1830
  async use(callback) {
1758
1831
  await this.start();
1832
+ let callbackFailed = false;
1759
1833
  try {
1760
1834
  return await callback(this);
1835
+ } catch (error) {
1836
+ callbackFailed = true;
1837
+ throw error;
1761
1838
  } finally {
1762
- await this.stop();
1839
+ try {
1840
+ await this.stop(callbackFailed ? "error" : "manual");
1841
+ } catch (stopError) {
1842
+ if (!callbackFailed) {
1843
+ throw stopError;
1844
+ }
1845
+ console.error("[Session] Failed to stop session after callback error:", stopError);
1846
+ }
1763
1847
  }
1764
1848
  }
1765
1849
  /**
@@ -2691,6 +2775,59 @@ var NottePersona = class {
2691
2775
  };
2692
2776
 
2693
2777
  // src/functions.ts
2778
+ import { writeFile } from "fs/promises";
2779
+
2780
+ // src/encryption.ts
2781
+ import { createDecipheriv, createHmac, pbkdf2Sync, timingSafeEqual } from "crypto";
2782
+ var SALT_LENGTH = 16;
2783
+ var PBKDF2_ITERATIONS = 1e5;
2784
+ var PBKDF2_KEY_LENGTH = 32;
2785
+ var FERNET_VERSION = 128;
2786
+ var FERNET_IV_LENGTH = 16;
2787
+ var FERNET_HMAC_LENGTH = 32;
2788
+ var FERNET_HEADER_LENGTH = 1 + 8 + FERNET_IV_LENGTH;
2789
+ var Encryption = class {
2790
+ constructor(rootKey) {
2791
+ if (!rootKey) {
2792
+ throw new Error("Root key cannot be empty");
2793
+ }
2794
+ this.rootKey = rootKey;
2795
+ }
2796
+ decrypt(token) {
2797
+ const combined = Buffer.from(token, "base64url");
2798
+ if (combined.length <= SALT_LENGTH) {
2799
+ throw new Error("Invalid encrypted token: too short to contain a salt");
2800
+ }
2801
+ const salt = combined.subarray(0, SALT_LENGTH);
2802
+ const fernetToken = combined.subarray(SALT_LENGTH).toString("utf8");
2803
+ const key = pbkdf2Sync(this.rootKey, salt, PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH, "sha256");
2804
+ return decryptFernet(fernetToken, key);
2805
+ }
2806
+ };
2807
+ function decryptFernet(token, key) {
2808
+ const signingKey = key.subarray(0, 16);
2809
+ const encryptionKey = key.subarray(16);
2810
+ const data = Buffer.from(token, "base64url");
2811
+ if (data.length < FERNET_HEADER_LENGTH + FERNET_HMAC_LENGTH) {
2812
+ throw new Error("Invalid Fernet token: too short");
2813
+ }
2814
+ if (data[0] !== FERNET_VERSION) {
2815
+ throw new Error(`Invalid Fernet token: unsupported version 0x${data[0].toString(16)}`);
2816
+ }
2817
+ const signed = data.subarray(0, data.length - FERNET_HMAC_LENGTH);
2818
+ const signature = data.subarray(data.length - FERNET_HMAC_LENGTH);
2819
+ const expected = createHmac("sha256", signingKey).update(signed).digest();
2820
+ if (!timingSafeEqual(signature, expected)) {
2821
+ throw new Error("Invalid Fernet token: signature verification failed");
2822
+ }
2823
+ const iv = signed.subarray(9, FERNET_HEADER_LENGTH);
2824
+ const ciphertext = signed.subarray(FERNET_HEADER_LENGTH);
2825
+ const decipher = createDecipheriv("aes-128-cbc", encryptionKey, iv);
2826
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
2827
+ }
2828
+
2829
+ // src/functions.ts
2830
+ var DOWNLOAD_TIMEOUT_MS = 3e4;
2694
2831
  var NotteFunction = class {
2695
2832
  constructor(client2, options) {
2696
2833
  this.client = client2;
@@ -2756,6 +2893,72 @@ var NotteFunction = class {
2756
2893
  throw new Error(`Failed to run function: ${error instanceof Error ? error.message : String(error)}`);
2757
2894
  }
2758
2895
  }
2896
+ /**
2897
+ * Get the download URL of the function code, decrypting it when the API
2898
+ * returns an encrypted one (Notte managed functions).
2899
+ */
2900
+ async getUrl(options = {}) {
2901
+ let response;
2902
+ try {
2903
+ response = await functionDownloadUrl({
2904
+ client: this.client.getClient(),
2905
+ path: {
2906
+ function_id: this.functionId
2907
+ },
2908
+ query: options.version ? { version: options.version } : {}
2909
+ });
2910
+ } catch (error) {
2911
+ throw new Error(`Failed to get function download url: ${error instanceof Error ? error.message : String(error)}`);
2912
+ }
2913
+ if (response?.error) {
2914
+ throw new Error(`Failed to get function download url: ${formatError(response.error)}`);
2915
+ }
2916
+ const url = response.data.url;
2917
+ if (isHttpUrl(url)) {
2918
+ return url;
2919
+ }
2920
+ const decryptionKey = options.decryption_key ?? this.decryptionKey;
2921
+ if (!decryptionKey) {
2922
+ throw new Error(
2923
+ "Decryption key is required to decrypt the function download url. Set it when creating the function, i.e. client.NotteFunction({ function_id: '<your-function-id>', decryption_key: '<your-key>' })."
2924
+ );
2925
+ }
2926
+ let decrypted;
2927
+ try {
2928
+ decrypted = new Encryption(decryptionKey).decrypt(url);
2929
+ } catch (error) {
2930
+ throw new Error(`Failed to decrypt the function download url: ${error instanceof Error ? error.message : String(error)}. Contact support@notte.cc if you need help.`);
2931
+ }
2932
+ if (!isHttpUrl(decrypted)) {
2933
+ throw new Error("Failed to decrypt the function download url: the decrypted value is not a URL. Contact support@notte.cc if you need help.");
2934
+ }
2935
+ return decrypted;
2936
+ }
2937
+ /**
2938
+ * Download the function code as a python file. Returns the code, and writes
2939
+ * it to `path` when one is provided.
2940
+ */
2941
+ async download(options = {}) {
2942
+ const { path } = options;
2943
+ if (path !== void 0 && !path.endsWith(".py")) {
2944
+ throw new Error(`Code file path must end with .py, got '${path}'`);
2945
+ }
2946
+ const url = await this.getUrl(options);
2947
+ let response;
2948
+ try {
2949
+ response = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
2950
+ } catch (error) {
2951
+ throw new Error(`Failed to download function code: ${error instanceof Error ? error.message : String(error)}`);
2952
+ }
2953
+ if (!response.ok) {
2954
+ throw new Error(`Failed to download function code: ${response.status} ${response.statusText}`);
2955
+ }
2956
+ const code = await response.text();
2957
+ if (path !== void 0) {
2958
+ await writeFile(path, code, "utf8");
2959
+ }
2960
+ return code;
2961
+ }
2759
2962
  /**
2760
2963
  * Get the function ID
2761
2964
  */
@@ -2763,6 +2966,9 @@ var NotteFunction = class {
2763
2966
  return this.functionId;
2764
2967
  }
2765
2968
  };
2969
+ function isHttpUrl(value) {
2970
+ return value.startsWith("https://") || value.startsWith("http://");
2971
+ }
2766
2972
 
2767
2973
  // src/client.ts
2768
2974
  import { z as z3 } from "zod";
@@ -2935,6 +3141,7 @@ var createClient2 = (config) => {
2935
3141
  };
2936
3142
  export {
2937
3143
  Agent,
3144
+ Encryption,
2938
3145
  NotteClient,
2939
3146
  NotteFunction,
2940
3147
  NottePersona,
@@ -2959,6 +3166,7 @@ export {
2959
3166
  functionDelete,
2960
3167
  functionDownloadUrl,
2961
3168
  functionFork,
3169
+ functionMetadataUpdate,
2962
3170
  functionRunGetMetadata,
2963
3171
  functionRunStart,
2964
3172
  functionRunStop,
@@ -2980,6 +3188,12 @@ export {
2980
3188
  listSecrets,
2981
3189
  listSessions,
2982
3190
  listVaults,
3191
+ mailboxConnectToken,
3192
+ mailboxDelete,
3193
+ mailboxGet,
3194
+ mailboxList,
3195
+ mailboxSync,
3196
+ mailboxUpdate,
2983
3197
  nudgePrompt,
2984
3198
  pageExecute,
2985
3199
  pageObserve,
@@ -2990,6 +3204,7 @@ export {
2990
3204
  personaEmailsList,
2991
3205
  personaGet,
2992
3206
  personaSmsList,
3207
+ personaUpdate,
2993
3208
  profileCookiesGet,
2994
3209
  profileCookiesSet,
2995
3210
  profileCreate,