notte-sdk 0.0.19 → 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/README.md +28 -0
- package/dist/index.d.mts +508 -34
- package/dist/index.d.ts +508 -34
- package/dist/index.js +163 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +161 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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,
|
|
@@ -55,6 +56,7 @@ __export(index_exports, {
|
|
|
55
56
|
functionDelete: () => functionDelete,
|
|
56
57
|
functionDownloadUrl: () => functionDownloadUrl,
|
|
57
58
|
functionFork: () => functionFork,
|
|
59
|
+
functionMetadataUpdate: () => functionMetadataUpdate,
|
|
58
60
|
functionRunGetMetadata: () => functionRunGetMetadata,
|
|
59
61
|
functionRunStart: () => functionRunStart,
|
|
60
62
|
functionRunStop: () => functionRunStop,
|
|
@@ -128,7 +130,7 @@ __export(index_exports, {
|
|
|
128
130
|
module.exports = __toCommonJS(index_exports);
|
|
129
131
|
|
|
130
132
|
// src/version.ts
|
|
131
|
-
var SDK_VERSION = true ? "0.0.
|
|
133
|
+
var SDK_VERSION = true ? "0.0.20" : "dev";
|
|
132
134
|
var SDK_PACKAGE_NAME = true ? "notte-sdk" : "notte-sdk";
|
|
133
135
|
|
|
134
136
|
// src/version-check.ts
|
|
@@ -1190,6 +1192,15 @@ var functionDownloadUrl = (options) => (options.client ?? client).get({
|
|
|
1190
1192
|
url: "/functions/{function_id}",
|
|
1191
1193
|
...options
|
|
1192
1194
|
});
|
|
1195
|
+
var functionMetadataUpdate = (options) => (options.client ?? client).patch({
|
|
1196
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
1197
|
+
url: "/functions/{function_id}",
|
|
1198
|
+
...options,
|
|
1199
|
+
headers: {
|
|
1200
|
+
"Content-Type": "application/json",
|
|
1201
|
+
...options.headers
|
|
1202
|
+
}
|
|
1203
|
+
});
|
|
1193
1204
|
var functionUpdate = (options) => (options.client ?? client).post({
|
|
1194
1205
|
...formDataBodySerializer,
|
|
1195
1206
|
security: [{ scheme: "bearer", type: "http" }],
|
|
@@ -1668,14 +1679,24 @@ var Session = class {
|
|
|
1668
1679
|
this.isActive = false;
|
|
1669
1680
|
this.response = null;
|
|
1670
1681
|
this.client = client2;
|
|
1671
|
-
const { open_viewer = false, ...sessionOptions } = options;
|
|
1682
|
+
const { open_viewer = false, headless, ...sessionOptions } = options;
|
|
1672
1683
|
this.options = sessionOptions;
|
|
1673
1684
|
this.openViewer = open_viewer;
|
|
1685
|
+
this.legacyHeadless = headless;
|
|
1674
1686
|
}
|
|
1675
1687
|
/**
|
|
1676
1688
|
* Start the session - creates a new session on the server
|
|
1677
1689
|
*/
|
|
1678
1690
|
async start() {
|
|
1691
|
+
if (this.legacyHeadless === false) {
|
|
1692
|
+
throw new Error(
|
|
1693
|
+
"Remote `headless: false` is no longer supported. `headless: true` sessions still include session replays and access to the live viewer."
|
|
1694
|
+
);
|
|
1695
|
+
}
|
|
1696
|
+
if (this.legacyHeadless === true) {
|
|
1697
|
+
console.warn("`headless: true` is deprecated and ignored; remote sessions always use the supported browser launch mode.");
|
|
1698
|
+
this.legacyHeadless = void 0;
|
|
1699
|
+
}
|
|
1679
1700
|
if (this.isActive) {
|
|
1680
1701
|
throw new Error("Session is already active");
|
|
1681
1702
|
}
|
|
@@ -1703,7 +1724,7 @@ var Session = class {
|
|
|
1703
1724
|
/**
|
|
1704
1725
|
* Stop the session - terminates the session on the server
|
|
1705
1726
|
*/
|
|
1706
|
-
async stop() {
|
|
1727
|
+
async stop(closeReason = "manual") {
|
|
1707
1728
|
if (!this.isActive || !this.sessionId) {
|
|
1708
1729
|
return;
|
|
1709
1730
|
}
|
|
@@ -1711,6 +1732,9 @@ var Session = class {
|
|
|
1711
1732
|
client: this.client.getClient(),
|
|
1712
1733
|
path: {
|
|
1713
1734
|
session_id: this.sessionId
|
|
1735
|
+
},
|
|
1736
|
+
query: {
|
|
1737
|
+
close_reason: closeReason
|
|
1714
1738
|
}
|
|
1715
1739
|
});
|
|
1716
1740
|
if (response?.error) {
|
|
@@ -1929,10 +1953,21 @@ var Session = class {
|
|
|
1929
1953
|
*/
|
|
1930
1954
|
async use(callback) {
|
|
1931
1955
|
await this.start();
|
|
1956
|
+
let callbackFailed = false;
|
|
1932
1957
|
try {
|
|
1933
1958
|
return await callback(this);
|
|
1959
|
+
} catch (error) {
|
|
1960
|
+
callbackFailed = true;
|
|
1961
|
+
throw error;
|
|
1934
1962
|
} finally {
|
|
1935
|
-
|
|
1963
|
+
try {
|
|
1964
|
+
await this.stop(callbackFailed ? "error" : "manual");
|
|
1965
|
+
} catch (stopError) {
|
|
1966
|
+
if (!callbackFailed) {
|
|
1967
|
+
throw stopError;
|
|
1968
|
+
}
|
|
1969
|
+
console.error("[Session] Failed to stop session after callback error:", stopError);
|
|
1970
|
+
}
|
|
1936
1971
|
}
|
|
1937
1972
|
}
|
|
1938
1973
|
/**
|
|
@@ -2864,6 +2899,59 @@ var NottePersona = class {
|
|
|
2864
2899
|
};
|
|
2865
2900
|
|
|
2866
2901
|
// src/functions.ts
|
|
2902
|
+
var import_promises = require("fs/promises");
|
|
2903
|
+
|
|
2904
|
+
// src/encryption.ts
|
|
2905
|
+
var import_node_crypto = require("crypto");
|
|
2906
|
+
var SALT_LENGTH = 16;
|
|
2907
|
+
var PBKDF2_ITERATIONS = 1e5;
|
|
2908
|
+
var PBKDF2_KEY_LENGTH = 32;
|
|
2909
|
+
var FERNET_VERSION = 128;
|
|
2910
|
+
var FERNET_IV_LENGTH = 16;
|
|
2911
|
+
var FERNET_HMAC_LENGTH = 32;
|
|
2912
|
+
var FERNET_HEADER_LENGTH = 1 + 8 + FERNET_IV_LENGTH;
|
|
2913
|
+
var Encryption = class {
|
|
2914
|
+
constructor(rootKey) {
|
|
2915
|
+
if (!rootKey) {
|
|
2916
|
+
throw new Error("Root key cannot be empty");
|
|
2917
|
+
}
|
|
2918
|
+
this.rootKey = rootKey;
|
|
2919
|
+
}
|
|
2920
|
+
decrypt(token) {
|
|
2921
|
+
const combined = Buffer.from(token, "base64url");
|
|
2922
|
+
if (combined.length <= SALT_LENGTH) {
|
|
2923
|
+
throw new Error("Invalid encrypted token: too short to contain a salt");
|
|
2924
|
+
}
|
|
2925
|
+
const salt = combined.subarray(0, SALT_LENGTH);
|
|
2926
|
+
const fernetToken = combined.subarray(SALT_LENGTH).toString("utf8");
|
|
2927
|
+
const key = (0, import_node_crypto.pbkdf2Sync)(this.rootKey, salt, PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH, "sha256");
|
|
2928
|
+
return decryptFernet(fernetToken, key);
|
|
2929
|
+
}
|
|
2930
|
+
};
|
|
2931
|
+
function decryptFernet(token, key) {
|
|
2932
|
+
const signingKey = key.subarray(0, 16);
|
|
2933
|
+
const encryptionKey = key.subarray(16);
|
|
2934
|
+
const data = Buffer.from(token, "base64url");
|
|
2935
|
+
if (data.length < FERNET_HEADER_LENGTH + FERNET_HMAC_LENGTH) {
|
|
2936
|
+
throw new Error("Invalid Fernet token: too short");
|
|
2937
|
+
}
|
|
2938
|
+
if (data[0] !== FERNET_VERSION) {
|
|
2939
|
+
throw new Error(`Invalid Fernet token: unsupported version 0x${data[0].toString(16)}`);
|
|
2940
|
+
}
|
|
2941
|
+
const signed = data.subarray(0, data.length - FERNET_HMAC_LENGTH);
|
|
2942
|
+
const signature = data.subarray(data.length - FERNET_HMAC_LENGTH);
|
|
2943
|
+
const expected = (0, import_node_crypto.createHmac)("sha256", signingKey).update(signed).digest();
|
|
2944
|
+
if (!(0, import_node_crypto.timingSafeEqual)(signature, expected)) {
|
|
2945
|
+
throw new Error("Invalid Fernet token: signature verification failed");
|
|
2946
|
+
}
|
|
2947
|
+
const iv = signed.subarray(9, FERNET_HEADER_LENGTH);
|
|
2948
|
+
const ciphertext = signed.subarray(FERNET_HEADER_LENGTH);
|
|
2949
|
+
const decipher = (0, import_node_crypto.createDecipheriv)("aes-128-cbc", encryptionKey, iv);
|
|
2950
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
// src/functions.ts
|
|
2954
|
+
var DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
2867
2955
|
var NotteFunction = class {
|
|
2868
2956
|
constructor(client2, options) {
|
|
2869
2957
|
this.client = client2;
|
|
@@ -2929,6 +3017,72 @@ var NotteFunction = class {
|
|
|
2929
3017
|
throw new Error(`Failed to run function: ${error instanceof Error ? error.message : String(error)}`);
|
|
2930
3018
|
}
|
|
2931
3019
|
}
|
|
3020
|
+
/**
|
|
3021
|
+
* Get the download URL of the function code, decrypting it when the API
|
|
3022
|
+
* returns an encrypted one (Notte managed functions).
|
|
3023
|
+
*/
|
|
3024
|
+
async getUrl(options = {}) {
|
|
3025
|
+
let response;
|
|
3026
|
+
try {
|
|
3027
|
+
response = await functionDownloadUrl({
|
|
3028
|
+
client: this.client.getClient(),
|
|
3029
|
+
path: {
|
|
3030
|
+
function_id: this.functionId
|
|
3031
|
+
},
|
|
3032
|
+
query: options.version ? { version: options.version } : {}
|
|
3033
|
+
});
|
|
3034
|
+
} catch (error) {
|
|
3035
|
+
throw new Error(`Failed to get function download url: ${error instanceof Error ? error.message : String(error)}`);
|
|
3036
|
+
}
|
|
3037
|
+
if (response?.error) {
|
|
3038
|
+
throw new Error(`Failed to get function download url: ${formatError(response.error)}`);
|
|
3039
|
+
}
|
|
3040
|
+
const url = response.data.url;
|
|
3041
|
+
if (isHttpUrl(url)) {
|
|
3042
|
+
return url;
|
|
3043
|
+
}
|
|
3044
|
+
const decryptionKey = options.decryption_key ?? this.decryptionKey;
|
|
3045
|
+
if (!decryptionKey) {
|
|
3046
|
+
throw new Error(
|
|
3047
|
+
"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>' })."
|
|
3048
|
+
);
|
|
3049
|
+
}
|
|
3050
|
+
let decrypted;
|
|
3051
|
+
try {
|
|
3052
|
+
decrypted = new Encryption(decryptionKey).decrypt(url);
|
|
3053
|
+
} catch (error) {
|
|
3054
|
+
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.`);
|
|
3055
|
+
}
|
|
3056
|
+
if (!isHttpUrl(decrypted)) {
|
|
3057
|
+
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.");
|
|
3058
|
+
}
|
|
3059
|
+
return decrypted;
|
|
3060
|
+
}
|
|
3061
|
+
/**
|
|
3062
|
+
* Download the function code as a python file. Returns the code, and writes
|
|
3063
|
+
* it to `path` when one is provided.
|
|
3064
|
+
*/
|
|
3065
|
+
async download(options = {}) {
|
|
3066
|
+
const { path } = options;
|
|
3067
|
+
if (path !== void 0 && !path.endsWith(".py")) {
|
|
3068
|
+
throw new Error(`Code file path must end with .py, got '${path}'`);
|
|
3069
|
+
}
|
|
3070
|
+
const url = await this.getUrl(options);
|
|
3071
|
+
let response;
|
|
3072
|
+
try {
|
|
3073
|
+
response = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
|
|
3074
|
+
} catch (error) {
|
|
3075
|
+
throw new Error(`Failed to download function code: ${error instanceof Error ? error.message : String(error)}`);
|
|
3076
|
+
}
|
|
3077
|
+
if (!response.ok) {
|
|
3078
|
+
throw new Error(`Failed to download function code: ${response.status} ${response.statusText}`);
|
|
3079
|
+
}
|
|
3080
|
+
const code = await response.text();
|
|
3081
|
+
if (path !== void 0) {
|
|
3082
|
+
await (0, import_promises.writeFile)(path, code, "utf8");
|
|
3083
|
+
}
|
|
3084
|
+
return code;
|
|
3085
|
+
}
|
|
2932
3086
|
/**
|
|
2933
3087
|
* Get the function ID
|
|
2934
3088
|
*/
|
|
@@ -2936,6 +3090,9 @@ var NotteFunction = class {
|
|
|
2936
3090
|
return this.functionId;
|
|
2937
3091
|
}
|
|
2938
3092
|
};
|
|
3093
|
+
function isHttpUrl(value) {
|
|
3094
|
+
return value.startsWith("https://") || value.startsWith("http://");
|
|
3095
|
+
}
|
|
2939
3096
|
|
|
2940
3097
|
// src/client.ts
|
|
2941
3098
|
var import_zod3 = require("zod");
|
|
@@ -3109,6 +3266,7 @@ var createClient2 = (config) => {
|
|
|
3109
3266
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3110
3267
|
0 && (module.exports = {
|
|
3111
3268
|
Agent,
|
|
3269
|
+
Encryption,
|
|
3112
3270
|
NotteClient,
|
|
3113
3271
|
NotteFunction,
|
|
3114
3272
|
NottePersona,
|
|
@@ -3132,6 +3290,7 @@ var createClient2 = (config) => {
|
|
|
3132
3290
|
functionDelete,
|
|
3133
3291
|
functionDownloadUrl,
|
|
3134
3292
|
functionFork,
|
|
3293
|
+
functionMetadataUpdate,
|
|
3135
3294
|
functionRunGetMetadata,
|
|
3136
3295
|
functionRunStart,
|
|
3137
3296
|
functionRunStop,
|