@ganju/utils 0.0.1
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/LICENSE +201 -0
- package/NOTICE +9 -0
- package/README.md +23 -0
- package/dist/apiError.d.ts +3 -0
- package/dist/apiError.d.ts.map +1 -0
- package/dist/apiError.js +36 -0
- package/dist/attachment.d.ts +33 -0
- package/dist/attachment.d.ts.map +1 -0
- package/dist/attachment.js +59 -0
- package/dist/base64.d.ts +10 -0
- package/dist/base64.d.ts.map +1 -0
- package/dist/base64.js +50 -0
- package/dist/channelDebounce.d.ts +26 -0
- package/dist/channelDebounce.d.ts.map +1 -0
- package/dist/channelDebounce.js +35 -0
- package/dist/channelNotifier.d.ts +9 -0
- package/dist/channelNotifier.d.ts.map +1 -0
- package/dist/channelNotifier.js +31 -0
- package/dist/chunking.d.ts +28 -0
- package/dist/chunking.d.ts.map +1 -0
- package/dist/chunking.js +97 -0
- package/dist/cliConstants.d.ts +9 -0
- package/dist/cliConstants.d.ts.map +1 -0
- package/dist/cliConstants.js +47 -0
- package/dist/coerce.d.ts +2 -0
- package/dist/coerce.d.ts.map +1 -0
- package/dist/coerce.js +13 -0
- package/dist/constants.d.ts +624 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +2220 -0
- package/dist/crypto.d.ts +14 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +93 -0
- package/dist/customCodeProject.d.ts +91 -0
- package/dist/customCodeProject.d.ts.map +1 -0
- package/dist/customCodeProject.js +154 -0
- package/dist/customCodeToken.d.ts +41 -0
- package/dist/customCodeToken.d.ts.map +1 -0
- package/dist/customCodeToken.js +115 -0
- package/dist/deadline.d.ts +6 -0
- package/dist/deadline.d.ts.map +1 -0
- package/dist/deadline.js +45 -0
- package/dist/discordSend.d.ts +25 -0
- package/dist/discordSend.d.ts.map +1 -0
- package/dist/discordSend.js +8 -0
- package/dist/embeddable.d.ts +2 -0
- package/dist/embeddable.d.ts.map +1 -0
- package/dist/embeddable.js +6 -0
- package/dist/exposedResource.d.ts +6 -0
- package/dist/exposedResource.d.ts.map +1 -0
- package/dist/exposedResource.js +18 -0
- package/dist/extractedDocument.d.ts +70 -0
- package/dist/extractedDocument.d.ts.map +1 -0
- package/dist/extractedDocument.js +2 -0
- package/dist/fetcher.d.ts +8 -0
- package/dist/fetcher.d.ts.map +1 -0
- package/dist/fetcher.js +68 -0
- package/dist/formatFilename.d.ts +2 -0
- package/dist/formatFilename.d.ts.map +1 -0
- package/dist/formatFilename.js +19 -0
- package/dist/formatRelative.d.ts +12 -0
- package/dist/formatRelative.d.ts.map +1 -0
- package/dist/formatRelative.js +28 -0
- package/dist/formatRelativeTime.d.ts +2 -0
- package/dist/formatRelativeTime.d.ts.map +1 -0
- package/dist/formatRelativeTime.js +49 -0
- package/dist/getEnv.d.ts +5 -0
- package/dist/getEnv.d.ts.map +1 -0
- package/dist/getEnv.js +11 -0
- package/dist/gmailSend.d.ts +24 -0
- package/dist/gmailSend.d.ts.map +1 -0
- package/dist/gmailSend.js +9 -0
- package/dist/index.d.ts +2839 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +155 -0
- package/dist/jsonSchemaToZodShape.d.ts +38 -0
- package/dist/jsonSchemaToZodShape.d.ts.map +1 -0
- package/dist/jsonSchemaToZodShape.js +117 -0
- package/dist/l2Normalize.d.ts +2 -0
- package/dist/l2Normalize.d.ts.map +1 -0
- package/dist/l2Normalize.js +25 -0
- package/dist/languageCookieDomain.d.ts +21 -0
- package/dist/languageCookieDomain.d.ts.map +1 -0
- package/dist/languageCookieDomain.js +39 -0
- package/dist/localizeZodIssue.d.ts +43 -0
- package/dist/localizeZodIssue.d.ts.map +1 -0
- package/dist/localizeZodIssue.js +90 -0
- package/dist/mailHeaders.d.ts +4 -0
- package/dist/mailHeaders.d.ts.map +1 -0
- package/dist/mailHeaders.js +11 -0
- package/dist/mcpProxy.d.ts +2 -0
- package/dist/mcpProxy.d.ts.map +1 -0
- package/dist/mcpProxy.js +21 -0
- package/dist/mimeMessage.d.ts +21 -0
- package/dist/mimeMessage.d.ts.map +1 -0
- package/dist/mimeMessage.js +58 -0
- package/dist/oauth.d.ts +21 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +75 -0
- package/dist/oauthProviders.d.ts +12 -0
- package/dist/oauthProviders.d.ts.map +1 -0
- package/dist/oauthProviders.js +99 -0
- package/dist/outlookSend.d.ts +19 -0
- package/dist/outlookSend.d.ts.map +1 -0
- package/dist/outlookSend.js +9 -0
- package/dist/parseHttpError.d.ts +2 -0
- package/dist/parseHttpError.d.ts.map +1 -0
- package/dist/parseHttpError.js +23 -0
- package/dist/planLimitError.d.ts +18 -0
- package/dist/planLimitError.d.ts.map +1 -0
- package/dist/planLimitError.js +35 -0
- package/dist/processQueueBatch.d.ts +17 -0
- package/dist/processQueueBatch.d.ts.map +1 -0
- package/dist/processQueueBatch.js +32 -0
- package/dist/reservedToolName.d.ts +16 -0
- package/dist/reservedToolName.d.ts.map +1 -0
- package/dist/reservedToolName.js +25 -0
- package/dist/resourceUri.d.ts +2 -0
- package/dist/resourceUri.d.ts.map +1 -0
- package/dist/resourceUri.js +19 -0
- package/dist/retry.d.ts +10 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +37 -0
- package/dist/sanitize.d.ts +7 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sanitize.js +69 -0
- package/dist/schema.d.ts +2175 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +1382 -0
- package/dist/sdkConstants.d.ts +15 -0
- package/dist/sdkConstants.d.ts.map +1 -0
- package/dist/sdkConstants.js +43 -0
- package/dist/slackSend.d.ts +38 -0
- package/dist/slackSend.d.ts.map +1 -0
- package/dist/slackSend.js +14 -0
- package/dist/slug.d.ts +5 -0
- package/dist/slug.d.ts.map +1 -0
- package/dist/slug.js +34 -0
- package/dist/slugifyTitle.d.ts +2 -0
- package/dist/slugifyTitle.d.ts.map +1 -0
- package/dist/slugifyTitle.js +11 -0
- package/dist/sources.d.ts +43 -0
- package/dist/sources.d.ts.map +1 -0
- package/dist/sources.js +73 -0
- package/dist/ssrf.d.ts +3 -0
- package/dist/ssrf.d.ts.map +1 -0
- package/dist/ssrf.js +46 -0
- package/dist/tallyUsageKinds.d.ts +9 -0
- package/dist/tallyUsageKinds.d.ts.map +1 -0
- package/dist/tallyUsageKinds.js +20 -0
- package/dist/telegramSend.d.ts +28 -0
- package/dist/telegramSend.d.ts.map +1 -0
- package/dist/telegramSend.js +9 -0
- package/dist/toolCatalog.d.ts +452 -0
- package/dist/toolCatalog.d.ts.map +1 -0
- package/dist/toolCatalog.js +552 -0
- package/dist/validateMessageVariables.d.ts +7 -0
- package/dist/validateMessageVariables.d.ts.map +1 -0
- package/dist/validateMessageVariables.js +16 -0
- package/dist/vendorError.d.ts +2 -0
- package/dist/vendorError.d.ts.map +1 -0
- package/dist/vendorError.js +25 -0
- package/dist/whatsappSend.d.ts +31 -0
- package/dist/whatsappSend.d.ts.map +1 -0
- package/dist/whatsappSend.js +11 -0
- package/package.json +76 -0
- package/src/apiError.ts +40 -0
- package/src/attachment.ts +97 -0
- package/src/base64.ts +60 -0
- package/src/channelDebounce.ts +76 -0
- package/src/channelNotifier.ts +39 -0
- package/src/chunking.ts +131 -0
- package/src/cliConstants.ts +50 -0
- package/src/coerce.ts +9 -0
- package/src/constants.ts +2542 -0
- package/src/crypto.ts +108 -0
- package/src/customCodeProject.ts +182 -0
- package/src/customCodeToken.ts +198 -0
- package/src/deadline.ts +43 -0
- package/src/discordSend.ts +41 -0
- package/src/embeddable.ts +4 -0
- package/src/exposedResource.ts +40 -0
- package/src/extractedDocument.ts +62 -0
- package/src/fetcher.ts +82 -0
- package/src/formatFilename.ts +19 -0
- package/src/formatRelative.ts +36 -0
- package/src/formatRelativeTime.ts +45 -0
- package/src/getEnv.ts +13 -0
- package/src/gmailSend.ts +34 -0
- package/src/index.ts +397 -0
- package/src/jsonSchemaToZodShape.ts +111 -0
- package/src/l2Normalize.ts +19 -0
- package/src/languageCookieDomain.ts +37 -0
- package/src/localizeZodIssue.ts +124 -0
- package/src/mailHeaders.ts +12 -0
- package/src/mcpProxy.ts +20 -0
- package/src/mimeMessage.ts +82 -0
- package/src/oauth.ts +103 -0
- package/src/oauthProviders.ts +118 -0
- package/src/outlookSend.ts +44 -0
- package/src/parseHttpError.ts +16 -0
- package/src/planLimitError.ts +50 -0
- package/src/processQueueBatch.ts +46 -0
- package/src/reservedToolName.ts +27 -0
- package/src/resourceUri.ts +17 -0
- package/src/retry.ts +43 -0
- package/src/sanitize.ts +67 -0
- package/src/schema.ts +1624 -0
- package/src/sdkConstants.ts +44 -0
- package/src/slackSend.ts +65 -0
- package/src/slug.ts +37 -0
- package/src/slugifyTitle.ts +8 -0
- package/src/sources.ts +117 -0
- package/src/ssrf.ts +34 -0
- package/src/tallyUsageKinds.ts +20 -0
- package/src/telegramSend.ts +39 -0
- package/src/toolCatalog.ts +635 -0
- package/src/validateMessageVariables.ts +18 -0
- package/src/vendorError.ts +19 -0
- package/src/whatsappSend.ts +47 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildMimeMessage = exports.chunkBase64 = exports.formatFilenameHeader = exports.sanitizeFilename = void 0;
|
|
4
|
+
const mailHeaders_1 = require("./mailHeaders");
|
|
5
|
+
const sanitizeFilename = (name) => {
|
|
6
|
+
const cleaned = name.replace(/[\r\n"]/g, '').trim();
|
|
7
|
+
return cleaned || 'attachment';
|
|
8
|
+
};
|
|
9
|
+
exports.sanitizeFilename = sanitizeFilename;
|
|
10
|
+
const formatFilenameHeader = (name) => {
|
|
11
|
+
const safe = (0, exports.sanitizeFilename)(name);
|
|
12
|
+
if (/^[\x20-\x7E]+$/.test(safe)) {
|
|
13
|
+
return `filename="${safe.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
14
|
+
}
|
|
15
|
+
return `filename*=UTF-8''${encodeURIComponent(safe)}`;
|
|
16
|
+
};
|
|
17
|
+
exports.formatFilenameHeader = formatFilenameHeader;
|
|
18
|
+
const chunkBase64 = (b64) => b64.replace(/(.{76})/g, '$1\r\n');
|
|
19
|
+
exports.chunkBase64 = chunkBase64;
|
|
20
|
+
const buildMimeMessage = (input) => {
|
|
21
|
+
const headers = [];
|
|
22
|
+
headers.push(`To: ${(0, mailHeaders_1.formatMailHeader)(input.to)}`);
|
|
23
|
+
if (input.cc)
|
|
24
|
+
headers.push(`Cc: ${(0, mailHeaders_1.formatMailHeader)(input.cc)}`);
|
|
25
|
+
if (input.bcc)
|
|
26
|
+
headers.push(`Bcc: ${(0, mailHeaders_1.formatMailHeader)(input.bcc)}`);
|
|
27
|
+
headers.push(`Subject: ${(0, mailHeaders_1.formatMailHeader)(input.subject)}`);
|
|
28
|
+
if (input.inReplyTo)
|
|
29
|
+
headers.push(`In-Reply-To: ${(0, mailHeaders_1.sanitizeMailHeader)(input.inReplyTo)}`);
|
|
30
|
+
if (input.references)
|
|
31
|
+
headers.push(`References: ${(0, mailHeaders_1.sanitizeMailHeader)(input.references)}`);
|
|
32
|
+
headers.push('MIME-Version: 1.0');
|
|
33
|
+
const bodyContentType = input.contentType || 'text/html';
|
|
34
|
+
if (!input.attachments || input.attachments.length === 0) {
|
|
35
|
+
headers.push(`Content-Type: ${bodyContentType}; charset=utf-8`);
|
|
36
|
+
return `${headers.join('\r\n')}\r\n\r\n${input.body}`;
|
|
37
|
+
}
|
|
38
|
+
const boundary = `_ganju_${crypto.randomUUID().replace(/-/g, '')}`;
|
|
39
|
+
headers.push(`Content-Type: multipart/mixed; boundary="${boundary}"`);
|
|
40
|
+
const parts = [headers.join('\r\n'), ''];
|
|
41
|
+
parts.push(`--${boundary}`);
|
|
42
|
+
parts.push(`Content-Type: ${bodyContentType}; charset=utf-8`);
|
|
43
|
+
parts.push('Content-Transfer-Encoding: 7bit');
|
|
44
|
+
parts.push('');
|
|
45
|
+
parts.push(input.body);
|
|
46
|
+
for (const att of input.attachments) {
|
|
47
|
+
const safeName = (0, exports.sanitizeFilename)(att.filename);
|
|
48
|
+
parts.push(`--${boundary}`);
|
|
49
|
+
parts.push(`Content-Type: ${att.mimeType}; name="${safeName.replace(/"/g, '\\"')}"`);
|
|
50
|
+
parts.push('Content-Transfer-Encoding: base64');
|
|
51
|
+
parts.push(`Content-Disposition: attachment; ${(0, exports.formatFilenameHeader)(att.filename)}`);
|
|
52
|
+
parts.push('');
|
|
53
|
+
parts.push((0, exports.chunkBase64)(att.base64));
|
|
54
|
+
}
|
|
55
|
+
parts.push(`--${boundary}--`);
|
|
56
|
+
return parts.join('\r\n');
|
|
57
|
+
};
|
|
58
|
+
exports.buildMimeMessage = buildMimeMessage;
|
package/dist/oauth.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface RefreshOAuthTokenInput {
|
|
2
|
+
tokenUrl: string;
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret: string;
|
|
5
|
+
refreshToken: string;
|
|
6
|
+
}
|
|
7
|
+
export interface RefreshedOAuthToken {
|
|
8
|
+
accessToken: string;
|
|
9
|
+
refreshToken?: string;
|
|
10
|
+
expiresIn?: number;
|
|
11
|
+
scope?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class OAuthReauthRequiredError extends Error {
|
|
14
|
+
readonly code: string;
|
|
15
|
+
constructor(code: string);
|
|
16
|
+
}
|
|
17
|
+
export declare const refreshOAuthToken: (input: RefreshOAuthTokenInput) => Promise<RefreshedOAuthToken>;
|
|
18
|
+
export declare const buildReauthMetadata: (previous: Record<string, unknown> | null, reason: string) => Record<string, unknown>;
|
|
19
|
+
export declare const clearReauthMetadata: (previous: Record<string, unknown> | null) => Record<string, unknown> | null;
|
|
20
|
+
export declare const isCredentialNeedingReauth: (metadata: unknown) => boolean;
|
|
21
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM;CAKzB;AAUD,eAAO,MAAM,iBAAiB,GAC5B,OAAO,sBAAsB,KAC5B,OAAO,CAAC,mBAAmB,CAsC7B,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACxC,QAAQ,MAAM,KACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAKvB,CAAC;AAEH,eAAO,MAAM,mBAAmB,GAC9B,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,KACvC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAO5B,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,UAAU,OAAO,KAChB,OAGF,CAAC"}
|
package/dist/oauth.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isCredentialNeedingReauth = exports.clearReauthMetadata = exports.buildReauthMetadata = exports.refreshOAuthToken = exports.OAuthReauthRequiredError = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
class OAuthReauthRequiredError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
constructor(code) {
|
|
8
|
+
super(`OAuth reauth required (${code})`);
|
|
9
|
+
this.name = 'OAuthReauthRequiredError';
|
|
10
|
+
this.code = code;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.OAuthReauthRequiredError = OAuthReauthRequiredError;
|
|
14
|
+
const parseErrorCode = (body) => {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(body)?.error;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const refreshOAuthToken = async (input) => {
|
|
23
|
+
const response = await fetch(input.tokenUrl, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
26
|
+
body: new URLSearchParams({
|
|
27
|
+
client_id: input.clientId,
|
|
28
|
+
client_secret: input.clientSecret,
|
|
29
|
+
grant_type: 'refresh_token',
|
|
30
|
+
refresh_token: input.refreshToken
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
const body = await response.text();
|
|
35
|
+
const code = parseErrorCode(body);
|
|
36
|
+
if (code && constants_1.constants.REAUTH_ERROR_CODES.includes(code)) {
|
|
37
|
+
throw new OAuthReauthRequiredError(code);
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`oauth refresh failed (${response.status}): ${body}`);
|
|
40
|
+
}
|
|
41
|
+
const tokens = (await response.json());
|
|
42
|
+
if (!tokens.access_token) {
|
|
43
|
+
throw new Error('oauth refresh missing access_token');
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
accessToken: tokens.access_token,
|
|
47
|
+
refreshToken: tokens.refresh_token,
|
|
48
|
+
expiresIn: tokens.expires_in,
|
|
49
|
+
scope: tokens.scope
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
exports.refreshOAuthToken = refreshOAuthToken;
|
|
53
|
+
const buildReauthMetadata = (previous, reason) => ({
|
|
54
|
+
...(previous || {}),
|
|
55
|
+
needsReauth: true,
|
|
56
|
+
reauthReason: reason,
|
|
57
|
+
reauthAt: new Date().toISOString()
|
|
58
|
+
});
|
|
59
|
+
exports.buildReauthMetadata = buildReauthMetadata;
|
|
60
|
+
const clearReauthMetadata = (previous) => {
|
|
61
|
+
if (!previous)
|
|
62
|
+
return null;
|
|
63
|
+
const next = { ...previous };
|
|
64
|
+
delete next.needsReauth;
|
|
65
|
+
delete next.reauthReason;
|
|
66
|
+
delete next.reauthAt;
|
|
67
|
+
return Object.keys(next).length > 0 ? next : null;
|
|
68
|
+
};
|
|
69
|
+
exports.clearReauthMetadata = clearReauthMetadata;
|
|
70
|
+
const isCredentialNeedingReauth = (metadata) => {
|
|
71
|
+
if (!metadata || typeof metadata !== 'object')
|
|
72
|
+
return false;
|
|
73
|
+
return metadata.needsReauth === true;
|
|
74
|
+
};
|
|
75
|
+
exports.isCredentialNeedingReauth = isCredentialNeedingReauth;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface OAuthProviderConfig {
|
|
2
|
+
authUrl: string;
|
|
3
|
+
tokenUrl: string;
|
|
4
|
+
clientIdEnv: string;
|
|
5
|
+
clientSecretEnv: string;
|
|
6
|
+
defaultScopes: string[];
|
|
7
|
+
scopeParam?: 'scope' | 'user_scope';
|
|
8
|
+
tokenSource?: 'top' | 'authed_user';
|
|
9
|
+
omitGrantType?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const oauthProviders: Record<string, OAuthProviderConfig>;
|
|
12
|
+
//# sourceMappingURL=oauthProviders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthProviders.d.ts","sourceRoot":"","sources":["../src/oauthProviders.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,EAAE,CAAC;IAIxB,UAAU,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;IAIpC,WAAW,CAAC,EAAE,KAAK,GAAG,aAAa,CAAC;IAKpC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAQD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAwF9D,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.oauthProviders = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
// The managed OAuth apps we run. This lives in @ganju/utils rather than in
|
|
6
|
+
// apps/api because three workers now need it: apps/api (the authorization flow),
|
|
7
|
+
// and apps/tool-broker, which mints short-lived access tokens for user code and
|
|
8
|
+
// must refresh with the same client credentials against the same token URLs.
|
|
9
|
+
// Adding a provider in one place and not the other is exactly the drift this
|
|
10
|
+
// prevents.
|
|
11
|
+
exports.oauthProviders = {
|
|
12
|
+
[constants_1.constants.OAUTH_PROVIDER_GOOGLE_GMAIL]: {
|
|
13
|
+
authUrl: constants_1.constants.GOOGLE_OAUTH_AUTH_URL,
|
|
14
|
+
tokenUrl: constants_1.constants.GOOGLE_OAUTH_TOKEN_URL,
|
|
15
|
+
clientIdEnv: 'GOOGLE_CLIENT_ID',
|
|
16
|
+
clientSecretEnv: 'GOOGLE_CLIENT_SECRET',
|
|
17
|
+
defaultScopes: [
|
|
18
|
+
'https://www.googleapis.com/auth/gmail.readonly',
|
|
19
|
+
'https://www.googleapis.com/auth/gmail.send',
|
|
20
|
+
'https://www.googleapis.com/auth/gmail.modify',
|
|
21
|
+
'https://www.googleapis.com/auth/gmail.compose',
|
|
22
|
+
'https://www.googleapis.com/auth/gmail.labels'
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
[constants_1.constants.OAUTH_PROVIDER_GOOGLE_DRIVE]: {
|
|
26
|
+
authUrl: constants_1.constants.GOOGLE_OAUTH_AUTH_URL,
|
|
27
|
+
tokenUrl: constants_1.constants.GOOGLE_OAUTH_TOKEN_URL,
|
|
28
|
+
clientIdEnv: 'GOOGLE_CLIENT_ID',
|
|
29
|
+
clientSecretEnv: 'GOOGLE_CLIENT_SECRET',
|
|
30
|
+
defaultScopes: [
|
|
31
|
+
'https://www.googleapis.com/auth/drive.readonly',
|
|
32
|
+
'https://www.googleapis.com/auth/drive.metadata.readonly'
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
[constants_1.constants.OAUTH_PROVIDER_GOOGLE_CALENDAR]: {
|
|
36
|
+
authUrl: constants_1.constants.GOOGLE_OAUTH_AUTH_URL,
|
|
37
|
+
tokenUrl: constants_1.constants.GOOGLE_OAUTH_TOKEN_URL,
|
|
38
|
+
clientIdEnv: 'GOOGLE_CLIENT_ID',
|
|
39
|
+
clientSecretEnv: 'GOOGLE_CLIENT_SECRET',
|
|
40
|
+
defaultScopes: [
|
|
41
|
+
'https://www.googleapis.com/auth/calendar.events',
|
|
42
|
+
'https://www.googleapis.com/auth/calendar.readonly'
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
[constants_1.constants.OAUTH_PROVIDER_MICROSOFT_OUTLOOK]: {
|
|
46
|
+
authUrl: constants_1.constants.MICROSOFT_OAUTH_AUTH_URL,
|
|
47
|
+
tokenUrl: constants_1.constants.MICROSOFT_OAUTH_TOKEN_URL,
|
|
48
|
+
clientIdEnv: 'MICROSOFT_CLIENT_ID',
|
|
49
|
+
clientSecretEnv: 'MICROSOFT_CLIENT_SECRET',
|
|
50
|
+
defaultScopes: [
|
|
51
|
+
'https://graph.microsoft.com/Mail.ReadWrite',
|
|
52
|
+
'https://graph.microsoft.com/Mail.Send',
|
|
53
|
+
'https://graph.microsoft.com/User.Read',
|
|
54
|
+
'offline_access'
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
[constants_1.constants.OAUTH_PROVIDER_ONE_DRIVE]: {
|
|
58
|
+
authUrl: constants_1.constants.MICROSOFT_OAUTH_AUTH_URL,
|
|
59
|
+
tokenUrl: constants_1.constants.MICROSOFT_OAUTH_TOKEN_URL,
|
|
60
|
+
clientIdEnv: 'MICROSOFT_CLIENT_ID',
|
|
61
|
+
clientSecretEnv: 'MICROSOFT_CLIENT_SECRET',
|
|
62
|
+
defaultScopes: [
|
|
63
|
+
'https://graph.microsoft.com/Files.Read',
|
|
64
|
+
'https://graph.microsoft.com/Files.Read.All',
|
|
65
|
+
'offline_access'
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
[constants_1.constants.OAUTH_PROVIDER_SLACK]: {
|
|
69
|
+
authUrl: constants_1.constants.SLACK_OAUTH_AUTH_URL,
|
|
70
|
+
tokenUrl: constants_1.constants.SLACK_OAUTH_TOKEN_URL,
|
|
71
|
+
clientIdEnv: 'SLACK_CLIENT_ID',
|
|
72
|
+
clientSecretEnv: 'SLACK_CLIENT_SECRET',
|
|
73
|
+
defaultScopes: [
|
|
74
|
+
'chat:write',
|
|
75
|
+
'channels:read',
|
|
76
|
+
'groups:read',
|
|
77
|
+
'mpim:read',
|
|
78
|
+
'im:read',
|
|
79
|
+
'users:read',
|
|
80
|
+
'users:read.email',
|
|
81
|
+
'files:write'
|
|
82
|
+
],
|
|
83
|
+
omitGrantType: true
|
|
84
|
+
},
|
|
85
|
+
[constants_1.constants.OAUTH_PROVIDER_SLACK_USER]: {
|
|
86
|
+
authUrl: constants_1.constants.SLACK_OAUTH_AUTH_URL,
|
|
87
|
+
tokenUrl: constants_1.constants.SLACK_OAUTH_TOKEN_URL,
|
|
88
|
+
clientIdEnv: 'SLACK_CLIENT_ID',
|
|
89
|
+
clientSecretEnv: 'SLACK_CLIENT_SECRET',
|
|
90
|
+
// search.messages is the only Slack endpoint that needs a user (xoxp)
|
|
91
|
+
// token. Kept as its own provider so the OAuth flow stays generic —
|
|
92
|
+
// scopes are requested in the user_scope bucket, and the access_token
|
|
93
|
+
// is read from authed_user on callback.
|
|
94
|
+
defaultScopes: ['search:read'],
|
|
95
|
+
scopeParam: 'user_scope',
|
|
96
|
+
tokenSource: 'authed_user',
|
|
97
|
+
omitGrantType: true
|
|
98
|
+
}
|
|
99
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type OutlookOperation = 'send-email' | 'reply-email' | 'forward-email' | 'create-draft' | 'update-draft';
|
|
2
|
+
export interface OutlookSendRequest {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
operation: OutlookOperation;
|
|
5
|
+
body: string;
|
|
6
|
+
contentType?: 'html' | 'text';
|
|
7
|
+
to?: string;
|
|
8
|
+
subject?: string;
|
|
9
|
+
cc?: string;
|
|
10
|
+
bcc?: string;
|
|
11
|
+
messageId?: string;
|
|
12
|
+
replyAll?: boolean;
|
|
13
|
+
draftId?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface OutlookSendResponse {
|
|
16
|
+
id: string;
|
|
17
|
+
conversationId?: string;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=outlookSend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outlookSend.d.ts","sourceRoot":"","sources":["../src/outlookSend.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,gBAAgB,GACxB,YAAY,GACZ,aAAa,GACb,eAAe,GACf,cAAc,GACd,cAAc,CAAC;AAEnB,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IAEb,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAG9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IAGb,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAIlC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Wire protocol shared between the MCP worker (which builds the request) and
|
|
3
|
+
// the resource-handler container (which talks to Microsoft Graph).
|
|
4
|
+
//
|
|
5
|
+
// The worker sends a multipart/form-data POST with one `metadata` field (JSON
|
|
6
|
+
// matching OutlookSendRequest) and zero-or-more `attachment` fields. The
|
|
7
|
+
// container decides per-attachment whether to inline it in the Graph message
|
|
8
|
+
// JSON (≤3MB) or upload it via createUploadSession with chunked PUTs.
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseHttpError.d.ts","sourceRoot":"","sources":["../src/parseHttpError.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,GAAU,UAAU,QAAQ,KAAG,OAAO,CAAC,MAAM,CAe9E,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseHttpErrorMessage = void 0;
|
|
4
|
+
const parseHttpErrorMessage = async (response) => {
|
|
5
|
+
const raw = await response.text();
|
|
6
|
+
if (!raw)
|
|
7
|
+
return `${response.status} ${response.statusText}`;
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(raw);
|
|
10
|
+
if (parsed?.error?.message && typeof parsed.error.message === 'string') {
|
|
11
|
+
return parsed.error.message;
|
|
12
|
+
}
|
|
13
|
+
if (typeof parsed?.error === 'string')
|
|
14
|
+
return parsed.error;
|
|
15
|
+
if (typeof parsed?.message === 'string')
|
|
16
|
+
return parsed.message;
|
|
17
|
+
return raw;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return raw;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
exports.parseHttpErrorMessage = parseHttpErrorMessage;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface PlanLimitDetails {
|
|
2
|
+
feature: string;
|
|
3
|
+
plan: string;
|
|
4
|
+
limit?: number | null;
|
|
5
|
+
used?: number | null;
|
|
6
|
+
}
|
|
7
|
+
export declare class PlanLimitError extends Error {
|
|
8
|
+
readonly status = 402;
|
|
9
|
+
readonly code: string;
|
|
10
|
+
readonly feature: string;
|
|
11
|
+
readonly plan: string;
|
|
12
|
+
readonly limit: number | null;
|
|
13
|
+
readonly used: number | null;
|
|
14
|
+
constructor(message: string, details: PlanLimitDetails);
|
|
15
|
+
toBody(): Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export declare const isPlanLimitError: (error: unknown) => error is PlanLimitError;
|
|
18
|
+
//# sourceMappingURL=planLimitError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planLimitError.d.ts","sourceRoot":"","sources":["../src/planLimitError.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,gBAAgB;IAE/B,OAAO,EAAE,MAAM,CAAC;IAEhB,IAAI,EAAE,MAAM,CAAC;IAEb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,MAAM,OAAO;IACtB,QAAQ,CAAC,IAAI,SAAmC;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB;IAWtD,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAUlC;AAED,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,cAC1B,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPlanLimitError = exports.PlanLimitError = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
class PlanLimitError extends Error {
|
|
6
|
+
status = 402;
|
|
7
|
+
code = constants_1.constants.PLAN_LIMIT_ERROR_CODE;
|
|
8
|
+
feature;
|
|
9
|
+
plan;
|
|
10
|
+
limit;
|
|
11
|
+
used;
|
|
12
|
+
constructor(message, details) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'PlanLimitError';
|
|
15
|
+
this.feature = details.feature;
|
|
16
|
+
this.plan = details.plan;
|
|
17
|
+
this.limit = details.limit ?? null;
|
|
18
|
+
this.used = details.used ?? null;
|
|
19
|
+
}
|
|
20
|
+
// The extra fields merged into the JSON error body so clients get structured
|
|
21
|
+
// context alongside the message.
|
|
22
|
+
toBody() {
|
|
23
|
+
return {
|
|
24
|
+
code: this.code,
|
|
25
|
+
feature: this.feature,
|
|
26
|
+
plan: this.plan,
|
|
27
|
+
limit: this.limit,
|
|
28
|
+
used: this.used,
|
|
29
|
+
upgrade: true
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.PlanLimitError = PlanLimitError;
|
|
34
|
+
const isPlanLimitError = (error) => error instanceof PlanLimitError;
|
|
35
|
+
exports.isPlanLimitError = isPlanLimitError;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface QueueMessageLike<T> {
|
|
2
|
+
body: T;
|
|
3
|
+
ack: () => void;
|
|
4
|
+
retry: (opts?: {
|
|
5
|
+
delaySeconds?: number;
|
|
6
|
+
}) => void;
|
|
7
|
+
}
|
|
8
|
+
export interface QueueBatchLike<T> {
|
|
9
|
+
queue: string;
|
|
10
|
+
messages: readonly QueueMessageLike<T>[];
|
|
11
|
+
}
|
|
12
|
+
export interface ProcessQueueBatchHandlers<T> {
|
|
13
|
+
process: (body: T) => Promise<void>;
|
|
14
|
+
onError: (error: unknown, body: T, queueName: string) => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare const processQueueBatch: <T>(batch: QueueBatchLike<T>, handlers: ProcessQueueBatchHandlers<T>) => Promise<void>;
|
|
17
|
+
//# sourceMappingURL=processQueueBatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processQueueBatch.d.ts","sourceRoot":"","sources":["../src/processQueueBatch.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC;IACR,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,yBAAyB,CAAC,CAAC;IAC1C,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE;AAED,eAAO,MAAM,iBAAiB,GAAU,CAAC,EACvC,OAAO,cAAc,CAAC,CAAC,CAAC,EACxB,UAAU,yBAAyB,CAAC,CAAC,CAAC,KACrC,OAAO,CAAC,IAAI,CAuBd,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processQueueBatch = void 0;
|
|
4
|
+
const retry_1 = require("./retry");
|
|
5
|
+
const constants_1 = require("./constants");
|
|
6
|
+
const processQueueBatch = async (batch, handlers) => {
|
|
7
|
+
for (const message of batch.messages) {
|
|
8
|
+
try {
|
|
9
|
+
await handlers.process(message.body);
|
|
10
|
+
message.ack();
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if ((0, retry_1.isTransientInfraError)(error)) {
|
|
14
|
+
// Transient Cloudflare platform hiccup (e.g. a Durable Object /
|
|
15
|
+
// Container reset). The job itself is fine, so re-run it after a short
|
|
16
|
+
// backoff without reporting an error or marking the work failed.
|
|
17
|
+
message.retry({ delaySeconds: constants_1.constants.TRANSIENT_BACKOFF_SECONDS });
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
await handlers.onError(error, message.body, batch.queue);
|
|
21
|
+
if ((0, retry_1.isRateLimitError)(error)) {
|
|
22
|
+
message.retry({
|
|
23
|
+
delaySeconds: constants_1.constants.RATE_LIMIT_BACKOFF_SECONDS
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
message.retry();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
exports.processQueueBatch = processQueueBatch;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is this MCP tool name reserved by the platform?
|
|
3
|
+
*
|
|
4
|
+
* Called on the WRITE path only — when a user names an `http-endpoint` tool or
|
|
5
|
+
* uploads a `custom-code` manifest. Deliberately NOT applied when reading a
|
|
6
|
+
* stored row back at boot: tightening this list must never stop an already
|
|
7
|
+
* installed tool from registering, which would be a silent failure the owner
|
|
8
|
+
* could only diagnose from logs. A legacy name that is reserved today keeps
|
|
9
|
+
* working; it simply loses the name to the native tool, because apps/mcp
|
|
10
|
+
* registers natives first.
|
|
11
|
+
*
|
|
12
|
+
* See RESERVED_TOOL_NAME_PREFIXES in constants.ts for why this reserves whole
|
|
13
|
+
* namespaces rather than the enumerated set of shipped keys.
|
|
14
|
+
*/
|
|
15
|
+
export declare const isReservedToolName: (name: string) => boolean;
|
|
16
|
+
//# sourceMappingURL=reservedToolName.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reservedToolName.d.ts","sourceRoot":"","sources":["../src/reservedToolName.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OAQjD,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isReservedToolName = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
const RESERVED = new Set(constants_1.constants.RESERVED_TOOL_NAMES);
|
|
6
|
+
/**
|
|
7
|
+
* Is this MCP tool name reserved by the platform?
|
|
8
|
+
*
|
|
9
|
+
* Called on the WRITE path only — when a user names an `http-endpoint` tool or
|
|
10
|
+
* uploads a `custom-code` manifest. Deliberately NOT applied when reading a
|
|
11
|
+
* stored row back at boot: tightening this list must never stop an already
|
|
12
|
+
* installed tool from registering, which would be a silent failure the owner
|
|
13
|
+
* could only diagnose from logs. A legacy name that is reserved today keeps
|
|
14
|
+
* working; it simply loses the name to the native tool, because apps/mcp
|
|
15
|
+
* registers natives first.
|
|
16
|
+
*
|
|
17
|
+
* See RESERVED_TOOL_NAME_PREFIXES in constants.ts for why this reserves whole
|
|
18
|
+
* namespaces rather than the enumerated set of shipped keys.
|
|
19
|
+
*/
|
|
20
|
+
const isReservedToolName = (name) => {
|
|
21
|
+
const lower = name.toLowerCase();
|
|
22
|
+
return (RESERVED.has(lower) ||
|
|
23
|
+
constants_1.constants.RESERVED_TOOL_NAME_PREFIXES.some(prefix => lower.startsWith(prefix)));
|
|
24
|
+
};
|
|
25
|
+
exports.isReservedToolName = isReservedToolName;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resourceUri.d.ts","sourceRoot":"","sources":["../src/resourceUri.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,MAIvB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resourceUriFromTitle = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
// The `resource://…` URI the dashboard generates from a title when the author
|
|
6
|
+
// doesn't type one, and the same one the broker derives for a script-created
|
|
7
|
+
// resource that arrives without a uri.
|
|
8
|
+
//
|
|
9
|
+
// Shared rather than duplicated because the two have to agree: a resource a
|
|
10
|
+
// tool writes should be addressable exactly like one a person uploaded, and a
|
|
11
|
+
// script asked to "replace the Q3 report" has to be able to name the row the
|
|
12
|
+
// dashboard created. Hyphens, not the underscores `slugifyTitle` produces —
|
|
13
|
+
// that one names MCP prompts and bot commands, where an underscore is legal and
|
|
14
|
+
// a hyphen isn't.
|
|
15
|
+
const resourceUriFromTitle = (title) => `${constants_1.constants.CUSTOM_CODE_RESOURCE_URI_PREFIX}${title
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
18
|
+
.replace(/^-+|-+$/g, '')}`;
|
|
19
|
+
exports.resourceUriFromTitle = resourceUriFromTitle;
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const sleep: (ms: number) => Promise<void>;
|
|
2
|
+
export declare const isRateLimitError: (err: unknown) => boolean;
|
|
3
|
+
export declare const isTransientInfraError: (err: unknown) => boolean;
|
|
4
|
+
export interface RateLimitRetryOptions {
|
|
5
|
+
maxAttempts?: number;
|
|
6
|
+
baseDelayMs?: number;
|
|
7
|
+
maxDelayMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare const withRateLimitRetry: <T>(fn: () => Promise<T>, options?: RateLimitRetryOptions) => Promise<T>;
|
|
10
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,KAAK,GAAI,IAAI,MAAM,KAAG,OAAO,CAAC,IAAI,CACQ,CAAC;AAExD,eAAO,MAAM,gBAAgB,GAAI,KAAK,OAAO,KAAG,OAG/C,CAAC;AAOF,eAAO,MAAM,qBAAqB,GAAI,KAAK,OAAO,KAAG,OAKpD,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,kBAAkB,GAAU,CAAC,EACxC,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,UAAS,qBAA0B,KAClC,OAAO,CAAC,CAAC,CAaX,CAAC"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withRateLimitRetry = exports.isTransientInfraError = exports.isRateLimitError = exports.sleep = void 0;
|
|
4
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
5
|
+
exports.sleep = sleep;
|
|
6
|
+
const isRateLimitError = (err) => {
|
|
7
|
+
const msg = String(err?.message ?? err);
|
|
8
|
+
return /\b429\b|quota|RESOURCE_EXHAUSTED|rate.?limit/i.test(msg);
|
|
9
|
+
};
|
|
10
|
+
exports.isRateLimitError = isRateLimitError;
|
|
11
|
+
// Transient Cloudflare platform errors that are safe to retry as-is: a Durable
|
|
12
|
+
// Object / Container (e.g. the resource-handler) getting evicted or reset
|
|
13
|
+
// mid-request, a code-update eviction, or a dropped internal connection. These
|
|
14
|
+
// are infra hiccups, not job failures, so callers should re-run the job rather
|
|
15
|
+
// than report an error or mark the work failed.
|
|
16
|
+
const isTransientInfraError = (err) => {
|
|
17
|
+
const msg = String(err?.message ?? err);
|
|
18
|
+
return /object to be reset|Durable Object reset|code (?:was|has been) updated|cannot access storage|Network connection lost|internal error.*Durable Object|Durable Object.*internal error/i.test(msg);
|
|
19
|
+
};
|
|
20
|
+
exports.isTransientInfraError = isTransientInfraError;
|
|
21
|
+
const withRateLimitRetry = async (fn, options = {}) => {
|
|
22
|
+
const { maxAttempts = 5, baseDelayMs = 500, maxDelayMs = 10_000 } = options;
|
|
23
|
+
let attempt = 0;
|
|
24
|
+
while (true) {
|
|
25
|
+
try {
|
|
26
|
+
return await fn();
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
attempt += 1;
|
|
30
|
+
if (attempt >= maxAttempts || !(0, exports.isRateLimitError)(err))
|
|
31
|
+
throw err;
|
|
32
|
+
const delay = Math.min(2 ** attempt * baseDelayMs, maxDelayMs);
|
|
33
|
+
await (0, exports.sleep)(delay);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
exports.withRateLimitRetry = withRateLimitRetry;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const decodeEntities: (value: string) => string;
|
|
2
|
+
export declare const escapeHtml: (value: string) => string;
|
|
3
|
+
export declare const parseOpenXmlProps: (xml: string) => Record<string, string>;
|
|
4
|
+
export declare const stripBase64Images: (text: string) => string;
|
|
5
|
+
export declare const sanitizeMetadataString: (value: string) => string;
|
|
6
|
+
export declare const serializeMetadataValue: (value: unknown) => unknown;
|
|
7
|
+
//# sourceMappingURL=sanitize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,KAAG,MAMpB,CAAC;AAK5B,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MAMhB,CAAC;AAE5B,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAUpE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MAQtC,CAAC;AAEZ,eAAO,MAAM,sBAAsB,GAAI,OAAO,MAAM,KAAG,MAK5C,CAAC;AAEZ,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,OAgBvD,CAAC"}
|