@flutchai/flutch-sdk 0.2.9 → 0.2.10
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.cjs +215 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -2
- package/dist/index.d.ts +61 -2
- package/dist/index.js +211 -11
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.cjs
CHANGED
|
@@ -53,6 +53,7 @@ var path2__namespace = /*#__PURE__*/_interopNamespace(path2);
|
|
|
53
53
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
54
54
|
var net__namespace = /*#__PURE__*/_interopNamespace(net);
|
|
55
55
|
var mongoose__default = /*#__PURE__*/_interopDefault(mongoose);
|
|
56
|
+
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
56
57
|
var LangGraph__namespace = /*#__PURE__*/_interopNamespace(LangGraph);
|
|
57
58
|
var axios2__default = /*#__PURE__*/_interopDefault(axios2);
|
|
58
59
|
|
|
@@ -1365,10 +1366,10 @@ exports.AbstractGraphBuilder = class AbstractGraphBuilder {
|
|
|
1365
1366
|
return null;
|
|
1366
1367
|
}
|
|
1367
1368
|
try {
|
|
1368
|
-
const
|
|
1369
|
-
const
|
|
1370
|
-
const manifestFullPath =
|
|
1371
|
-
const manifestContent = await
|
|
1369
|
+
const fs3 = await import('fs/promises');
|
|
1370
|
+
const path4 = await import('path');
|
|
1371
|
+
const manifestFullPath = path4.resolve(this.manifestPath);
|
|
1372
|
+
const manifestContent = await fs3.readFile(manifestFullPath, "utf-8");
|
|
1372
1373
|
const manifest = JSON.parse(manifestContent);
|
|
1373
1374
|
this.manifest = manifest;
|
|
1374
1375
|
return manifest;
|
|
@@ -1387,10 +1388,10 @@ exports.AbstractGraphBuilder = class AbstractGraphBuilder {
|
|
|
1387
1388
|
return null;
|
|
1388
1389
|
}
|
|
1389
1390
|
try {
|
|
1390
|
-
const
|
|
1391
|
-
const
|
|
1392
|
-
const manifestFullPath =
|
|
1393
|
-
const manifestContent =
|
|
1391
|
+
const fs3 = __require("fs");
|
|
1392
|
+
const path4 = __require("path");
|
|
1393
|
+
const manifestFullPath = path4.resolve(this.manifestPath);
|
|
1394
|
+
const manifestContent = fs3.readFileSync(manifestFullPath, "utf-8");
|
|
1394
1395
|
const manifest = JSON.parse(manifestContent);
|
|
1395
1396
|
this.manifest = manifest;
|
|
1396
1397
|
return manifest;
|
|
@@ -1430,12 +1431,12 @@ exports.AbstractGraphBuilder = class AbstractGraphBuilder {
|
|
|
1430
1431
|
let configSchema = null;
|
|
1431
1432
|
if (versionConfig.configSchemaPath) {
|
|
1432
1433
|
try {
|
|
1433
|
-
const
|
|
1434
|
+
const fs3 = await import('fs/promises');
|
|
1434
1435
|
const schemaPath = path2__namespace.resolve(
|
|
1435
1436
|
process.cwd(),
|
|
1436
1437
|
versionConfig.configSchemaPath
|
|
1437
1438
|
);
|
|
1438
|
-
const schemaContent = await
|
|
1439
|
+
const schemaContent = await fs3.readFile(schemaPath, "utf-8");
|
|
1439
1440
|
const schemaData = JSON.parse(schemaContent);
|
|
1440
1441
|
configSchema = schemaData.schema;
|
|
1441
1442
|
} catch (error) {
|
|
@@ -7325,6 +7326,205 @@ exports.StaticDiscovery = class StaticDiscovery {
|
|
|
7325
7326
|
exports.StaticDiscovery = __decorateClass([
|
|
7326
7327
|
common.Injectable()
|
|
7327
7328
|
], exports.StaticDiscovery);
|
|
7329
|
+
var ALGORITHM = "aes-256-cbc";
|
|
7330
|
+
var IV_LENGTH = 16;
|
|
7331
|
+
function encryptTokens(tokens, key) {
|
|
7332
|
+
const iv = crypto__namespace.randomBytes(IV_LENGTH);
|
|
7333
|
+
const cipher = crypto__namespace.createCipheriv(
|
|
7334
|
+
ALGORITHM,
|
|
7335
|
+
Buffer.from(key),
|
|
7336
|
+
iv
|
|
7337
|
+
);
|
|
7338
|
+
const json = JSON.stringify(tokens);
|
|
7339
|
+
let encrypted = cipher.update(json, "utf8", "hex");
|
|
7340
|
+
encrypted += cipher.final("hex");
|
|
7341
|
+
return iv.toString("hex") + ":" + encrypted;
|
|
7342
|
+
}
|
|
7343
|
+
function decryptTokens(encrypted, key) {
|
|
7344
|
+
const separatorIndex = encrypted.indexOf(":");
|
|
7345
|
+
if (separatorIndex === -1) {
|
|
7346
|
+
throw new Error("Invalid encrypted token format");
|
|
7347
|
+
}
|
|
7348
|
+
const ivHex = encrypted.substring(0, separatorIndex);
|
|
7349
|
+
const encryptedData = encrypted.substring(separatorIndex + 1);
|
|
7350
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
7351
|
+
const decipher = crypto__namespace.createDecipheriv(
|
|
7352
|
+
ALGORITHM,
|
|
7353
|
+
Buffer.from(key),
|
|
7354
|
+
iv
|
|
7355
|
+
);
|
|
7356
|
+
let decrypted = decipher.update(encryptedData, "hex", "utf8");
|
|
7357
|
+
decrypted += decipher.final("utf8");
|
|
7358
|
+
return JSON.parse(decrypted);
|
|
7359
|
+
}
|
|
7360
|
+
|
|
7361
|
+
// src/oauth/oauth-token.manager.ts
|
|
7362
|
+
var EXPIRY_BUFFER_MS = 6e4;
|
|
7363
|
+
var OAuthTokenManager = class {
|
|
7364
|
+
store;
|
|
7365
|
+
encryptionKey;
|
|
7366
|
+
cache = /* @__PURE__ */ new Map();
|
|
7367
|
+
constructor(options) {
|
|
7368
|
+
if (!options.encryptionKey || options.encryptionKey.length < 32) {
|
|
7369
|
+
throw new Error(
|
|
7370
|
+
"OAUTH_ENCRYPTION_KEY must be at least 32 characters for AES-256-CBC"
|
|
7371
|
+
);
|
|
7372
|
+
}
|
|
7373
|
+
this.store = options.store;
|
|
7374
|
+
this.encryptionKey = options.encryptionKey;
|
|
7375
|
+
}
|
|
7376
|
+
/**
|
|
7377
|
+
* Get a fresh access token for the given provider.
|
|
7378
|
+
* Returns from cache → store → refresh flow (in that order).
|
|
7379
|
+
*/
|
|
7380
|
+
async getAccessToken(config) {
|
|
7381
|
+
const cached = this.cache.get(config.provider);
|
|
7382
|
+
if (cached && cached.expiresAt > Date.now() + EXPIRY_BUFFER_MS) {
|
|
7383
|
+
return cached.token;
|
|
7384
|
+
}
|
|
7385
|
+
const encrypted = await this.store.get(config.provider);
|
|
7386
|
+
if (!encrypted) {
|
|
7387
|
+
throw new Error(
|
|
7388
|
+
`No OAuth tokens found for "${config.provider}". Complete the OAuth consent flow first and call saveTokens().`
|
|
7389
|
+
);
|
|
7390
|
+
}
|
|
7391
|
+
const tokens = decryptTokens(encrypted, this.encryptionKey);
|
|
7392
|
+
if (tokens.expiresAt > Date.now() + EXPIRY_BUFFER_MS) {
|
|
7393
|
+
this.setCache(config.provider, tokens.accessToken, tokens.expiresAt);
|
|
7394
|
+
return tokens.accessToken;
|
|
7395
|
+
}
|
|
7396
|
+
const refreshed = await this.refreshAccessToken(config, tokens.refreshToken);
|
|
7397
|
+
await this.persistTokens(config.provider, refreshed);
|
|
7398
|
+
return refreshed.accessToken;
|
|
7399
|
+
}
|
|
7400
|
+
/**
|
|
7401
|
+
* Store tokens after the initial OAuth consent flow.
|
|
7402
|
+
* Call this once after the user completes the OAuth redirect.
|
|
7403
|
+
*/
|
|
7404
|
+
async saveTokens(provider, tokens) {
|
|
7405
|
+
await this.persistTokens(provider, tokens);
|
|
7406
|
+
}
|
|
7407
|
+
/**
|
|
7408
|
+
* Remove all tokens for a provider.
|
|
7409
|
+
*/
|
|
7410
|
+
async revokeTokens(provider) {
|
|
7411
|
+
await this.store.delete(provider);
|
|
7412
|
+
this.cache.delete(provider);
|
|
7413
|
+
}
|
|
7414
|
+
/**
|
|
7415
|
+
* Check if tokens exist for a provider (without decrypting).
|
|
7416
|
+
*/
|
|
7417
|
+
async hasTokens(provider) {
|
|
7418
|
+
const encrypted = await this.store.get(provider);
|
|
7419
|
+
return encrypted !== null;
|
|
7420
|
+
}
|
|
7421
|
+
async refreshAccessToken(config, refreshToken) {
|
|
7422
|
+
try {
|
|
7423
|
+
const response = await axios2__default.default.post(
|
|
7424
|
+
config.tokenUrl,
|
|
7425
|
+
new URLSearchParams({
|
|
7426
|
+
grant_type: "refresh_token",
|
|
7427
|
+
client_id: config.clientId,
|
|
7428
|
+
client_secret: config.clientSecret,
|
|
7429
|
+
refresh_token: refreshToken
|
|
7430
|
+
}).toString(),
|
|
7431
|
+
{
|
|
7432
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
7433
|
+
timeout: 1e4
|
|
7434
|
+
}
|
|
7435
|
+
);
|
|
7436
|
+
const data = response.data;
|
|
7437
|
+
return {
|
|
7438
|
+
accessToken: data.access_token,
|
|
7439
|
+
// Some providers rotate refresh tokens; keep the old one if not rotated
|
|
7440
|
+
refreshToken: data.refresh_token || refreshToken,
|
|
7441
|
+
expiresAt: Date.now() + (data.expires_in || 3600) * 1e3
|
|
7442
|
+
};
|
|
7443
|
+
} catch (error) {
|
|
7444
|
+
const status = error?.response?.status;
|
|
7445
|
+
const body = error?.response?.data;
|
|
7446
|
+
throw new Error(
|
|
7447
|
+
`OAuth refresh failed for "${config.provider}": ${status || "network error"} ${JSON.stringify(body) || error.message}`
|
|
7448
|
+
);
|
|
7449
|
+
}
|
|
7450
|
+
}
|
|
7451
|
+
async persistTokens(provider, tokens) {
|
|
7452
|
+
const encrypted = encryptTokens(tokens, this.encryptionKey);
|
|
7453
|
+
await this.store.save(provider, encrypted);
|
|
7454
|
+
this.setCache(provider, tokens.accessToken, tokens.expiresAt);
|
|
7455
|
+
}
|
|
7456
|
+
setCache(provider, token, expiresAt) {
|
|
7457
|
+
this.cache.set(provider, { token, expiresAt });
|
|
7458
|
+
}
|
|
7459
|
+
};
|
|
7460
|
+
var FileTokenStore = class {
|
|
7461
|
+
constructor(filePath) {
|
|
7462
|
+
this.filePath = filePath;
|
|
7463
|
+
}
|
|
7464
|
+
async get(provider) {
|
|
7465
|
+
const data = this.readFile();
|
|
7466
|
+
return data[provider] ?? null;
|
|
7467
|
+
}
|
|
7468
|
+
async save(provider, encrypted) {
|
|
7469
|
+
const data = this.readFile();
|
|
7470
|
+
data[provider] = encrypted;
|
|
7471
|
+
this.writeFile(data);
|
|
7472
|
+
}
|
|
7473
|
+
async delete(provider) {
|
|
7474
|
+
const data = this.readFile();
|
|
7475
|
+
delete data[provider];
|
|
7476
|
+
this.writeFile(data);
|
|
7477
|
+
}
|
|
7478
|
+
readFile() {
|
|
7479
|
+
try {
|
|
7480
|
+
if (fs__namespace.existsSync(this.filePath)) {
|
|
7481
|
+
const content = fs__namespace.readFileSync(this.filePath, "utf8");
|
|
7482
|
+
return JSON.parse(content);
|
|
7483
|
+
}
|
|
7484
|
+
} catch {
|
|
7485
|
+
}
|
|
7486
|
+
return {};
|
|
7487
|
+
}
|
|
7488
|
+
writeFile(data) {
|
|
7489
|
+
const dir = path2__namespace.dirname(this.filePath);
|
|
7490
|
+
if (!fs__namespace.existsSync(dir)) {
|
|
7491
|
+
fs__namespace.mkdirSync(dir, { recursive: true });
|
|
7492
|
+
}
|
|
7493
|
+
fs__namespace.writeFileSync(this.filePath, JSON.stringify(data, null, 2), "utf8");
|
|
7494
|
+
}
|
|
7495
|
+
};
|
|
7496
|
+
|
|
7497
|
+
// src/oauth/stores/mongo-token.store.ts
|
|
7498
|
+
var DEFAULT_COLLECTION = "oauth_tokens";
|
|
7499
|
+
var MongoTokenStore = class {
|
|
7500
|
+
constructor(db, collectionName) {
|
|
7501
|
+
this.db = db;
|
|
7502
|
+
this.collectionName = collectionName ?? DEFAULT_COLLECTION;
|
|
7503
|
+
}
|
|
7504
|
+
collectionName;
|
|
7505
|
+
initialized = false;
|
|
7506
|
+
async get(provider) {
|
|
7507
|
+
await this.ensureIndex();
|
|
7508
|
+
const doc = await this.db.collection(this.collectionName).findOne({ provider });
|
|
7509
|
+
return doc?.encrypted ?? null;
|
|
7510
|
+
}
|
|
7511
|
+
async save(provider, encrypted) {
|
|
7512
|
+
await this.ensureIndex();
|
|
7513
|
+
await this.db.collection(this.collectionName).updateOne(
|
|
7514
|
+
{ provider },
|
|
7515
|
+
{ $set: { provider, encrypted, updatedAt: /* @__PURE__ */ new Date() } },
|
|
7516
|
+
{ upsert: true }
|
|
7517
|
+
);
|
|
7518
|
+
}
|
|
7519
|
+
async delete(provider) {
|
|
7520
|
+
await this.db.collection(this.collectionName).deleteOne({ provider });
|
|
7521
|
+
}
|
|
7522
|
+
async ensureIndex() {
|
|
7523
|
+
if (this.initialized) return;
|
|
7524
|
+
await this.db.collection(this.collectionName).createIndex({ provider: 1 }, { unique: true });
|
|
7525
|
+
this.initialized = true;
|
|
7526
|
+
}
|
|
7527
|
+
};
|
|
7328
7528
|
|
|
7329
7529
|
exports.AttachmentType = AttachmentType;
|
|
7330
7530
|
exports.BaseGraphServiceController = exports.GraphController;
|
|
@@ -7337,6 +7537,7 @@ exports.ChatFeature = ChatFeature;
|
|
|
7337
7537
|
exports.DEFAULT_ATTACHMENT_THRESHOLD = DEFAULT_ATTACHMENT_THRESHOLD;
|
|
7338
7538
|
exports.DEFAULT_TRACER_OPTIONS = DEFAULT_TRACER_OPTIONS;
|
|
7339
7539
|
exports.Endpoint = Endpoint;
|
|
7540
|
+
exports.FileTokenStore = FileTokenStore;
|
|
7340
7541
|
exports.GraphEngineType = GraphEngineType;
|
|
7341
7542
|
exports.GraphManifestSchema = GraphManifestSchema;
|
|
7342
7543
|
exports.GraphManifestValidator = GraphManifestValidator;
|
|
@@ -7347,6 +7548,8 @@ exports.McpToolFilter = McpToolFilter;
|
|
|
7347
7548
|
exports.ModelInitializer = ModelInitializer;
|
|
7348
7549
|
exports.ModelProvider = ModelProvider;
|
|
7349
7550
|
exports.ModelType = ModelType;
|
|
7551
|
+
exports.MongoTokenStore = MongoTokenStore;
|
|
7552
|
+
exports.OAuthTokenManager = OAuthTokenManager;
|
|
7350
7553
|
exports.RetrieverSearchType = RetrieverSearchType;
|
|
7351
7554
|
exports.StreamChannel = StreamChannel;
|
|
7352
7555
|
exports.UIEndpoint = UIEndpoint;
|
|
@@ -7362,7 +7565,9 @@ exports.createEndpointDescriptors = createEndpointDescriptors;
|
|
|
7362
7565
|
exports.createGraphAttachment = createGraphAttachment;
|
|
7363
7566
|
exports.createMongoClientAdapter = createMongoClientAdapter;
|
|
7364
7567
|
exports.createStaticMessage = createStaticMessage;
|
|
7568
|
+
exports.decryptTokens = decryptTokens;
|
|
7365
7569
|
exports.dispatchAttachments = dispatchAttachments;
|
|
7570
|
+
exports.encryptTokens = encryptTokens;
|
|
7366
7571
|
exports.executeToolWithAttachments = executeToolWithAttachments;
|
|
7367
7572
|
exports.findCallbackMethod = findCallbackMethod;
|
|
7368
7573
|
exports.findEndpointMethod = findEndpointMethod;
|