@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,2220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.constants = void 0;
|
|
4
|
+
const cliConstants_1 = require("./cliConstants");
|
|
5
|
+
const sdkConstants_1 = require("./sdkConstants");
|
|
6
|
+
const USER_ROLE_ADMIN = 'ADMIN';
|
|
7
|
+
const USER_ROLES = [USER_ROLE_ADMIN];
|
|
8
|
+
const STATUS_COMPLETED = 'COMPLETED';
|
|
9
|
+
const STATUS_PENDING = 'PENDING';
|
|
10
|
+
const STATUS_FAILED = 'FAILED';
|
|
11
|
+
const STATUS_ACTIVE = 'ACTIVE';
|
|
12
|
+
const STATUS_DISABLED = 'DISABLED';
|
|
13
|
+
const STATUS_BLOCKED = 'BLOCKED';
|
|
14
|
+
const STATUS = [
|
|
15
|
+
STATUS_COMPLETED,
|
|
16
|
+
STATUS_PENDING,
|
|
17
|
+
STATUS_FAILED,
|
|
18
|
+
STATUS_ACTIVE,
|
|
19
|
+
STATUS_DISABLED,
|
|
20
|
+
STATUS_BLOCKED
|
|
21
|
+
];
|
|
22
|
+
const USER_STATUS = [STATUS_ACTIVE, STATUS_BLOCKED];
|
|
23
|
+
const CHANNEL_STATUS = [STATUS_ACTIVE, STATUS_DISABLED];
|
|
24
|
+
const RESOURCE_STATUS = [STATUS_PENDING, STATUS_COMPLETED, STATUS_FAILED];
|
|
25
|
+
const INVITATION_STATUS = [STATUS_PENDING, STATUS_COMPLETED, STATUS_DISABLED];
|
|
26
|
+
const INVITATION_SCOPE_ORGANIZATION = 'ORGANIZATION';
|
|
27
|
+
const INVITATION_SCOPE_PROJECT = 'PROJECT';
|
|
28
|
+
const INVITATION_SCOPES = [
|
|
29
|
+
INVITATION_SCOPE_ORGANIZATION,
|
|
30
|
+
INVITATION_SCOPE_PROJECT
|
|
31
|
+
];
|
|
32
|
+
const INVITATION_RESPONSE_ACCEPT = 'accept';
|
|
33
|
+
const INVITATION_RESPONSE_DECLINE = 'decline';
|
|
34
|
+
const INVITATION_RESPONSES = [
|
|
35
|
+
INVITATION_RESPONSE_ACCEPT,
|
|
36
|
+
INVITATION_RESPONSE_DECLINE
|
|
37
|
+
];
|
|
38
|
+
const INVITATION_EXPIRY_DAYS = 7;
|
|
39
|
+
const INVITATION_TOKEN_BYTES = 24;
|
|
40
|
+
// Contact form (marketing site → /contact API endpoint) field limits.
|
|
41
|
+
const CONTACT_MAX_NAME_LENGTH = 200;
|
|
42
|
+
const CONTACT_MAX_EMAIL_LENGTH = 254;
|
|
43
|
+
const CONTACT_MIN_MESSAGE_LENGTH = 5;
|
|
44
|
+
const CONTACT_MAX_MESSAGE_LENGTH = 5000;
|
|
45
|
+
const SLUG_BYTES = 16;
|
|
46
|
+
const SLUG_PATTERN = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
47
|
+
const SERVICE_NAME_API = 'api';
|
|
48
|
+
const SERVICE_NAME_MCP = 'mcp';
|
|
49
|
+
const SERVICE_NAME_RESOURCE_HANDLER = 'resource_handler';
|
|
50
|
+
// The custom-code broker. Its own label rather than reusing 'mcp': a failure
|
|
51
|
+
// here is a connection or secret that user code could not obtain, which is a
|
|
52
|
+
// different thing to debug from an MCP request that went wrong.
|
|
53
|
+
const SERVICE_NAME_TOOL_BROKER = 'tool_broker';
|
|
54
|
+
const SERVICE_NAMES = [
|
|
55
|
+
SERVICE_NAME_API,
|
|
56
|
+
SERVICE_NAME_MCP,
|
|
57
|
+
SERVICE_NAME_RESOURCE_HANDLER,
|
|
58
|
+
SERVICE_NAME_TOOL_BROKER
|
|
59
|
+
];
|
|
60
|
+
const SOCIAL_PROVIDER_GOOGLE = 'google';
|
|
61
|
+
const SOCIAL_PROVIDER_GITHUB = 'github';
|
|
62
|
+
const SOCIAL_PROVIDERS = [SOCIAL_PROVIDER_GOOGLE, SOCIAL_PROVIDER_GITHUB];
|
|
63
|
+
const OAUTH_PROVIDER_GOOGLE_GMAIL = 'google-gmail';
|
|
64
|
+
const OAUTH_PROVIDER_GOOGLE_DRIVE = 'google-drive';
|
|
65
|
+
const OAUTH_PROVIDER_GOOGLE_CALENDAR = 'google-calendar';
|
|
66
|
+
const OAUTH_PROVIDER_MICROSOFT_OUTLOOK = 'microsoft-outlook';
|
|
67
|
+
const OAUTH_PROVIDER_ONE_DRIVE = 'microsoft-onedrive';
|
|
68
|
+
const OAUTH_PROVIDER_SLACK = 'slack';
|
|
69
|
+
const OAUTH_PROVIDER_SLACK_USER = 'slack-user';
|
|
70
|
+
const OAUTH_PROVIDERS = [
|
|
71
|
+
OAUTH_PROVIDER_GOOGLE_GMAIL,
|
|
72
|
+
OAUTH_PROVIDER_GOOGLE_DRIVE,
|
|
73
|
+
OAUTH_PROVIDER_GOOGLE_CALENDAR,
|
|
74
|
+
OAUTH_PROVIDER_MICROSOFT_OUTLOOK,
|
|
75
|
+
OAUTH_PROVIDER_ONE_DRIVE,
|
|
76
|
+
OAUTH_PROVIDER_SLACK,
|
|
77
|
+
OAUTH_PROVIDER_SLACK_USER
|
|
78
|
+
];
|
|
79
|
+
const GOOGLE_OAUTH_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
|
|
80
|
+
const MICROSOFT_OAUTH_AUTH_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize';
|
|
81
|
+
const SLACK_OAUTH_AUTH_URL = 'https://slack.com/oauth/v2/authorize';
|
|
82
|
+
const OAUTH_AUTH_URLS = {
|
|
83
|
+
[OAUTH_PROVIDER_GOOGLE_GMAIL]: GOOGLE_OAUTH_AUTH_URL,
|
|
84
|
+
[OAUTH_PROVIDER_GOOGLE_DRIVE]: GOOGLE_OAUTH_AUTH_URL,
|
|
85
|
+
[OAUTH_PROVIDER_GOOGLE_CALENDAR]: GOOGLE_OAUTH_AUTH_URL,
|
|
86
|
+
[OAUTH_PROVIDER_MICROSOFT_OUTLOOK]: MICROSOFT_OAUTH_AUTH_URL,
|
|
87
|
+
[OAUTH_PROVIDER_ONE_DRIVE]: MICROSOFT_OAUTH_AUTH_URL,
|
|
88
|
+
[OAUTH_PROVIDER_SLACK]: SLACK_OAUTH_AUTH_URL,
|
|
89
|
+
[OAUTH_PROVIDER_SLACK_USER]: SLACK_OAUTH_AUTH_URL
|
|
90
|
+
};
|
|
91
|
+
const GOOGLE_OAUTH_TOKEN_URL = 'https://oauth2.googleapis.com/token';
|
|
92
|
+
const MICROSOFT_OAUTH_TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
|
|
93
|
+
const SLACK_OAUTH_TOKEN_URL = 'https://slack.com/api/oauth.v2.access';
|
|
94
|
+
const OAUTH_TOKEN_URLS = {
|
|
95
|
+
[OAUTH_PROVIDER_GOOGLE_GMAIL]: GOOGLE_OAUTH_TOKEN_URL,
|
|
96
|
+
[OAUTH_PROVIDER_GOOGLE_DRIVE]: GOOGLE_OAUTH_TOKEN_URL,
|
|
97
|
+
[OAUTH_PROVIDER_GOOGLE_CALENDAR]: GOOGLE_OAUTH_TOKEN_URL,
|
|
98
|
+
[OAUTH_PROVIDER_MICROSOFT_OUTLOOK]: MICROSOFT_OAUTH_TOKEN_URL,
|
|
99
|
+
[OAUTH_PROVIDER_ONE_DRIVE]: MICROSOFT_OAUTH_TOKEN_URL,
|
|
100
|
+
[OAUTH_PROVIDER_SLACK]: SLACK_OAUTH_TOKEN_URL,
|
|
101
|
+
[OAUTH_PROVIDER_SLACK_USER]: SLACK_OAUTH_TOKEN_URL
|
|
102
|
+
};
|
|
103
|
+
// Refresh an OAuth token when less than this remains, so a long call doesn't
|
|
104
|
+
// expire mid-flight. Shared by every credential-refresh path (native OAuth and
|
|
105
|
+
// MCP-proxy OAuth, in both apps/api and apps/mcp).
|
|
106
|
+
const CREDENTIAL_REFRESH_BUFFER_MS = 60 * 1000;
|
|
107
|
+
const GOOGLE_CALENDAR_API_BASE = 'https://www.googleapis.com/calendar/v3';
|
|
108
|
+
const GOOGLE_DRIVE_API_BASE = 'https://www.googleapis.com/drive/v3';
|
|
109
|
+
const GOOGLE_DRIVE_DEFAULT_PAGE_SIZE = 200;
|
|
110
|
+
const GOOGLE_DRIVE_MAX_FOLDER_PAGES = 50;
|
|
111
|
+
const GOOGLE_DRIVE_TOKEN_REFRESH_LEEWAY_MS = 60_000;
|
|
112
|
+
const GOOGLE_DRIVE_URI_PREFIX = 'gdrive://files/';
|
|
113
|
+
const GOOGLE_DRIVE_FILE_FIELDS = 'id,name,mimeType,modifiedTime,createdTime,size,md5Checksum,version,parents,owners(emailAddress,displayName),webViewLink,iconLink,description,trashed,capabilities/canDownload';
|
|
114
|
+
const GOOGLE_DRIVE_LIST_FIELDS = `nextPageToken,files(${GOOGLE_DRIVE_FILE_FIELDS})`;
|
|
115
|
+
const GOOGLE_DRIVE_TAB_MY_DRIVE = 'my-drive';
|
|
116
|
+
const GOOGLE_DRIVE_TAB_SHARED_WITH_ME = 'shared-with-me';
|
|
117
|
+
const GOOGLE_DRIVE_TAB_SHARED_DRIVES = 'shared-drives';
|
|
118
|
+
const GOOGLE_DRIVE_TAB_STARRED = 'starred';
|
|
119
|
+
const GOOGLE_DRIVE_TABS = [
|
|
120
|
+
GOOGLE_DRIVE_TAB_MY_DRIVE,
|
|
121
|
+
GOOGLE_DRIVE_TAB_SHARED_WITH_ME,
|
|
122
|
+
GOOGLE_DRIVE_TAB_SHARED_DRIVES,
|
|
123
|
+
GOOGLE_DRIVE_TAB_STARRED
|
|
124
|
+
];
|
|
125
|
+
const GOOGLE_DRIVE_TAB_LABEL_MY_DRIVE = 'My Drive';
|
|
126
|
+
const GOOGLE_DRIVE_TAB_LABEL_SHARED_WITH_ME = 'Shared with me';
|
|
127
|
+
const GOOGLE_DRIVE_TAB_LABEL_SHARED_DRIVES = 'Shared drives';
|
|
128
|
+
const GOOGLE_DRIVE_TAB_LABEL_STARRED = 'Starred';
|
|
129
|
+
const GOOGLE_DRIVE_TAB_LABELS = [
|
|
130
|
+
{ value: GOOGLE_DRIVE_TAB_MY_DRIVE, label: GOOGLE_DRIVE_TAB_LABEL_MY_DRIVE },
|
|
131
|
+
{
|
|
132
|
+
value: GOOGLE_DRIVE_TAB_SHARED_WITH_ME,
|
|
133
|
+
label: GOOGLE_DRIVE_TAB_LABEL_SHARED_WITH_ME
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
value: GOOGLE_DRIVE_TAB_SHARED_DRIVES,
|
|
137
|
+
label: GOOGLE_DRIVE_TAB_LABEL_SHARED_DRIVES
|
|
138
|
+
},
|
|
139
|
+
{ value: GOOGLE_DRIVE_TAB_STARRED, label: GOOGLE_DRIVE_TAB_LABEL_STARRED }
|
|
140
|
+
];
|
|
141
|
+
const MICROSOFT_GRAPH_API_BASE = 'https://graph.microsoft.com/v1.0';
|
|
142
|
+
const ONE_DRIVE_DEFAULT_PAGE_SIZE = 200;
|
|
143
|
+
const ONE_DRIVE_MAX_FOLDER_PAGES = 50;
|
|
144
|
+
const ONE_DRIVE_TOKEN_REFRESH_LEEWAY_MS = 60_000;
|
|
145
|
+
const ONE_DRIVE_URI_PREFIX = 'onedrive://files/';
|
|
146
|
+
const ONE_DRIVE_ITEM_SELECT = 'id,name,size,webUrl,createdDateTime,lastModifiedDateTime,eTag,cTag,file,folder,parentReference,deleted,@microsoft.graph.downloadUrl';
|
|
147
|
+
const ONE_DRIVE_TAB_MY_FILES = 'my-files';
|
|
148
|
+
const ONE_DRIVE_TAB_SHARED_WITH_ME = 'shared-with-me';
|
|
149
|
+
const ONE_DRIVE_TAB_RECENT = 'recent';
|
|
150
|
+
const ONE_DRIVE_TAB_DRIVES = 'drives';
|
|
151
|
+
const ONE_DRIVE_TABS = [
|
|
152
|
+
ONE_DRIVE_TAB_MY_FILES,
|
|
153
|
+
ONE_DRIVE_TAB_SHARED_WITH_ME,
|
|
154
|
+
ONE_DRIVE_TAB_RECENT,
|
|
155
|
+
ONE_DRIVE_TAB_DRIVES
|
|
156
|
+
];
|
|
157
|
+
const ONE_DRIVE_TAB_LABEL_MY_FILES = 'My files';
|
|
158
|
+
const ONE_DRIVE_TAB_LABEL_SHARED_WITH_ME = 'Shared with me';
|
|
159
|
+
const ONE_DRIVE_TAB_LABEL_RECENT = 'Recent';
|
|
160
|
+
const ONE_DRIVE_TAB_LABEL_DRIVES = 'Drives';
|
|
161
|
+
const ONE_DRIVE_TAB_LABELS = [
|
|
162
|
+
{ value: ONE_DRIVE_TAB_MY_FILES, label: ONE_DRIVE_TAB_LABEL_MY_FILES },
|
|
163
|
+
{
|
|
164
|
+
value: ONE_DRIVE_TAB_SHARED_WITH_ME,
|
|
165
|
+
label: ONE_DRIVE_TAB_LABEL_SHARED_WITH_ME
|
|
166
|
+
},
|
|
167
|
+
{ value: ONE_DRIVE_TAB_RECENT, label: ONE_DRIVE_TAB_LABEL_RECENT },
|
|
168
|
+
{ value: ONE_DRIVE_TAB_DRIVES, label: ONE_DRIVE_TAB_LABEL_DRIVES }
|
|
169
|
+
];
|
|
170
|
+
const REAUTH_ERROR_CODES = ['invalid_grant', 'invalid_token'];
|
|
171
|
+
const RESOURCE_TYPE_TOOLTIP_MIN_WIDTH = 300;
|
|
172
|
+
const SEARCH_DEBOUNCE_MS = 300;
|
|
173
|
+
const SCHEMA_DEFINITION_TYPES = [
|
|
174
|
+
'string',
|
|
175
|
+
'number',
|
|
176
|
+
'boolean',
|
|
177
|
+
'object',
|
|
178
|
+
'array'
|
|
179
|
+
];
|
|
180
|
+
const ROLE_MESSAGE_USER = 'user';
|
|
181
|
+
const ROLE_MESSAGE_ASSISTANT = 'assistant';
|
|
182
|
+
const ROLE_MESSAGE_SYSTEM = 'system';
|
|
183
|
+
const ROLE_MESSAGE_TOOL = 'tool';
|
|
184
|
+
const ROLE_MESSAGES = [ROLE_MESSAGE_USER, ROLE_MESSAGE_ASSISTANT];
|
|
185
|
+
const CHANNEL_ROLE_MESSAGES = [
|
|
186
|
+
ROLE_MESSAGE_USER,
|
|
187
|
+
ROLE_MESSAGE_ASSISTANT,
|
|
188
|
+
ROLE_MESSAGE_SYSTEM,
|
|
189
|
+
ROLE_MESSAGE_TOOL
|
|
190
|
+
];
|
|
191
|
+
const RESOURCE_TYPE_STATIC = 'static';
|
|
192
|
+
const RESOURCE_TYPE_TEMPLATE = 'template';
|
|
193
|
+
const RESOURCE_TYPES = [RESOURCE_TYPE_STATIC, RESOURCE_TYPE_TEMPLATE];
|
|
194
|
+
const RESOURCE_SOURCE_TYPE_FILE = 'FILE';
|
|
195
|
+
const RESOURCE_SOURCE_TYPE_WEBSITE = 'WEBSITE';
|
|
196
|
+
const RESOURCE_SOURCE_TYPE_GOOGLE_DRIVE_FOLDER = 'GOOGLE_DRIVE_FOLDER';
|
|
197
|
+
const RESOURCE_SOURCE_TYPE_ONE_DRIVE_FOLDER = 'ONE_DRIVE_FOLDER';
|
|
198
|
+
// Written by a user script through the broker, and by nothing else. This is a
|
|
199
|
+
// provenance marker before it is a category: a script may replace a resource
|
|
200
|
+
// carrying it and may not touch any other row, which is what stops a tool from
|
|
201
|
+
// overwriting a document its owner uploaded. Nothing in the dashboard or the
|
|
202
|
+
// upload paths ever sets it.
|
|
203
|
+
const RESOURCE_SOURCE_TYPE_CUSTOM_CODE = 'CUSTOM_CODE';
|
|
204
|
+
const RESOURCE_SOURCE_TYPES = [
|
|
205
|
+
RESOURCE_SOURCE_TYPE_FILE,
|
|
206
|
+
RESOURCE_SOURCE_TYPE_WEBSITE,
|
|
207
|
+
RESOURCE_SOURCE_TYPE_GOOGLE_DRIVE_FOLDER,
|
|
208
|
+
RESOURCE_SOURCE_TYPE_ONE_DRIVE_FOLDER,
|
|
209
|
+
RESOURCE_SOURCE_TYPE_CUSTOM_CODE
|
|
210
|
+
];
|
|
211
|
+
const CRAWL_RENDERER_CHEERIO = 'cheerio';
|
|
212
|
+
const CRAWL_RENDERER_PLAYWRIGHT = 'playwright';
|
|
213
|
+
const CRAWL_RENDERERS = [CRAWL_RENDERER_CHEERIO, CRAWL_RENDERER_PLAYWRIGHT];
|
|
214
|
+
const CRAWL_DEFAULT_MAX_PAGES = 100;
|
|
215
|
+
const CRAWL_MAX_PAGES_LIMIT = 1000;
|
|
216
|
+
const CRAWL_DEFAULT_MAX_DEPTH = 3;
|
|
217
|
+
const CRAWL_MAX_DEPTH_LIMIT = 10;
|
|
218
|
+
const CRAWL_PAGE_FETCH_TIMEOUT_MS = 30000;
|
|
219
|
+
const CRAWL_USER_AGENT = 'Mozilla/5.0 (compatible; GanjuCrawler/1.0; +https://ganju.ai)';
|
|
220
|
+
const CRAWL_PAGE_QUEUE_BATCH_SIZE = 100;
|
|
221
|
+
const RESOURCE_ICON_THEME_DARK = 'dark';
|
|
222
|
+
const RESOURCE_ICON_THEME_LIGHT = 'light';
|
|
223
|
+
const RESOURCE_ICON_THEMES = [
|
|
224
|
+
RESOURCE_ICON_THEME_DARK,
|
|
225
|
+
RESOURCE_ICON_THEME_LIGHT
|
|
226
|
+
];
|
|
227
|
+
const ENCODING_UTF8 = 'utf-8';
|
|
228
|
+
const ENCODINGS = [
|
|
229
|
+
ENCODING_UTF8,
|
|
230
|
+
'ascii',
|
|
231
|
+
'base64',
|
|
232
|
+
'latin1',
|
|
233
|
+
'utf-16le',
|
|
234
|
+
'binary'
|
|
235
|
+
];
|
|
236
|
+
const LANGUAGE_EN = 'en';
|
|
237
|
+
const LANGUAGE_ES = 'es';
|
|
238
|
+
const LANGUAGES = [LANGUAGE_EN, LANGUAGE_ES];
|
|
239
|
+
/**
|
|
240
|
+
* Countries where Spanish is the (or an) official language.
|
|
241
|
+
*
|
|
242
|
+
* Location, not `Accept-Language`, is what picks a language for a first-time
|
|
243
|
+
* visitor: the Spanish pages carry Colombian legal content (Ley 1480, Ley
|
|
244
|
+
* 1581), so where someone is matters more than how their browser happens to be
|
|
245
|
+
* configured. Both surfaces read this list — the website's `/` Pages Function
|
|
246
|
+
* and the dashboard's middleware — so a visitor who lands on ganju.ai and then
|
|
247
|
+
* signs in at app.ganju.ai gets the same language on both.
|
|
248
|
+
*/
|
|
249
|
+
const SPANISH_COUNTRIES = [
|
|
250
|
+
'AR',
|
|
251
|
+
'BO',
|
|
252
|
+
'CL',
|
|
253
|
+
'CO',
|
|
254
|
+
'CR',
|
|
255
|
+
'CU',
|
|
256
|
+
'DO',
|
|
257
|
+
'EC',
|
|
258
|
+
'ES',
|
|
259
|
+
'GQ',
|
|
260
|
+
'GT',
|
|
261
|
+
'HN',
|
|
262
|
+
'MX',
|
|
263
|
+
'NI',
|
|
264
|
+
'PA',
|
|
265
|
+
'PE',
|
|
266
|
+
'PR',
|
|
267
|
+
'PY',
|
|
268
|
+
'SV',
|
|
269
|
+
'UY',
|
|
270
|
+
'VE'
|
|
271
|
+
];
|
|
272
|
+
/**
|
|
273
|
+
* Where an explicit language choice is remembered. The website and the
|
|
274
|
+
* dashboard sit on different hosts under one registrable domain, and the cookie
|
|
275
|
+
* is scoped to that domain (see `languageCookieDomain`) so a choice made on
|
|
276
|
+
* ganju.ai is still honoured after signing in at app.ganju.ai.
|
|
277
|
+
*/
|
|
278
|
+
const LANGUAGE_COOKIE = 'ganju_lang';
|
|
279
|
+
const LANGUAGE_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
280
|
+
/** Query parameter that carries an explicit switch, e.g. `?lang=es`. */
|
|
281
|
+
const LANGUAGE_QUERY_PARAM = 'lang';
|
|
282
|
+
const MIMETYPE_TEXT = 'text/plain';
|
|
283
|
+
const MIMETYPE_TEXT_CSV = 'text/csv';
|
|
284
|
+
const MIMETYPE_TEXT_HTML = 'text/html';
|
|
285
|
+
const MIMETYPE_TEXT_MARKDOWN = 'text/markdown';
|
|
286
|
+
const MIMETYPE_IMAGE_PNG = 'image/png';
|
|
287
|
+
const MIMETYPE_IMAGE_GIF = 'image/gif';
|
|
288
|
+
const MIMETYPE_IMAGE_JPEG = 'image/jpeg';
|
|
289
|
+
const MIMETYPE_IMAGE_WEBP = 'image/webp';
|
|
290
|
+
const MIMETYPE_IMAGE_SVG_XML = 'image/svg+xml';
|
|
291
|
+
const MIMETYPE_APPLICATION_PDF = 'application/pdf';
|
|
292
|
+
const MIMETYPE_APPLICATION_JSON = 'application/json';
|
|
293
|
+
const MIMETYPE_APPLICATION_MSWORD = 'application/msword';
|
|
294
|
+
const MIMETYPE_APPLICATION_VND_MS_EXCEL = 'application/vnd.ms-excel';
|
|
295
|
+
const MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_SPREADSHEETML_SHEET = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
|
296
|
+
const MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_WORDPROCESSINGML_DOCUMENT = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
|
|
297
|
+
const MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_PRESENTATIONML_PRESENTATION = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
|
|
298
|
+
const MIMETYPE_APPLICATION_VND_GOOGLE_APPS_DOCUMENT = 'application/vnd.google-apps.document';
|
|
299
|
+
const MIMETYPE_APPLICATION_VND_GOOGLE_APPS_SPREADSHEET = 'application/vnd.google-apps.spreadsheet';
|
|
300
|
+
const MIMETYPE_APPLICATION_VND_GOOGLE_APPS_PRESENTATION = 'application/vnd.google-apps.presentation';
|
|
301
|
+
const MIMETYPE_APPLICATION_VND_GOOGLE_APPS_FOLDER = 'application/vnd.google-apps.folder';
|
|
302
|
+
const MIMETYPE_APPLICATION_XML = 'application/xml';
|
|
303
|
+
const MIMETYPE_APPLICATION_JAVASCRIPT = 'application/javascript';
|
|
304
|
+
const MIMETYPE_APPLICATION_TYPESCRIPT = 'application/typescript';
|
|
305
|
+
const MIMETYPE_APPLICATION_YAML = 'application/yaml';
|
|
306
|
+
const MIMETYPE_APPLICATION_X_YAML = 'application/x-yaml';
|
|
307
|
+
const MIMETYPE_APPLICATION_TOML = 'application/toml';
|
|
308
|
+
const MIMETYPE_APPLICATION_X_SH = 'application/x-sh';
|
|
309
|
+
const MIMETYPE_APPLICATION_SQL = 'application/sql';
|
|
310
|
+
const MIMETYPE_APPLICATION_GRAPHQL = 'application/graphql';
|
|
311
|
+
const MIMETYPE_APPLICATION_LD_JSON = 'application/ld+json';
|
|
312
|
+
const MIMETYPE_APPLICATION_XHTML_XML = 'application/xhtml+xml';
|
|
313
|
+
const MIMETYPE_APPLICATION_X_HTTPD_PHP = 'application/x-httpd-php';
|
|
314
|
+
const MIMETYPE_APPLICATION_X_PYTHON_CODE = 'application/x-python-code';
|
|
315
|
+
const MIMETYPE_APPLICATION_X_WWW_FORM_URLENCODED = 'application/x-www-form-urlencoded';
|
|
316
|
+
const MIMETYPE_APPLICATION_CSV = 'application/csv';
|
|
317
|
+
const MIMETYPE_APPLICATION_X_RUBY = 'application/x-ruby';
|
|
318
|
+
const MIMETYPE_APPLICATION_X_PERL = 'application/x-perl';
|
|
319
|
+
const MIMETYPE_APPLICATION_OCTET_STREAM = 'application/octet-stream';
|
|
320
|
+
const MIMETYPES = [
|
|
321
|
+
MIMETYPE_TEXT,
|
|
322
|
+
MIMETYPE_TEXT_CSV,
|
|
323
|
+
MIMETYPE_TEXT_HTML,
|
|
324
|
+
MIMETYPE_TEXT_MARKDOWN,
|
|
325
|
+
MIMETYPE_IMAGE_PNG,
|
|
326
|
+
MIMETYPE_IMAGE_GIF,
|
|
327
|
+
MIMETYPE_IMAGE_JPEG,
|
|
328
|
+
MIMETYPE_IMAGE_WEBP,
|
|
329
|
+
MIMETYPE_IMAGE_SVG_XML,
|
|
330
|
+
MIMETYPE_APPLICATION_PDF,
|
|
331
|
+
MIMETYPE_APPLICATION_JSON,
|
|
332
|
+
MIMETYPE_APPLICATION_MSWORD,
|
|
333
|
+
MIMETYPE_APPLICATION_VND_MS_EXCEL,
|
|
334
|
+
MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_SPREADSHEETML_SHEET,
|
|
335
|
+
MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_WORDPROCESSINGML_DOCUMENT,
|
|
336
|
+
MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_PRESENTATIONML_PRESENTATION,
|
|
337
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_DOCUMENT,
|
|
338
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_SPREADSHEET,
|
|
339
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_PRESENTATION,
|
|
340
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_FOLDER,
|
|
341
|
+
MIMETYPE_APPLICATION_XML,
|
|
342
|
+
MIMETYPE_APPLICATION_JAVASCRIPT,
|
|
343
|
+
MIMETYPE_APPLICATION_TYPESCRIPT,
|
|
344
|
+
MIMETYPE_APPLICATION_YAML,
|
|
345
|
+
MIMETYPE_APPLICATION_X_YAML,
|
|
346
|
+
MIMETYPE_APPLICATION_TOML,
|
|
347
|
+
MIMETYPE_APPLICATION_X_SH,
|
|
348
|
+
MIMETYPE_APPLICATION_SQL,
|
|
349
|
+
MIMETYPE_APPLICATION_GRAPHQL,
|
|
350
|
+
MIMETYPE_APPLICATION_LD_JSON,
|
|
351
|
+
MIMETYPE_APPLICATION_XHTML_XML,
|
|
352
|
+
MIMETYPE_APPLICATION_X_HTTPD_PHP,
|
|
353
|
+
MIMETYPE_APPLICATION_X_PYTHON_CODE,
|
|
354
|
+
MIMETYPE_APPLICATION_X_WWW_FORM_URLENCODED,
|
|
355
|
+
MIMETYPE_APPLICATION_CSV,
|
|
356
|
+
MIMETYPE_APPLICATION_X_RUBY,
|
|
357
|
+
MIMETYPE_APPLICATION_X_PERL,
|
|
358
|
+
MIMETYPE_APPLICATION_OCTET_STREAM
|
|
359
|
+
];
|
|
360
|
+
// Sensible file extension for a mime type, used to name a downloaded/forwarded
|
|
361
|
+
// file (e.g. a proxied MCP resource sent into a channel) when the source has no
|
|
362
|
+
// usable filename. Not exhaustive — callers fall back to .bin / .txt.
|
|
363
|
+
const EXTENSION_BY_MIME = {
|
|
364
|
+
[MIMETYPE_TEXT]: 'txt',
|
|
365
|
+
[MIMETYPE_TEXT_CSV]: 'csv',
|
|
366
|
+
[MIMETYPE_TEXT_HTML]: 'html',
|
|
367
|
+
[MIMETYPE_TEXT_MARKDOWN]: 'md',
|
|
368
|
+
[MIMETYPE_APPLICATION_JSON]: 'json',
|
|
369
|
+
[MIMETYPE_APPLICATION_PDF]: 'pdf',
|
|
370
|
+
[MIMETYPE_APPLICATION_XML]: 'xml',
|
|
371
|
+
[MIMETYPE_IMAGE_PNG]: 'png',
|
|
372
|
+
[MIMETYPE_IMAGE_JPEG]: 'jpg',
|
|
373
|
+
[MIMETYPE_IMAGE_GIF]: 'gif',
|
|
374
|
+
[MIMETYPE_IMAGE_WEBP]: 'webp',
|
|
375
|
+
[MIMETYPE_IMAGE_SVG_XML]: 'svg'
|
|
376
|
+
};
|
|
377
|
+
const TEXT_MIME_TYPES = [
|
|
378
|
+
MIMETYPE_TEXT,
|
|
379
|
+
MIMETYPE_TEXT_CSV,
|
|
380
|
+
MIMETYPE_TEXT_HTML,
|
|
381
|
+
MIMETYPE_APPLICATION_JSON,
|
|
382
|
+
MIMETYPE_APPLICATION_XML,
|
|
383
|
+
MIMETYPE_APPLICATION_JAVASCRIPT,
|
|
384
|
+
MIMETYPE_APPLICATION_TYPESCRIPT,
|
|
385
|
+
MIMETYPE_APPLICATION_YAML,
|
|
386
|
+
MIMETYPE_APPLICATION_X_YAML,
|
|
387
|
+
MIMETYPE_APPLICATION_TOML,
|
|
388
|
+
MIMETYPE_APPLICATION_X_SH,
|
|
389
|
+
MIMETYPE_APPLICATION_SQL,
|
|
390
|
+
MIMETYPE_APPLICATION_GRAPHQL,
|
|
391
|
+
MIMETYPE_APPLICATION_LD_JSON,
|
|
392
|
+
MIMETYPE_APPLICATION_XHTML_XML,
|
|
393
|
+
MIMETYPE_APPLICATION_X_HTTPD_PHP,
|
|
394
|
+
MIMETYPE_APPLICATION_X_PYTHON_CODE,
|
|
395
|
+
MIMETYPE_APPLICATION_X_WWW_FORM_URLENCODED,
|
|
396
|
+
MIMETYPE_APPLICATION_CSV,
|
|
397
|
+
MIMETYPE_APPLICATION_X_RUBY,
|
|
398
|
+
MIMETYPE_APPLICATION_X_PERL
|
|
399
|
+
];
|
|
400
|
+
const EMBEDDABLE_MIME_TYPES = [
|
|
401
|
+
...TEXT_MIME_TYPES,
|
|
402
|
+
MIMETYPE_APPLICATION_PDF,
|
|
403
|
+
MIMETYPE_APPLICATION_MSWORD,
|
|
404
|
+
MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_WORDPROCESSINGML_DOCUMENT,
|
|
405
|
+
MIMETYPE_APPLICATION_VND_MS_EXCEL,
|
|
406
|
+
MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_SPREADSHEETML_SHEET,
|
|
407
|
+
MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_PRESENTATIONML_PRESENTATION,
|
|
408
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_DOCUMENT,
|
|
409
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_SPREADSHEET,
|
|
410
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_PRESENTATION
|
|
411
|
+
];
|
|
412
|
+
const GOOGLE_DRIVE_EXPORT_MIME_TYPES = {
|
|
413
|
+
[MIMETYPE_APPLICATION_VND_GOOGLE_APPS_DOCUMENT]: MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_WORDPROCESSINGML_DOCUMENT,
|
|
414
|
+
[MIMETYPE_APPLICATION_VND_GOOGLE_APPS_SPREADSHEET]: MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_SPREADSHEETML_SHEET,
|
|
415
|
+
[MIMETYPE_APPLICATION_VND_GOOGLE_APPS_PRESENTATION]: MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_PRESENTATIONML_PRESENTATION
|
|
416
|
+
};
|
|
417
|
+
const GOOGLE_DRIVE_EXPORT_EXTENSIONS = {
|
|
418
|
+
[MIMETYPE_APPLICATION_VND_GOOGLE_APPS_DOCUMENT]: 'docx',
|
|
419
|
+
[MIMETYPE_APPLICATION_VND_GOOGLE_APPS_SPREADSHEET]: 'xlsx',
|
|
420
|
+
[MIMETYPE_APPLICATION_VND_GOOGLE_APPS_PRESENTATION]: 'pptx'
|
|
421
|
+
};
|
|
422
|
+
const USER_AVATAR_MIME_TYPES = [
|
|
423
|
+
MIMETYPE_IMAGE_PNG,
|
|
424
|
+
MIMETYPE_IMAGE_JPEG,
|
|
425
|
+
MIMETYPE_IMAGE_WEBP,
|
|
426
|
+
MIMETYPE_IMAGE_GIF
|
|
427
|
+
];
|
|
428
|
+
const MAX_FILE_SIZE = 500 * 1024 * 1024; // 500MB
|
|
429
|
+
const MAX_AVATAR_SIZE = 5 * 1024 * 1024; // 5MB
|
|
430
|
+
// Gmail caps a sent message at 25MB encoded. Combined raw attachment size
|
|
431
|
+
// must stay under ~18MB so the base64 multipart envelope fits.
|
|
432
|
+
const GMAIL_MAX_RAW_ATTACHMENT_BYTES = 18 * 1024 * 1024;
|
|
433
|
+
// Microsoft Graph: attachments ≤3MB go inline in the message JSON; larger
|
|
434
|
+
// ones need createUploadSession + chunked PUT. We enforce a per-attachment
|
|
435
|
+
// cap so a single huge file fails fast instead of after a partial upload.
|
|
436
|
+
const OUTLOOK_ATTACHMENT_INLINE_THRESHOLD = 3 * 1024 * 1024;
|
|
437
|
+
const OUTLOOK_MAX_ATTACHMENT_BYTES = 150 * 1024 * 1024;
|
|
438
|
+
const OUTLOOK_UPLOAD_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
439
|
+
// Slack Web API base. Per-file cap for files.uploadV2 — Slack's hard limit
|
|
440
|
+
// is 1GB but anything that large should be a shared link, not an upload,
|
|
441
|
+
// and 100MB keeps memory pressure on the resource-handler container sane.
|
|
442
|
+
const SLACK_API_BASE = 'https://slack.com/api';
|
|
443
|
+
const SLACK_MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
|
|
444
|
+
// Telegram Bot API per-method file caps (sendPhoto vs sendDocument differ).
|
|
445
|
+
const TELEGRAM_MAX_PHOTO_BYTES = 10 * 1024 * 1024;
|
|
446
|
+
const TELEGRAM_MAX_FILE_BYTES = 50 * 1024 * 1024;
|
|
447
|
+
const RATE_LIMIT_BACKOFF_SECONDS = 60;
|
|
448
|
+
// Short backoff for transient Cloudflare platform errors (e.g. a Durable Object
|
|
449
|
+
// / Container reset) so the retried job lands after the object has recovered.
|
|
450
|
+
const TRANSIENT_BACKOFF_SECONDS = 5;
|
|
451
|
+
const CHANNEL_PLATFORM_TELEGRAM = 'telegram';
|
|
452
|
+
const CHANNEL_PLATFORM_SLACK = 'slack';
|
|
453
|
+
const CHANNEL_PLATFORM_WHATSAPP = 'whatsapp';
|
|
454
|
+
const CHANNEL_PLATFORM_DISCORD = 'discord';
|
|
455
|
+
const CHANNEL_PLATFORMS = [
|
|
456
|
+
CHANNEL_PLATFORM_TELEGRAM,
|
|
457
|
+
CHANNEL_PLATFORM_SLACK,
|
|
458
|
+
CHANNEL_PLATFORM_WHATSAPP,
|
|
459
|
+
CHANNEL_PLATFORM_DISCORD
|
|
460
|
+
];
|
|
461
|
+
const CHANNEL_CONVERSATION_SCOPE_PRIVATE = 'private';
|
|
462
|
+
const CHANNEL_CONVERSATION_SCOPE_GROUP = 'group';
|
|
463
|
+
const CHANNEL_CONVERSATION_SCOPE_CHANNEL = 'channel';
|
|
464
|
+
const CHANNEL_CONVERSATION_SCOPES = [
|
|
465
|
+
CHANNEL_CONVERSATION_SCOPE_PRIVATE,
|
|
466
|
+
CHANNEL_CONVERSATION_SCOPE_GROUP,
|
|
467
|
+
CHANNEL_CONVERSATION_SCOPE_CHANNEL
|
|
468
|
+
];
|
|
469
|
+
// Kinds of artifact usage — what was exercised on a request. Shared by channel
|
|
470
|
+
// message usage and the artifact_execution audit trail (who ran what, when).
|
|
471
|
+
const USAGE_KIND_PROMPT = 'prompt';
|
|
472
|
+
const USAGE_KIND_RESOURCE = 'resource';
|
|
473
|
+
const USAGE_KIND_TOOL = 'tool';
|
|
474
|
+
const USAGE_KINDS = [USAGE_KIND_PROMPT, USAGE_KIND_RESOURCE, USAGE_KIND_TOOL];
|
|
475
|
+
const LLM_PROVIDER_ANTHROPIC = 'anthropic';
|
|
476
|
+
const LLM_PROVIDER_OPENAI = 'openai';
|
|
477
|
+
const LLM_PROVIDER_OPENAI_COMPATIBLE = 'openai-compatible';
|
|
478
|
+
const LLM_PROVIDER_GOOGLE = 'google';
|
|
479
|
+
const LLM_PROVIDERS = [
|
|
480
|
+
LLM_PROVIDER_ANTHROPIC,
|
|
481
|
+
LLM_PROVIDER_OPENAI,
|
|
482
|
+
LLM_PROVIDER_OPENAI_COMPATIBLE,
|
|
483
|
+
LLM_PROVIDER_GOOGLE
|
|
484
|
+
];
|
|
485
|
+
const DEFAULT_LLM_PROVIDER = LLM_PROVIDER_GOOGLE;
|
|
486
|
+
// ⚠️ This is the SHARED platform model — every Free turn and every paid turn on
|
|
487
|
+
// an org with no LLM of its own runs here, and WE pay that inference. The whole
|
|
488
|
+
// plan economics rest on Flash-Lite's rate ($0.25/M in · $1.50/M out): the
|
|
489
|
+
// planning figure of ~$0.004 per channel turn, Free's ~$0.41/month worst case,
|
|
490
|
+
// and the shared-key overage rate all derive from it. Moving to Gemini 3 Flash
|
|
491
|
+
// doubles every one of those; a Sonnet-class model is ~15×. Treat a change here
|
|
492
|
+
// as a pricing change, not a config tweak — the plan rates have to be redone
|
|
493
|
+
// alongside it.
|
|
494
|
+
const DEFAULT_LLM_MODEL = 'gemini-3.1-flash-lite';
|
|
495
|
+
const DEFAULT_LLM_SYSTEM_PROMPT = 'You are a helpful assistant. Answer the user using the tools and resources provided to you whenever they are relevant, and prefer that information over your prior knowledge. Cite the resources you used when applicable. Be concise and accurate. If you cannot find a clear answer in the available context, say so honestly instead of guessing.';
|
|
496
|
+
const LLM_CATALOG = [
|
|
497
|
+
{
|
|
498
|
+
provider: LLM_PROVIDER_GOOGLE,
|
|
499
|
+
model: 'gemini-3-flash-preview',
|
|
500
|
+
label: 'Gemini 3 Flash'
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
provider: LLM_PROVIDER_GOOGLE,
|
|
504
|
+
model: 'gemini-3.1-pro-preview',
|
|
505
|
+
label: 'Gemini 3.1 Pro'
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
provider: LLM_PROVIDER_OPENAI,
|
|
509
|
+
model: 'gpt-4o-mini',
|
|
510
|
+
label: 'GPT-4o mini'
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
provider: LLM_PROVIDER_OPENAI,
|
|
514
|
+
model: 'gpt-4o',
|
|
515
|
+
label: 'GPT-4o'
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
provider: LLM_PROVIDER_ANTHROPIC,
|
|
519
|
+
model: 'claude-haiku-4-5',
|
|
520
|
+
label: 'Claude Haiku 4.5'
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
provider: LLM_PROVIDER_ANTHROPIC,
|
|
524
|
+
model: 'claude-sonnet-4-6',
|
|
525
|
+
label: 'Claude Sonnet 4.6'
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
provider: LLM_PROVIDER_ANTHROPIC,
|
|
529
|
+
model: 'claude-opus-4.7',
|
|
530
|
+
label: 'Claude Sonnet 4.7'
|
|
531
|
+
}
|
|
532
|
+
];
|
|
533
|
+
const LLM_SYSTEM_DEFAULT = 'SYSTEM_DEFAULT';
|
|
534
|
+
const MAX_TOOL_LOOPS = 8;
|
|
535
|
+
// How many recent messages a channel turn replays as context.
|
|
536
|
+
const CHANNEL_HISTORY_LIMIT = 20;
|
|
537
|
+
// Tighter turn envelope when a channel runs on the shared platform model key
|
|
538
|
+
// (Free, no org LLM configured) — we pay that inference, so we bound the two
|
|
539
|
+
// dominant cost drivers: input tokens (smaller history) and number of model
|
|
540
|
+
// calls (fewer tool loops). Orgs that bring their own key get the full envelope
|
|
541
|
+
// above. Lower numbers also cut latency on the same path.
|
|
542
|
+
const SHARED_KEY_HISTORY_LIMIT = 10;
|
|
543
|
+
const SHARED_KEY_MAX_TOOL_LOOPS = 6;
|
|
544
|
+
// Hard ceiling on how many tools a single channel turn exposes to the model.
|
|
545
|
+
//
|
|
546
|
+
// Tool count is the dominant input-token driver, and it is super-linear: our own
|
|
547
|
+
// channel_message rows show a 5-tool artifact averaging 1,103 input tokens/turn
|
|
548
|
+
// against 13,109 for a 12-tool one — 2.4× the tools, 12× the tokens, because
|
|
549
|
+
// every schema is re-sent on every model call and a turn makes ~3. Uncapped, an
|
|
550
|
+
// 80-tool artifact costs ~$21 per 1,000 turns, which is above the shared-key
|
|
551
|
+
// overage rate; capped at 40 it tops out near $11 and stays profitable.
|
|
552
|
+
//
|
|
553
|
+
// This is also a quality control, not only a cost one — tool selection degrades
|
|
554
|
+
// badly past a few dozen options. Applies to CHANNEL turns only: MCP-client
|
|
555
|
+
// traffic pays its own token cost on the client's model, so it isn't capped
|
|
556
|
+
// here.
|
|
557
|
+
const CHANNEL_MAX_TOOLS = 40;
|
|
558
|
+
// Message debounce — people type the way they talk, in bursts ("hey" / "quick
|
|
559
|
+
// question" / "about the invoice"). Answering each fragment separately produces
|
|
560
|
+
// three half-informed replies and bills three assistant turns. Instead a burst
|
|
561
|
+
// from ONE participant in ONE conversation is buffered and answered once.
|
|
562
|
+
//
|
|
563
|
+
// The window restarts on every new message (so it tracks the typist), bounded by
|
|
564
|
+
// CHANNEL_DEBOUNCE_MAX_WAIT_MS from the first buffered message so a chatty user
|
|
565
|
+
// still gets an answer, and by CHANNEL_DEBOUNCE_MAX_MESSAGES so one batch can't
|
|
566
|
+
// grow without limit. `debounceMs: 0` on channel.config disables buffering and
|
|
567
|
+
// restores the answer-every-message behavior.
|
|
568
|
+
const CHANNEL_DEBOUNCE_DEFAULT_MS = 5000;
|
|
569
|
+
const CHANNEL_DEBOUNCE_MIN_MS = 500;
|
|
570
|
+
const CHANNEL_DEBOUNCE_MAX_MS = 30_000;
|
|
571
|
+
const CHANNEL_DEBOUNCE_MAX_WAIT_MS = 60_000;
|
|
572
|
+
const CHANNEL_DEBOUNCE_MAX_MESSAGES = 20;
|
|
573
|
+
// Disables buffering for a channel when set as `config.debounceMs`.
|
|
574
|
+
const CHANNEL_DEBOUNCE_DISABLED = 0;
|
|
575
|
+
// How buffered texts are joined into the single user turn the model sees. A
|
|
576
|
+
// newline keeps each fragment on its own line rather than running them together.
|
|
577
|
+
const CHANNEL_DEBOUNCE_JOIN = '\n';
|
|
578
|
+
// Backoff between retries when the DO can't hand a flushed batch to the worker,
|
|
579
|
+
// and how many times it tries before dropping the batch rather than looping.
|
|
580
|
+
const CHANNEL_DEBOUNCE_RETRY_MS = 5000;
|
|
581
|
+
const CHANNEL_DEBOUNCE_MAX_ATTEMPTS = 3;
|
|
582
|
+
const TELEGRAM_SECRET_HEADER = 'x-telegram-bot-api-secret-token';
|
|
583
|
+
const TELEGRAM_API_BASE = 'https://api.telegram.org';
|
|
584
|
+
const TELEGRAM_MESSAGE_LIMIT = 3500;
|
|
585
|
+
// Slack channel (inbound bot webhook). Slack signs every request to the
|
|
586
|
+
// Events API / slash-command Request URL with the app signing secret; we verify
|
|
587
|
+
// `v0:{timestamp}:{rawBody}` against the x-slack-signature header and reject
|
|
588
|
+
// stale timestamps (replay protection). A `section` block's mrkdwn text caps at
|
|
589
|
+
// 3000 chars — we chunk a bit under that. Source links become Block Kit URL
|
|
590
|
+
// buttons (≤5 per actions block on Slack's side).
|
|
591
|
+
const SLACK_SIGNATURE_HEADER = 'x-slack-signature';
|
|
592
|
+
const SLACK_TIMESTAMP_HEADER = 'x-slack-request-timestamp';
|
|
593
|
+
const SLACK_RETRY_NUM_HEADER = 'x-slack-retry-num';
|
|
594
|
+
const SLACK_SIGNATURE_VERSION = 'v0';
|
|
595
|
+
const SLACK_SIGNATURE_MAX_SKEW_SECONDS = 300;
|
|
596
|
+
const SLACK_MESSAGE_LIMIT = 2900;
|
|
597
|
+
const SLACK_MAX_SOURCE_BUTTONS = 10;
|
|
598
|
+
// The Slack app configuration Ganju needs, surfaced in the UI when connecting a
|
|
599
|
+
// Slack channel. Required scopes back the runner's calls (mentions, DMs,
|
|
600
|
+
// posting, file upload); recommended scopes only enrich names/titles and
|
|
601
|
+
// degrade gracefully; bot events are what the webhook subscribes to.
|
|
602
|
+
const SLACK_REQUIRED_SCOPES = [
|
|
603
|
+
'app_mentions:read',
|
|
604
|
+
'im:history',
|
|
605
|
+
'chat:write',
|
|
606
|
+
'files:write'
|
|
607
|
+
];
|
|
608
|
+
const SLACK_RECOMMENDED_SCOPES = ['users:read', 'channels:read', 'groups:read'];
|
|
609
|
+
const SLACK_BOT_EVENTS = ['app_mention', 'message.im'];
|
|
610
|
+
// Discord. Free-form messages / @mentions / DMs arrive over a persistent Gateway
|
|
611
|
+
// WebSocket (held in a Durable Object); native slash commands arrive over a
|
|
612
|
+
// separate Ed25519-signed Interactions HTTP endpoint. The bot identity token,
|
|
613
|
+
// the application id (for command registration + interaction follow-ups), and
|
|
614
|
+
// the application public key (for verifying interactions) are all stored as the
|
|
615
|
+
// channel's credentials.
|
|
616
|
+
const DISCORD_API_BASE = 'https://discord.com/api/v10';
|
|
617
|
+
// Gateway connection query (JSON encoding, API v10). The actual socket URL is
|
|
618
|
+
// discovered from GET /gateway/bot (or the resume_gateway_url after READY).
|
|
619
|
+
const DISCORD_GATEWAY_QUERY = '?v=10&encoding=json';
|
|
620
|
+
const DISCORD_MESSAGE_LIMIT = 2000;
|
|
621
|
+
// Default bot upload ceiling for a server without boosts. Larger files should be
|
|
622
|
+
// shared as links, not uploaded; this also keeps the container's memory sane.
|
|
623
|
+
const DISCORD_MAX_UPLOAD_BYTES = 25 * 1024 * 1024;
|
|
624
|
+
// Discord renders at most 5 buttons per action row — source links become a
|
|
625
|
+
// single row of link buttons (mirroring Telegram's inline keyboard).
|
|
626
|
+
const DISCORD_MAX_SOURCE_BUTTONS = 5;
|
|
627
|
+
// Gateway intents the bot identifies with: GUILDS (1<<0) + GUILD_MESSAGES
|
|
628
|
+
// (1<<9) + DIRECT_MESSAGES (1<<12) + MESSAGE_CONTENT (1<<15, privileged — must
|
|
629
|
+
// be enabled in the Developer Portal to read free-form text in servers).
|
|
630
|
+
const DISCORD_INTENTS = (1 << 0) | (1 << 9) | (1 << 12) | (1 << 15);
|
|
631
|
+
// Interactions are signed with Ed25519 over `{timestamp}{rawBody}`; verify
|
|
632
|
+
// against the application public key.
|
|
633
|
+
const DISCORD_SIGNATURE_HEADER = 'x-signature-ed25519';
|
|
634
|
+
const DISCORD_TIMESTAMP_HEADER = 'x-signature-timestamp';
|
|
635
|
+
// Interaction request types and response types (Discord API).
|
|
636
|
+
const DISCORD_INTERACTION_TYPE_PING = 1;
|
|
637
|
+
const DISCORD_INTERACTION_TYPE_APPLICATION_COMMAND = 2;
|
|
638
|
+
const DISCORD_INTERACTION_RESPONSE_PONG = 1;
|
|
639
|
+
const DISCORD_INTERACTION_RESPONSE_DEFERRED = 5;
|
|
640
|
+
// Channel types we map to conversation scopes: DM (1) / group DM (3) = private;
|
|
641
|
+
// everything else (guild text channels, threads) = channel.
|
|
642
|
+
const DISCORD_CHANNEL_TYPE_DM = 1;
|
|
643
|
+
const DISCORD_CHANNEL_TYPE_GROUP_DM = 3;
|
|
644
|
+
// Discord Gateway opcodes (the persistent WebSocket protocol the bot speaks to
|
|
645
|
+
// receive messages). See https://discord.com/developers/docs/topics/gateway.
|
|
646
|
+
const DISCORD_GATEWAY_OP_DISPATCH = 0;
|
|
647
|
+
const DISCORD_GATEWAY_OP_HEARTBEAT = 1;
|
|
648
|
+
const DISCORD_GATEWAY_OP_IDENTIFY = 2;
|
|
649
|
+
const DISCORD_GATEWAY_OP_RESUME = 6;
|
|
650
|
+
const DISCORD_GATEWAY_OP_RECONNECT = 7;
|
|
651
|
+
const DISCORD_GATEWAY_OP_INVALID_SESSION = 9;
|
|
652
|
+
const DISCORD_GATEWAY_OP_HELLO = 10;
|
|
653
|
+
const DISCORD_GATEWAY_OP_HEARTBEAT_ACK = 11;
|
|
654
|
+
// Default heartbeat cadence (ms) until HELLO supplies the real interval.
|
|
655
|
+
const DISCORD_GATEWAY_DEFAULT_HEARTBEAT_MS = 41250;
|
|
656
|
+
// WhatsApp (Meta Cloud API). Inbound messages and the GET verification handshake
|
|
657
|
+
// hit the same per-channel Request URL the tenant configures in their Meta app
|
|
658
|
+
// dashboard. Meta signs every POST with the app secret over the RAW body —
|
|
659
|
+
// `sha256=<hmac>` in the x-hub-signature-256 header (mirrors Slack's scheme).
|
|
660
|
+
// The GET handshake echoes hub.challenge when hub.verify_token matches the
|
|
661
|
+
// channel's stored token. Like Slack, there's no group concept for the Cloud
|
|
662
|
+
// API bot — every conversation is a 1:1 (private) chat keyed by the user's wa_id.
|
|
663
|
+
const WHATSAPP_API_BASE = 'https://graph.facebook.com';
|
|
664
|
+
const WHATSAPP_API_VERSION = 'v25.0';
|
|
665
|
+
// A WhatsApp text message body caps at 4096 chars; we chunk a bit under that.
|
|
666
|
+
const WHATSAPP_MESSAGE_LIMIT = 4000;
|
|
667
|
+
const WHATSAPP_SIGNATURE_HEADER = 'x-hub-signature-256';
|
|
668
|
+
const WHATSAPP_SIGNATURE_PREFIX = 'sha256=';
|
|
669
|
+
// GET-handshake query params + the expected hub.mode value.
|
|
670
|
+
const WHATSAPP_HUB_MODE_PARAM = 'hub.mode';
|
|
671
|
+
const WHATSAPP_HUB_VERIFY_TOKEN_PARAM = 'hub.verify_token';
|
|
672
|
+
const WHATSAPP_HUB_CHALLENGE_PARAM = 'hub.challenge';
|
|
673
|
+
const WHATSAPP_HUB_MODE_SUBSCRIBE = 'subscribe';
|
|
674
|
+
// Cloud API media ceilings by category (Meta-enforced). Documents are the
|
|
675
|
+
// largest; images the smallest. Used to reject an oversize send before upload.
|
|
676
|
+
const WHATSAPP_MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
677
|
+
const WHATSAPP_MAX_VIDEO_BYTES = 16 * 1024 * 1024;
|
|
678
|
+
const WHATSAPP_MAX_AUDIO_BYTES = 16 * 1024 * 1024;
|
|
679
|
+
const WHATSAPP_MAX_DOCUMENT_BYTES = 100 * 1024 * 1024;
|
|
680
|
+
const EMBEDDING_MODEL = 'gemini-embedding-001';
|
|
681
|
+
// 1536, not the model's native 3072. Matryoshka training means the first half of
|
|
682
|
+
// the vector carries almost all the signal: measured on our own corpus, 1536
|
|
683
|
+
// agrees with exact 3072 search on 93% of top-10 results — while the HNSW index
|
|
684
|
+
// we already run agrees only 73% of the time, so this costs less fidelity than
|
|
685
|
+
// the index itself does. In exchange the vector and its index entry both halve,
|
|
686
|
+
// taking embedded storage from ~12.9x expansion to ~6.9x.
|
|
687
|
+
//
|
|
688
|
+
// Two things depend on this staying in step: the halfvec(1536) column in
|
|
689
|
+
// packages/db, and the l2Normalize call in both embed paths — the API does NOT
|
|
690
|
+
// normalise output below 3072 dimensions.
|
|
691
|
+
const EMBEDDING_DIMENSIONS = 1536;
|
|
692
|
+
const CHUNK_TARGET_CHARS = 2000;
|
|
693
|
+
const CHUNK_OVERLAP_CHARS = 200;
|
|
694
|
+
const EMBED_BATCH_SIZE = 96;
|
|
695
|
+
const RESOURCE_HANDLER_SLEEP_AFTER = '10m';
|
|
696
|
+
const DOCS_URL = 'https://docs.ganju.ai';
|
|
697
|
+
const BASE64_DATA_URI_RE = /data:image\/[a-zA-Z0-9+\-.]+;base64,[A-Za-z0-9+/=\s]+/g;
|
|
698
|
+
const RAW_BASE64_BLOB_RE = /[A-Za-z0-9+/]{512,}={0,2}/g;
|
|
699
|
+
const CHUNK_SEPARATORS = [
|
|
700
|
+
{ split: /\n(?=#{1,6}\s)/, join: '\n' },
|
|
701
|
+
{ split: '\n\n', join: '\n\n' },
|
|
702
|
+
{ split: '\n', join: '\n' },
|
|
703
|
+
{ split: '. ', join: '. ' },
|
|
704
|
+
{ split: ' ', join: ' ' }
|
|
705
|
+
];
|
|
706
|
+
const DEFAULT_MAX_TOKENS = 4096;
|
|
707
|
+
const RESOURCE_TOOL_KEY_LIST_RESOURCES = 'list-resources';
|
|
708
|
+
const RESOURCE_TOOL_KEY_SEARCH_RESOURCES = 'search-resources';
|
|
709
|
+
const RESOURCE_TOOL_KEY_READ_RESOURCE = 'read-resource';
|
|
710
|
+
const RESOURCE_TOOL_KEY_SEND_RESOURCE = 'send-resource';
|
|
711
|
+
const PROMPT_TOOL_KEY_LIST_PROMPTS = 'list-prompts';
|
|
712
|
+
const RESOURCE_TOOL_KEYS = [
|
|
713
|
+
RESOURCE_TOOL_KEY_LIST_RESOURCES,
|
|
714
|
+
RESOURCE_TOOL_KEY_SEARCH_RESOURCES,
|
|
715
|
+
RESOURCE_TOOL_KEY_READ_RESOURCE,
|
|
716
|
+
RESOURCE_TOOL_KEY_SEND_RESOURCE,
|
|
717
|
+
PROMPT_TOOL_KEY_LIST_PROMPTS
|
|
718
|
+
];
|
|
719
|
+
const URI_BEARING_RESOURCE_TOOL_KEYS = [
|
|
720
|
+
RESOURCE_TOOL_KEY_READ_RESOURCE,
|
|
721
|
+
RESOURCE_TOOL_KEY_SEND_RESOURCE
|
|
722
|
+
];
|
|
723
|
+
const CALENDAR_TOOL_KEY_PREFIX = 'calendar-';
|
|
724
|
+
// Google Calendar `sendUpdates` query values — who gets emailed on event changes.
|
|
725
|
+
const CALENDAR_SEND_UPDATES_ALL = 'all';
|
|
726
|
+
const CALENDAR_SEND_UPDATES_EXTERNAL_ONLY = 'externalOnly';
|
|
727
|
+
const CALENDAR_SEND_UPDATES_NONE = 'none';
|
|
728
|
+
const CALENDAR_SEND_UPDATES_VALUES = [
|
|
729
|
+
CALENDAR_SEND_UPDATES_ALL,
|
|
730
|
+
CALENDAR_SEND_UPDATES_EXTERNAL_ONLY,
|
|
731
|
+
CALENDAR_SEND_UPDATES_NONE
|
|
732
|
+
];
|
|
733
|
+
// Google Calendar event `visibility`. DEFAULT means "inherit the calendar's
|
|
734
|
+
// default" and is omitted from the request body.
|
|
735
|
+
const CALENDAR_VISIBILITY_DEFAULT = 'default';
|
|
736
|
+
const CALENDAR_VISIBILITY_PUBLIC = 'public';
|
|
737
|
+
const CALENDAR_VISIBILITY_PRIVATE = 'private';
|
|
738
|
+
const CALENDAR_VISIBILITY_VALUES = [
|
|
739
|
+
CALENDAR_VISIBILITY_DEFAULT,
|
|
740
|
+
CALENDAR_VISIBILITY_PUBLIC,
|
|
741
|
+
CALENDAR_VISIBILITY_PRIVATE
|
|
742
|
+
];
|
|
743
|
+
const CALENDAR_DEFAULT_CALENDAR_ID = 'primary';
|
|
744
|
+
const CALENDAR_DEFAULT_EVENT_DURATION_MINUTES = 60;
|
|
745
|
+
const CALENDAR_CONFERENCE_TYPE_GOOGLE_MEET = 'hangoutsMeet';
|
|
746
|
+
const CALENDAR_TOOL_FIELDS = {
|
|
747
|
+
'calendar-list-calendars': [
|
|
748
|
+
{
|
|
749
|
+
key: 'defaultMaxResults',
|
|
750
|
+
label: 'Default max results',
|
|
751
|
+
type: 'number',
|
|
752
|
+
min: 1,
|
|
753
|
+
max: 50,
|
|
754
|
+
help: 'Used when a call omits maxResults (1–50).'
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
key: 'defaultWindowDays',
|
|
758
|
+
label: 'Default look-ahead (days)',
|
|
759
|
+
type: 'number',
|
|
760
|
+
min: 1,
|
|
761
|
+
help: 'When no end time is given, list events this many days ahead.'
|
|
762
|
+
}
|
|
763
|
+
],
|
|
764
|
+
'calendar-create-event': [
|
|
765
|
+
{
|
|
766
|
+
key: 'defaultDurationMinutes',
|
|
767
|
+
label: 'Default duration (minutes)',
|
|
768
|
+
type: 'number',
|
|
769
|
+
min: 1,
|
|
770
|
+
help: 'Used when an event is created without an end time.'
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
key: 'addGoogleMeet',
|
|
774
|
+
label: 'Add a Google Meet link',
|
|
775
|
+
type: 'boolean',
|
|
776
|
+
help: 'Attach a Meet conference to every new event.'
|
|
777
|
+
},
|
|
778
|
+
{ key: 'defaultLocation', label: 'Default location', type: 'text' },
|
|
779
|
+
{
|
|
780
|
+
key: 'defaultVisibility',
|
|
781
|
+
label: 'Visibility',
|
|
782
|
+
type: 'select',
|
|
783
|
+
options: [
|
|
784
|
+
{ value: CALENDAR_VISIBILITY_DEFAULT, label: 'Calendar default' },
|
|
785
|
+
{ value: CALENDAR_VISIBILITY_PUBLIC, label: 'Public' },
|
|
786
|
+
{ value: CALENDAR_VISIBILITY_PRIVATE, label: 'Private' }
|
|
787
|
+
]
|
|
788
|
+
}
|
|
789
|
+
],
|
|
790
|
+
'calendar-find-free-slots': [
|
|
791
|
+
{
|
|
792
|
+
key: 'workingHoursStart',
|
|
793
|
+
label: 'Working hours start (0–23)',
|
|
794
|
+
type: 'number',
|
|
795
|
+
min: 0,
|
|
796
|
+
max: 23,
|
|
797
|
+
help: 'Local hour. Requires a default time zone to take effect.'
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
key: 'workingHoursEnd',
|
|
801
|
+
label: 'Working hours end (1–24)',
|
|
802
|
+
type: 'number',
|
|
803
|
+
min: 1,
|
|
804
|
+
max: 24
|
|
805
|
+
},
|
|
806
|
+
{ key: 'workingDays', label: 'Working days', type: 'weekdays' },
|
|
807
|
+
{
|
|
808
|
+
key: 'defaultDurationMinutes',
|
|
809
|
+
label: 'Default slot length (minutes)',
|
|
810
|
+
type: 'number',
|
|
811
|
+
min: 1
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
key: 'bufferMinutes',
|
|
815
|
+
label: 'Buffer between meetings (minutes)',
|
|
816
|
+
type: 'number',
|
|
817
|
+
min: 0
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
key: 'minNoticeHours',
|
|
821
|
+
label: 'Minimum notice (hours)',
|
|
822
|
+
type: 'number',
|
|
823
|
+
min: 0
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
key: 'maxAdvanceDays',
|
|
827
|
+
label: 'Max advance (days)',
|
|
828
|
+
type: 'number',
|
|
829
|
+
min: 1
|
|
830
|
+
}
|
|
831
|
+
]
|
|
832
|
+
};
|
|
833
|
+
// Cal.com uses a personal API key (no OAuth). The key is stored like any other
|
|
834
|
+
// credential (artifact_credential, provider 'calcom', encrypted accessToken).
|
|
835
|
+
const API_KEY_PROVIDER_CALCOM = 'calcom';
|
|
836
|
+
// Tavily web search uses a personal API key (no OAuth), same storage pattern.
|
|
837
|
+
const API_KEY_PROVIDER_TAVILY = 'tavily';
|
|
838
|
+
// API-key providers surfaced in the Tools UI's "Add API key" affordance (one
|
|
839
|
+
// key per provider, validated against the vendor before storage).
|
|
840
|
+
const API_KEY_PROVIDERS = [API_KEY_PROVIDER_CALCOM, API_KEY_PROVIDER_TAVILY];
|
|
841
|
+
// http-endpoint secrets are stored as artifact_credential rows too, but unlike
|
|
842
|
+
// the API-key providers above a single artifact can hold MANY of them (one per
|
|
843
|
+
// endpoint), each referenced by id from a tool's auth config and carrying a
|
|
844
|
+
// human label in metadata. There's no vendor to validate against. This is kept
|
|
845
|
+
// out of API_KEY_PROVIDERS so the catalog UI doesn't treat it as a one-key
|
|
846
|
+
// "Add API key" integration.
|
|
847
|
+
const CREDENTIAL_PROVIDER_HTTP_ENDPOINT = 'http-endpoint';
|
|
848
|
+
// mcp-proxy secrets behave exactly like http-endpoint ones: many per artifact
|
|
849
|
+
// (one per proxied server), referenced by id from a tool's auth config and
|
|
850
|
+
// labelled in metadata, with no vendor to validate against. Same multi-row,
|
|
851
|
+
// per-tool storage — kept out of API_KEY_PROVIDERS for the same reason.
|
|
852
|
+
const CREDENTIAL_PROVIDER_MCP_PROXY = 'mcp-proxy';
|
|
853
|
+
// custom-code secrets are the `ctx.secret(name)` values a user script reads at
|
|
854
|
+
// runtime. Same multi-row shape as the two above — many per artifact, labelled
|
|
855
|
+
// in metadata, no vendor to validate — but the label is also the LOOKUP KEY the
|
|
856
|
+
// script passes to ctx.secret(), not just a display string. The broker resolves
|
|
857
|
+
// them; the secret value itself never reaches user code as a binding. Defined in
|
|
858
|
+
// cliConstants because `ganju secret` writes under it.
|
|
859
|
+
// Providers whose secrets are per-tool (many rows per artifact, deleted with the
|
|
860
|
+
// tool that owns them) rather than one-per-provider. See createCredential /
|
|
861
|
+
// removeTool in apps/api ArtifactController.
|
|
862
|
+
const PER_TOOL_CREDENTIAL_PROVIDERS = [
|
|
863
|
+
CREDENTIAL_PROVIDER_HTTP_ENDPOINT,
|
|
864
|
+
CREDENTIAL_PROVIDER_MCP_PROXY,
|
|
865
|
+
cliConstants_1.CREDENTIAL_PROVIDER_CUSTOM_CODE
|
|
866
|
+
];
|
|
867
|
+
// Every provider the generic credential create endpoint accepts.
|
|
868
|
+
const CREDENTIAL_PROVIDERS = [
|
|
869
|
+
API_KEY_PROVIDER_CALCOM,
|
|
870
|
+
API_KEY_PROVIDER_TAVILY,
|
|
871
|
+
CREDENTIAL_PROVIDER_HTTP_ENDPOINT,
|
|
872
|
+
CREDENTIAL_PROVIDER_MCP_PROXY,
|
|
873
|
+
cliConstants_1.CREDENTIAL_PROVIDER_CUSTOM_CODE
|
|
874
|
+
];
|
|
875
|
+
const CALCOM_API_BASE = 'https://api.cal.com/v2';
|
|
876
|
+
// Cal.com pins behavior per endpoint with the `cal-api-version` header.
|
|
877
|
+
const CALCOM_API_VERSION_EVENT_TYPES = '2024-06-14';
|
|
878
|
+
const CALCOM_API_VERSION_SLOTS = '2024-09-04';
|
|
879
|
+
const CALCOM_API_VERSION_BOOKINGS = '2026-02-25';
|
|
880
|
+
const CALCOM_TOOL_KEY_PREFIX = 'calcom-';
|
|
881
|
+
// Tavily web search. The key is validated against the live API before it is
|
|
882
|
+
// persisted (a minimal 1-result search), then stored as an artifact_credential
|
|
883
|
+
// (provider 'tavily', encrypted accessToken, no refresh token) like Cal.com.
|
|
884
|
+
const TAVILY_API_BASE = 'https://api.tavily.com';
|
|
885
|
+
const TAVILY_SEARCH_DEPTH_BASIC = 'basic';
|
|
886
|
+
const TAVILY_SEARCH_DEPTH_ADVANCED = 'advanced';
|
|
887
|
+
const TAVILY_SEARCH_DEPTHS = [
|
|
888
|
+
TAVILY_SEARCH_DEPTH_BASIC,
|
|
889
|
+
TAVILY_SEARCH_DEPTH_ADVANCED
|
|
890
|
+
];
|
|
891
|
+
const TAVILY_TOPIC_GENERAL = 'general';
|
|
892
|
+
const TAVILY_TOPIC_NEWS = 'news';
|
|
893
|
+
const TAVILY_TOPICS = [TAVILY_TOPIC_GENERAL, TAVILY_TOPIC_NEWS];
|
|
894
|
+
const TAVILY_DEFAULT_MAX_RESULTS = 5;
|
|
895
|
+
const TAVILY_MAX_RESULTS_LIMIT = 20;
|
|
896
|
+
const WEB_TOOL_KEY_PREFIX = 'web-';
|
|
897
|
+
const WEB_TOOL_KEY_SEARCH = 'web-search';
|
|
898
|
+
const WEB_TOOL_KEY_EXTRACT = 'web-extract';
|
|
899
|
+
const WEB_TOOL_KEYS = [WEB_TOOL_KEY_SEARCH, WEB_TOOL_KEY_EXTRACT];
|
|
900
|
+
// `http-endpoint` is a proxied tool definition: a single tool_definition row
|
|
901
|
+
// (key below) whose installed artifact_tool rows each describe one HTTP call.
|
|
902
|
+
// At MCP server boot, every such row registers one named MCP tool derived from
|
|
903
|
+
// its config (name/title/description/inputSchema). It lets users expose their
|
|
904
|
+
// own backends to the agent without a TypeScript handler.
|
|
905
|
+
const TOOL_DEFINITION_KEY_HTTP_ENDPOINT = 'http-endpoint';
|
|
906
|
+
const HTTP_ENDPOINT_METHOD_GET = 'GET';
|
|
907
|
+
const HTTP_ENDPOINT_METHOD_POST = 'POST';
|
|
908
|
+
const HTTP_ENDPOINT_METHOD_PUT = 'PUT';
|
|
909
|
+
const HTTP_ENDPOINT_METHOD_PATCH = 'PATCH';
|
|
910
|
+
const HTTP_ENDPOINT_METHOD_DELETE = 'DELETE';
|
|
911
|
+
const HTTP_ENDPOINT_METHODS = [
|
|
912
|
+
HTTP_ENDPOINT_METHOD_GET,
|
|
913
|
+
HTTP_ENDPOINT_METHOD_POST,
|
|
914
|
+
HTTP_ENDPOINT_METHOD_PUT,
|
|
915
|
+
HTTP_ENDPOINT_METHOD_PATCH,
|
|
916
|
+
HTTP_ENDPOINT_METHOD_DELETE
|
|
917
|
+
];
|
|
918
|
+
const HTTP_ENDPOINT_BODY_KIND_NONE = 'none';
|
|
919
|
+
const HTTP_ENDPOINT_BODY_KIND_JSON = 'json';
|
|
920
|
+
const HTTP_ENDPOINT_BODY_KIND_FORM = 'form';
|
|
921
|
+
const HTTP_ENDPOINT_BODY_KIND_TEXT = 'text';
|
|
922
|
+
const HTTP_ENDPOINT_BODY_KINDS = [
|
|
923
|
+
HTTP_ENDPOINT_BODY_KIND_NONE,
|
|
924
|
+
HTTP_ENDPOINT_BODY_KIND_JSON,
|
|
925
|
+
HTTP_ENDPOINT_BODY_KIND_FORM,
|
|
926
|
+
HTTP_ENDPOINT_BODY_KIND_TEXT
|
|
927
|
+
];
|
|
928
|
+
const HTTP_ENDPOINT_AUTH_KIND_NONE = 'none';
|
|
929
|
+
const HTTP_ENDPOINT_AUTH_KIND_BEARER = 'bearer';
|
|
930
|
+
const HTTP_ENDPOINT_AUTH_KIND_BASIC = 'basic';
|
|
931
|
+
const HTTP_ENDPOINT_AUTH_KIND_API_KEY = 'api-key';
|
|
932
|
+
const HTTP_ENDPOINT_AUTH_KIND_OAUTH = 'oauth';
|
|
933
|
+
const HTTP_ENDPOINT_AUTH_KINDS = [
|
|
934
|
+
HTTP_ENDPOINT_AUTH_KIND_NONE,
|
|
935
|
+
HTTP_ENDPOINT_AUTH_KIND_BEARER,
|
|
936
|
+
HTTP_ENDPOINT_AUTH_KIND_BASIC,
|
|
937
|
+
HTTP_ENDPOINT_AUTH_KIND_API_KEY,
|
|
938
|
+
HTTP_ENDPOINT_AUTH_KIND_OAUTH
|
|
939
|
+
];
|
|
940
|
+
const HTTP_ENDPOINT_RESPONSE_CONTENT_TYPE_AUTO = 'auto';
|
|
941
|
+
const HTTP_ENDPOINT_RESPONSE_CONTENT_TYPE_JSON = 'json';
|
|
942
|
+
const HTTP_ENDPOINT_RESPONSE_CONTENT_TYPE_TEXT = 'text';
|
|
943
|
+
const HTTP_ENDPOINT_DEFAULT_TIMEOUT_MS = 10_000;
|
|
944
|
+
const HTTP_ENDPOINT_MAX_TIMEOUT_MS = 30_000;
|
|
945
|
+
// Response body cap returned to the model; truncated past this with a marker.
|
|
946
|
+
const HTTP_ENDPOINT_DEFAULT_MAX_BYTES = 256 * 1024;
|
|
947
|
+
// Hard ceiling on the outgoing request body, regardless of config.
|
|
948
|
+
const HTTP_ENDPOINT_MAX_REQUEST_BYTES = 1024 * 1024;
|
|
949
|
+
// `mcp-proxy` is the second proxied tool definition: a single tool_definition
|
|
950
|
+
// row (key below) whose installed artifact_tool rows each describe one remote
|
|
951
|
+
// MCP server (a vendor's official server — GitHub, Notion, …). Unlike
|
|
952
|
+
// http-endpoint (one row → one tool), one mcp-proxy row produces MANY MCP tools,
|
|
953
|
+
// one per remote tool discovered from that server. Discovery happens once at
|
|
954
|
+
// configure-time (apps/api) and the tool list is stored in
|
|
955
|
+
// artifact_tool.metadata.discovery so the stateless MCP boot loop can register
|
|
956
|
+
// the tools without a remote round-trip; only tools/call connects to the remote.
|
|
957
|
+
const TOOL_DEFINITION_KEY_MCP_PROXY = 'mcp-proxy';
|
|
958
|
+
const MCP_PROXY_TRANSPORT_STREAMABLE_HTTP = 'streamable-http';
|
|
959
|
+
const MCP_PROXY_TRANSPORT_SSE = 'sse';
|
|
960
|
+
const MCP_PROXY_TRANSPORTS = [
|
|
961
|
+
MCP_PROXY_TRANSPORT_STREAMABLE_HTTP,
|
|
962
|
+
MCP_PROXY_TRANSPORT_SSE
|
|
963
|
+
];
|
|
964
|
+
const MCP_PROXY_AUTH_KIND_NONE = 'none';
|
|
965
|
+
const MCP_PROXY_AUTH_KIND_BEARER = 'bearer';
|
|
966
|
+
const MCP_PROXY_AUTH_KIND_HEADER = 'header';
|
|
967
|
+
const MCP_PROXY_AUTH_KIND_OAUTH = 'oauth';
|
|
968
|
+
const MCP_PROXY_AUTH_KINDS = [
|
|
969
|
+
MCP_PROXY_AUTH_KIND_NONE,
|
|
970
|
+
MCP_PROXY_AUTH_KIND_BEARER,
|
|
971
|
+
MCP_PROXY_AUTH_KIND_HEADER,
|
|
972
|
+
MCP_PROXY_AUTH_KIND_OAUTH
|
|
973
|
+
];
|
|
974
|
+
const MCP_PROXY_DEFAULT_TIMEOUT_MS = 10_000;
|
|
975
|
+
const MCP_PROXY_MAX_TIMEOUT_MS = 30_000;
|
|
976
|
+
// Cap on how many remote tools a single proxied server may register, so a
|
|
977
|
+
// chatty vendor can't flood the artifact's tool list.
|
|
978
|
+
const MCP_PROXY_MAX_TOOLS = 100;
|
|
979
|
+
// Response cap returned to the model from a proxied tools/call; truncated past
|
|
980
|
+
// this with a marker (same ceiling as http-endpoint).
|
|
981
|
+
const MCP_PROXY_MAX_RESPONSE_BYTES = 256 * 1024;
|
|
982
|
+
// Proxied tools register as `<prefix><sep><remoteKey>` so the vendor prefix is
|
|
983
|
+
// visually distinct from native `<group>-<verb>-<object>` names.
|
|
984
|
+
const MCP_PROXY_TOOL_NAME_SEP = '__';
|
|
985
|
+
// Max length of the composed local tool name. Remote names are untrusted; the
|
|
986
|
+
// cap matches the tool-name limit MCP clients (incl. the Anthropic API) enforce.
|
|
987
|
+
const MCP_PROXY_TOOL_NAME_MAX = 64;
|
|
988
|
+
// `custom-code` is the third proxied tool definition, and the only one whose
|
|
989
|
+
// behaviour is USER CODE rather than user configuration: one artifact_tool row
|
|
990
|
+
// per artifact, pointing at a Cloudflare Worker deployed to our Workers for
|
|
991
|
+
// Platforms dispatch namespace. Like mcp-proxy, one row produces MANY MCP tools
|
|
992
|
+
// — but the tool list comes from the manifest the user uploads rather than from
|
|
993
|
+
// a remote round-trip, and it is versioned (artifact_tool_version) so code and
|
|
994
|
+
// contract move together and a rollback restores both.
|
|
995
|
+
//
|
|
996
|
+
// The row's config holds only `activeVersionId` plus the egress/limit settings;
|
|
997
|
+
// names and schemas live on the version. That's what keeps MCP boot off the
|
|
998
|
+
// dispatcher — a slow or broken script must never break tools/list. See
|
|
999
|
+
// docs/CUSTOM_TOOLS.md ("Boot contract").
|
|
1000
|
+
const TOOL_DEFINITION_KEY_CUSTOM_CODE = 'custom-code';
|
|
1001
|
+
// A version is uploaded as `draft`, becomes `published` when it is made active,
|
|
1002
|
+
// and is `archived` when a later version replaces it. Exactly one version per
|
|
1003
|
+
// tool is ever `published` — the one config.activeVersionId points at.
|
|
1004
|
+
const CUSTOM_CODE_VERSION_STATUS_DRAFT = 'draft';
|
|
1005
|
+
const CUSTOM_CODE_VERSION_STATUS_PUBLISHED = 'published';
|
|
1006
|
+
const CUSTOM_CODE_VERSION_STATUS_ARCHIVED = 'archived';
|
|
1007
|
+
const CUSTOM_CODE_VERSION_STATUSES = [
|
|
1008
|
+
CUSTOM_CODE_VERSION_STATUS_DRAFT,
|
|
1009
|
+
CUSTOM_CODE_VERSION_STATUS_PUBLISHED,
|
|
1010
|
+
CUSTOM_CODE_VERSION_STATUS_ARCHIVED
|
|
1011
|
+
];
|
|
1012
|
+
// Where a version's stored source came from, which decides whether it can be
|
|
1013
|
+
// opened in the dashboard editor.
|
|
1014
|
+
//
|
|
1015
|
+
// 'editor' — the bytes at sourceKey are exactly what a person typed. They are
|
|
1016
|
+
// the deployed module too: dashboard code imports the SDK from a sibling module
|
|
1017
|
+
// the upload always attaches, so there is no build step between the text box and
|
|
1018
|
+
// the running Worker, and no second copy that can drift from it.
|
|
1019
|
+
//
|
|
1020
|
+
// 'bundle' — the CLI compiled and uploaded it. Deployable, but minified and
|
|
1021
|
+
// machine-shaped; the editor shows it read-only rather than inviting someone to
|
|
1022
|
+
// overwrite a real build with the contents of a text box. It is the default
|
|
1023
|
+
// because every version that predates the editor arrived this way.
|
|
1024
|
+
const CUSTOM_CODE_SOURCE_KIND_EDITOR = 'editor';
|
|
1025
|
+
const CUSTOM_CODE_SOURCE_KIND_BUNDLE = 'bundle';
|
|
1026
|
+
const CUSTOM_CODE_SOURCE_KINDS = [
|
|
1027
|
+
CUSTOM_CODE_SOURCE_KIND_EDITOR,
|
|
1028
|
+
CUSTOM_CODE_SOURCE_KIND_BUNDLE
|
|
1029
|
+
];
|
|
1030
|
+
// The SDK, shipped as a second ES module beside every uploaded script rather
|
|
1031
|
+
// than bundled into it. Attached on every deploy, including CLI ones: a bundle
|
|
1032
|
+
// that inlined the SDK simply never imports it, and one extra module costs less
|
|
1033
|
+
// than a branch in the deploy path that has to know which kind it is holding.
|
|
1034
|
+
const CUSTOM_CODE_SDK_MODULE = 'ganju-sdk.js';
|
|
1035
|
+
// The module the dispatcher calls. Every other file in a project is reached only
|
|
1036
|
+
// by being imported from it, directly or otherwise.
|
|
1037
|
+
const CUSTOM_CODE_MAIN_MODULE = 'index.js';
|
|
1038
|
+
// How many files one script may hold, and how long a path may be. Neither is a
|
|
1039
|
+
// runtime limit — a Worker takes far more — they bound what one editor session
|
|
1040
|
+
// can produce, and keep the stored envelope small enough to stay a single R2
|
|
1041
|
+
// object read on every deploy.
|
|
1042
|
+
const CUSTOM_CODE_MAX_FILES = 25;
|
|
1043
|
+
const CUSTOM_CODE_MAX_FILE_PATH = 100;
|
|
1044
|
+
// WfP script name: `artifact_<artifactId>`. The id, never the slug — slugs are
|
|
1045
|
+
// user-editable and a rename would orphan the deployed script.
|
|
1046
|
+
const CUSTOM_CODE_SCRIPT_NAME_PREFIX = 'artifact_';
|
|
1047
|
+
// A second script per artifact, `artifact_<id>_preview`, that the Test panel
|
|
1048
|
+
// deploys a draft into and calls.
|
|
1049
|
+
//
|
|
1050
|
+
// Separate from the live script and not a mode of it: testing must never disturb
|
|
1051
|
+
// what MCP clients are being served, and the only way to be sure of that is for
|
|
1052
|
+
// the test to run somewhere else entirely. It is overwritten by the next test
|
|
1053
|
+
// and deleted after each run.
|
|
1054
|
+
const CUSTOM_CODE_PREVIEW_SCRIPT_SUFFIX = '_preview';
|
|
1055
|
+
// How long a preview token stays valid.
|
|
1056
|
+
//
|
|
1057
|
+
// The live token needs no expiry — the broker refuses any token whose version is
|
|
1058
|
+
// not the active one, so publishing rotates it. A preview token has no such
|
|
1059
|
+
// anchor: its whole purpose is to work for a version that is NOT active, so
|
|
1060
|
+
// something else has to end it. Ten minutes is longer than any test run and far
|
|
1061
|
+
// shorter than the life of a script that failed to delete.
|
|
1062
|
+
const CUSTOM_CODE_PREVIEW_TOKEN_TTL_MS = 10 * 60 * 1_000;
|
|
1063
|
+
// How long a single test run may take before the API gives up on it. Shorter
|
|
1064
|
+
// than the script's own CPU ceiling is not the point — this bounds the wait of
|
|
1065
|
+
// someone watching a spinner.
|
|
1066
|
+
const CUSTOM_CODE_TEST_TIMEOUT_MS = 30_000;
|
|
1067
|
+
// R2 prefix for uploaded bundles. Keyed by artifact + version so a rollback can
|
|
1068
|
+
// always find the exact source that produced the running script.
|
|
1069
|
+
const CUSTOM_CODE_SOURCE_KEY_PREFIX = 'custom-code';
|
|
1070
|
+
// How many MCP tools one script may declare. Deliberately tighter than
|
|
1071
|
+
// MCP_PROXY_MAX_TOOLS (100): a proxied vendor server is a fixed remote surface,
|
|
1072
|
+
// whereas a manifest is user-authored and tool count is the dominant input-token
|
|
1073
|
+
// driver on channel turns (see CHANNEL_MAX_TOOLS). A channel only ever sees 40
|
|
1074
|
+
// of them; this cap bounds the manifest itself, which every MCP client pays for.
|
|
1075
|
+
const CUSTOM_CODE_MAX_TOOLS = 50;
|
|
1076
|
+
// Max length of a declared tool name — the limit MCP clients (incl. the
|
|
1077
|
+
// Anthropic API) enforce, same ceiling as the composed mcp-proxy names.
|
|
1078
|
+
const CUSTOM_CODE_TOOL_NAME_MAX = 64;
|
|
1079
|
+
// Per-invocation dispatch timeout, clamped rather than rejected (same shape as
|
|
1080
|
+
// http-endpoint / mcp-proxy). This bounds how long apps/mcp waits on the
|
|
1081
|
+
// dispatcher; the per-script CPU ceiling on the namespace is a separate control
|
|
1082
|
+
// and is a Phase 2 item.
|
|
1083
|
+
const CUSTOM_CODE_DEFAULT_TIMEOUT_MS = 10_000;
|
|
1084
|
+
const CUSTOM_CODE_MAX_TIMEOUT_MS = 30_000;
|
|
1085
|
+
// Response body cap returned to the model; truncated past this with a marker
|
|
1086
|
+
// (same ceiling as the other two proxied definitions).
|
|
1087
|
+
const CUSTOM_CODE_MAX_RESPONSE_BYTES = 256 * 1024;
|
|
1088
|
+
// Hard ceiling on an uploaded bundle, checked before the body is streamed to R2.
|
|
1089
|
+
// Set well below Cloudflare's own script-size ceiling so a rejection happens
|
|
1090
|
+
// here — with a legible error — rather than at deploy time in Phase 2.
|
|
1091
|
+
// Three hops have to agree on a wire shape, and none of them share a module
|
|
1092
|
+
// graph at runtime — apps/mcp dispatches, the user's script runs inside the
|
|
1093
|
+
// namespace, and apps/tool-broker serves its host capabilities. Keeping every
|
|
1094
|
+
// path, header and binding name below is what stops the three from drifting.
|
|
1095
|
+
// The dispatcher POSTs to the user script at this origin. The hostname is never
|
|
1096
|
+
// resolved: a dispatch-namespace `fetch` is routed by script name, so the URL
|
|
1097
|
+
// only has to be a well-formed absolute URL the user script can parse.
|
|
1098
|
+
const CUSTOM_CODE_INVOKE_ORIGIN = 'https://tool.ganju.internal';
|
|
1099
|
+
const CUSTOM_CODE_INVOKE_PATH = '/invoke';
|
|
1100
|
+
// A reserved tool name the SDK answers itself, without running user code. The
|
|
1101
|
+
// publish pipeline calls it once against the freshly uploaded script and expects
|
|
1102
|
+
// back the list of tool names the bundle actually exports. Declared in
|
|
1103
|
+
// ./sdkConstants — it is one of the few values that ships inside every deployed
|
|
1104
|
+
// script — and re-exported below with the rest.
|
|
1105
|
+
// Reserved MCP tool names
|
|
1106
|
+
//
|
|
1107
|
+
// `http-endpoint` and `custom-code` let the user choose the MCP tool name, and
|
|
1108
|
+
// apps/mcp registers every tool on an artifact into ONE flat namespace. A
|
|
1109
|
+
// user-chosen name equal to a native tool's definition key is therefore a
|
|
1110
|
+
// collision, and both possible resolutions are bad: whichever registers second
|
|
1111
|
+
// is silently dropped, and the channel runner attributes the call to the other
|
|
1112
|
+
// one. Rejecting the name at the write path is the only place the user can be
|
|
1113
|
+
// told about it.
|
|
1114
|
+
//
|
|
1115
|
+
// Reserved by NAMESPACE, not by an enumerated list of the ~60 shipped keys.
|
|
1116
|
+
// A blocklist would answer "is this name taken today" — which is the wrong
|
|
1117
|
+
// question, because a name that is free at publish time is taken the moment
|
|
1118
|
+
// someone installs a native tool that uses it. Owning the prefix means a tool
|
|
1119
|
+
// added to any of these groups later can never collide with a name already
|
|
1120
|
+
// published, so this list only changes when a whole new group is added.
|
|
1121
|
+
//
|
|
1122
|
+
// mcp-proxy needs no entry: its names are always `<prefix>__<remote>`, and no
|
|
1123
|
+
// native key contains the separator.
|
|
1124
|
+
const RESERVED_TOOL_NAME_PREFIXES = [
|
|
1125
|
+
'gmail-',
|
|
1126
|
+
'outlook-',
|
|
1127
|
+
'slack-',
|
|
1128
|
+
CALENDAR_TOOL_KEY_PREFIX,
|
|
1129
|
+
CALCOM_TOOL_KEY_PREFIX,
|
|
1130
|
+
WEB_TOOL_KEY_PREFIX
|
|
1131
|
+
];
|
|
1132
|
+
// The keys that don't belong to a prefixed group: the RAG core the channel
|
|
1133
|
+
// runner intercepts by name, the smoke-test tool, the three proxied definition
|
|
1134
|
+
// keys (each registers under its own key when installed natively), and the
|
|
1135
|
+
// health tool the SDK answers before it routes to user code.
|
|
1136
|
+
const RESERVED_TOOL_NAMES = [
|
|
1137
|
+
...RESOURCE_TOOL_KEYS,
|
|
1138
|
+
'greeting',
|
|
1139
|
+
TOOL_DEFINITION_KEY_HTTP_ENDPOINT,
|
|
1140
|
+
TOOL_DEFINITION_KEY_MCP_PROXY,
|
|
1141
|
+
TOOL_DEFINITION_KEY_CUSTOM_CODE,
|
|
1142
|
+
sdkConstants_1.CUSTOM_CODE_HEALTH_TOOL
|
|
1143
|
+
];
|
|
1144
|
+
// Shared by both write paths so there is one string to translate. It has to
|
|
1145
|
+
// carry a word `matchStatus` recognises (errorHandler.ts), because the
|
|
1146
|
+
// http-endpoint path re-throws the issue message as a plain Error and anything
|
|
1147
|
+
// unrecognised becomes an opaque 500 instead of a 400.
|
|
1148
|
+
const RESERVED_TOOL_NAME_MESSAGE = 'Invalid tool name — reserved by the platform';
|
|
1149
|
+
// A declared connection that names no managed provider. Fixed string for the
|
|
1150
|
+
// same two reasons as the message above: localizeZodIssue keys on the exact
|
|
1151
|
+
// English text, and the value has to carry a word matchStatus recognises or the
|
|
1152
|
+
// custom-code config path — which rethrows the issue message as a plain Error —
|
|
1153
|
+
// answers 500 where it means 400. The offending entry is pinpointed by the issue
|
|
1154
|
+
// path (`connections.2`), not by the message.
|
|
1155
|
+
const CUSTOM_CODE_UNKNOWN_CONNECTION_MESSAGE = 'Invalid connection — no managed provider by that name';
|
|
1156
|
+
// An output schema has to describe an object, because that is what MCP's
|
|
1157
|
+
// `structuredContent` is. A schema of any other type compiles to an empty shape
|
|
1158
|
+
// and can never be satisfied, so the tool would declare structured output and
|
|
1159
|
+
// then report every call as a failure to produce it.
|
|
1160
|
+
//
|
|
1161
|
+
// A fixed string rather than one that names the offending type: localizeZodIssue
|
|
1162
|
+
// keys its translations on the exact English text. The issue `path` says which
|
|
1163
|
+
// entry, which is what a 50-tool manifest needs anyway.
|
|
1164
|
+
const OUTPUT_SCHEMA_NOT_OBJECT_MESSAGE = 'Invalid output schema — it must describe an object';
|
|
1165
|
+
// The script bindings and the broker routes they call live in ./sdkConstants:
|
|
1166
|
+
// they are the SDK's runtime contract, so they are bundled into every deployed
|
|
1167
|
+
// script and must not drag this module in with them.
|
|
1168
|
+
// Outbound-worker parameters the dispatcher attaches to every `.get()`. The
|
|
1169
|
+
// outbound worker reads them from its own env to screen the script's `fetch`
|
|
1170
|
+
// calls — which is why egress control can't be bypassed by editing the script.
|
|
1171
|
+
const CUSTOM_CODE_OUTBOUND_PARAM_ARTIFACT_ID = 'artifactId';
|
|
1172
|
+
const CUSTOM_CODE_OUTBOUND_PARAM_ALLOWED_HOSTS = 'allowedHosts';
|
|
1173
|
+
// Hosts the outbound worker allows regardless of a tool's `allowedHosts`.
|
|
1174
|
+
//
|
|
1175
|
+
// The dispatch namespace routes every subrequest that originates inside it
|
|
1176
|
+
// through the outbound worker — including the ones the BROKER makes on the
|
|
1177
|
+
// script's behalf, which is not obvious until you watch it happen. Without this
|
|
1178
|
+
// list, `ctx.resources.search` breaks the moment a tool sets an allow-list
|
|
1179
|
+
// (the embedding call is a fetch to Gemini), and so does refreshing a
|
|
1180
|
+
// connection (a fetch to the provider's token endpoint).
|
|
1181
|
+
//
|
|
1182
|
+
// `allowedHosts` exists to bound where a tool's OWN code may reach, so the
|
|
1183
|
+
// platform's capabilities have no business being caught by it. Reaching these
|
|
1184
|
+
// hosts is not itself a capability: without our API key or client secret,
|
|
1185
|
+
// neither answers usefully.
|
|
1186
|
+
//
|
|
1187
|
+
// The SSRF screen still applies to these — this widens the allow-list, never
|
|
1188
|
+
// the private-address block.
|
|
1189
|
+
const CUSTOM_CODE_PLATFORM_HOSTS = [
|
|
1190
|
+
// Embeddings for ctx.resources.search.
|
|
1191
|
+
'generativelanguage.googleapis.com',
|
|
1192
|
+
// Token endpoints the broker refreshes managed connections against. Kept in
|
|
1193
|
+
// step with oauthProviders by hostname rather than by URL.
|
|
1194
|
+
'oauth2.googleapis.com',
|
|
1195
|
+
'login.microsoftonline.com',
|
|
1196
|
+
'slack.com'
|
|
1197
|
+
];
|
|
1198
|
+
// `GANJU_TOOL_TOKEN` is `<base64url(payload)>.<base64url(hmac)>`, signed with
|
|
1199
|
+
// CUSTOM_CODE_TOKEN_SECRET. It carries the artifact and the version it was
|
|
1200
|
+
// minted for; the broker checks the signature AND that the version is still the
|
|
1201
|
+
// artifact's active one, so a superseded script's token stops working the moment
|
|
1202
|
+
// a newer version is published. Never accept an artifact id from a request body.
|
|
1203
|
+
const CUSTOM_CODE_TOKEN_SECRET_ENV = 'CUSTOM_CODE_TOKEN_SECRET';
|
|
1204
|
+
const CUSTOM_CODE_TOKEN_VERSION = 'v1';
|
|
1205
|
+
// Cloudflare credentials the publish pipeline needs to upload a script into the
|
|
1206
|
+
// dispatch namespace.
|
|
1207
|
+
//
|
|
1208
|
+
// The token deliberately does NOT use Cloudflare's own `CLOUDFLARE_API_TOKEN`
|
|
1209
|
+
// name. Wrangler reads that variable from the .env in its working directory and
|
|
1210
|
+
// authenticates with it instead of the developer's OAuth login — so putting our
|
|
1211
|
+
// token there silently reroutes every `wrangler deploy` through a token scoped
|
|
1212
|
+
// only to Workers Scripts, breaking any deploy that touches zone routes and
|
|
1213
|
+
// making `wrangler login` refuse to run. Ours is namespaced so the two can
|
|
1214
|
+
// coexist. `CLOUDFLARE_ACCOUNT_ID` keeps its standard name: wrangler reads it
|
|
1215
|
+
// too, but selecting the right account is the behaviour we want.
|
|
1216
|
+
const CUSTOM_CODE_ACCOUNT_ID_ENV = 'CLOUDFLARE_ACCOUNT_ID';
|
|
1217
|
+
const CUSTOM_CODE_API_TOKEN_ENV = 'CUSTOM_CODE_CF_API_TOKEN';
|
|
1218
|
+
const CUSTOM_CODE_NAMESPACE_ENV = 'CUSTOM_CODE_DISPATCH_NAMESPACE';
|
|
1219
|
+
// The broker worker each uploaded script is service-bound to. An env var rather
|
|
1220
|
+
// than a constant because the name is environment-suffixed
|
|
1221
|
+
// (ganju-tool-broker-development / -production).
|
|
1222
|
+
const CUSTOM_CODE_BROKER_SERVICE_ENV = 'CUSTOM_CODE_BROKER_SERVICE';
|
|
1223
|
+
// Compatibility date pinned for every uploaded user script. Deliberately a
|
|
1224
|
+
// constant rather than "today": a script uploaded now and re-uploaded on
|
|
1225
|
+
// rollback months later must run identically both times.
|
|
1226
|
+
const CUSTOM_CODE_COMPATIBILITY_DATE = '2025-11-17';
|
|
1227
|
+
// Per-invocation CPU ceiling applied to each user script at upload time. Far
|
|
1228
|
+
// tighter than our own workers' 30s — this is the technical cap that bounds
|
|
1229
|
+
// what one adversarial call can cost us, so an infinite loop in a customer's
|
|
1230
|
+
// tool is billed as five seconds rather than as whatever it wanted.
|
|
1231
|
+
const CUSTOM_CODE_SCRIPT_CPU_MS = 5_000;
|
|
1232
|
+
// The ctx.log() caps also live in ./sdkConstants — the buffer that enforces them
|
|
1233
|
+
// runs inside the isolate.
|
|
1234
|
+
// ctx.sendFile destinations.
|
|
1235
|
+
//
|
|
1236
|
+
// The one capability a user script genuinely cannot reproduce: a script is
|
|
1237
|
+
// capped at 128MiB, holds no R2 binding, and has no path to the resource-handler
|
|
1238
|
+
// container. So the bytes never enter the isolate — the script names a resource
|
|
1239
|
+
// and a destination, and the broker does the read and the multipart assembly on
|
|
1240
|
+
// its behalf.
|
|
1241
|
+
//
|
|
1242
|
+
// Each destination maps onto a container route the native handlers already
|
|
1243
|
+
// drive, which is why the list is these three and not every provider we hold a
|
|
1244
|
+
// connection for: a destination here means a send path that has already solved
|
|
1245
|
+
// chunked uploads and MIME assembly for that vendor.
|
|
1246
|
+
const CUSTOM_CODE_SEND_FILE_TARGET_GMAIL = 'gmail';
|
|
1247
|
+
const CUSTOM_CODE_SEND_FILE_TARGET_OUTLOOK = 'outlook';
|
|
1248
|
+
const CUSTOM_CODE_SEND_FILE_TARGET_SLACK = 'slack';
|
|
1249
|
+
const CUSTOM_CODE_SEND_FILE_TARGETS = [
|
|
1250
|
+
CUSTOM_CODE_SEND_FILE_TARGET_GMAIL,
|
|
1251
|
+
CUSTOM_CODE_SEND_FILE_TARGET_OUTLOOK,
|
|
1252
|
+
CUSTOM_CODE_SEND_FILE_TARGET_SLACK
|
|
1253
|
+
];
|
|
1254
|
+
// Which managed connection each destination sends as. sendFile spends the
|
|
1255
|
+
// artifact's credential, so it passes through the SAME `connections` allow-list
|
|
1256
|
+
// ctx.connection() does — a script that may not read the Gmail token must not be
|
|
1257
|
+
// able to send mail as that account by naming a different capability.
|
|
1258
|
+
const CUSTOM_CODE_SEND_FILE_PROVIDERS = {
|
|
1259
|
+
[CUSTOM_CODE_SEND_FILE_TARGET_GMAIL]: OAUTH_PROVIDER_GOOGLE_GMAIL,
|
|
1260
|
+
[CUSTOM_CODE_SEND_FILE_TARGET_OUTLOOK]: OAUTH_PROVIDER_MICROSOFT_OUTLOOK,
|
|
1261
|
+
[CUSTOM_CODE_SEND_FILE_TARGET_SLACK]: OAUTH_PROVIDER_SLACK
|
|
1262
|
+
};
|
|
1263
|
+
// The resource-handler container route each destination posts to. These are the
|
|
1264
|
+
// routes the native handlers use, unchanged — the container is where MIME
|
|
1265
|
+
// assembly and chunked upload already live, and a second implementation in the
|
|
1266
|
+
// broker is exactly what this indirection avoids.
|
|
1267
|
+
const CUSTOM_CODE_SEND_FILE_PATHS = {
|
|
1268
|
+
[CUSTOM_CODE_SEND_FILE_TARGET_GMAIL]: '/gmail/send',
|
|
1269
|
+
[CUSTOM_CODE_SEND_FILE_TARGET_OUTLOOK]: '/outlook/send',
|
|
1270
|
+
[CUSTOM_CODE_SEND_FILE_TARGET_SLACK]: '/slack/send'
|
|
1271
|
+
};
|
|
1272
|
+
// How many resources one sendFile call may carry. Bounded because each one is
|
|
1273
|
+
// read out of R2 into the broker's 128MiB isolate before it is streamed on; the
|
|
1274
|
+
// per-destination byte caps below bound total size, this bounds the read fan-out.
|
|
1275
|
+
const CUSTOM_CODE_SEND_FILE_MAX_URIS = 10;
|
|
1276
|
+
// Ceilings on what one ctx.resources.create call may write.
|
|
1277
|
+
//
|
|
1278
|
+
// Two numbers rather than one, because the two payloads land in different
|
|
1279
|
+
// places. Inline text becomes a Postgres column on a row every resource listing
|
|
1280
|
+
// reads, so it stays small; bytes become an R2 object nothing reads until it is
|
|
1281
|
+
// sent, so it can be larger.
|
|
1282
|
+
//
|
|
1283
|
+
// Both are far below the 128MiB isolate ceiling, and deliberately so: unlike
|
|
1284
|
+
// sendFile, this payload DOES transit user code — the script is holding it —
|
|
1285
|
+
// and it is then carried again as base64 in the broker request, so the real
|
|
1286
|
+
// footprint is roughly 2.3x the number here. The file ceiling is also under
|
|
1287
|
+
// every destination's attachment cap, so anything a script can create it can
|
|
1288
|
+
// also send.
|
|
1289
|
+
const CUSTOM_CODE_MAX_RESOURCE_TEXT_BYTES = 1024 * 1024;
|
|
1290
|
+
const CUSTOM_CODE_MAX_RESOURCE_FILE_BYTES = 10 * 1024 * 1024;
|
|
1291
|
+
// Default URI scheme for a script-created resource, matching what the dashboard
|
|
1292
|
+
// generates for an uploaded file — a resource a tool wrote should be addressable
|
|
1293
|
+
// exactly like one a person uploaded, including by sendFile.
|
|
1294
|
+
const CUSTOM_CODE_RESOURCE_URI_PREFIX = 'resource://';
|
|
1295
|
+
// How far a script's resource writes reach, declared per tool rather than
|
|
1296
|
+
// assumed.
|
|
1297
|
+
//
|
|
1298
|
+
// `own` — the default — lets a script replace and delete only what a script
|
|
1299
|
+
// created. It is the safe floor: a tool cannot touch a document its owner
|
|
1300
|
+
// uploaded or a page the crawler indexed, so a buggy or model-generated tool has
|
|
1301
|
+
// nothing of the customer's to destroy.
|
|
1302
|
+
//
|
|
1303
|
+
// `all` lifts that, which is what a tool whose job is to prune a stale crawl or
|
|
1304
|
+
// retire an imported folder actually needs. It is declared in the tool's config
|
|
1305
|
+
// and enforced by the broker, so it cannot be granted from inside the script —
|
|
1306
|
+
// the same shape as allowedHosts and connections, and for the same reason: a
|
|
1307
|
+
// capability the code can widen is not a capability, it is a comment.
|
|
1308
|
+
const CUSTOM_CODE_RESOURCE_ACCESS_OWN = 'own';
|
|
1309
|
+
const CUSTOM_CODE_RESOURCE_ACCESS_ALL = 'all';
|
|
1310
|
+
const CUSTOM_CODE_RESOURCE_ACCESS_VALUES = [
|
|
1311
|
+
CUSTOM_CODE_RESOURCE_ACCESS_OWN,
|
|
1312
|
+
CUSTOM_CODE_RESOURCE_ACCESS_ALL
|
|
1313
|
+
];
|
|
1314
|
+
// Refusing to touch a row a script does not own. Two messages rather than one
|
|
1315
|
+
// because the two calls leave the reader in different positions: the fix for a
|
|
1316
|
+
// create is a different uri, and there is no fix for a delete — the resource
|
|
1317
|
+
// simply is not the script's to remove. Both are phrased for the model that
|
|
1318
|
+
// will read them, which needs to do something else rather than retry.
|
|
1319
|
+
const CUSTOM_CODE_RESOURCE_NOT_OWNED_MESSAGE = 'A resource with this uri already exists on this artifact and was not created by a tool, so it cannot be replaced. Choose a different uri.';
|
|
1320
|
+
const CUSTOM_CODE_RESOURCE_NOT_DELETABLE_MESSAGE = 'This resource was not created by a tool, so it cannot be deleted from one. Set this tool\'s resource access to "all" to allow it, or remove it from the Resources page.';
|
|
1321
|
+
// Deleting a parent without saying so would take its children with it through
|
|
1322
|
+
// the FK cascade — silently, and leaving the artifact's counters describing rows
|
|
1323
|
+
// that no longer exist. Refused instead, naming the flag that means it.
|
|
1324
|
+
const CUSTOM_CODE_RESOURCE_HAS_CHILDREN_MESSAGE = 'This resource has children, which would be removed with it. Pass { children: true } to confirm.';
|
|
1325
|
+
// Indexing a payload nothing can extract text from produces zero chunks and a
|
|
1326
|
+
// resource that looks indexed and never matches anything. Refused up front.
|
|
1327
|
+
const CUSTOM_CODE_RESOURCE_NOT_EMBEDDABLE_MESSAGE = 'This file type cannot be indexed for search. Create it without index, or pass the text as content.';
|
|
1328
|
+
// Validation messages for ctx.resources.create. Constants rather than inline
|
|
1329
|
+
// strings because localizeZodIssue keys its translations on the exact English
|
|
1330
|
+
// text, so the two files have to name the same value.
|
|
1331
|
+
const CUSTOM_CODE_RESOURCE_PAYLOAD_MESSAGE = 'Pass exactly one of content (text) or bytes (base64)';
|
|
1332
|
+
const CUSTOM_CODE_RESOURCE_TEXT_TOO_LARGE_MESSAGE = `Inline content exceeds the ${CUSTOM_CODE_MAX_RESOURCE_TEXT_BYTES / (1024 * 1024)}MB limit`;
|
|
1333
|
+
const CUSTOM_CODE_RESOURCE_FILE_TOO_LARGE_MESSAGE = `File bytes exceed the ${CUSTOM_CODE_MAX_RESOURCE_FILE_BYTES / (1024 * 1024)}MB limit`;
|
|
1334
|
+
const MCP_REQUEST_METHOD_INITIALIZE = 'initialize';
|
|
1335
|
+
const MCP_REQUEST_METHOD_PING = 'ping';
|
|
1336
|
+
const MCP_REQUEST_METHOD_TOOLS_LIST = 'tools/list';
|
|
1337
|
+
const MCP_REQUEST_METHOD_TOOLS_CALL = 'tools/call';
|
|
1338
|
+
const MCP_REQUEST_METHOD_RESOURCES_LIST = 'resources/list';
|
|
1339
|
+
const MCP_REQUEST_METHOD_RESOURCES_TEMPLATES_LIST = 'resources/templates/list';
|
|
1340
|
+
const MCP_REQUEST_METHOD_RESOURCES_READ = 'resources/read';
|
|
1341
|
+
const MCP_REQUEST_METHOD_PROMPTS_LIST = 'prompts/list';
|
|
1342
|
+
const MCP_REQUEST_METHOD_PROMPTS_GET = 'prompts/get';
|
|
1343
|
+
const MCP_REQUEST_METHODS = [
|
|
1344
|
+
MCP_REQUEST_METHOD_INITIALIZE,
|
|
1345
|
+
MCP_REQUEST_METHOD_PING,
|
|
1346
|
+
MCP_REQUEST_METHOD_TOOLS_LIST,
|
|
1347
|
+
MCP_REQUEST_METHOD_TOOLS_CALL,
|
|
1348
|
+
MCP_REQUEST_METHOD_RESOURCES_LIST,
|
|
1349
|
+
MCP_REQUEST_METHOD_RESOURCES_TEMPLATES_LIST,
|
|
1350
|
+
MCP_REQUEST_METHOD_RESOURCES_READ,
|
|
1351
|
+
MCP_REQUEST_METHOD_PROMPTS_LIST,
|
|
1352
|
+
MCP_REQUEST_METHOD_PROMPTS_GET
|
|
1353
|
+
];
|
|
1354
|
+
const MCP_AUTH_KIND_JWT = 'jwt';
|
|
1355
|
+
const MCP_AUTH_KIND_INTERNAL = 'internal';
|
|
1356
|
+
const MCP_AUTH_KINDS = [MCP_AUTH_KIND_JWT, MCP_AUTH_KIND_INTERNAL];
|
|
1357
|
+
const MCP_SESSION_HEADER = 'mcp-session-id';
|
|
1358
|
+
const MCP_INTERNAL_HEADER = 'x-ganju-internal-secret';
|
|
1359
|
+
const MCP_CHANNEL_ID_HEADER = 'x-ganju-channel-id';
|
|
1360
|
+
const MCP_CHANNEL_PLATFORM_HEADER = 'x-ganju-channel-platform';
|
|
1361
|
+
const MCP_CHANNEL_CLIENT_USER_AGENT = 'ganju-channel/0.0.1';
|
|
1362
|
+
const JWKS_KV_KEY = 'jwks:v1';
|
|
1363
|
+
const JWKS_TTL_SECONDS = 600;
|
|
1364
|
+
// Standard OIDC scopes the better-auth oauthProvider honors for user OAuth
|
|
1365
|
+
// flows. Advertised in the OAuth discovery documents (RFC 8414
|
|
1366
|
+
// authorization-server metadata and RFC 9728 protected-resource metadata).
|
|
1367
|
+
const OAUTH_SCOPES_SUPPORTED = ['openid', 'profile', 'email', 'offline_access'];
|
|
1368
|
+
// Ganju-specific OAuth scopes for MCP access. `mcp:read` is the default scope
|
|
1369
|
+
// minted by the bot-on-behalf-of grant (a custom grant that bypasses the OIDC
|
|
1370
|
+
// authorize endpoint). `artifact:<slug>` — built from this prefix — gates a
|
|
1371
|
+
// subjectless machine token to a single MCP server in the MCP auth middleware.
|
|
1372
|
+
// Neither is in better-auth's OIDC scope allowlist, so they are NOT advertised
|
|
1373
|
+
// in the discovery documents: a standard OIDC client requesting an advertised
|
|
1374
|
+
// scope that isn't allowlisted would be rejected with `invalid_scope`.
|
|
1375
|
+
// TODO: Net: mcp:read is a cosmetic claim on bot tokens; artifact: is a code path that can't fire. Neither affects security today. They become real only if you build the per-server confinement feature (which would make artifact:<slug> issued + enforced). This is more for OIDC.
|
|
1376
|
+
const MCP_SCOPE_READ = 'mcp:read';
|
|
1377
|
+
const ARTIFACT_SCOPE_PREFIX = 'artifact:';
|
|
1378
|
+
// The CLI signs in as a public OAuth client through a loopback redirect
|
|
1379
|
+
// (RFC 8252) — the flow `wrangler`, `gh` and `vercel` all use. It registers
|
|
1380
|
+
// itself through RFC 7591 dynamic registration on first login rather than
|
|
1381
|
+
// relying on a client row someone had to provision by hand, and caches the id
|
|
1382
|
+
// it was given.
|
|
1383
|
+
//
|
|
1384
|
+
// A fixed port would fail whenever something else already held it, so the CLI
|
|
1385
|
+
// binds the first one here that is free. All of them are registered up front
|
|
1386
|
+
// rather than only the one that gets used: the provider does implement RFC 8252
|
|
1387
|
+
// loopback matching, where the port is ignored for a 127.0.0.0/8 redirect, but
|
|
1388
|
+
// that is one library's behaviour and this is a login that has already opened
|
|
1389
|
+
// someone's browser by the time it would fail.
|
|
1390
|
+
// Recent custom-tool invocations, as `ganju logs` reads them.
|
|
1391
|
+
const CUSTOM_CODE_LOGS_DEFAULT_LIMIT = 20;
|
|
1392
|
+
const CUSTOM_CODE_LOGS_MAX_LIMIT = 100;
|
|
1393
|
+
const BOT_GRANT_TYPE = 'urn:ganju:bot-on-behalf-of';
|
|
1394
|
+
const EXTERNAL_LINK_VERIFICATION_PREFIX = 'external_link:';
|
|
1395
|
+
const EXTERNAL_LINK_TTL_SECONDS = 600;
|
|
1396
|
+
const BOT_ACCESS_TOKEN_TTL_SECONDS = 3600;
|
|
1397
|
+
const LINK_CODE_LENGTH = 12;
|
|
1398
|
+
const LINK_CODE_ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
|
|
1399
|
+
const BOT_COMMAND_LINK = 'link';
|
|
1400
|
+
const RESERVED_BOT_COMMANDS = [BOT_COMMAND_LINK];
|
|
1401
|
+
const RESERVED_SLUGS = [
|
|
1402
|
+
'www',
|
|
1403
|
+
'api',
|
|
1404
|
+
'app',
|
|
1405
|
+
'admin',
|
|
1406
|
+
'auth',
|
|
1407
|
+
'mcp',
|
|
1408
|
+
'dev',
|
|
1409
|
+
'prod',
|
|
1410
|
+
'staging',
|
|
1411
|
+
'development',
|
|
1412
|
+
'production',
|
|
1413
|
+
'docs',
|
|
1414
|
+
'blog',
|
|
1415
|
+
'help',
|
|
1416
|
+
'support',
|
|
1417
|
+
'status',
|
|
1418
|
+
'mail',
|
|
1419
|
+
'ftp',
|
|
1420
|
+
'ns',
|
|
1421
|
+
'ns1',
|
|
1422
|
+
'ns2',
|
|
1423
|
+
'cdn',
|
|
1424
|
+
'static',
|
|
1425
|
+
'assets',
|
|
1426
|
+
'auth',
|
|
1427
|
+
'oauth',
|
|
1428
|
+
'authentication',
|
|
1429
|
+
'authorization',
|
|
1430
|
+
'login',
|
|
1431
|
+
'logout',
|
|
1432
|
+
'signin',
|
|
1433
|
+
'signup',
|
|
1434
|
+
'register',
|
|
1435
|
+
'me',
|
|
1436
|
+
'user',
|
|
1437
|
+
'users',
|
|
1438
|
+
'account',
|
|
1439
|
+
'accounts',
|
|
1440
|
+
'billing',
|
|
1441
|
+
'settings',
|
|
1442
|
+
'health',
|
|
1443
|
+
'metrics',
|
|
1444
|
+
'internal'
|
|
1445
|
+
];
|
|
1446
|
+
const PLAN_FREE = 'FREE';
|
|
1447
|
+
const PLAN_PRO = 'PRO';
|
|
1448
|
+
const PLAN_ENTERPRISE = 'ENTERPRISE';
|
|
1449
|
+
const PLANS = [PLAN_FREE, PLAN_PRO, PLAN_ENTERPRISE];
|
|
1450
|
+
// Mirror Stripe's subscription statuses so the webhook can store them verbatim.
|
|
1451
|
+
const SUBSCRIPTION_STATUS_ACTIVE = 'active';
|
|
1452
|
+
const SUBSCRIPTION_STATUS_TRIALING = 'trialing';
|
|
1453
|
+
const SUBSCRIPTION_STATUS_PAST_DUE = 'past_due';
|
|
1454
|
+
const SUBSCRIPTION_STATUS_CANCELED = 'canceled';
|
|
1455
|
+
const SUBSCRIPTION_STATUS_INCOMPLETE = 'incomplete';
|
|
1456
|
+
const SUBSCRIPTION_STATUS_INCOMPLETE_EXPIRED = 'incomplete_expired';
|
|
1457
|
+
const SUBSCRIPTION_STATUS_UNPAID = 'unpaid';
|
|
1458
|
+
const SUBSCRIPTION_STATUS_PAUSED = 'paused';
|
|
1459
|
+
const SUBSCRIPTION_STATUSES = [
|
|
1460
|
+
SUBSCRIPTION_STATUS_ACTIVE,
|
|
1461
|
+
SUBSCRIPTION_STATUS_TRIALING,
|
|
1462
|
+
SUBSCRIPTION_STATUS_PAST_DUE,
|
|
1463
|
+
SUBSCRIPTION_STATUS_CANCELED,
|
|
1464
|
+
SUBSCRIPTION_STATUS_INCOMPLETE,
|
|
1465
|
+
SUBSCRIPTION_STATUS_INCOMPLETE_EXPIRED,
|
|
1466
|
+
SUBSCRIPTION_STATUS_UNPAID,
|
|
1467
|
+
SUBSCRIPTION_STATUS_PAUSED
|
|
1468
|
+
];
|
|
1469
|
+
// A subscription in one of these states still grants its paid entitlements.
|
|
1470
|
+
// Anything else falls back to the Free plan's limits.
|
|
1471
|
+
const SUBSCRIPTION_ENTITLED_STATUSES = [
|
|
1472
|
+
SUBSCRIPTION_STATUS_ACTIVE,
|
|
1473
|
+
SUBSCRIPTION_STATUS_TRIALING,
|
|
1474
|
+
SUBSCRIPTION_STATUS_PAST_DUE
|
|
1475
|
+
];
|
|
1476
|
+
const MB = 1024 * 1024;
|
|
1477
|
+
const GB = 1024 * 1024 * 1024;
|
|
1478
|
+
// Pricing numbers (kept in sync with apps/website/src/lib/pricing.ts).
|
|
1479
|
+
//
|
|
1480
|
+
// $29 rather than $20 is positioning, not margin — $20 is comfortably safe on
|
|
1481
|
+
// the cost model. Code hosting, a CLI and managed OAuth put this beside Zapier
|
|
1482
|
+
// Professional (~$30) and Pipedream (~$29); $20 anchors it as a tool, $29 as a
|
|
1483
|
+
// platform.
|
|
1484
|
+
const PRICING_PRO_BASE_USD = 29;
|
|
1485
|
+
// Total assistant turns included per month, of any kind.
|
|
1486
|
+
const PRICING_INCLUDED_MESSAGES = 3_000;
|
|
1487
|
+
// How many of those included turns may run on the SHARED platform model. Shared
|
|
1488
|
+
// turns draw from the total pool like any other, but only up to this sub-cap —
|
|
1489
|
+
// past it they meter at the shared rate below even if the org is still under its
|
|
1490
|
+
// 3,000. The sub-cap exists because these are the only turns we pay inference on.
|
|
1491
|
+
const PRICING_INCLUDED_SHARED_MESSAGES = 1_000;
|
|
1492
|
+
// Embedded content costs more than it looks. The billed figure is chunk TEXT,
|
|
1493
|
+
// but each chunk also carries its halfvec plus an HNSW index entry — fixed
|
|
1494
|
+
// overhead per chunk, regardless of how little text the chunk holds. At the
|
|
1495
|
+
// current 1536 dimensions that's a ~6.9x expansion, so at Neon's $0.35/GB-month
|
|
1496
|
+
// a billed GB costs us ~$2.41/GB-month. (It was ~12.9x and ~$4.51 at 3072
|
|
1497
|
+
// dimensions, which is what motivated both this allowance and EMBEDDING_DIMENSIONS.)
|
|
1498
|
+
//
|
|
1499
|
+
// 1 GB rather than 5: at 5 GB the included allowance alone would have cost ~$22
|
|
1500
|
+
// of a $29 plan on the old basis, and ~$12 on the current one. 1 GB is still
|
|
1501
|
+
// ~500,000 pages of text — generous for the real use case, and it caps the
|
|
1502
|
+
// downside of a single heavy account.
|
|
1503
|
+
const PRICING_INCLUDED_EMBEDDED_GB = 1;
|
|
1504
|
+
// Two message rates, because the two kinds of turn cost us wildly different
|
|
1505
|
+
// amounts. A turn on the customer's own key costs us ~nothing — $2/1,000 is a
|
|
1506
|
+
// platform fee for running the loop and serving RAG. A turn on our model costs
|
|
1507
|
+
// ~$4/1,000 (up to ~$11 on a tool-rich artifact), so it's sold at $15/1,000: a
|
|
1508
|
+
// real margin rather than a subsidy. Selling it beats blocking — every Free→Pro
|
|
1509
|
+
// converter is on our key the day they convert, so forcing BYO breaks the bot
|
|
1510
|
+
// of every single upgrade.
|
|
1511
|
+
const PRICING_MESSAGE_PER_1K_USD = 2;
|
|
1512
|
+
const PRICING_SHARED_MESSAGE_PER_1K_USD = 15;
|
|
1513
|
+
// Abuse backstop on shared-model turns for paid plans — deliberately absurd as a
|
|
1514
|
+
// month of legitimate use (~3,300/day, and it would invoice five figures), so it
|
|
1515
|
+
// only ever catches a runaway loop or a stolen channel token. Raise it for an
|
|
1516
|
+
// Enterprise contract rather than treating it as a plan feature.
|
|
1517
|
+
const PRICING_SHARED_KEY_HARD_CAP = 100_000;
|
|
1518
|
+
// $2, up from $0.50 — which was set believing storage cost us ~$0.50/GB and was
|
|
1519
|
+
// therefore "at cost". It wasn't: see the expansion note above.
|
|
1520
|
+
//
|
|
1521
|
+
// This sat below cost when it was raised (~$4.51/GB at 3072 dimensions). The gap
|
|
1522
|
+
// was then closed from the COST side rather than by raising the price again:
|
|
1523
|
+
// halving the embedding to 1536 dimensions brought the real figure to ~$2.41/GB,
|
|
1524
|
+
// so $2 is now roughly break-even. Raising CHUNK_TARGET_CHARS would tip it
|
|
1525
|
+
// positive without touching this number.
|
|
1526
|
+
const PRICING_EMBEDDED_PER_GB_USD = 2;
|
|
1527
|
+
const PRICING_CUSTOM_DOMAIN_USD = 15;
|
|
1528
|
+
const PLAN_LIMITS = {
|
|
1529
|
+
FREE: {
|
|
1530
|
+
maxProjects: 1,
|
|
1531
|
+
maxToolsPerArtifact: 7,
|
|
1532
|
+
maxPromptsPerArtifact: 3,
|
|
1533
|
+
maxChannelsPerArtifact: 1,
|
|
1534
|
+
maxRawStorageBytes: 30 * MB,
|
|
1535
|
+
maxEmbeddedBytes: 5 * MB,
|
|
1536
|
+
// Free runs on the shared platform model key, so we pay inference. The cap
|
|
1537
|
+
// is deliberately a trial-sized amount, not a home — anyone who wants more
|
|
1538
|
+
// for free can self-host (Apache-2.0). Cost per message is further bounded
|
|
1539
|
+
// by the tighter shared-key turn envelope (history + tool loops) below.
|
|
1540
|
+
monthlyMessageCap: 100,
|
|
1541
|
+
// Free can't bring its own key, so its whole allowance runs on our model:
|
|
1542
|
+
// every one of these three numbers is the same 100 turns seen from a
|
|
1543
|
+
// different angle. There is no overage path off Free — you upgrade.
|
|
1544
|
+
includedSharedMessages: 100,
|
|
1545
|
+
sharedKeyHardCap: 100,
|
|
1546
|
+
canInvite: false,
|
|
1547
|
+
// Free orgs use the shared platform model only; bringing your own model is a
|
|
1548
|
+
// paid feature.
|
|
1549
|
+
canUseCustomLlm: false,
|
|
1550
|
+
canUseCustomCode: false,
|
|
1551
|
+
maxHttpEndpointsPerArtifact: 3,
|
|
1552
|
+
includedMessages: 100,
|
|
1553
|
+
includedEmbeddedBytes: 5 * MB
|
|
1554
|
+
},
|
|
1555
|
+
PRO: {
|
|
1556
|
+
maxProjects: null,
|
|
1557
|
+
maxToolsPerArtifact: null,
|
|
1558
|
+
maxPromptsPerArtifact: null,
|
|
1559
|
+
maxChannelsPerArtifact: null,
|
|
1560
|
+
maxRawStorageBytes: null,
|
|
1561
|
+
maxEmbeddedBytes: null,
|
|
1562
|
+
monthlyMessageCap: null,
|
|
1563
|
+
// Pro has no hard message cap: own-key turns are unlimited and metered, and
|
|
1564
|
+
// shared turns keep running past the included allowance at the shared rate.
|
|
1565
|
+
// A 1,000-turn shared buffer is ~33/day — a typical small-business bot's
|
|
1566
|
+
// entire month — so most Pro customers never see the overage at all.
|
|
1567
|
+
includedSharedMessages: PRICING_INCLUDED_SHARED_MESSAGES,
|
|
1568
|
+
sharedKeyHardCap: PRICING_SHARED_KEY_HARD_CAP,
|
|
1569
|
+
canInvite: true,
|
|
1570
|
+
canUseCustomLlm: true,
|
|
1571
|
+
canUseCustomCode: true,
|
|
1572
|
+
maxHttpEndpointsPerArtifact: null,
|
|
1573
|
+
includedMessages: PRICING_INCLUDED_MESSAGES,
|
|
1574
|
+
includedEmbeddedBytes: PRICING_INCLUDED_EMBEDDED_GB * GB
|
|
1575
|
+
},
|
|
1576
|
+
ENTERPRISE: {
|
|
1577
|
+
maxProjects: null,
|
|
1578
|
+
maxToolsPerArtifact: null,
|
|
1579
|
+
maxPromptsPerArtifact: null,
|
|
1580
|
+
maxChannelsPerArtifact: null,
|
|
1581
|
+
maxRawStorageBytes: null,
|
|
1582
|
+
maxEmbeddedBytes: null,
|
|
1583
|
+
monthlyMessageCap: null,
|
|
1584
|
+
includedSharedMessages: PRICING_INCLUDED_SHARED_MESSAGES,
|
|
1585
|
+
sharedKeyHardCap: PRICING_SHARED_KEY_HARD_CAP,
|
|
1586
|
+
canInvite: true,
|
|
1587
|
+
canUseCustomLlm: true,
|
|
1588
|
+
canUseCustomCode: true,
|
|
1589
|
+
maxHttpEndpointsPerArtifact: null,
|
|
1590
|
+
includedMessages: PRICING_INCLUDED_MESSAGES,
|
|
1591
|
+
includedEmbeddedBytes: PRICING_INCLUDED_EMBEDDED_GB * GB
|
|
1592
|
+
}
|
|
1593
|
+
};
|
|
1594
|
+
// Quota features — used as the `feature` discriminator on PlanLimitError so the
|
|
1595
|
+
// dashboard can tailor the upgrade prompt.
|
|
1596
|
+
const PLAN_FEATURE_ORGANIZATION = 'organization';
|
|
1597
|
+
const PLAN_FEATURE_PROJECT = 'project';
|
|
1598
|
+
const PLAN_FEATURE_TOOL = 'tool';
|
|
1599
|
+
const PLAN_FEATURE_PROMPT = 'prompt';
|
|
1600
|
+
const PLAN_FEATURE_CHANNEL = 'channel';
|
|
1601
|
+
const PLAN_FEATURE_INVITE = 'invite';
|
|
1602
|
+
const PLAN_FEATURE_LLM = 'llm';
|
|
1603
|
+
const PLAN_FEATURE_CUSTOM_CODE = 'customCode';
|
|
1604
|
+
const PLAN_FEATURE_HTTP_ENDPOINT = 'httpEndpoint';
|
|
1605
|
+
const PLAN_FEATURE_RAW_STORAGE = 'rawStorage';
|
|
1606
|
+
const PLAN_FEATURE_EMBEDDED_STORAGE = 'embeddedStorage';
|
|
1607
|
+
const PLAN_FEATURE_MESSAGE = 'message';
|
|
1608
|
+
// Stable code returned on a quota block so clients can branch on it (402).
|
|
1609
|
+
const PLAN_LIMIT_ERROR_CODE = 'PLAN_LIMIT_EXCEEDED';
|
|
1610
|
+
// Stripe Billing Meter event names. The metering cron reports per-period
|
|
1611
|
+
// OVERAGE (usage above the plan's included allowance) to these meters; the
|
|
1612
|
+
// meters' prices on the subscription turn that into charges. Embedded storage
|
|
1613
|
+
// is reported in whole MB, messages as a raw count.
|
|
1614
|
+
//
|
|
1615
|
+
// Messages report to two separate meters because the two kinds of turn bill at
|
|
1616
|
+
// different rates — a turn on the org's own key is a platform fee, a turn on our
|
|
1617
|
+
// model is inference we bought. One meter can't price both.
|
|
1618
|
+
const STRIPE_METER_MESSAGES = 'ganju_channel_messages';
|
|
1619
|
+
const STRIPE_METER_SHARED_MESSAGES = 'ganju_shared_messages';
|
|
1620
|
+
const STRIPE_METER_EMBEDDED = 'ganju_embedded_storage';
|
|
1621
|
+
// Legal documents a user accepts, and the version they're on. Bump the version
|
|
1622
|
+
// when the document changes materially — existing users are then re-prompted,
|
|
1623
|
+
// and the old acceptance stays on record. Keep in sync with the "Last updated"
|
|
1624
|
+
// date on apps/website/src/md/{terms,privacy}.md.
|
|
1625
|
+
const CONSENT_DOCUMENT_TERMS = 'terms';
|
|
1626
|
+
const CONSENT_DOCUMENT_PRIVACY = 'privacy';
|
|
1627
|
+
const CONSENT_DOCUMENTS = [CONSENT_DOCUMENT_TERMS, CONSENT_DOCUMENT_PRIVACY];
|
|
1628
|
+
const CONSENT_CURRENT_VERSION = '2026-08-01';
|
|
1629
|
+
const CONSENT_SOURCE_SIGNUP = 'signup';
|
|
1630
|
+
const CONSENT_SOURCE_REACCEPT = 'reaccept';
|
|
1631
|
+
const CONSENT_SOURCES = [CONSENT_SOURCE_SIGNUP, CONSENT_SOURCE_REACCEPT];
|
|
1632
|
+
/**
|
|
1633
|
+
* Retention windows, in days, for the append-only tables that would otherwise
|
|
1634
|
+
* grow forever. These are the numbers the privacy policy publishes — change
|
|
1635
|
+
* one here and the policy has to change with it.
|
|
1636
|
+
*
|
|
1637
|
+
* `mcpRequest` is the sharpest of these: it stores the arguments AND results of
|
|
1638
|
+
* every tool call, which can include mail bodies, calendar entries, and drive
|
|
1639
|
+
* documents pulled from a connected account.
|
|
1640
|
+
*/
|
|
1641
|
+
const RETENTION_DAYS = {
|
|
1642
|
+
// Tool-call arguments and results.
|
|
1643
|
+
mcpRequest: 90,
|
|
1644
|
+
// Stack traces, paths, IPs.
|
|
1645
|
+
errorLog: 90,
|
|
1646
|
+
// Channel conversation history.
|
|
1647
|
+
channelMessage: 365,
|
|
1648
|
+
// "Who ran what, when" audit rows.
|
|
1649
|
+
artifactExecution: 365
|
|
1650
|
+
};
|
|
1651
|
+
// Sessions are purged once they've been expired for this long — the row is
|
|
1652
|
+
// useless after expiry, but a short grace period keeps debugging possible.
|
|
1653
|
+
const RETENTION_EXPIRED_SESSION_DAYS = 30;
|
|
1654
|
+
// Error alerting. The sweep emails a digest of new server-side failures so a
|
|
1655
|
+
// breach or outage is noticed rather than sitting in a table — the DPA commits
|
|
1656
|
+
// to notifying customers within 72 hours of BECOMING AWARE, which requires a
|
|
1657
|
+
// way to become aware.
|
|
1658
|
+
// Cron expressions from apps/api/wrangler.toml. The scheduled handler branches
|
|
1659
|
+
// on these, so the strings must match the config exactly.
|
|
1660
|
+
const CRON_HOURLY = '0 * * * *';
|
|
1661
|
+
const CRON_ERROR_ALERTS = '*/15 * * * *';
|
|
1662
|
+
const ALERT_STATE_KEY_ERROR_LOG = 'error_log';
|
|
1663
|
+
// Only alert on genuine server failures. 4xx rows are expected client errors
|
|
1664
|
+
// (validation, not-found, quota) and would drown the signal.
|
|
1665
|
+
const ALERT_MIN_STATUS = 500;
|
|
1666
|
+
// Rows pulled per run. A larger backlog is counted but not itemised.
|
|
1667
|
+
const ALERT_MAX_ROWS = 500;
|
|
1668
|
+
// Distinct error signatures listed in the email body.
|
|
1669
|
+
const ALERT_MAX_GROUPS = 20;
|
|
1670
|
+
// Message prefix length used to group errors into a signature.
|
|
1671
|
+
const ALERT_SIGNATURE_LENGTH = 120;
|
|
1672
|
+
// At or above this many errors in one run, the subject is flagged as a spike.
|
|
1673
|
+
const ALERT_SPIKE_THRESHOLD = 25;
|
|
1674
|
+
// Rows deleted per table per purge run. The cron runs hourly, so this drains a
|
|
1675
|
+
// backlog over a few passes instead of holding one very long transaction.
|
|
1676
|
+
const RETENTION_PURGE_BATCH = 5_000;
|
|
1677
|
+
exports.constants = {
|
|
1678
|
+
MB,
|
|
1679
|
+
GB,
|
|
1680
|
+
PLAN_FREE,
|
|
1681
|
+
PLAN_PRO,
|
|
1682
|
+
PLAN_ENTERPRISE,
|
|
1683
|
+
PLANS,
|
|
1684
|
+
SUBSCRIPTION_STATUS_ACTIVE,
|
|
1685
|
+
SUBSCRIPTION_STATUS_TRIALING,
|
|
1686
|
+
SUBSCRIPTION_STATUS_PAST_DUE,
|
|
1687
|
+
SUBSCRIPTION_STATUS_CANCELED,
|
|
1688
|
+
SUBSCRIPTION_STATUS_INCOMPLETE,
|
|
1689
|
+
SUBSCRIPTION_STATUS_INCOMPLETE_EXPIRED,
|
|
1690
|
+
SUBSCRIPTION_STATUS_UNPAID,
|
|
1691
|
+
SUBSCRIPTION_STATUS_PAUSED,
|
|
1692
|
+
SUBSCRIPTION_STATUSES,
|
|
1693
|
+
SUBSCRIPTION_ENTITLED_STATUSES,
|
|
1694
|
+
PLAN_LIMITS,
|
|
1695
|
+
PRICING_PRO_BASE_USD,
|
|
1696
|
+
PRICING_INCLUDED_MESSAGES,
|
|
1697
|
+
PRICING_INCLUDED_SHARED_MESSAGES,
|
|
1698
|
+
PRICING_SHARED_MESSAGE_PER_1K_USD,
|
|
1699
|
+
PRICING_SHARED_KEY_HARD_CAP,
|
|
1700
|
+
PRICING_INCLUDED_EMBEDDED_GB,
|
|
1701
|
+
PRICING_MESSAGE_PER_1K_USD,
|
|
1702
|
+
PRICING_EMBEDDED_PER_GB_USD,
|
|
1703
|
+
PRICING_CUSTOM_DOMAIN_USD,
|
|
1704
|
+
PLAN_FEATURE_ORGANIZATION,
|
|
1705
|
+
PLAN_FEATURE_PROJECT,
|
|
1706
|
+
PLAN_FEATURE_TOOL,
|
|
1707
|
+
PLAN_FEATURE_PROMPT,
|
|
1708
|
+
PLAN_FEATURE_CHANNEL,
|
|
1709
|
+
PLAN_FEATURE_INVITE,
|
|
1710
|
+
PLAN_FEATURE_LLM,
|
|
1711
|
+
PLAN_FEATURE_CUSTOM_CODE,
|
|
1712
|
+
PLAN_FEATURE_HTTP_ENDPOINT,
|
|
1713
|
+
PLAN_FEATURE_RAW_STORAGE,
|
|
1714
|
+
PLAN_FEATURE_EMBEDDED_STORAGE,
|
|
1715
|
+
PLAN_FEATURE_MESSAGE,
|
|
1716
|
+
PLAN_LIMIT_ERROR_CODE,
|
|
1717
|
+
STRIPE_METER_MESSAGES,
|
|
1718
|
+
STRIPE_METER_SHARED_MESSAGES,
|
|
1719
|
+
STRIPE_METER_EMBEDDED,
|
|
1720
|
+
CONSENT_DOCUMENT_TERMS,
|
|
1721
|
+
CONSENT_DOCUMENT_PRIVACY,
|
|
1722
|
+
CONSENT_DOCUMENTS,
|
|
1723
|
+
CONSENT_CURRENT_VERSION,
|
|
1724
|
+
CONSENT_SOURCE_SIGNUP,
|
|
1725
|
+
CONSENT_SOURCE_REACCEPT,
|
|
1726
|
+
CONSENT_SOURCES,
|
|
1727
|
+
RETENTION_DAYS,
|
|
1728
|
+
RETENTION_EXPIRED_SESSION_DAYS,
|
|
1729
|
+
RETENTION_PURGE_BATCH,
|
|
1730
|
+
CRON_HOURLY,
|
|
1731
|
+
CRON_ERROR_ALERTS,
|
|
1732
|
+
ALERT_STATE_KEY_ERROR_LOG,
|
|
1733
|
+
ALERT_MIN_STATUS,
|
|
1734
|
+
ALERT_MAX_ROWS,
|
|
1735
|
+
ALERT_MAX_GROUPS,
|
|
1736
|
+
ALERT_SIGNATURE_LENGTH,
|
|
1737
|
+
ALERT_SPIKE_THRESHOLD,
|
|
1738
|
+
USER_ROLE_ADMIN,
|
|
1739
|
+
USER_ROLES,
|
|
1740
|
+
INVITATION_STATUS,
|
|
1741
|
+
INVITATION_SCOPE_ORGANIZATION,
|
|
1742
|
+
INVITATION_SCOPE_PROJECT,
|
|
1743
|
+
INVITATION_SCOPES,
|
|
1744
|
+
INVITATION_RESPONSE_ACCEPT,
|
|
1745
|
+
INVITATION_RESPONSE_DECLINE,
|
|
1746
|
+
INVITATION_RESPONSES,
|
|
1747
|
+
INVITATION_EXPIRY_DAYS,
|
|
1748
|
+
INVITATION_TOKEN_BYTES,
|
|
1749
|
+
CONTACT_MAX_NAME_LENGTH,
|
|
1750
|
+
CONTACT_MAX_EMAIL_LENGTH,
|
|
1751
|
+
CONTACT_MIN_MESSAGE_LENGTH,
|
|
1752
|
+
CONTACT_MAX_MESSAGE_LENGTH,
|
|
1753
|
+
SLUG_BYTES,
|
|
1754
|
+
SLUG_PATTERN,
|
|
1755
|
+
STATUS_COMPLETED,
|
|
1756
|
+
STATUS_PENDING,
|
|
1757
|
+
STATUS_FAILED,
|
|
1758
|
+
STATUS_ACTIVE,
|
|
1759
|
+
STATUS_DISABLED,
|
|
1760
|
+
STATUS_BLOCKED,
|
|
1761
|
+
STATUS,
|
|
1762
|
+
USER_STATUS,
|
|
1763
|
+
CHANNEL_STATUS,
|
|
1764
|
+
RESOURCE_STATUS,
|
|
1765
|
+
SERVICE_NAME_API,
|
|
1766
|
+
SERVICE_NAME_MCP,
|
|
1767
|
+
SERVICE_NAME_RESOURCE_HANDLER,
|
|
1768
|
+
SERVICE_NAME_TOOL_BROKER,
|
|
1769
|
+
SOCIAL_PROVIDER_GOOGLE,
|
|
1770
|
+
SOCIAL_PROVIDER_GITHUB,
|
|
1771
|
+
SOCIAL_PROVIDERS,
|
|
1772
|
+
OAUTH_PROVIDER_GOOGLE_GMAIL,
|
|
1773
|
+
OAUTH_PROVIDER_GOOGLE_DRIVE,
|
|
1774
|
+
OAUTH_PROVIDER_GOOGLE_CALENDAR,
|
|
1775
|
+
OAUTH_PROVIDER_MICROSOFT_OUTLOOK,
|
|
1776
|
+
OAUTH_PROVIDER_ONE_DRIVE,
|
|
1777
|
+
OAUTH_PROVIDER_SLACK,
|
|
1778
|
+
OAUTH_PROVIDER_SLACK_USER,
|
|
1779
|
+
OAUTH_PROVIDERS,
|
|
1780
|
+
GOOGLE_OAUTH_AUTH_URL,
|
|
1781
|
+
MICROSOFT_OAUTH_AUTH_URL,
|
|
1782
|
+
SLACK_OAUTH_AUTH_URL,
|
|
1783
|
+
OAUTH_AUTH_URLS,
|
|
1784
|
+
GOOGLE_OAUTH_TOKEN_URL,
|
|
1785
|
+
MICROSOFT_OAUTH_TOKEN_URL,
|
|
1786
|
+
SLACK_OAUTH_TOKEN_URL,
|
|
1787
|
+
OAUTH_TOKEN_URLS,
|
|
1788
|
+
CREDENTIAL_REFRESH_BUFFER_MS,
|
|
1789
|
+
GOOGLE_CALENDAR_API_BASE,
|
|
1790
|
+
GOOGLE_DRIVE_API_BASE,
|
|
1791
|
+
GOOGLE_DRIVE_DEFAULT_PAGE_SIZE,
|
|
1792
|
+
GOOGLE_DRIVE_MAX_FOLDER_PAGES,
|
|
1793
|
+
GOOGLE_DRIVE_TOKEN_REFRESH_LEEWAY_MS,
|
|
1794
|
+
GOOGLE_DRIVE_URI_PREFIX,
|
|
1795
|
+
GOOGLE_DRIVE_FILE_FIELDS,
|
|
1796
|
+
GOOGLE_DRIVE_LIST_FIELDS,
|
|
1797
|
+
GOOGLE_DRIVE_TAB_MY_DRIVE,
|
|
1798
|
+
GOOGLE_DRIVE_TAB_SHARED_WITH_ME,
|
|
1799
|
+
GOOGLE_DRIVE_TAB_SHARED_DRIVES,
|
|
1800
|
+
GOOGLE_DRIVE_TAB_STARRED,
|
|
1801
|
+
GOOGLE_DRIVE_TABS,
|
|
1802
|
+
GOOGLE_DRIVE_TAB_LABEL_MY_DRIVE,
|
|
1803
|
+
GOOGLE_DRIVE_TAB_LABEL_SHARED_WITH_ME,
|
|
1804
|
+
GOOGLE_DRIVE_TAB_LABEL_SHARED_DRIVES,
|
|
1805
|
+
GOOGLE_DRIVE_TAB_LABEL_STARRED,
|
|
1806
|
+
GOOGLE_DRIVE_TAB_LABELS,
|
|
1807
|
+
MICROSOFT_GRAPH_API_BASE,
|
|
1808
|
+
ONE_DRIVE_DEFAULT_PAGE_SIZE,
|
|
1809
|
+
ONE_DRIVE_MAX_FOLDER_PAGES,
|
|
1810
|
+
ONE_DRIVE_TOKEN_REFRESH_LEEWAY_MS,
|
|
1811
|
+
ONE_DRIVE_URI_PREFIX,
|
|
1812
|
+
ONE_DRIVE_ITEM_SELECT,
|
|
1813
|
+
ONE_DRIVE_TAB_MY_FILES,
|
|
1814
|
+
ONE_DRIVE_TAB_SHARED_WITH_ME,
|
|
1815
|
+
ONE_DRIVE_TAB_RECENT,
|
|
1816
|
+
ONE_DRIVE_TAB_DRIVES,
|
|
1817
|
+
ONE_DRIVE_TABS,
|
|
1818
|
+
ONE_DRIVE_TAB_LABEL_MY_FILES,
|
|
1819
|
+
ONE_DRIVE_TAB_LABEL_SHARED_WITH_ME,
|
|
1820
|
+
ONE_DRIVE_TAB_LABEL_RECENT,
|
|
1821
|
+
ONE_DRIVE_TAB_LABEL_DRIVES,
|
|
1822
|
+
ONE_DRIVE_TAB_LABELS,
|
|
1823
|
+
REAUTH_ERROR_CODES,
|
|
1824
|
+
RESOURCE_TYPE_TOOLTIP_MIN_WIDTH,
|
|
1825
|
+
SEARCH_DEBOUNCE_MS,
|
|
1826
|
+
GOOGLE_DRIVE_EXPORT_MIME_TYPES,
|
|
1827
|
+
GOOGLE_DRIVE_EXPORT_EXTENSIONS,
|
|
1828
|
+
SERVICE_NAMES,
|
|
1829
|
+
SCHEMA_DEFINITION_TYPES,
|
|
1830
|
+
ROLE_MESSAGE_USER,
|
|
1831
|
+
ROLE_MESSAGE_ASSISTANT,
|
|
1832
|
+
ROLE_MESSAGE_SYSTEM,
|
|
1833
|
+
ROLE_MESSAGE_TOOL,
|
|
1834
|
+
ROLE_MESSAGES,
|
|
1835
|
+
CHANNEL_ROLE_MESSAGES,
|
|
1836
|
+
LANGUAGE_EN,
|
|
1837
|
+
LANGUAGE_ES,
|
|
1838
|
+
LANGUAGES,
|
|
1839
|
+
SPANISH_COUNTRIES,
|
|
1840
|
+
LANGUAGE_COOKIE,
|
|
1841
|
+
LANGUAGE_COOKIE_MAX_AGE,
|
|
1842
|
+
LANGUAGE_QUERY_PARAM,
|
|
1843
|
+
MIMETYPE_TEXT,
|
|
1844
|
+
MIMETYPE_TEXT_CSV,
|
|
1845
|
+
MIMETYPE_TEXT_HTML,
|
|
1846
|
+
MIMETYPE_TEXT_MARKDOWN,
|
|
1847
|
+
MIMETYPE_IMAGE_PNG,
|
|
1848
|
+
MIMETYPE_IMAGE_GIF,
|
|
1849
|
+
MIMETYPE_IMAGE_JPEG,
|
|
1850
|
+
MIMETYPE_IMAGE_WEBP,
|
|
1851
|
+
MIMETYPE_IMAGE_SVG_XML,
|
|
1852
|
+
MIMETYPE_APPLICATION_PDF,
|
|
1853
|
+
MIMETYPE_APPLICATION_JSON,
|
|
1854
|
+
MIMETYPE_APPLICATION_MSWORD,
|
|
1855
|
+
MIMETYPE_APPLICATION_VND_MS_EXCEL,
|
|
1856
|
+
MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_SPREADSHEETML_SHEET,
|
|
1857
|
+
MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_WORDPROCESSINGML_DOCUMENT,
|
|
1858
|
+
MIMETYPE_APPLICATION_VND_OPENXMLFORMATS_OFFICEDOCUMENT_PRESENTATIONML_PRESENTATION,
|
|
1859
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_DOCUMENT,
|
|
1860
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_SPREADSHEET,
|
|
1861
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_PRESENTATION,
|
|
1862
|
+
MIMETYPE_APPLICATION_VND_GOOGLE_APPS_FOLDER,
|
|
1863
|
+
MIMETYPE_APPLICATION_XML,
|
|
1864
|
+
MIMETYPE_APPLICATION_JAVASCRIPT,
|
|
1865
|
+
MIMETYPE_APPLICATION_TYPESCRIPT,
|
|
1866
|
+
MIMETYPE_APPLICATION_YAML,
|
|
1867
|
+
MIMETYPE_APPLICATION_X_YAML,
|
|
1868
|
+
MIMETYPE_APPLICATION_TOML,
|
|
1869
|
+
MIMETYPE_APPLICATION_X_SH,
|
|
1870
|
+
MIMETYPE_APPLICATION_SQL,
|
|
1871
|
+
MIMETYPE_APPLICATION_GRAPHQL,
|
|
1872
|
+
MIMETYPE_APPLICATION_LD_JSON,
|
|
1873
|
+
MIMETYPE_APPLICATION_XHTML_XML,
|
|
1874
|
+
MIMETYPE_APPLICATION_X_HTTPD_PHP,
|
|
1875
|
+
MIMETYPE_APPLICATION_X_PYTHON_CODE,
|
|
1876
|
+
MIMETYPE_APPLICATION_X_WWW_FORM_URLENCODED,
|
|
1877
|
+
MIMETYPE_APPLICATION_CSV,
|
|
1878
|
+
MIMETYPE_APPLICATION_X_RUBY,
|
|
1879
|
+
MIMETYPE_APPLICATION_X_PERL,
|
|
1880
|
+
MIMETYPE_APPLICATION_OCTET_STREAM,
|
|
1881
|
+
MIMETYPES,
|
|
1882
|
+
EXTENSION_BY_MIME,
|
|
1883
|
+
TEXT_MIME_TYPES,
|
|
1884
|
+
EMBEDDABLE_MIME_TYPES,
|
|
1885
|
+
USER_AVATAR_MIME_TYPES,
|
|
1886
|
+
RESOURCE_TYPE_STATIC,
|
|
1887
|
+
RESOURCE_TYPE_TEMPLATE,
|
|
1888
|
+
RESOURCE_TYPES,
|
|
1889
|
+
RESOURCE_SOURCE_TYPE_FILE,
|
|
1890
|
+
RESOURCE_SOURCE_TYPE_WEBSITE,
|
|
1891
|
+
RESOURCE_SOURCE_TYPE_GOOGLE_DRIVE_FOLDER,
|
|
1892
|
+
RESOURCE_SOURCE_TYPE_ONE_DRIVE_FOLDER,
|
|
1893
|
+
RESOURCE_SOURCE_TYPE_CUSTOM_CODE,
|
|
1894
|
+
RESOURCE_SOURCE_TYPES,
|
|
1895
|
+
CRAWL_RENDERER_CHEERIO,
|
|
1896
|
+
CRAWL_RENDERER_PLAYWRIGHT,
|
|
1897
|
+
CRAWL_RENDERERS,
|
|
1898
|
+
CRAWL_DEFAULT_MAX_PAGES,
|
|
1899
|
+
CRAWL_MAX_PAGES_LIMIT,
|
|
1900
|
+
CRAWL_DEFAULT_MAX_DEPTH,
|
|
1901
|
+
CRAWL_MAX_DEPTH_LIMIT,
|
|
1902
|
+
CRAWL_PAGE_FETCH_TIMEOUT_MS,
|
|
1903
|
+
CRAWL_PAGE_QUEUE_BATCH_SIZE,
|
|
1904
|
+
CRAWL_USER_AGENT,
|
|
1905
|
+
RESOURCE_ICON_THEME_DARK,
|
|
1906
|
+
RESOURCE_ICON_THEME_LIGHT,
|
|
1907
|
+
RESOURCE_ICON_THEMES,
|
|
1908
|
+
MAX_FILE_SIZE,
|
|
1909
|
+
MAX_AVATAR_SIZE,
|
|
1910
|
+
GMAIL_MAX_RAW_ATTACHMENT_BYTES,
|
|
1911
|
+
OUTLOOK_ATTACHMENT_INLINE_THRESHOLD,
|
|
1912
|
+
OUTLOOK_MAX_ATTACHMENT_BYTES,
|
|
1913
|
+
OUTLOOK_UPLOAD_CHUNK_BYTES,
|
|
1914
|
+
SLACK_API_BASE,
|
|
1915
|
+
SLACK_MAX_UPLOAD_BYTES,
|
|
1916
|
+
TELEGRAM_MAX_PHOTO_BYTES,
|
|
1917
|
+
TELEGRAM_MAX_FILE_BYTES,
|
|
1918
|
+
RATE_LIMIT_BACKOFF_SECONDS,
|
|
1919
|
+
TRANSIENT_BACKOFF_SECONDS,
|
|
1920
|
+
ENCODINGS,
|
|
1921
|
+
ENCODING_UTF8,
|
|
1922
|
+
CHANNEL_PLATFORM_TELEGRAM,
|
|
1923
|
+
CHANNEL_PLATFORM_SLACK,
|
|
1924
|
+
CHANNEL_PLATFORM_WHATSAPP,
|
|
1925
|
+
CHANNEL_PLATFORM_DISCORD,
|
|
1926
|
+
CHANNEL_PLATFORMS,
|
|
1927
|
+
CHANNEL_CONVERSATION_SCOPE_PRIVATE,
|
|
1928
|
+
CHANNEL_CONVERSATION_SCOPE_GROUP,
|
|
1929
|
+
CHANNEL_CONVERSATION_SCOPE_CHANNEL,
|
|
1930
|
+
CHANNEL_CONVERSATION_SCOPES,
|
|
1931
|
+
USAGE_KIND_PROMPT,
|
|
1932
|
+
USAGE_KIND_RESOURCE,
|
|
1933
|
+
USAGE_KIND_TOOL,
|
|
1934
|
+
USAGE_KINDS,
|
|
1935
|
+
LLM_PROVIDER_ANTHROPIC,
|
|
1936
|
+
LLM_PROVIDER_OPENAI,
|
|
1937
|
+
LLM_PROVIDER_OPENAI_COMPATIBLE,
|
|
1938
|
+
LLM_PROVIDER_GOOGLE,
|
|
1939
|
+
LLM_PROVIDERS,
|
|
1940
|
+
DEFAULT_LLM_PROVIDER,
|
|
1941
|
+
DEFAULT_LLM_MODEL,
|
|
1942
|
+
DEFAULT_LLM_SYSTEM_PROMPT,
|
|
1943
|
+
LLM_CATALOG,
|
|
1944
|
+
LLM_SYSTEM_DEFAULT,
|
|
1945
|
+
MAX_TOOL_LOOPS,
|
|
1946
|
+
CHANNEL_HISTORY_LIMIT,
|
|
1947
|
+
SHARED_KEY_HISTORY_LIMIT,
|
|
1948
|
+
SHARED_KEY_MAX_TOOL_LOOPS,
|
|
1949
|
+
CHANNEL_MAX_TOOLS,
|
|
1950
|
+
CHANNEL_DEBOUNCE_DEFAULT_MS,
|
|
1951
|
+
CHANNEL_DEBOUNCE_MIN_MS,
|
|
1952
|
+
CHANNEL_DEBOUNCE_MAX_MS,
|
|
1953
|
+
CHANNEL_DEBOUNCE_MAX_WAIT_MS,
|
|
1954
|
+
CHANNEL_DEBOUNCE_MAX_MESSAGES,
|
|
1955
|
+
CHANNEL_DEBOUNCE_DISABLED,
|
|
1956
|
+
CHANNEL_DEBOUNCE_JOIN,
|
|
1957
|
+
CHANNEL_DEBOUNCE_RETRY_MS,
|
|
1958
|
+
CHANNEL_DEBOUNCE_MAX_ATTEMPTS,
|
|
1959
|
+
TELEGRAM_SECRET_HEADER,
|
|
1960
|
+
TELEGRAM_API_BASE,
|
|
1961
|
+
TELEGRAM_MESSAGE_LIMIT,
|
|
1962
|
+
SLACK_SIGNATURE_HEADER,
|
|
1963
|
+
SLACK_TIMESTAMP_HEADER,
|
|
1964
|
+
SLACK_RETRY_NUM_HEADER,
|
|
1965
|
+
SLACK_SIGNATURE_VERSION,
|
|
1966
|
+
SLACK_SIGNATURE_MAX_SKEW_SECONDS,
|
|
1967
|
+
SLACK_MESSAGE_LIMIT,
|
|
1968
|
+
SLACK_MAX_SOURCE_BUTTONS,
|
|
1969
|
+
SLACK_REQUIRED_SCOPES,
|
|
1970
|
+
SLACK_RECOMMENDED_SCOPES,
|
|
1971
|
+
SLACK_BOT_EVENTS,
|
|
1972
|
+
DISCORD_API_BASE,
|
|
1973
|
+
DISCORD_GATEWAY_QUERY,
|
|
1974
|
+
DISCORD_MESSAGE_LIMIT,
|
|
1975
|
+
DISCORD_MAX_UPLOAD_BYTES,
|
|
1976
|
+
DISCORD_MAX_SOURCE_BUTTONS,
|
|
1977
|
+
DISCORD_INTENTS,
|
|
1978
|
+
DISCORD_SIGNATURE_HEADER,
|
|
1979
|
+
DISCORD_TIMESTAMP_HEADER,
|
|
1980
|
+
DISCORD_INTERACTION_TYPE_PING,
|
|
1981
|
+
DISCORD_INTERACTION_TYPE_APPLICATION_COMMAND,
|
|
1982
|
+
DISCORD_INTERACTION_RESPONSE_PONG,
|
|
1983
|
+
DISCORD_INTERACTION_RESPONSE_DEFERRED,
|
|
1984
|
+
DISCORD_CHANNEL_TYPE_DM,
|
|
1985
|
+
DISCORD_CHANNEL_TYPE_GROUP_DM,
|
|
1986
|
+
DISCORD_GATEWAY_OP_DISPATCH,
|
|
1987
|
+
DISCORD_GATEWAY_OP_HEARTBEAT,
|
|
1988
|
+
DISCORD_GATEWAY_OP_IDENTIFY,
|
|
1989
|
+
DISCORD_GATEWAY_OP_RESUME,
|
|
1990
|
+
DISCORD_GATEWAY_OP_RECONNECT,
|
|
1991
|
+
DISCORD_GATEWAY_OP_INVALID_SESSION,
|
|
1992
|
+
DISCORD_GATEWAY_OP_HELLO,
|
|
1993
|
+
DISCORD_GATEWAY_OP_HEARTBEAT_ACK,
|
|
1994
|
+
DISCORD_GATEWAY_DEFAULT_HEARTBEAT_MS,
|
|
1995
|
+
WHATSAPP_API_BASE,
|
|
1996
|
+
WHATSAPP_API_VERSION,
|
|
1997
|
+
WHATSAPP_MESSAGE_LIMIT,
|
|
1998
|
+
WHATSAPP_SIGNATURE_HEADER,
|
|
1999
|
+
WHATSAPP_SIGNATURE_PREFIX,
|
|
2000
|
+
WHATSAPP_HUB_MODE_PARAM,
|
|
2001
|
+
WHATSAPP_HUB_VERIFY_TOKEN_PARAM,
|
|
2002
|
+
WHATSAPP_HUB_CHALLENGE_PARAM,
|
|
2003
|
+
WHATSAPP_HUB_MODE_SUBSCRIBE,
|
|
2004
|
+
WHATSAPP_MAX_IMAGE_BYTES,
|
|
2005
|
+
WHATSAPP_MAX_VIDEO_BYTES,
|
|
2006
|
+
WHATSAPP_MAX_AUDIO_BYTES,
|
|
2007
|
+
WHATSAPP_MAX_DOCUMENT_BYTES,
|
|
2008
|
+
EMBEDDING_MODEL,
|
|
2009
|
+
EMBEDDING_DIMENSIONS,
|
|
2010
|
+
CHUNK_TARGET_CHARS,
|
|
2011
|
+
CHUNK_OVERLAP_CHARS,
|
|
2012
|
+
EMBED_BATCH_SIZE,
|
|
2013
|
+
RESOURCE_HANDLER_SLEEP_AFTER,
|
|
2014
|
+
DOCS_URL,
|
|
2015
|
+
BASE64_DATA_URI_RE,
|
|
2016
|
+
RAW_BASE64_BLOB_RE,
|
|
2017
|
+
CHUNK_SEPARATORS,
|
|
2018
|
+
DEFAULT_MAX_TOKENS,
|
|
2019
|
+
RESOURCE_TOOL_KEY_LIST_RESOURCES,
|
|
2020
|
+
RESOURCE_TOOL_KEY_SEARCH_RESOURCES,
|
|
2021
|
+
RESOURCE_TOOL_KEY_READ_RESOURCE,
|
|
2022
|
+
RESOURCE_TOOL_KEY_SEND_RESOURCE,
|
|
2023
|
+
PROMPT_TOOL_KEY_LIST_PROMPTS,
|
|
2024
|
+
RESOURCE_TOOL_KEYS,
|
|
2025
|
+
URI_BEARING_RESOURCE_TOOL_KEYS,
|
|
2026
|
+
CALENDAR_TOOL_KEY_PREFIX,
|
|
2027
|
+
CALENDAR_SEND_UPDATES_ALL,
|
|
2028
|
+
CALENDAR_SEND_UPDATES_EXTERNAL_ONLY,
|
|
2029
|
+
CALENDAR_SEND_UPDATES_NONE,
|
|
2030
|
+
CALENDAR_SEND_UPDATES_VALUES,
|
|
2031
|
+
CALENDAR_VISIBILITY_DEFAULT,
|
|
2032
|
+
CALENDAR_VISIBILITY_PUBLIC,
|
|
2033
|
+
CALENDAR_VISIBILITY_PRIVATE,
|
|
2034
|
+
CALENDAR_VISIBILITY_VALUES,
|
|
2035
|
+
CALENDAR_DEFAULT_CALENDAR_ID,
|
|
2036
|
+
CALENDAR_DEFAULT_EVENT_DURATION_MINUTES,
|
|
2037
|
+
CALENDAR_CONFERENCE_TYPE_GOOGLE_MEET,
|
|
2038
|
+
CALENDAR_TOOL_FIELDS,
|
|
2039
|
+
API_KEY_PROVIDER_CALCOM,
|
|
2040
|
+
API_KEY_PROVIDER_TAVILY,
|
|
2041
|
+
API_KEY_PROVIDERS,
|
|
2042
|
+
CREDENTIAL_PROVIDER_HTTP_ENDPOINT,
|
|
2043
|
+
CREDENTIAL_PROVIDER_MCP_PROXY,
|
|
2044
|
+
CREDENTIAL_PROVIDER_CUSTOM_CODE: cliConstants_1.CREDENTIAL_PROVIDER_CUSTOM_CODE,
|
|
2045
|
+
PER_TOOL_CREDENTIAL_PROVIDERS,
|
|
2046
|
+
CREDENTIAL_PROVIDERS,
|
|
2047
|
+
CALCOM_API_BASE,
|
|
2048
|
+
CALCOM_API_VERSION_EVENT_TYPES,
|
|
2049
|
+
CALCOM_API_VERSION_SLOTS,
|
|
2050
|
+
CALCOM_API_VERSION_BOOKINGS,
|
|
2051
|
+
CALCOM_TOOL_KEY_PREFIX,
|
|
2052
|
+
TAVILY_API_BASE,
|
|
2053
|
+
TAVILY_SEARCH_DEPTH_BASIC,
|
|
2054
|
+
TAVILY_SEARCH_DEPTH_ADVANCED,
|
|
2055
|
+
TAVILY_SEARCH_DEPTHS,
|
|
2056
|
+
TAVILY_TOPIC_GENERAL,
|
|
2057
|
+
TAVILY_TOPIC_NEWS,
|
|
2058
|
+
TAVILY_TOPICS,
|
|
2059
|
+
TAVILY_DEFAULT_MAX_RESULTS,
|
|
2060
|
+
TAVILY_MAX_RESULTS_LIMIT,
|
|
2061
|
+
WEB_TOOL_KEY_PREFIX,
|
|
2062
|
+
WEB_TOOL_KEY_SEARCH,
|
|
2063
|
+
WEB_TOOL_KEY_EXTRACT,
|
|
2064
|
+
WEB_TOOL_KEYS,
|
|
2065
|
+
TOOL_DEFINITION_KEY_HTTP_ENDPOINT,
|
|
2066
|
+
HTTP_ENDPOINT_METHOD_GET,
|
|
2067
|
+
HTTP_ENDPOINT_METHOD_POST,
|
|
2068
|
+
HTTP_ENDPOINT_METHOD_PUT,
|
|
2069
|
+
HTTP_ENDPOINT_METHOD_PATCH,
|
|
2070
|
+
HTTP_ENDPOINT_METHOD_DELETE,
|
|
2071
|
+
HTTP_ENDPOINT_METHODS,
|
|
2072
|
+
HTTP_ENDPOINT_BODY_KIND_NONE,
|
|
2073
|
+
HTTP_ENDPOINT_BODY_KIND_JSON,
|
|
2074
|
+
HTTP_ENDPOINT_BODY_KIND_FORM,
|
|
2075
|
+
HTTP_ENDPOINT_BODY_KIND_TEXT,
|
|
2076
|
+
HTTP_ENDPOINT_BODY_KINDS,
|
|
2077
|
+
HTTP_ENDPOINT_AUTH_KIND_NONE,
|
|
2078
|
+
HTTP_ENDPOINT_AUTH_KIND_BEARER,
|
|
2079
|
+
HTTP_ENDPOINT_AUTH_KIND_BASIC,
|
|
2080
|
+
HTTP_ENDPOINT_AUTH_KIND_API_KEY,
|
|
2081
|
+
HTTP_ENDPOINT_AUTH_KIND_OAUTH,
|
|
2082
|
+
HTTP_ENDPOINT_AUTH_KINDS,
|
|
2083
|
+
HTTP_ENDPOINT_RESPONSE_CONTENT_TYPE_AUTO,
|
|
2084
|
+
HTTP_ENDPOINT_RESPONSE_CONTENT_TYPE_JSON,
|
|
2085
|
+
HTTP_ENDPOINT_RESPONSE_CONTENT_TYPE_TEXT,
|
|
2086
|
+
HTTP_ENDPOINT_DEFAULT_TIMEOUT_MS,
|
|
2087
|
+
HTTP_ENDPOINT_MAX_TIMEOUT_MS,
|
|
2088
|
+
HTTP_ENDPOINT_DEFAULT_MAX_BYTES,
|
|
2089
|
+
HTTP_ENDPOINT_MAX_REQUEST_BYTES,
|
|
2090
|
+
TOOL_DEFINITION_KEY_MCP_PROXY,
|
|
2091
|
+
MCP_PROXY_TRANSPORT_STREAMABLE_HTTP,
|
|
2092
|
+
MCP_PROXY_TRANSPORT_SSE,
|
|
2093
|
+
MCP_PROXY_TRANSPORTS,
|
|
2094
|
+
MCP_PROXY_AUTH_KIND_NONE,
|
|
2095
|
+
MCP_PROXY_AUTH_KIND_BEARER,
|
|
2096
|
+
MCP_PROXY_AUTH_KIND_HEADER,
|
|
2097
|
+
MCP_PROXY_AUTH_KIND_OAUTH,
|
|
2098
|
+
MCP_PROXY_AUTH_KINDS,
|
|
2099
|
+
MCP_PROXY_DEFAULT_TIMEOUT_MS,
|
|
2100
|
+
MCP_PROXY_MAX_TIMEOUT_MS,
|
|
2101
|
+
MCP_PROXY_MAX_TOOLS,
|
|
2102
|
+
MCP_PROXY_MAX_RESPONSE_BYTES,
|
|
2103
|
+
MCP_PROXY_TOOL_NAME_SEP,
|
|
2104
|
+
MCP_PROXY_TOOL_NAME_MAX,
|
|
2105
|
+
TOOL_DEFINITION_KEY_CUSTOM_CODE,
|
|
2106
|
+
CUSTOM_CODE_VERSION_STATUS_DRAFT,
|
|
2107
|
+
CUSTOM_CODE_VERSION_STATUS_PUBLISHED,
|
|
2108
|
+
CUSTOM_CODE_VERSION_STATUS_ARCHIVED,
|
|
2109
|
+
CUSTOM_CODE_SOURCE_KIND_EDITOR,
|
|
2110
|
+
CUSTOM_CODE_SOURCE_KIND_BUNDLE,
|
|
2111
|
+
CUSTOM_CODE_SOURCE_KINDS,
|
|
2112
|
+
CUSTOM_CODE_SDK_MODULE,
|
|
2113
|
+
CUSTOM_CODE_SDK_SPECIFIER: cliConstants_1.CUSTOM_CODE_SDK_SPECIFIER,
|
|
2114
|
+
CUSTOM_CODE_MAIN_MODULE,
|
|
2115
|
+
CUSTOM_CODE_MAX_FILES,
|
|
2116
|
+
CUSTOM_CODE_MAX_FILE_PATH,
|
|
2117
|
+
CUSTOM_CODE_VERSION_STATUSES,
|
|
2118
|
+
CUSTOM_CODE_SCRIPT_NAME_PREFIX,
|
|
2119
|
+
CUSTOM_CODE_PREVIEW_SCRIPT_SUFFIX,
|
|
2120
|
+
CUSTOM_CODE_PREVIEW_TOKEN_TTL_MS,
|
|
2121
|
+
CUSTOM_CODE_TEST_TIMEOUT_MS,
|
|
2122
|
+
CUSTOM_CODE_SOURCE_KEY_PREFIX,
|
|
2123
|
+
CUSTOM_CODE_MAX_TOOLS,
|
|
2124
|
+
CUSTOM_CODE_TOOL_NAME_MAX,
|
|
2125
|
+
CUSTOM_CODE_DEFAULT_TIMEOUT_MS,
|
|
2126
|
+
CUSTOM_CODE_MAX_TIMEOUT_MS,
|
|
2127
|
+
CUSTOM_CODE_MAX_RESPONSE_BYTES,
|
|
2128
|
+
CUSTOM_CODE_MAX_BUNDLE_BYTES: cliConstants_1.CUSTOM_CODE_MAX_BUNDLE_BYTES,
|
|
2129
|
+
CUSTOM_CODE_INVOKE_ORIGIN,
|
|
2130
|
+
CUSTOM_CODE_INVOKE_PATH,
|
|
2131
|
+
CUSTOM_CODE_HEALTH_TOOL: sdkConstants_1.CUSTOM_CODE_HEALTH_TOOL,
|
|
2132
|
+
RESERVED_TOOL_NAME_PREFIXES,
|
|
2133
|
+
RESERVED_TOOL_NAMES,
|
|
2134
|
+
RESERVED_TOOL_NAME_MESSAGE,
|
|
2135
|
+
CUSTOM_CODE_UNKNOWN_CONNECTION_MESSAGE,
|
|
2136
|
+
OUTPUT_SCHEMA_NOT_OBJECT_MESSAGE,
|
|
2137
|
+
CUSTOM_CODE_BINDING_TOKEN: sdkConstants_1.CUSTOM_CODE_BINDING_TOKEN,
|
|
2138
|
+
CUSTOM_CODE_BINDING_BROKER: sdkConstants_1.CUSTOM_CODE_BINDING_BROKER,
|
|
2139
|
+
CUSTOM_CODE_BROKER_ORIGIN: sdkConstants_1.CUSTOM_CODE_BROKER_ORIGIN,
|
|
2140
|
+
CUSTOM_CODE_BROKER_PATH_CONNECTION: sdkConstants_1.CUSTOM_CODE_BROKER_PATH_CONNECTION,
|
|
2141
|
+
CUSTOM_CODE_BROKER_PATH_SECRET: sdkConstants_1.CUSTOM_CODE_BROKER_PATH_SECRET,
|
|
2142
|
+
CUSTOM_CODE_BROKER_PATH_RESOURCES_SEARCH: sdkConstants_1.CUSTOM_CODE_BROKER_PATH_RESOURCES_SEARCH,
|
|
2143
|
+
CUSTOM_CODE_BROKER_PATH_RESOURCES_READ: sdkConstants_1.CUSTOM_CODE_BROKER_PATH_RESOURCES_READ,
|
|
2144
|
+
CUSTOM_CODE_BROKER_PATH_RESOURCES_LIST: sdkConstants_1.CUSTOM_CODE_BROKER_PATH_RESOURCES_LIST,
|
|
2145
|
+
CUSTOM_CODE_BROKER_PATH_RESOURCES_CREATE: sdkConstants_1.CUSTOM_CODE_BROKER_PATH_RESOURCES_CREATE,
|
|
2146
|
+
CUSTOM_CODE_BROKER_PATH_RESOURCES_DELETE: sdkConstants_1.CUSTOM_CODE_BROKER_PATH_RESOURCES_DELETE,
|
|
2147
|
+
CUSTOM_CODE_BROKER_PATH_SEND_FILE: sdkConstants_1.CUSTOM_CODE_BROKER_PATH_SEND_FILE,
|
|
2148
|
+
CUSTOM_CODE_OUTBOUND_PARAM_ARTIFACT_ID,
|
|
2149
|
+
CUSTOM_CODE_OUTBOUND_PARAM_ALLOWED_HOSTS,
|
|
2150
|
+
CUSTOM_CODE_PLATFORM_HOSTS,
|
|
2151
|
+
CUSTOM_CODE_TOKEN_SECRET_ENV,
|
|
2152
|
+
CUSTOM_CODE_TOKEN_VERSION,
|
|
2153
|
+
CUSTOM_CODE_ACCOUNT_ID_ENV,
|
|
2154
|
+
CUSTOM_CODE_API_TOKEN_ENV,
|
|
2155
|
+
CUSTOM_CODE_NAMESPACE_ENV,
|
|
2156
|
+
CUSTOM_CODE_BROKER_SERVICE_ENV,
|
|
2157
|
+
CUSTOM_CODE_COMPATIBILITY_DATE,
|
|
2158
|
+
CUSTOM_CODE_SCRIPT_CPU_MS,
|
|
2159
|
+
CUSTOM_CODE_MAX_LOGS: sdkConstants_1.CUSTOM_CODE_MAX_LOGS,
|
|
2160
|
+
CUSTOM_CODE_MAX_LOG_LENGTH: sdkConstants_1.CUSTOM_CODE_MAX_LOG_LENGTH,
|
|
2161
|
+
CUSTOM_CODE_SEND_FILE_TARGET_GMAIL,
|
|
2162
|
+
CUSTOM_CODE_SEND_FILE_TARGET_OUTLOOK,
|
|
2163
|
+
CUSTOM_CODE_SEND_FILE_TARGET_SLACK,
|
|
2164
|
+
CUSTOM_CODE_SEND_FILE_TARGETS,
|
|
2165
|
+
CUSTOM_CODE_SEND_FILE_PROVIDERS,
|
|
2166
|
+
CUSTOM_CODE_SEND_FILE_PATHS,
|
|
2167
|
+
CUSTOM_CODE_SEND_FILE_MAX_URIS,
|
|
2168
|
+
CUSTOM_CODE_MAX_RESOURCE_TEXT_BYTES,
|
|
2169
|
+
CUSTOM_CODE_MAX_RESOURCE_FILE_BYTES,
|
|
2170
|
+
CUSTOM_CODE_RESOURCE_URI_PREFIX,
|
|
2171
|
+
CUSTOM_CODE_RESOURCE_NOT_OWNED_MESSAGE,
|
|
2172
|
+
CUSTOM_CODE_RESOURCE_NOT_DELETABLE_MESSAGE,
|
|
2173
|
+
CUSTOM_CODE_RESOURCE_HAS_CHILDREN_MESSAGE,
|
|
2174
|
+
CUSTOM_CODE_RESOURCE_NOT_EMBEDDABLE_MESSAGE,
|
|
2175
|
+
CUSTOM_CODE_RESOURCE_ACCESS_OWN,
|
|
2176
|
+
CUSTOM_CODE_RESOURCE_ACCESS_ALL,
|
|
2177
|
+
CUSTOM_CODE_RESOURCE_ACCESS_VALUES,
|
|
2178
|
+
CUSTOM_CODE_RESOURCE_PAYLOAD_MESSAGE,
|
|
2179
|
+
CUSTOM_CODE_RESOURCE_TEXT_TOO_LARGE_MESSAGE,
|
|
2180
|
+
CUSTOM_CODE_RESOURCE_FILE_TOO_LARGE_MESSAGE,
|
|
2181
|
+
RESERVED_SLUGS,
|
|
2182
|
+
MCP_INTERNAL_HEADER,
|
|
2183
|
+
MCP_CHANNEL_ID_HEADER,
|
|
2184
|
+
MCP_CHANNEL_PLATFORM_HEADER,
|
|
2185
|
+
MCP_CHANNEL_CLIENT_USER_AGENT,
|
|
2186
|
+
MCP_SESSION_HEADER,
|
|
2187
|
+
MCP_REQUEST_METHOD_INITIALIZE,
|
|
2188
|
+
MCP_REQUEST_METHOD_PING,
|
|
2189
|
+
MCP_REQUEST_METHOD_TOOLS_LIST,
|
|
2190
|
+
MCP_REQUEST_METHOD_TOOLS_CALL,
|
|
2191
|
+
MCP_REQUEST_METHOD_RESOURCES_LIST,
|
|
2192
|
+
MCP_REQUEST_METHOD_RESOURCES_TEMPLATES_LIST,
|
|
2193
|
+
MCP_REQUEST_METHOD_RESOURCES_READ,
|
|
2194
|
+
MCP_REQUEST_METHOD_PROMPTS_LIST,
|
|
2195
|
+
MCP_REQUEST_METHOD_PROMPTS_GET,
|
|
2196
|
+
MCP_REQUEST_METHODS,
|
|
2197
|
+
MCP_AUTH_KIND_JWT,
|
|
2198
|
+
MCP_AUTH_KIND_INTERNAL,
|
|
2199
|
+
MCP_AUTH_KINDS,
|
|
2200
|
+
JWKS_KV_KEY,
|
|
2201
|
+
JWKS_TTL_SECONDS,
|
|
2202
|
+
OAUTH_SCOPES_SUPPORTED,
|
|
2203
|
+
MCP_SCOPE_READ,
|
|
2204
|
+
ARTIFACT_SCOPE_PREFIX,
|
|
2205
|
+
CLI_OAUTH_CLIENT_NAME: cliConstants_1.CLI_OAUTH_CLIENT_NAME,
|
|
2206
|
+
CLI_OAUTH_REDIRECT_PATH: cliConstants_1.CLI_OAUTH_REDIRECT_PATH,
|
|
2207
|
+
CLI_OAUTH_REDIRECT_PORTS: cliConstants_1.CLI_OAUTH_REDIRECT_PORTS,
|
|
2208
|
+
CLI_OAUTH_SCOPES: cliConstants_1.CLI_OAUTH_SCOPES,
|
|
2209
|
+
CLI_TOKEN_REFRESH_SKEW_SECONDS: cliConstants_1.CLI_TOKEN_REFRESH_SKEW_SECONDS,
|
|
2210
|
+
CUSTOM_CODE_LOGS_DEFAULT_LIMIT,
|
|
2211
|
+
CUSTOM_CODE_LOGS_MAX_LIMIT,
|
|
2212
|
+
BOT_GRANT_TYPE,
|
|
2213
|
+
EXTERNAL_LINK_VERIFICATION_PREFIX,
|
|
2214
|
+
EXTERNAL_LINK_TTL_SECONDS,
|
|
2215
|
+
BOT_ACCESS_TOKEN_TTL_SECONDS,
|
|
2216
|
+
LINK_CODE_LENGTH,
|
|
2217
|
+
LINK_CODE_ALPHABET,
|
|
2218
|
+
BOT_COMMAND_LINK,
|
|
2219
|
+
RESERVED_BOT_COMMANDS
|
|
2220
|
+
};
|