notte-sdk 0.0.19 → 0.0.21

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
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  Agent: () => Agent,
34
+ Encryption: () => Encryption,
34
35
  NotteClient: () => NotteClient,
35
36
  NotteFunction: () => NotteFunction,
36
37
  NottePersona: () => NottePersona,
@@ -42,6 +43,12 @@ __export(index_exports, {
42
43
  agentStop: () => agentStop,
43
44
  anythingStart: () => anythingStart,
44
45
  client: () => client,
46
+ connectLinkContext: () => connectLinkContext,
47
+ connectLinkMailboxConnect: () => connectLinkMailboxConnect,
48
+ connectLinkMailboxSync: () => connectLinkMailboxSync,
49
+ connectLinkMailboxes: () => connectLinkMailboxes,
50
+ connectLinkStatus: () => connectLinkStatus,
51
+ connectLinkSubmit: () => connectLinkSubmit,
45
52
  createClient: () => createClient2,
46
53
  default: () => NotteClient,
47
54
  deleteSecret: () => deleteSecret,
@@ -55,10 +62,13 @@ __export(index_exports, {
55
62
  functionDelete: () => functionDelete,
56
63
  functionDownloadUrl: () => functionDownloadUrl,
57
64
  functionFork: () => functionFork,
65
+ functionMetadataUpdate: () => functionMetadataUpdate,
66
+ functionRollback: () => functionRollback,
58
67
  functionRunGetMetadata: () => functionRunGetMetadata,
59
68
  functionRunStart: () => functionRunStart,
60
69
  functionRunStop: () => functionRunStop,
61
70
  functionRunUpdateMetadata: () => functionRunUpdateMetadata,
71
+ functionRuntimeHealth: () => functionRuntimeHealth,
62
72
  functionScheduleDelete: () => functionScheduleDelete,
63
73
  functionScheduleSet: () => functionScheduleSet,
64
74
  functionUpdate: () => functionUpdate,
@@ -82,6 +92,7 @@ __export(index_exports, {
82
92
  mailboxList: () => mailboxList,
83
93
  mailboxSync: () => mailboxSync,
84
94
  mailboxUpdate: () => mailboxUpdate,
95
+ managedAuthConnectLinkCreate: () => managedAuthConnectLinkCreate,
85
96
  nudgePrompt: () => nudgePrompt,
86
97
  pageExecute: () => pageExecute,
87
98
  pageObserve: () => pageObserve,
@@ -128,7 +139,7 @@ __export(index_exports, {
128
139
  module.exports = __toCommonJS(index_exports);
129
140
 
130
141
  // src/version.ts
131
- var SDK_VERSION = true ? "0.0.19" : "dev";
142
+ var SDK_VERSION = true ? "0.0.21" : "dev";
132
143
  var SDK_PACKAGE_NAME = true ? "notte-sdk" : "notte-sdk";
133
144
 
134
145
  // src/version-check.ts
@@ -1165,6 +1176,11 @@ var listAgents = (options) => (options?.client ?? client).get({
1165
1176
  url: "/agents",
1166
1177
  ...options
1167
1178
  });
1179
+ var functionRuntimeHealth = (options) => (options?.client ?? client).get({
1180
+ security: [{ scheme: "bearer", type: "http" }],
1181
+ url: "/functions/health",
1182
+ ...options
1183
+ });
1168
1184
  var listFunctions = (options) => (options?.client ?? client).get({
1169
1185
  security: [{ scheme: "bearer", type: "http" }],
1170
1186
  url: "/functions",
@@ -1190,6 +1206,15 @@ var functionDownloadUrl = (options) => (options.client ?? client).get({
1190
1206
  url: "/functions/{function_id}",
1191
1207
  ...options
1192
1208
  });
1209
+ var functionMetadataUpdate = (options) => (options.client ?? client).patch({
1210
+ security: [{ scheme: "bearer", type: "http" }],
1211
+ url: "/functions/{function_id}",
1212
+ ...options,
1213
+ headers: {
1214
+ "Content-Type": "application/json",
1215
+ ...options.headers
1216
+ }
1217
+ });
1193
1218
  var functionUpdate = (options) => (options.client ?? client).post({
1194
1219
  ...formDataBodySerializer,
1195
1220
  security: [{ scheme: "bearer", type: "http" }],
@@ -1205,6 +1230,15 @@ var functionFork = (options) => (options.client ?? client).post({
1205
1230
  url: "/functions/{function_id}/fork",
1206
1231
  ...options
1207
1232
  });
1233
+ var functionRollback = (options) => (options.client ?? client).post({
1234
+ security: [{ scheme: "bearer", type: "http" }],
1235
+ url: "/functions/{function_id}/rollback",
1236
+ ...options,
1237
+ headers: {
1238
+ "Content-Type": "application/json",
1239
+ ...options.headers
1240
+ }
1241
+ });
1208
1242
  var functionScheduleDelete = (options) => (options.client ?? client).delete({
1209
1243
  security: [{ scheme: "bearer", type: "http" }],
1210
1244
  url: "/functions/{function_id}/schedule",
@@ -1455,6 +1489,42 @@ var mailboxUpdate = (options) => (options.client ?? client).patch({
1455
1489
  ...options.headers
1456
1490
  }
1457
1491
  });
1492
+ var managedAuthConnectLinkCreate = (options) => (options.client ?? client).post({
1493
+ security: [{ scheme: "bearer", type: "http" }],
1494
+ url: "/managed-auth/connect-links",
1495
+ ...options,
1496
+ headers: {
1497
+ "Content-Type": "application/json",
1498
+ ...options.headers
1499
+ }
1500
+ });
1501
+ var connectLinkContext = (options) => (options.client ?? client).post({ url: "/managed-auth/connect-links/context", ...options });
1502
+ var connectLinkMailboxes = (options) => (options.client ?? client).post({ url: "/managed-auth/connect-links/mailboxes", ...options });
1503
+ var connectLinkMailboxConnect = (options) => (options.client ?? client).post({
1504
+ url: "/managed-auth/connect-links/mailboxes/connect",
1505
+ ...options,
1506
+ headers: {
1507
+ "Content-Type": "application/json",
1508
+ ...options.headers
1509
+ }
1510
+ });
1511
+ var connectLinkMailboxSync = (options) => (options.client ?? client).post({
1512
+ url: "/managed-auth/connect-links/mailboxes/sync",
1513
+ ...options,
1514
+ headers: {
1515
+ "Content-Type": "application/json",
1516
+ ...options.headers
1517
+ }
1518
+ });
1519
+ var connectLinkSubmit = (options) => (options.client ?? client).post({
1520
+ url: "/managed-auth/connect-links/submit",
1521
+ ...options,
1522
+ headers: {
1523
+ "Content-Type": "application/json",
1524
+ ...options.headers
1525
+ }
1526
+ });
1527
+ var connectLinkStatus = (options) => (options.client ?? client).get({ url: "/managed-auth/connect-links/status", ...options });
1458
1528
  var improvePrompt = (options) => (options.client ?? client).post({
1459
1529
  url: "/prompts/improve",
1460
1530
  ...options,
@@ -1668,14 +1738,24 @@ var Session = class {
1668
1738
  this.isActive = false;
1669
1739
  this.response = null;
1670
1740
  this.client = client2;
1671
- const { open_viewer = false, ...sessionOptions } = options;
1741
+ const { open_viewer = false, headless, ...sessionOptions } = options;
1672
1742
  this.options = sessionOptions;
1673
1743
  this.openViewer = open_viewer;
1744
+ this.legacyHeadless = headless;
1674
1745
  }
1675
1746
  /**
1676
1747
  * Start the session - creates a new session on the server
1677
1748
  */
1678
1749
  async start() {
1750
+ if (this.legacyHeadless === false) {
1751
+ throw new Error(
1752
+ "Remote `headless: false` is no longer supported. `headless: true` sessions still include session replays and access to the live viewer."
1753
+ );
1754
+ }
1755
+ if (this.legacyHeadless === true) {
1756
+ console.warn("`headless: true` is deprecated and ignored; remote sessions always use the supported browser launch mode.");
1757
+ this.legacyHeadless = void 0;
1758
+ }
1679
1759
  if (this.isActive) {
1680
1760
  throw new Error("Session is already active");
1681
1761
  }
@@ -1703,7 +1783,7 @@ var Session = class {
1703
1783
  /**
1704
1784
  * Stop the session - terminates the session on the server
1705
1785
  */
1706
- async stop() {
1786
+ async stop(closeReason = "manual") {
1707
1787
  if (!this.isActive || !this.sessionId) {
1708
1788
  return;
1709
1789
  }
@@ -1711,6 +1791,9 @@ var Session = class {
1711
1791
  client: this.client.getClient(),
1712
1792
  path: {
1713
1793
  session_id: this.sessionId
1794
+ },
1795
+ query: {
1796
+ close_reason: closeReason
1714
1797
  }
1715
1798
  });
1716
1799
  if (response?.error) {
@@ -1929,10 +2012,21 @@ var Session = class {
1929
2012
  */
1930
2013
  async use(callback) {
1931
2014
  await this.start();
2015
+ let callbackFailed = false;
1932
2016
  try {
1933
2017
  return await callback(this);
2018
+ } catch (error) {
2019
+ callbackFailed = true;
2020
+ throw error;
1934
2021
  } finally {
1935
- await this.stop();
2022
+ try {
2023
+ await this.stop(callbackFailed ? "error" : "manual");
2024
+ } catch (stopError) {
2025
+ if (!callbackFailed) {
2026
+ throw stopError;
2027
+ }
2028
+ console.error("[Session] Failed to stop session after callback error:", stopError);
2029
+ }
1936
2030
  }
1937
2031
  }
1938
2032
  /**
@@ -2864,6 +2958,59 @@ var NottePersona = class {
2864
2958
  };
2865
2959
 
2866
2960
  // src/functions.ts
2961
+ var import_promises = require("fs/promises");
2962
+
2963
+ // src/encryption.ts
2964
+ var import_node_crypto = require("crypto");
2965
+ var SALT_LENGTH = 16;
2966
+ var PBKDF2_ITERATIONS = 1e5;
2967
+ var PBKDF2_KEY_LENGTH = 32;
2968
+ var FERNET_VERSION = 128;
2969
+ var FERNET_IV_LENGTH = 16;
2970
+ var FERNET_HMAC_LENGTH = 32;
2971
+ var FERNET_HEADER_LENGTH = 1 + 8 + FERNET_IV_LENGTH;
2972
+ var Encryption = class {
2973
+ constructor(rootKey) {
2974
+ if (!rootKey) {
2975
+ throw new Error("Root key cannot be empty");
2976
+ }
2977
+ this.rootKey = rootKey;
2978
+ }
2979
+ decrypt(token) {
2980
+ const combined = Buffer.from(token, "base64url");
2981
+ if (combined.length <= SALT_LENGTH) {
2982
+ throw new Error("Invalid encrypted token: too short to contain a salt");
2983
+ }
2984
+ const salt = combined.subarray(0, SALT_LENGTH);
2985
+ const fernetToken = combined.subarray(SALT_LENGTH).toString("utf8");
2986
+ const key = (0, import_node_crypto.pbkdf2Sync)(this.rootKey, salt, PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH, "sha256");
2987
+ return decryptFernet(fernetToken, key);
2988
+ }
2989
+ };
2990
+ function decryptFernet(token, key) {
2991
+ const signingKey = key.subarray(0, 16);
2992
+ const encryptionKey = key.subarray(16);
2993
+ const data = Buffer.from(token, "base64url");
2994
+ if (data.length < FERNET_HEADER_LENGTH + FERNET_HMAC_LENGTH) {
2995
+ throw new Error("Invalid Fernet token: too short");
2996
+ }
2997
+ if (data[0] !== FERNET_VERSION) {
2998
+ throw new Error(`Invalid Fernet token: unsupported version 0x${data[0].toString(16)}`);
2999
+ }
3000
+ const signed = data.subarray(0, data.length - FERNET_HMAC_LENGTH);
3001
+ const signature = data.subarray(data.length - FERNET_HMAC_LENGTH);
3002
+ const expected = (0, import_node_crypto.createHmac)("sha256", signingKey).update(signed).digest();
3003
+ if (!(0, import_node_crypto.timingSafeEqual)(signature, expected)) {
3004
+ throw new Error("Invalid Fernet token: signature verification failed");
3005
+ }
3006
+ const iv = signed.subarray(9, FERNET_HEADER_LENGTH);
3007
+ const ciphertext = signed.subarray(FERNET_HEADER_LENGTH);
3008
+ const decipher = (0, import_node_crypto.createDecipheriv)("aes-128-cbc", encryptionKey, iv);
3009
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
3010
+ }
3011
+
3012
+ // src/functions.ts
3013
+ var DOWNLOAD_TIMEOUT_MS = 3e4;
2867
3014
  var NotteFunction = class {
2868
3015
  constructor(client2, options) {
2869
3016
  this.client = client2;
@@ -2893,6 +3040,48 @@ var NotteFunction = class {
2893
3040
  throw new Error(`Failed to get function run metadata: ${error instanceof Error ? error.message : String(error)}`);
2894
3041
  }
2895
3042
  }
3043
+ /**
3044
+ * List the runs of this function.
3045
+ *
3046
+ * `only_active` is sent as `false` unless the caller says otherwise, and that
3047
+ * is deliberate rather than redundant. `GET /functions/{function_id}/runs`
3048
+ * now defaults it to `false` server-side as well, but it used to default to
3049
+ * `true`: the request model was a bare subclass of the session one, where
3050
+ * "only active" reads sensibly as "list my running sessions" and, for "list
3051
+ * this function's runs", was a trap. Hitting the endpoint with no query
3052
+ * parameters returned only the runs executing right now - an empty list for
3053
+ * any function that had finished. That cost us a bug in the anything-api
3054
+ * console, where a function with 15 recorded runs displayed "Nothing has run
3055
+ * this endpoint yet".
3056
+ *
3057
+ * Sending it explicitly pins this method to the documented behaviour against
3058
+ * whichever API version it is talking to, including deployments that predate
3059
+ * the server-side change. Callers who want only in-flight runs pass
3060
+ * `only_active: true`.
3061
+ */
3062
+ async runs(options = {}) {
3063
+ try {
3064
+ const response = await listFunctionRunsByFunctionId({
3065
+ client: this.client.getClient(),
3066
+ path: {
3067
+ function_id: this.functionId
3068
+ },
3069
+ query: {
3070
+ ...options,
3071
+ only_active: options.only_active ?? false
3072
+ }
3073
+ });
3074
+ if (response?.error) {
3075
+ throw new Error(`Failed to list function runs: ${formatError(response.error)}`);
3076
+ }
3077
+ return response.data;
3078
+ } catch (error) {
3079
+ if (error instanceof Error && error.message.startsWith("Failed to list function runs:")) {
3080
+ throw error;
3081
+ }
3082
+ throw new Error(`Failed to list function runs: ${error instanceof Error ? error.message : String(error)}`);
3083
+ }
3084
+ }
2896
3085
  /**
2897
3086
  * Start a function run
2898
3087
  */
@@ -2929,6 +3118,72 @@ var NotteFunction = class {
2929
3118
  throw new Error(`Failed to run function: ${error instanceof Error ? error.message : String(error)}`);
2930
3119
  }
2931
3120
  }
3121
+ /**
3122
+ * Get the download URL of the function code, decrypting it when the API
3123
+ * returns an encrypted one (Notte managed functions).
3124
+ */
3125
+ async getUrl(options = {}) {
3126
+ let response;
3127
+ try {
3128
+ response = await functionDownloadUrl({
3129
+ client: this.client.getClient(),
3130
+ path: {
3131
+ function_id: this.functionId
3132
+ },
3133
+ query: options.version ? { version: options.version } : {}
3134
+ });
3135
+ } catch (error) {
3136
+ throw new Error(`Failed to get function download url: ${error instanceof Error ? error.message : String(error)}`);
3137
+ }
3138
+ if (response?.error) {
3139
+ throw new Error(`Failed to get function download url: ${formatError(response.error)}`);
3140
+ }
3141
+ const url = response.data.url;
3142
+ if (isHttpUrl(url)) {
3143
+ return url;
3144
+ }
3145
+ const decryptionKey = options.decryption_key ?? this.decryptionKey;
3146
+ if (!decryptionKey) {
3147
+ throw new Error(
3148
+ "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>' })."
3149
+ );
3150
+ }
3151
+ let decrypted;
3152
+ try {
3153
+ decrypted = new Encryption(decryptionKey).decrypt(url);
3154
+ } catch (error) {
3155
+ 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.`);
3156
+ }
3157
+ if (!isHttpUrl(decrypted)) {
3158
+ 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.");
3159
+ }
3160
+ return decrypted;
3161
+ }
3162
+ /**
3163
+ * Download the function code as a python file. Returns the code, and writes
3164
+ * it to `path` when one is provided.
3165
+ */
3166
+ async download(options = {}) {
3167
+ const { path } = options;
3168
+ if (path !== void 0 && !path.endsWith(".py")) {
3169
+ throw new Error(`Code file path must end with .py, got '${path}'`);
3170
+ }
3171
+ const url = await this.getUrl(options);
3172
+ let response;
3173
+ try {
3174
+ response = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
3175
+ } catch (error) {
3176
+ throw new Error(`Failed to download function code: ${error instanceof Error ? error.message : String(error)}`);
3177
+ }
3178
+ if (!response.ok) {
3179
+ throw new Error(`Failed to download function code: ${response.status} ${response.statusText}`);
3180
+ }
3181
+ const code = await response.text();
3182
+ if (path !== void 0) {
3183
+ await (0, import_promises.writeFile)(path, code, "utf8");
3184
+ }
3185
+ return code;
3186
+ }
2932
3187
  /**
2933
3188
  * Get the function ID
2934
3189
  */
@@ -2936,6 +3191,9 @@ var NotteFunction = class {
2936
3191
  return this.functionId;
2937
3192
  }
2938
3193
  };
3194
+ function isHttpUrl(value) {
3195
+ return value.startsWith("https://") || value.startsWith("http://");
3196
+ }
2939
3197
 
2940
3198
  // src/client.ts
2941
3199
  var import_zod3 = require("zod");
@@ -3109,6 +3367,7 @@ var createClient2 = (config) => {
3109
3367
  // Annotate the CommonJS export names for ESM import in node:
3110
3368
  0 && (module.exports = {
3111
3369
  Agent,
3370
+ Encryption,
3112
3371
  NotteClient,
3113
3372
  NotteFunction,
3114
3373
  NottePersona,
@@ -3120,6 +3379,12 @@ var createClient2 = (config) => {
3120
3379
  agentStop,
3121
3380
  anythingStart,
3122
3381
  client,
3382
+ connectLinkContext,
3383
+ connectLinkMailboxConnect,
3384
+ connectLinkMailboxSync,
3385
+ connectLinkMailboxes,
3386
+ connectLinkStatus,
3387
+ connectLinkSubmit,
3123
3388
  createClient,
3124
3389
  deleteSecret,
3125
3390
  fileDownload,
@@ -3132,10 +3397,13 @@ var createClient2 = (config) => {
3132
3397
  functionDelete,
3133
3398
  functionDownloadUrl,
3134
3399
  functionFork,
3400
+ functionMetadataUpdate,
3401
+ functionRollback,
3135
3402
  functionRunGetMetadata,
3136
3403
  functionRunStart,
3137
3404
  functionRunStop,
3138
3405
  functionRunUpdateMetadata,
3406
+ functionRuntimeHealth,
3139
3407
  functionScheduleDelete,
3140
3408
  functionScheduleSet,
3141
3409
  functionUpdate,
@@ -3159,6 +3427,7 @@ var createClient2 = (config) => {
3159
3427
  mailboxList,
3160
3428
  mailboxSync,
3161
3429
  mailboxUpdate,
3430
+ managedAuthConnectLinkCreate,
3162
3431
  nudgePrompt,
3163
3432
  pageExecute,
3164
3433
  pageObserve,