@kadoa/mcp 0.3.8 → 0.3.9-rc.2
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 +501 -230
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -28820,6 +28820,17 @@ function isArrayBufferView(val) {
|
|
|
28820
28820
|
}
|
|
28821
28821
|
return result;
|
|
28822
28822
|
}
|
|
28823
|
+
function getGlobal() {
|
|
28824
|
+
if (typeof globalThis !== "undefined")
|
|
28825
|
+
return globalThis;
|
|
28826
|
+
if (typeof self !== "undefined")
|
|
28827
|
+
return self;
|
|
28828
|
+
if (typeof window !== "undefined")
|
|
28829
|
+
return window;
|
|
28830
|
+
if (typeof global !== "undefined")
|
|
28831
|
+
return global;
|
|
28832
|
+
return {};
|
|
28833
|
+
}
|
|
28823
28834
|
function forEach(obj, fn, { allOwnKeys = false } = {}) {
|
|
28824
28835
|
if (obj === null || typeof obj === "undefined") {
|
|
28825
28836
|
return;
|
|
@@ -28906,10 +28917,14 @@ var toString, getPrototypeOf, iterator, toStringTag, kindOf, kindOfTest = (type)
|
|
|
28906
28917
|
} catch (e) {
|
|
28907
28918
|
return false;
|
|
28908
28919
|
}
|
|
28909
|
-
}, isDate, isFile,
|
|
28920
|
+
}, isDate, isFile, isReactNativeBlob = (value) => {
|
|
28921
|
+
return !!(value && typeof value.uri !== "undefined");
|
|
28922
|
+
}, isReactNative = (formData) => formData && typeof formData.getParts !== "undefined", isBlob, isFileList, isStream = (val) => isObject2(val) && isFunction(val.pipe), G, FormDataCtor, isFormData = (thing) => {
|
|
28910
28923
|
let kind;
|
|
28911
|
-
return thing && (
|
|
28912
|
-
}, isURLSearchParams, isReadableStream, isRequest, isResponse, isHeaders, trim = (str) =>
|
|
28924
|
+
return thing && (FormDataCtor && thing instanceof FormDataCtor || isFunction(thing.append) && ((kind = kindOf(thing)) === "formdata" || kind === "object" && isFunction(thing.toString) && thing.toString() === "[object FormData]"));
|
|
28925
|
+
}, isURLSearchParams, isReadableStream, isRequest, isResponse, isHeaders, trim = (str) => {
|
|
28926
|
+
return str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
|
|
28927
|
+
}, _global, isContextDefined = (context) => !isUndefined(context) && context !== _global, extend2 = (a, b, thisArg, { allOwnKeys } = {}) => {
|
|
28913
28928
|
forEach(b, (val, key) => {
|
|
28914
28929
|
if (thisArg && isFunction(val)) {
|
|
28915
28930
|
Object.defineProperty(a, key, {
|
|
@@ -29089,6 +29104,8 @@ var init_utils = __esm(() => {
|
|
|
29089
29104
|
isFile = kindOfTest("File");
|
|
29090
29105
|
isBlob = kindOfTest("Blob");
|
|
29091
29106
|
isFileList = kindOfTest("FileList");
|
|
29107
|
+
G = getGlobal();
|
|
29108
|
+
FormDataCtor = typeof G.FormData !== "undefined" ? G.FormData : undefined;
|
|
29092
29109
|
isURLSearchParams = kindOfTest("URLSearchParams");
|
|
29093
29110
|
[isReadableStream, isRequest, isResponse, isHeaders] = [
|
|
29094
29111
|
"ReadableStream",
|
|
@@ -29146,6 +29163,8 @@ var init_utils = __esm(() => {
|
|
|
29146
29163
|
isUndefined,
|
|
29147
29164
|
isDate,
|
|
29148
29165
|
isFile,
|
|
29166
|
+
isReactNativeBlob,
|
|
29167
|
+
isReactNative,
|
|
29149
29168
|
isBlob,
|
|
29150
29169
|
isRegExp,
|
|
29151
29170
|
isFunction,
|
|
@@ -29197,11 +29216,20 @@ var init_AxiosError = __esm(() => {
|
|
|
29197
29216
|
const axiosError = new AxiosError(error48.message, code || error48.code, config2, request, response);
|
|
29198
29217
|
axiosError.cause = error48;
|
|
29199
29218
|
axiosError.name = error48.name;
|
|
29219
|
+
if (error48.status != null && axiosError.status == null) {
|
|
29220
|
+
axiosError.status = error48.status;
|
|
29221
|
+
}
|
|
29200
29222
|
customProps && Object.assign(axiosError, customProps);
|
|
29201
29223
|
return axiosError;
|
|
29202
29224
|
}
|
|
29203
29225
|
constructor(message, code, config2, request, response) {
|
|
29204
29226
|
super(message);
|
|
29227
|
+
Object.defineProperty(this, "message", {
|
|
29228
|
+
value: message,
|
|
29229
|
+
enumerable: true,
|
|
29230
|
+
writable: true,
|
|
29231
|
+
configurable: true
|
|
29232
|
+
});
|
|
29205
29233
|
this.name = "AxiosError";
|
|
29206
29234
|
this.isAxiosError = true;
|
|
29207
29235
|
code && (this.code = code);
|
|
@@ -39414,6 +39442,10 @@ function toFormData(obj, formData, options) {
|
|
|
39414
39442
|
}
|
|
39415
39443
|
function defaultVisitor(value, key, path) {
|
|
39416
39444
|
let arr = value;
|
|
39445
|
+
if (utils_default.isReactNative(formData) && utils_default.isReactNativeBlob(value)) {
|
|
39446
|
+
formData.append(renderKey(path, key, dots), convertValue(value));
|
|
39447
|
+
return false;
|
|
39448
|
+
}
|
|
39417
39449
|
if (value && !path && typeof value === "object") {
|
|
39418
39450
|
if (utils_default.endsWith(key, "{}")) {
|
|
39419
39451
|
key = metaTokens ? key : key.slice(0, -2);
|
|
@@ -39763,66 +39795,70 @@ var init_defaults = __esm(() => {
|
|
|
39763
39795
|
defaults = {
|
|
39764
39796
|
transitional: transitional_default,
|
|
39765
39797
|
adapter: ["xhr", "http", "fetch"],
|
|
39766
|
-
transformRequest: [
|
|
39767
|
-
|
|
39768
|
-
|
|
39769
|
-
|
|
39770
|
-
|
|
39771
|
-
|
|
39772
|
-
|
|
39773
|
-
|
|
39774
|
-
|
|
39775
|
-
|
|
39776
|
-
|
|
39777
|
-
|
|
39778
|
-
|
|
39779
|
-
|
|
39780
|
-
|
|
39781
|
-
|
|
39782
|
-
|
|
39783
|
-
|
|
39784
|
-
|
|
39785
|
-
|
|
39786
|
-
|
|
39787
|
-
|
|
39788
|
-
|
|
39789
|
-
if (
|
|
39790
|
-
|
|
39798
|
+
transformRequest: [
|
|
39799
|
+
function transformRequest(data, headers) {
|
|
39800
|
+
const contentType = headers.getContentType() || "";
|
|
39801
|
+
const hasJSONContentType = contentType.indexOf("application/json") > -1;
|
|
39802
|
+
const isObjectPayload = utils_default.isObject(data);
|
|
39803
|
+
if (isObjectPayload && utils_default.isHTMLForm(data)) {
|
|
39804
|
+
data = new FormData(data);
|
|
39805
|
+
}
|
|
39806
|
+
const isFormData2 = utils_default.isFormData(data);
|
|
39807
|
+
if (isFormData2) {
|
|
39808
|
+
return hasJSONContentType ? JSON.stringify(formDataToJSON_default(data)) : data;
|
|
39809
|
+
}
|
|
39810
|
+
if (utils_default.isArrayBuffer(data) || utils_default.isBuffer(data) || utils_default.isStream(data) || utils_default.isFile(data) || utils_default.isBlob(data) || utils_default.isReadableStream(data)) {
|
|
39811
|
+
return data;
|
|
39812
|
+
}
|
|
39813
|
+
if (utils_default.isArrayBufferView(data)) {
|
|
39814
|
+
return data.buffer;
|
|
39815
|
+
}
|
|
39816
|
+
if (utils_default.isURLSearchParams(data)) {
|
|
39817
|
+
headers.setContentType("application/x-www-form-urlencoded;charset=utf-8", false);
|
|
39818
|
+
return data.toString();
|
|
39819
|
+
}
|
|
39820
|
+
let isFileList2;
|
|
39821
|
+
if (isObjectPayload) {
|
|
39822
|
+
if (contentType.indexOf("application/x-www-form-urlencoded") > -1) {
|
|
39823
|
+
return toURLEncodedForm(data, this.formSerializer).toString();
|
|
39824
|
+
}
|
|
39825
|
+
if ((isFileList2 = utils_default.isFileList(data)) || contentType.indexOf("multipart/form-data") > -1) {
|
|
39826
|
+
const _FormData = this.env && this.env.FormData;
|
|
39827
|
+
return toFormData_default(isFileList2 ? { "files[]": data } : data, _FormData && new _FormData, this.formSerializer);
|
|
39828
|
+
}
|
|
39791
39829
|
}
|
|
39792
|
-
if (
|
|
39793
|
-
|
|
39794
|
-
return
|
|
39830
|
+
if (isObjectPayload || hasJSONContentType) {
|
|
39831
|
+
headers.setContentType("application/json", false);
|
|
39832
|
+
return stringifySafely(data);
|
|
39795
39833
|
}
|
|
39796
|
-
}
|
|
39797
|
-
if (isObjectPayload || hasJSONContentType) {
|
|
39798
|
-
headers.setContentType("application/json", false);
|
|
39799
|
-
return stringifySafely(data);
|
|
39800
|
-
}
|
|
39801
|
-
return data;
|
|
39802
|
-
}],
|
|
39803
|
-
transformResponse: [function transformResponse(data) {
|
|
39804
|
-
const transitional = this.transitional || defaults.transitional;
|
|
39805
|
-
const forcedJSONParsing = transitional && transitional.forcedJSONParsing;
|
|
39806
|
-
const JSONRequested = this.responseType === "json";
|
|
39807
|
-
if (utils_default.isResponse(data) || utils_default.isReadableStream(data)) {
|
|
39808
39834
|
return data;
|
|
39809
39835
|
}
|
|
39810
|
-
|
|
39811
|
-
|
|
39812
|
-
|
|
39813
|
-
|
|
39814
|
-
|
|
39815
|
-
|
|
39816
|
-
|
|
39817
|
-
|
|
39818
|
-
|
|
39836
|
+
],
|
|
39837
|
+
transformResponse: [
|
|
39838
|
+
function transformResponse(data) {
|
|
39839
|
+
const transitional = this.transitional || defaults.transitional;
|
|
39840
|
+
const forcedJSONParsing = transitional && transitional.forcedJSONParsing;
|
|
39841
|
+
const JSONRequested = this.responseType === "json";
|
|
39842
|
+
if (utils_default.isResponse(data) || utils_default.isReadableStream(data)) {
|
|
39843
|
+
return data;
|
|
39844
|
+
}
|
|
39845
|
+
if (data && utils_default.isString(data) && (forcedJSONParsing && !this.responseType || JSONRequested)) {
|
|
39846
|
+
const silentJSONParsing = transitional && transitional.silentJSONParsing;
|
|
39847
|
+
const strictJSONParsing = !silentJSONParsing && JSONRequested;
|
|
39848
|
+
try {
|
|
39849
|
+
return JSON.parse(data, this.parseReviver);
|
|
39850
|
+
} catch (e) {
|
|
39851
|
+
if (strictJSONParsing) {
|
|
39852
|
+
if (e.name === "SyntaxError") {
|
|
39853
|
+
throw AxiosError_default.from(e, AxiosError_default.ERR_BAD_RESPONSE, this, null, this.response);
|
|
39854
|
+
}
|
|
39855
|
+
throw e;
|
|
39819
39856
|
}
|
|
39820
|
-
throw e;
|
|
39821
39857
|
}
|
|
39822
39858
|
}
|
|
39859
|
+
return data;
|
|
39823
39860
|
}
|
|
39824
|
-
|
|
39825
|
-
}],
|
|
39861
|
+
],
|
|
39826
39862
|
timeout: 0,
|
|
39827
39863
|
xsrfCookieName: "XSRF-TOKEN",
|
|
39828
39864
|
xsrfHeaderName: "X-XSRF-TOKEN",
|
|
@@ -40118,7 +40154,14 @@ var init_AxiosHeaders = __esm(() => {
|
|
|
40118
40154
|
return this;
|
|
40119
40155
|
}
|
|
40120
40156
|
};
|
|
40121
|
-
AxiosHeaders.accessor([
|
|
40157
|
+
AxiosHeaders.accessor([
|
|
40158
|
+
"Content-Type",
|
|
40159
|
+
"Content-Length",
|
|
40160
|
+
"Accept",
|
|
40161
|
+
"Accept-Encoding",
|
|
40162
|
+
"User-Agent",
|
|
40163
|
+
"Authorization"
|
|
40164
|
+
]);
|
|
40122
40165
|
utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
|
|
40123
40166
|
let mapped = key[0].toUpperCase() + key.slice(1);
|
|
40124
40167
|
return {
|
|
@@ -41381,7 +41424,7 @@ var require_follow_redirects = __commonJS((exports, module) => {
|
|
|
41381
41424
|
});
|
|
41382
41425
|
|
|
41383
41426
|
// node_modules/axios/lib/env/data.js
|
|
41384
|
-
var VERSION = "1.13.
|
|
41427
|
+
var VERSION = "1.13.6";
|
|
41385
41428
|
|
|
41386
41429
|
// node_modules/axios/lib/helpers/parseProtocol.js
|
|
41387
41430
|
function parseProtocol(url3) {
|
|
@@ -41791,11 +41834,14 @@ var progressEventReducer = (listener, isDownloadStream, freq = 3) => {
|
|
|
41791
41834
|
}, freq);
|
|
41792
41835
|
}, progressEventDecorator = (total, throttled) => {
|
|
41793
41836
|
const lengthComputable = total != null;
|
|
41794
|
-
return [
|
|
41795
|
-
|
|
41796
|
-
|
|
41797
|
-
|
|
41798
|
-
|
|
41837
|
+
return [
|
|
41838
|
+
(loaded) => throttled[0]({
|
|
41839
|
+
lengthComputable,
|
|
41840
|
+
total,
|
|
41841
|
+
loaded
|
|
41842
|
+
}),
|
|
41843
|
+
throttled[1]
|
|
41844
|
+
];
|
|
41799
41845
|
}, asyncDecorator = (fn) => (...args) => utils_default.asap(() => fn(...args));
|
|
41800
41846
|
var init_progressEventReducer = __esm(() => {
|
|
41801
41847
|
init_speedometer();
|
|
@@ -41925,10 +41971,7 @@ class Http2Sessions {
|
|
|
41925
41971
|
};
|
|
41926
41972
|
}
|
|
41927
41973
|
session.once("close", removeSession);
|
|
41928
|
-
let entry = [
|
|
41929
|
-
session,
|
|
41930
|
-
options
|
|
41931
|
-
];
|
|
41974
|
+
let entry = [session, options];
|
|
41932
41975
|
authoritySessions ? authoritySessions.push(entry) : authoritySessions = this.sessions[authority] = [entry];
|
|
41933
41976
|
return session;
|
|
41934
41977
|
}
|
|
@@ -42049,12 +42092,7 @@ var init_http = __esm(() => {
|
|
|
42049
42092
|
const authority = options.protocol + "//" + options.hostname + ":" + (options.port || (options.protocol === "https:" ? 443 : 80));
|
|
42050
42093
|
const { http2Options, headers } = options;
|
|
42051
42094
|
const session = http2Sessions.getSession(authority, http2Options);
|
|
42052
|
-
const {
|
|
42053
|
-
HTTP2_HEADER_SCHEME,
|
|
42054
|
-
HTTP2_HEADER_METHOD,
|
|
42055
|
-
HTTP2_HEADER_PATH,
|
|
42056
|
-
HTTP2_HEADER_STATUS
|
|
42057
|
-
} = http2.constants;
|
|
42095
|
+
const { HTTP2_HEADER_SCHEME, HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_STATUS } = http2.constants;
|
|
42058
42096
|
const http2Headers = {
|
|
42059
42097
|
[HTTP2_HEADER_SCHEME]: options.protocol.replace(":", ""),
|
|
42060
42098
|
[HTTP2_HEADER_METHOD]: options.method,
|
|
@@ -42241,9 +42279,12 @@ var init_http = __esm(() => {
|
|
|
42241
42279
|
if (!utils_default.isStream(data)) {
|
|
42242
42280
|
data = stream3.Readable.from(data, { objectMode: false });
|
|
42243
42281
|
}
|
|
42244
|
-
data = stream3.pipeline([
|
|
42245
|
-
|
|
42246
|
-
|
|
42282
|
+
data = stream3.pipeline([
|
|
42283
|
+
data,
|
|
42284
|
+
new AxiosTransformStream_default({
|
|
42285
|
+
maxRate: utils_default.toFiniteNumber(maxUploadRate)
|
|
42286
|
+
})
|
|
42287
|
+
], utils_default.noop);
|
|
42247
42288
|
onUploadProgress && data.on("progress", flushOnFinish(data, progressEventDecorator(contentLength, progressEventReducer(asyncDecorator(onUploadProgress), false, 3))));
|
|
42248
42289
|
}
|
|
42249
42290
|
let auth = undefined;
|
|
@@ -43073,11 +43114,7 @@ var DEFAULT_CHUNK_SIZE, isFunction2, globalFetchAPI, ReadableStream2, TextEncode
|
|
|
43073
43114
|
}, seedCache, getFetch = (config2) => {
|
|
43074
43115
|
let env = config2 && config2.env || {};
|
|
43075
43116
|
const { fetch: fetch2, Request, Response: Response2 } = env;
|
|
43076
|
-
const seeds = [
|
|
43077
|
-
Request,
|
|
43078
|
-
Response2,
|
|
43079
|
-
fetch2
|
|
43080
|
-
];
|
|
43117
|
+
const seeds = [Request, Response2, fetch2];
|
|
43081
43118
|
let len = seeds.length, i = len, seed, target, map3 = seedCache;
|
|
43082
43119
|
while (i--) {
|
|
43083
43120
|
seed = seeds[i];
|
|
@@ -43102,10 +43139,7 @@ var init_fetch = __esm(() => {
|
|
|
43102
43139
|
Request,
|
|
43103
43140
|
Response: Response2
|
|
43104
43141
|
}))(utils_default.global);
|
|
43105
|
-
({
|
|
43106
|
-
ReadableStream: ReadableStream2,
|
|
43107
|
-
TextEncoder: TextEncoder2
|
|
43108
|
-
} = utils_default.global);
|
|
43142
|
+
({ ReadableStream: ReadableStream2, TextEncoder: TextEncoder2 } = utils_default.global);
|
|
43109
43143
|
seedCache = new Map;
|
|
43110
43144
|
adapter = getFetch();
|
|
43111
43145
|
});
|
|
@@ -47552,7 +47586,7 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
|
|
|
47552
47586
|
const newSettings = await Promise.all(eventTypes.map(async (eventType) => {
|
|
47553
47587
|
const existing = existingSettings.find((s) => s.eventType === eventType);
|
|
47554
47588
|
if (existing?.id) {
|
|
47555
|
-
const existingChannelIds = (existing.channels || []).map((
|
|
47589
|
+
const existingChannelIds = (existing.channels || []).map((channel) => channel.id).filter(Boolean);
|
|
47556
47590
|
const mergedChannelIds = [
|
|
47557
47591
|
.../* @__PURE__ */ new Set([...existingChannelIds, ...channelIds])
|
|
47558
47592
|
];
|
|
@@ -47678,86 +47712,245 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
|
|
|
47678
47712
|
}));
|
|
47679
47713
|
return channels;
|
|
47680
47714
|
}
|
|
47681
|
-
}, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.
|
|
47715
|
+
}, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.24.2", SDK_NAME = "kadoa-node-sdk", SDK_LANGUAGE = "node", debug5, isDrainControlMessage = (message) => message.type === "control.draining", isRealtimeEvent = (message) => message.type !== "heartbeat" && message.type !== "control.draining", _Realtime = class _Realtime2 {
|
|
47682
47716
|
constructor(config2) {
|
|
47717
|
+
this.drainingSockets = /* @__PURE__ */ new Set;
|
|
47683
47718
|
this.lastHeartbeat = Date.now();
|
|
47684
47719
|
this.isConnecting = false;
|
|
47685
47720
|
this.eventListeners = /* @__PURE__ */ new Set;
|
|
47686
47721
|
this.connectionListeners = /* @__PURE__ */ new Set;
|
|
47687
47722
|
this.errorListeners = /* @__PURE__ */ new Set;
|
|
47723
|
+
this.isClosed = false;
|
|
47724
|
+
this.hasConnectedOnce = false;
|
|
47725
|
+
this.recentEventIds = /* @__PURE__ */ new Set;
|
|
47726
|
+
this.recentEventIdQueue = [];
|
|
47727
|
+
this.maxRecentEventIds = 1000;
|
|
47688
47728
|
this.apiKey = config2.apiKey;
|
|
47689
47729
|
this.heartbeatInterval = config2.heartbeatInterval || 1e4;
|
|
47690
|
-
this.reconnectDelay = config2.reconnectDelay
|
|
47730
|
+
this.reconnectDelay = this.normalizeReconnectDelay(config2.reconnectDelay);
|
|
47691
47731
|
this.missedHeartbeatsLimit = config2.missedHeartbeatsLimit || 30000;
|
|
47692
47732
|
}
|
|
47693
47733
|
async connect() {
|
|
47694
|
-
if (this.isConnecting)
|
|
47734
|
+
if (this.isClosed || this.isConnecting || this.activeSocket) {
|
|
47695
47735
|
return;
|
|
47736
|
+
}
|
|
47696
47737
|
this.isConnecting = true;
|
|
47697
47738
|
try {
|
|
47698
|
-
const
|
|
47699
|
-
|
|
47700
|
-
|
|
47701
|
-
"Content-Type": "application/json",
|
|
47702
|
-
"x-api-key": `${this.apiKey}`,
|
|
47703
|
-
"x-sdk-version": SDK_VERSION
|
|
47704
|
-
}
|
|
47705
|
-
});
|
|
47706
|
-
const { access_token, team_id } = await response.json();
|
|
47707
|
-
await new Promise((resolve, reject) => {
|
|
47708
|
-
this.socket = new WebSocket(`${WSS_API_URI}?access_token=${access_token}`);
|
|
47709
|
-
this.socket.onopen = () => {
|
|
47710
|
-
this.isConnecting = false;
|
|
47711
|
-
this.lastHeartbeat = Date.now();
|
|
47712
|
-
if (this.socket?.readyState === WebSocket.OPEN) {
|
|
47713
|
-
this.socket.send(JSON.stringify({
|
|
47714
|
-
action: "subscribe",
|
|
47715
|
-
channel: team_id
|
|
47716
|
-
}));
|
|
47717
|
-
debug5("Connected to WebSocket");
|
|
47718
|
-
this.notifyConnectionListeners(true);
|
|
47719
|
-
}
|
|
47720
|
-
this.startHeartbeatCheck();
|
|
47721
|
-
resolve();
|
|
47722
|
-
};
|
|
47723
|
-
this.socket.onmessage = (event) => {
|
|
47724
|
-
try {
|
|
47725
|
-
const data = JSON.parse(event.data);
|
|
47726
|
-
if (data.type === "heartbeat") {
|
|
47727
|
-
this.handleHeartbeat();
|
|
47728
|
-
} else {
|
|
47729
|
-
if (data?.id) {
|
|
47730
|
-
fetch(`${REALTIME_API_URI}/api/v1/events/ack`, {
|
|
47731
|
-
method: "POST",
|
|
47732
|
-
headers: { "Content-Type": "application/json" },
|
|
47733
|
-
body: JSON.stringify({ id: data.id })
|
|
47734
|
-
});
|
|
47735
|
-
}
|
|
47736
|
-
this.notifyEventListeners(data);
|
|
47737
|
-
}
|
|
47738
|
-
} catch (err) {
|
|
47739
|
-
debug5("Failed to parse incoming message: %O", err);
|
|
47740
|
-
}
|
|
47741
|
-
};
|
|
47742
|
-
this.socket.onclose = () => {
|
|
47743
|
-
debug5("WebSocket disconnected. Attempting to reconnect...");
|
|
47744
|
-
this.isConnecting = false;
|
|
47745
|
-
this.stopHeartbeatCheck();
|
|
47746
|
-
this.notifyConnectionListeners(false, "Connection closed");
|
|
47747
|
-
setTimeout(() => this.connect(), this.reconnectDelay);
|
|
47748
|
-
};
|
|
47749
|
-
this.socket.onerror = (error48) => {
|
|
47750
|
-
debug5("WebSocket error: %O", error48);
|
|
47751
|
-
this.isConnecting = false;
|
|
47752
|
-
this.notifyErrorListeners(error48);
|
|
47753
|
-
reject(error48);
|
|
47754
|
-
};
|
|
47755
|
-
});
|
|
47739
|
+
const { access_token, team_id } = await this.getOAuthToken();
|
|
47740
|
+
await this.openSocket(access_token, team_id, "active");
|
|
47741
|
+
this.hasConnectedOnce = true;
|
|
47756
47742
|
} catch (err) {
|
|
47757
47743
|
debug5("Failed to connect: %O", err);
|
|
47758
47744
|
this.isConnecting = false;
|
|
47759
|
-
|
|
47745
|
+
this.notifyErrorListeners(err);
|
|
47746
|
+
if (!this.hasConnectedOnce) {
|
|
47747
|
+
throw err;
|
|
47748
|
+
}
|
|
47749
|
+
this.scheduleReconnect();
|
|
47750
|
+
}
|
|
47751
|
+
}
|
|
47752
|
+
async getOAuthToken() {
|
|
47753
|
+
const response = await fetch(`${PUBLIC_API_URI}/v4/oauth2/token`, {
|
|
47754
|
+
method: "POST",
|
|
47755
|
+
headers: {
|
|
47756
|
+
"Content-Type": "application/json",
|
|
47757
|
+
"x-api-key": `${this.apiKey}`,
|
|
47758
|
+
"x-sdk-version": SDK_VERSION
|
|
47759
|
+
}
|
|
47760
|
+
});
|
|
47761
|
+
return await response.json();
|
|
47762
|
+
}
|
|
47763
|
+
async openSocket(accessToken, teamId, role) {
|
|
47764
|
+
await new Promise((resolve, reject) => {
|
|
47765
|
+
const socket = new WebSocket(`${WSS_API_URI}?access_token=${accessToken}`);
|
|
47766
|
+
let settled = false;
|
|
47767
|
+
socket.onopen = () => {
|
|
47768
|
+
const subscribeMessage = {
|
|
47769
|
+
action: "subscribe",
|
|
47770
|
+
channel: teamId
|
|
47771
|
+
};
|
|
47772
|
+
if (this.lastCursor) {
|
|
47773
|
+
subscribeMessage.lastCursor = this.lastCursor;
|
|
47774
|
+
}
|
|
47775
|
+
socket.send(JSON.stringify(subscribeMessage));
|
|
47776
|
+
this.promoteSocket(socket, role);
|
|
47777
|
+
this.isConnecting = false;
|
|
47778
|
+
this.lastHeartbeat = Date.now();
|
|
47779
|
+
this.startHeartbeatCheck();
|
|
47780
|
+
debug5("Connected to WebSocket");
|
|
47781
|
+
if (!settled) {
|
|
47782
|
+
settled = true;
|
|
47783
|
+
resolve();
|
|
47784
|
+
}
|
|
47785
|
+
};
|
|
47786
|
+
socket.onmessage = (event) => {
|
|
47787
|
+
this.handleSocketMessage(socket, event.data);
|
|
47788
|
+
};
|
|
47789
|
+
socket.onclose = () => {
|
|
47790
|
+
this.handleSocketClose(socket);
|
|
47791
|
+
if (!settled) {
|
|
47792
|
+
settled = true;
|
|
47793
|
+
reject(new Error("WebSocket closed before opening"));
|
|
47794
|
+
}
|
|
47795
|
+
};
|
|
47796
|
+
socket.onerror = (error48) => {
|
|
47797
|
+
this.notifyErrorListeners(error48);
|
|
47798
|
+
if (!settled) {
|
|
47799
|
+
settled = true;
|
|
47800
|
+
reject(error48);
|
|
47801
|
+
return;
|
|
47802
|
+
}
|
|
47803
|
+
if (socket === this.activeSocket) {
|
|
47804
|
+
this.handleUnexpectedDisconnect("Socket error");
|
|
47805
|
+
}
|
|
47806
|
+
};
|
|
47807
|
+
});
|
|
47808
|
+
}
|
|
47809
|
+
promoteSocket(socket, role) {
|
|
47810
|
+
if (role === "replacement" && this.activeSocket && this.activeSocket !== socket) {
|
|
47811
|
+
this.drainingSockets.add(this.activeSocket);
|
|
47812
|
+
}
|
|
47813
|
+
this.activeSocket = socket;
|
|
47814
|
+
this.drainingSockets.delete(socket);
|
|
47815
|
+
if (role === "active" || !this.hasConnectedOnce) {
|
|
47816
|
+
this.notifyConnectionListeners(true);
|
|
47817
|
+
}
|
|
47818
|
+
}
|
|
47819
|
+
handleSocketMessage(socket, rawData) {
|
|
47820
|
+
try {
|
|
47821
|
+
const payload = typeof rawData === "string" ? rawData : rawData.toString?.() ?? "";
|
|
47822
|
+
const data = JSON.parse(payload);
|
|
47823
|
+
if (data.type === "heartbeat") {
|
|
47824
|
+
if (socket === this.activeSocket) {
|
|
47825
|
+
this.handleHeartbeat();
|
|
47826
|
+
}
|
|
47827
|
+
return;
|
|
47828
|
+
}
|
|
47829
|
+
if (isDrainControlMessage(data)) {
|
|
47830
|
+
this.handleDrainSignal(socket, data);
|
|
47831
|
+
return;
|
|
47832
|
+
}
|
|
47833
|
+
if (!isRealtimeEvent(data)) {
|
|
47834
|
+
return;
|
|
47835
|
+
}
|
|
47836
|
+
if (typeof data._cursor === "string") {
|
|
47837
|
+
this.lastCursor = data._cursor;
|
|
47838
|
+
}
|
|
47839
|
+
if (typeof data.id === "string") {
|
|
47840
|
+
fetch(`${REALTIME_API_URI}/api/v1/events/ack`, {
|
|
47841
|
+
method: "POST",
|
|
47842
|
+
headers: { "Content-Type": "application/json" },
|
|
47843
|
+
body: JSON.stringify({ id: data.id })
|
|
47844
|
+
}).catch((error48) => {
|
|
47845
|
+
debug5("Failed to acknowledge event %s: %O", data.id, error48);
|
|
47846
|
+
});
|
|
47847
|
+
}
|
|
47848
|
+
if (this.isDuplicateEvent(data.id)) {
|
|
47849
|
+
return;
|
|
47850
|
+
}
|
|
47851
|
+
this.notifyEventListeners(data);
|
|
47852
|
+
} catch (err) {
|
|
47853
|
+
debug5("Failed to parse incoming message: %O", err);
|
|
47854
|
+
}
|
|
47855
|
+
}
|
|
47856
|
+
handleDrainSignal(socket, message) {
|
|
47857
|
+
if (socket !== this.activeSocket || this.isClosed) {
|
|
47858
|
+
return;
|
|
47859
|
+
}
|
|
47860
|
+
debug5("Received drain signal, preparing replacement socket");
|
|
47861
|
+
this.drainingSockets.add(socket);
|
|
47862
|
+
this.scheduleDrainReconnect(message.retryAfterMs);
|
|
47863
|
+
}
|
|
47864
|
+
handleSocketClose(socket) {
|
|
47865
|
+
const wasActiveSocket = socket === this.activeSocket;
|
|
47866
|
+
this.drainingSockets.delete(socket);
|
|
47867
|
+
if (!wasActiveSocket) {
|
|
47868
|
+
return;
|
|
47869
|
+
}
|
|
47870
|
+
this.activeSocket = undefined;
|
|
47871
|
+
this.stopHeartbeatCheck();
|
|
47872
|
+
if (this.isClosed) {
|
|
47873
|
+
return;
|
|
47874
|
+
}
|
|
47875
|
+
if (this.drainingSockets.size > 0) {
|
|
47876
|
+
debug5("Draining socket closed after replacement was scheduled");
|
|
47877
|
+
return;
|
|
47878
|
+
}
|
|
47879
|
+
this.handleUnexpectedDisconnect("Connection closed");
|
|
47880
|
+
}
|
|
47881
|
+
handleUnexpectedDisconnect(reason) {
|
|
47882
|
+
this.isConnecting = false;
|
|
47883
|
+
this.notifyConnectionListeners(false, reason);
|
|
47884
|
+
this.scheduleReconnect();
|
|
47885
|
+
}
|
|
47886
|
+
scheduleReconnect(replacement = false) {
|
|
47887
|
+
if (this.isClosed || this.reconnectTimer) {
|
|
47888
|
+
return;
|
|
47889
|
+
}
|
|
47890
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
47891
|
+
this.reconnectTimer = undefined;
|
|
47892
|
+
if (this.isClosed || this.isConnecting || !replacement && this.activeSocket) {
|
|
47893
|
+
return;
|
|
47894
|
+
}
|
|
47895
|
+
this.isConnecting = true;
|
|
47896
|
+
try {
|
|
47897
|
+
const { access_token, team_id } = await this.getOAuthToken();
|
|
47898
|
+
await this.openSocket(access_token, team_id, replacement ? "replacement" : "active");
|
|
47899
|
+
} catch (err) {
|
|
47900
|
+
debug5("Reconnect failed: %O", err);
|
|
47901
|
+
this.isConnecting = false;
|
|
47902
|
+
this.notifyErrorListeners(err);
|
|
47903
|
+
this.scheduleReconnect(replacement);
|
|
47904
|
+
}
|
|
47905
|
+
}, this.reconnectDelay);
|
|
47906
|
+
}
|
|
47907
|
+
scheduleDrainReconnect(retryAfterMs) {
|
|
47908
|
+
if (this.isClosed || this.reconnectTimer) {
|
|
47909
|
+
return;
|
|
47910
|
+
}
|
|
47911
|
+
let safeDelayMs = this.reconnectDelay;
|
|
47912
|
+
if (typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) && retryAfterMs >= 0 && retryAfterMs <= _Realtime2.MAX_RECONNECT_DELAY_MS) {
|
|
47913
|
+
safeDelayMs = Math.trunc(retryAfterMs);
|
|
47914
|
+
}
|
|
47915
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
47916
|
+
this.reconnectTimer = undefined;
|
|
47917
|
+
if (this.isClosed || this.isConnecting) {
|
|
47918
|
+
return;
|
|
47919
|
+
}
|
|
47920
|
+
this.isConnecting = true;
|
|
47921
|
+
try {
|
|
47922
|
+
const { access_token, team_id } = await this.getOAuthToken();
|
|
47923
|
+
await this.openSocket(access_token, team_id, "replacement");
|
|
47924
|
+
} catch (err) {
|
|
47925
|
+
debug5("Reconnect failed: %O", err);
|
|
47926
|
+
this.isConnecting = false;
|
|
47927
|
+
this.notifyErrorListeners(err);
|
|
47928
|
+
this.scheduleReconnect(true);
|
|
47929
|
+
}
|
|
47930
|
+
}, safeDelayMs);
|
|
47931
|
+
}
|
|
47932
|
+
normalizeReconnectDelay(delay) {
|
|
47933
|
+
if (typeof delay !== "number" || !Number.isFinite(delay)) {
|
|
47934
|
+
return _Realtime2.DEFAULT_RECONNECT_DELAY_MS;
|
|
47760
47935
|
}
|
|
47936
|
+
return Math.min(Math.max(0, Math.trunc(delay)), _Realtime2.MAX_RECONNECT_DELAY_MS);
|
|
47937
|
+
}
|
|
47938
|
+
isDuplicateEvent(eventId) {
|
|
47939
|
+
if (!eventId) {
|
|
47940
|
+
return false;
|
|
47941
|
+
}
|
|
47942
|
+
if (this.recentEventIds.has(eventId)) {
|
|
47943
|
+
return true;
|
|
47944
|
+
}
|
|
47945
|
+
this.recentEventIds.add(eventId);
|
|
47946
|
+
this.recentEventIdQueue.push(eventId);
|
|
47947
|
+
if (this.recentEventIdQueue.length > this.maxRecentEventIds) {
|
|
47948
|
+
const expiredId = this.recentEventIdQueue.shift();
|
|
47949
|
+
if (expiredId) {
|
|
47950
|
+
this.recentEventIds.delete(expiredId);
|
|
47951
|
+
}
|
|
47952
|
+
}
|
|
47953
|
+
return false;
|
|
47761
47954
|
}
|
|
47762
47955
|
handleHeartbeat() {
|
|
47763
47956
|
debug5("Heartbeat received");
|
|
@@ -47785,22 +47978,24 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
|
|
|
47785
47978
|
this.errorListeners.forEach((listener) => {
|
|
47786
47979
|
try {
|
|
47787
47980
|
listener(error48);
|
|
47788
|
-
} catch (
|
|
47789
|
-
debug5("Error in error listener: %O",
|
|
47981
|
+
} catch (listenerError) {
|
|
47982
|
+
debug5("Error in error listener: %O", listenerError);
|
|
47790
47983
|
}
|
|
47791
47984
|
});
|
|
47792
47985
|
}
|
|
47793
47986
|
startHeartbeatCheck() {
|
|
47987
|
+
this.stopHeartbeatCheck();
|
|
47794
47988
|
this.missedHeartbeatCheckTimer = setInterval(() => {
|
|
47795
|
-
if (Date.now() - this.lastHeartbeat > this.missedHeartbeatsLimit) {
|
|
47989
|
+
if (this.activeSocket && Date.now() - this.lastHeartbeat > this.missedHeartbeatsLimit) {
|
|
47796
47990
|
debug5("No heartbeat received in 30 seconds! Closing connection.");
|
|
47797
|
-
this.
|
|
47991
|
+
this.activeSocket.close();
|
|
47798
47992
|
}
|
|
47799
47993
|
}, this.heartbeatInterval);
|
|
47800
47994
|
}
|
|
47801
47995
|
stopHeartbeatCheck() {
|
|
47802
47996
|
if (this.missedHeartbeatCheckTimer) {
|
|
47803
47997
|
clearInterval(this.missedHeartbeatCheckTimer);
|
|
47998
|
+
this.missedHeartbeatCheckTimer = undefined;
|
|
47804
47999
|
}
|
|
47805
48000
|
}
|
|
47806
48001
|
onEvent(listener) {
|
|
@@ -47811,6 +48006,9 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
|
|
|
47811
48006
|
}
|
|
47812
48007
|
onConnection(listener) {
|
|
47813
48008
|
this.connectionListeners.add(listener);
|
|
48009
|
+
if (this.isConnected()) {
|
|
48010
|
+
listener(true);
|
|
48011
|
+
}
|
|
47814
48012
|
return () => {
|
|
47815
48013
|
this.connectionListeners.delete(listener);
|
|
47816
48014
|
};
|
|
@@ -47822,19 +48020,27 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
|
|
|
47822
48020
|
};
|
|
47823
48021
|
}
|
|
47824
48022
|
close() {
|
|
47825
|
-
|
|
47826
|
-
|
|
47827
|
-
this.
|
|
47828
|
-
this.
|
|
47829
|
-
}
|
|
48023
|
+
this.isClosed = true;
|
|
48024
|
+
if (this.reconnectTimer) {
|
|
48025
|
+
clearTimeout(this.reconnectTimer);
|
|
48026
|
+
this.reconnectTimer = undefined;
|
|
48027
|
+
}
|
|
48028
|
+
this.stopHeartbeatCheck();
|
|
48029
|
+
this.activeSocket?.close();
|
|
48030
|
+
this.activeSocket = undefined;
|
|
48031
|
+
this.drainingSockets.forEach((socket) => {
|
|
48032
|
+
socket.close();
|
|
48033
|
+
});
|
|
48034
|
+
this.drainingSockets.clear();
|
|
48035
|
+
this.isConnecting = false;
|
|
47830
48036
|
this.eventListeners.clear();
|
|
47831
48037
|
this.connectionListeners.clear();
|
|
47832
48038
|
this.errorListeners.clear();
|
|
47833
48039
|
}
|
|
47834
48040
|
isConnected() {
|
|
47835
|
-
return this.
|
|
48041
|
+
return this.activeSocket?.readyState === WebSocket.OPEN;
|
|
47836
48042
|
}
|
|
47837
|
-
}, UserService = class {
|
|
48043
|
+
}, Realtime, UserService = class {
|
|
47838
48044
|
constructor(client) {
|
|
47839
48045
|
this.client = client;
|
|
47840
48046
|
}
|
|
@@ -48915,6 +49121,9 @@ var init_dist2 = __esm(() => {
|
|
|
48915
49121
|
if (typeof WebSocket === "undefined") {
|
|
48916
49122
|
global.WebSocket = __require2("ws");
|
|
48917
49123
|
}
|
|
49124
|
+
_Realtime.DEFAULT_RECONNECT_DELAY_MS = 5000;
|
|
49125
|
+
_Realtime.MAX_RECONNECT_DELAY_MS = 60000;
|
|
49126
|
+
Realtime = _Realtime;
|
|
48918
49127
|
DEFAULT_POLLING_OPTIONS = {
|
|
48919
49128
|
pollIntervalMs: 1e4,
|
|
48920
49129
|
timeoutMs: 5 * 60 * 1000
|
|
@@ -48958,13 +49167,37 @@ function createKadoaClient(auth) {
|
|
|
48958
49167
|
});
|
|
48959
49168
|
return client;
|
|
48960
49169
|
}
|
|
48961
|
-
var ctxRefreshMutex;
|
|
49170
|
+
var refreshRawMutex, ctxRefreshMutex;
|
|
48962
49171
|
var init_client = __esm(() => {
|
|
48963
49172
|
init_dist2();
|
|
49173
|
+
refreshRawMutex = new Map;
|
|
48964
49174
|
ctxRefreshMutex = new WeakMap;
|
|
48965
49175
|
});
|
|
48966
49176
|
|
|
48967
49177
|
// src/client.ts
|
|
49178
|
+
var exports_client = {};
|
|
49179
|
+
__export(exports_client, {
|
|
49180
|
+
refreshSupabaseJwtRaw: () => refreshSupabaseJwtRaw,
|
|
49181
|
+
refreshSupabaseJwt: () => refreshSupabaseJwt,
|
|
49182
|
+
isJwtExpired: () => isJwtExpired,
|
|
49183
|
+
getValidJwt: () => getValidJwt,
|
|
49184
|
+
decodeJwtClaims: () => decodeJwtClaims,
|
|
49185
|
+
createKadoaClient: () => createKadoaClient2,
|
|
49186
|
+
SessionExpiredError: () => SessionExpiredError,
|
|
49187
|
+
KadoaSdkException: () => KadoaSdkException,
|
|
49188
|
+
KadoaClient: () => KadoaClient
|
|
49189
|
+
});
|
|
49190
|
+
function createKadoaClient2(auth) {
|
|
49191
|
+
const client = new KadoaClient({ bearerToken: auth.jwt });
|
|
49192
|
+
client.axiosInstance.interceptors.request.use((config2) => {
|
|
49193
|
+
config2.headers["x-kadoa-source"] = "mcp";
|
|
49194
|
+
if (auth.teamId) {
|
|
49195
|
+
config2.headers["x-team-id"] = auth.teamId;
|
|
49196
|
+
}
|
|
49197
|
+
return config2;
|
|
49198
|
+
});
|
|
49199
|
+
return client;
|
|
49200
|
+
}
|
|
48968
49201
|
function decodeJwtClaims(jwt2) {
|
|
48969
49202
|
try {
|
|
48970
49203
|
const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
|
|
@@ -48986,36 +49219,57 @@ function isJwtExpired(jwt2) {
|
|
|
48986
49219
|
return true;
|
|
48987
49220
|
}
|
|
48988
49221
|
}
|
|
48989
|
-
async function
|
|
48990
|
-
|
|
48991
|
-
|
|
48992
|
-
|
|
49222
|
+
async function refreshSupabaseJwtRaw(supabaseRefreshToken) {
|
|
49223
|
+
const inflight = refreshRawMutex2.get(supabaseRefreshToken);
|
|
49224
|
+
if (inflight) {
|
|
49225
|
+
console.error(`[JWT_REFRESH] DEDUP: reusing in-flight raw refresh`);
|
|
49226
|
+
return inflight;
|
|
48993
49227
|
}
|
|
49228
|
+
const promise3 = _doRefreshRaw(supabaseRefreshToken).finally(() => {
|
|
49229
|
+
refreshRawMutex2.delete(supabaseRefreshToken);
|
|
49230
|
+
});
|
|
49231
|
+
refreshRawMutex2.set(supabaseRefreshToken, promise3);
|
|
49232
|
+
return promise3;
|
|
49233
|
+
}
|
|
49234
|
+
async function _doRefreshRaw(supabaseRefreshToken) {
|
|
48994
49235
|
const supabaseUrl = process.env.SUPABASE_URL;
|
|
48995
49236
|
if (!supabaseUrl) {
|
|
48996
49237
|
console.error("[JWT_REFRESH] SUPABASE_URL not set, cannot refresh");
|
|
49238
|
+
return null;
|
|
49239
|
+
}
|
|
49240
|
+
const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
|
|
49241
|
+
method: "POST",
|
|
49242
|
+
headers: {
|
|
49243
|
+
"Content-Type": "application/json",
|
|
49244
|
+
apikey: process.env.SUPABASE_ANON_KEY
|
|
49245
|
+
},
|
|
49246
|
+
body: JSON.stringify({ refresh_token: supabaseRefreshToken })
|
|
49247
|
+
});
|
|
49248
|
+
if (res.ok) {
|
|
49249
|
+
const data = await res.json();
|
|
49250
|
+
return { jwt: data.access_token, refreshToken: data.refresh_token };
|
|
49251
|
+
}
|
|
49252
|
+
const body = await res.text().catch(() => "");
|
|
49253
|
+
console.error(`[JWT_REFRESH] FAIL: Supabase returned ${res.status} (refreshToken=${supabaseRefreshToken.slice(0, 12)}...): ${body}`);
|
|
49254
|
+
if (body.includes("session_expired") || body.includes("refresh_token_not_found") || body.includes("refresh_token_already_used")) {
|
|
49255
|
+
throw new SessionExpiredError("Your Kadoa session has expired due to inactivity. Please reconnect to re-authenticate.");
|
|
49256
|
+
}
|
|
49257
|
+
return null;
|
|
49258
|
+
}
|
|
49259
|
+
async function refreshSupabaseJwt(ctx) {
|
|
49260
|
+
if (!ctx.supabaseRefreshToken) {
|
|
49261
|
+
console.error("[JWT_REFRESH] No refresh token available, cannot refresh");
|
|
48997
49262
|
return;
|
|
48998
49263
|
}
|
|
48999
49264
|
try {
|
|
49000
49265
|
const refreshToken = ctx.supabaseRefreshToken;
|
|
49001
49266
|
console.error(`[JWT_REFRESH] Refreshing Supabase JWT (refreshToken=${refreshToken.slice(0, 12)}..., team=${ctx.teamId ?? "unknown"})`);
|
|
49002
|
-
const
|
|
49003
|
-
|
|
49004
|
-
headers: {
|
|
49005
|
-
"Content-Type": "application/json",
|
|
49006
|
-
apikey: process.env.SUPABASE_ANON_KEY
|
|
49007
|
-
},
|
|
49008
|
-
body: JSON.stringify({ refresh_token: refreshToken })
|
|
49009
|
-
});
|
|
49010
|
-
if (!res.ok) {
|
|
49011
|
-
const body = await res.text().catch(() => "");
|
|
49012
|
-
console.error(`[JWT_REFRESH] FAIL: Supabase returned ${res.status} (refreshToken=${refreshToken.slice(0, 12)}...): ${body}`);
|
|
49267
|
+
const result = await refreshSupabaseJwtRaw(refreshToken);
|
|
49268
|
+
if (!result)
|
|
49013
49269
|
return;
|
|
49014
|
-
|
|
49015
|
-
|
|
49016
|
-
ctx.
|
|
49017
|
-
ctx.supabaseRefreshToken = data.refresh_token;
|
|
49018
|
-
ctx.client.setBearerToken(data.access_token);
|
|
49270
|
+
ctx.supabaseJwt = result.jwt;
|
|
49271
|
+
ctx.supabaseRefreshToken = result.refreshToken;
|
|
49272
|
+
ctx.client.setBearerToken(result.jwt);
|
|
49019
49273
|
try {
|
|
49020
49274
|
await ctx.persist?.({
|
|
49021
49275
|
supabaseJwt: ctx.supabaseJwt,
|
|
@@ -49025,9 +49279,11 @@ async function refreshSupabaseJwt(ctx) {
|
|
|
49025
49279
|
} catch (e) {
|
|
49026
49280
|
console.error("[JWT_REFRESH] WARN: persist failed, tokens updated in-memory only:", e);
|
|
49027
49281
|
}
|
|
49028
|
-
console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"}, newRefreshToken=${
|
|
49029
|
-
return
|
|
49282
|
+
console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"}, newRefreshToken=${result.refreshToken.slice(0, 12)}...)`);
|
|
49283
|
+
return result.jwt;
|
|
49030
49284
|
} catch (error48) {
|
|
49285
|
+
if (error48 instanceof SessionExpiredError)
|
|
49286
|
+
throw error48;
|
|
49031
49287
|
console.error("[JWT_REFRESH] FAIL: threw", error48);
|
|
49032
49288
|
return;
|
|
49033
49289
|
}
|
|
@@ -49048,9 +49304,16 @@ async function getValidJwt(ctx) {
|
|
|
49048
49304
|
ctxRefreshMutex2.set(ctx, promise3);
|
|
49049
49305
|
return promise3;
|
|
49050
49306
|
}
|
|
49051
|
-
var ctxRefreshMutex2;
|
|
49307
|
+
var SessionExpiredError, refreshRawMutex2, ctxRefreshMutex2;
|
|
49052
49308
|
var init_client2 = __esm(() => {
|
|
49053
49309
|
init_dist2();
|
|
49310
|
+
SessionExpiredError = class SessionExpiredError extends Error {
|
|
49311
|
+
constructor(message) {
|
|
49312
|
+
super(message ?? "Supabase session expired. Please re-authenticate.");
|
|
49313
|
+
this.name = "SessionExpiredError";
|
|
49314
|
+
}
|
|
49315
|
+
};
|
|
49316
|
+
refreshRawMutex2 = new Map;
|
|
49054
49317
|
ctxRefreshMutex2 = new WeakMap;
|
|
49055
49318
|
});
|
|
49056
49319
|
|
|
@@ -49168,6 +49431,10 @@ function registerTools(server, ctx) {
|
|
|
49168
49431
|
}
|
|
49169
49432
|
return await handler(...args);
|
|
49170
49433
|
} catch (error48) {
|
|
49434
|
+
if (error48 instanceof SessionExpiredError) {
|
|
49435
|
+
console.error(`[Tool Error] ${name}: session expired, user must re-authenticate`);
|
|
49436
|
+
return errorResult("Your session has expired. Please reconnect the MCP server to re-authenticate.");
|
|
49437
|
+
}
|
|
49171
49438
|
let message = classifyError(error48);
|
|
49172
49439
|
if (KadoaHttpException.isInstance(error48) && error48.httpStatus === 403) {
|
|
49173
49440
|
try {
|
|
@@ -53892,45 +54159,6 @@ function generatePKCE() {
|
|
|
53892
54159
|
const challenge = createHash2("sha256").update(verifier).digest("base64url");
|
|
53893
54160
|
return { verifier, challenge };
|
|
53894
54161
|
}
|
|
53895
|
-
async function refreshSupabaseToken(supabaseRefreshToken, context) {
|
|
53896
|
-
const inflight = supabaseRefreshMutex.get(supabaseRefreshToken);
|
|
53897
|
-
if (inflight) {
|
|
53898
|
-
console.error(`[AUTH] REFRESH_DEDUP: reusing in-flight refresh (${context})`);
|
|
53899
|
-
return inflight;
|
|
53900
|
-
}
|
|
53901
|
-
const promise3 = (async () => {
|
|
53902
|
-
const supabaseUrl = process.env.SUPABASE_URL;
|
|
53903
|
-
if (!supabaseUrl || !supabaseRefreshToken)
|
|
53904
|
-
return null;
|
|
53905
|
-
try {
|
|
53906
|
-
const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
|
|
53907
|
-
method: "POST",
|
|
53908
|
-
headers: {
|
|
53909
|
-
"Content-Type": "application/json",
|
|
53910
|
-
apikey: process.env.SUPABASE_ANON_KEY
|
|
53911
|
-
},
|
|
53912
|
-
body: JSON.stringify({ refresh_token: supabaseRefreshToken })
|
|
53913
|
-
});
|
|
53914
|
-
if (res.ok) {
|
|
53915
|
-
const data = await res.json();
|
|
53916
|
-
const newClaims = jwtClaims(data.access_token);
|
|
53917
|
-
console.log(`[AUTH] REFRESH_OK: Supabase JWT refreshed (${context}, newEmail=${newClaims.email})`);
|
|
53918
|
-
return { jwt: data.access_token, refreshToken: data.refresh_token };
|
|
53919
|
-
}
|
|
53920
|
-
const body = await res.text().catch(() => "");
|
|
53921
|
-
console.error(`[AUTH] REFRESH_FAIL: Supabase returned ${res.status} (${context}): ${body.slice(0, 200)}`);
|
|
53922
|
-
return null;
|
|
53923
|
-
} catch (err) {
|
|
53924
|
-
console.error(`[AUTH] REFRESH_FAIL: Supabase refresh threw (${context}):`, err);
|
|
53925
|
-
return null;
|
|
53926
|
-
}
|
|
53927
|
-
})();
|
|
53928
|
-
supabaseRefreshMutex.set(supabaseRefreshToken, promise3);
|
|
53929
|
-
promise3.finally(() => {
|
|
53930
|
-
supabaseRefreshMutex.delete(supabaseRefreshToken);
|
|
53931
|
-
});
|
|
53932
|
-
return promise3;
|
|
53933
|
-
}
|
|
53934
54162
|
function jwtClaims(jwt2) {
|
|
53935
54163
|
try {
|
|
53936
54164
|
const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
|
|
@@ -54238,12 +54466,22 @@ class KadoaOAuthProvider {
|
|
|
54238
54466
|
let { supabaseJwt, supabaseRefreshToken } = entry;
|
|
54239
54467
|
const claims = jwtClaims(entry.supabaseJwt);
|
|
54240
54468
|
const context = `email=${claims.email}, team=${entry.teamId}`;
|
|
54241
|
-
|
|
54242
|
-
|
|
54243
|
-
|
|
54244
|
-
|
|
54245
|
-
|
|
54246
|
-
|
|
54469
|
+
try {
|
|
54470
|
+
const { refreshSupabaseJwtRaw: refreshSupabaseJwtRaw2, SessionExpiredError: SessionExpiredError2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
|
|
54471
|
+
const refreshed = await refreshSupabaseJwtRaw2(supabaseRefreshToken);
|
|
54472
|
+
if (refreshed) {
|
|
54473
|
+
supabaseJwt = refreshed.jwt;
|
|
54474
|
+
supabaseRefreshToken = refreshed.refreshToken;
|
|
54475
|
+
console.error(`[AUTH] REFRESH_OK: Supabase JWT refreshed (${context})`);
|
|
54476
|
+
} else {
|
|
54477
|
+
console.error(`[AUTH] REFRESH_WARN: using stale Supabase JWT as fallback (${context})`);
|
|
54478
|
+
}
|
|
54479
|
+
} catch (error48) {
|
|
54480
|
+
if (error48 instanceof Error && error48.name === "SessionExpiredError") {
|
|
54481
|
+
console.error(`[AUTH] REFRESH_DEAD: session permanently expired (${context}): ${error48.message}`);
|
|
54482
|
+
throw new InvalidTokenError("Supabase session expired. Please re-authenticate.");
|
|
54483
|
+
}
|
|
54484
|
+
console.error(`[AUTH] REFRESH_WARN: unexpected error, using stale JWT (${context}):`, error48);
|
|
54247
54485
|
}
|
|
54248
54486
|
const freshClaims = jwtClaims(supabaseJwt);
|
|
54249
54487
|
const teamId = freshClaims.activeTeamId ?? entry.teamId;
|
|
@@ -54370,7 +54608,7 @@ class KadoaOAuthProvider {
|
|
|
54370
54608
|
codeChallenge: pending.params.codeChallenge,
|
|
54371
54609
|
clientId: pending.client.client_id,
|
|
54372
54610
|
redirectUri: pending.params.redirectUri,
|
|
54373
|
-
expiresAt: Date.now() +
|
|
54611
|
+
expiresAt: Date.now() + 600000
|
|
54374
54612
|
}, 600);
|
|
54375
54613
|
const redirectUrl = new URL(pending.params.redirectUri);
|
|
54376
54614
|
redirectUrl.searchParams.set("code", mcpCode);
|
|
@@ -54799,12 +55037,11 @@ function renderLoginPage(state, error48) {
|
|
|
54799
55037
|
function escapeHtml(str) {
|
|
54800
55038
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
54801
55039
|
}
|
|
54802
|
-
var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL
|
|
55040
|
+
var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL;
|
|
54803
55041
|
var init_auth2 = __esm(() => {
|
|
54804
55042
|
init_errors4();
|
|
54805
55043
|
TEAM_SELECTION_TTL = 10 * 60 * 1000;
|
|
54806
55044
|
ACCESS_TOKEN_TTL = 7 * 24 * 3600;
|
|
54807
|
-
supabaseRefreshMutex = new Map;
|
|
54808
55045
|
});
|
|
54809
55046
|
|
|
54810
55047
|
// src/redis-store.ts
|
|
@@ -54997,6 +55234,39 @@ async function startHttpServer(options) {
|
|
|
54997
55234
|
return;
|
|
54998
55235
|
}
|
|
54999
55236
|
const identity = `jwt:${auth.userId.slice(0, 8)}...:team=${auth.teamId.slice(0, 8)}...`;
|
|
55237
|
+
if (isJwtExpired(auth.jwt)) {
|
|
55238
|
+
try {
|
|
55239
|
+
const refreshed = await refreshSupabaseJwtRaw(auth.refreshToken);
|
|
55240
|
+
if (refreshed) {
|
|
55241
|
+
auth.jwt = refreshed.jwt;
|
|
55242
|
+
auth.refreshToken = refreshed.refreshToken;
|
|
55243
|
+
const entry = await store.get("access_tokens", auth.mcpToken);
|
|
55244
|
+
if (entry) {
|
|
55245
|
+
const remainingMs = entry.expiresAt - Date.now();
|
|
55246
|
+
if (remainingMs > 0) {
|
|
55247
|
+
await store.set("access_tokens", auth.mcpToken, {
|
|
55248
|
+
...entry,
|
|
55249
|
+
supabaseJwt: refreshed.jwt,
|
|
55250
|
+
supabaseRefreshToken: refreshed.refreshToken
|
|
55251
|
+
}, Math.ceil(remainingMs / 1000));
|
|
55252
|
+
console.error(`[PROACTIVE_REFRESH] OK: JWT refreshed on ${method} (${identity})`);
|
|
55253
|
+
}
|
|
55254
|
+
}
|
|
55255
|
+
}
|
|
55256
|
+
} catch (error48) {
|
|
55257
|
+
if (error48 instanceof SessionExpiredError) {
|
|
55258
|
+
console.error(`[PROACTIVE_REFRESH] Session dead on ${method} (${identity}): ${error48.message}`);
|
|
55259
|
+
await store.del("access_tokens", auth.mcpToken);
|
|
55260
|
+
res.status(401).json({
|
|
55261
|
+
jsonrpc: "2.0",
|
|
55262
|
+
error: { code: -32001, message: error48.message },
|
|
55263
|
+
id: req.body?.id ?? null
|
|
55264
|
+
});
|
|
55265
|
+
return;
|
|
55266
|
+
}
|
|
55267
|
+
console.error(`[PROACTIVE_REFRESH] WARN: refresh failed on ${method}, continuing with stale JWT`, error48);
|
|
55268
|
+
}
|
|
55269
|
+
}
|
|
55000
55270
|
try {
|
|
55001
55271
|
console.error(`[MCP] POST method=${method} auth=${identity}`);
|
|
55002
55272
|
const transport = new StreamableHTTPServerTransport({
|
|
@@ -55074,6 +55344,7 @@ var init_http2 = __esm(async () => {
|
|
|
55074
55344
|
init_bearerAuth();
|
|
55075
55345
|
init_auth2();
|
|
55076
55346
|
init_redis_store();
|
|
55347
|
+
init_client2();
|
|
55077
55348
|
await init_src();
|
|
55078
55349
|
});
|
|
55079
55350
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kadoa/mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9-rc.2",
|
|
4
4
|
"description": "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"prepublishOnly": "bun run check-types && bun run test:unit && bun run build"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@kadoa/node-sdk": "^0.
|
|
27
|
+
"@kadoa/node-sdk": "^0.24.2",
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
29
29
|
"express": "^5.2.1",
|
|
30
30
|
"ioredis": "^5.6.1",
|