@joai/core 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2458 -0
- package/dist/index.mjs +303 -0
- package/package.json +33 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
//#region src/config/urls.ts
|
|
2
|
+
const toSubdomainBaseUrl = (env, subdomain) => {
|
|
3
|
+
if (env === "local") return `http://${subdomain}.joai--api.test`;
|
|
4
|
+
if (env === "devnet") return `https://devnet-${subdomain}.joai.ai`;
|
|
5
|
+
if (env === "testnet") return `https://testnet-${subdomain}.joai.ai`;
|
|
6
|
+
return `https://${subdomain}.joai.ai`;
|
|
7
|
+
};
|
|
8
|
+
const getApiBaseUrl = (env) => toSubdomainBaseUrl(env, "api");
|
|
9
|
+
const getCortexBaseUrl = (env) => {
|
|
10
|
+
if (env === "local") return "http://localhost:3010";
|
|
11
|
+
if (env === "devnet") return "https://devnet-cortex.joai.ai";
|
|
12
|
+
if (env === "testnet") return "https://testnet-cortex.joai.ai";
|
|
13
|
+
return "https://cortex.joai.ai";
|
|
14
|
+
};
|
|
15
|
+
const getAppBaseUrl = (env) => {
|
|
16
|
+
if (env === "local") return "http://app.joai--api.test:3004";
|
|
17
|
+
if (env === "devnet") return "https://devnet.joai.ai";
|
|
18
|
+
if (env === "testnet") return "https://testnet.joai.ai";
|
|
19
|
+
return "https://joai.ai";
|
|
20
|
+
};
|
|
21
|
+
const getMcpServerAgentUrl = (env, agent) => `${toSubdomainBaseUrl(env, "cortex")}/mcp/agents/${agent}`;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/config/constants.ts
|
|
24
|
+
const JoaiUrls = {
|
|
25
|
+
TelegramGroup: "https://telegram.joai.ai",
|
|
26
|
+
Twitter: "https://x.com/JoAiAgents",
|
|
27
|
+
Reddit: "https://www.reddit.com/r/JoAi/",
|
|
28
|
+
GitHub: "https://github.com/JoAiHQ",
|
|
29
|
+
LinkedIn: "https://www.linkedin.com/company/joai",
|
|
30
|
+
Docs: "https://docs.joai.ai",
|
|
31
|
+
DocsApi: "https://docs.joai.ai/api",
|
|
32
|
+
DocsWebhooks: "https://docs.joai.ai/webhooks",
|
|
33
|
+
AppStore: "https://apps.apple.com/app/joai/id6741204807",
|
|
34
|
+
PlayStore: "https://play.google.com/store/apps/details?id=ai.joai.app"
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/http/service.ts
|
|
38
|
+
var HttpService = class {
|
|
39
|
+
constructor(baseUrl, cookies, authToken, options) {
|
|
40
|
+
this.get = async (url, options) => await this.request("GET", url, void 0, options);
|
|
41
|
+
this.post = async (url, data, options) => await this.request("POST", url, data, options);
|
|
42
|
+
this.put = async (url, data, options) => await this.request("PUT", url, data, options);
|
|
43
|
+
this.delete = async (url, options) => await this.request("DELETE", url, void 0, options);
|
|
44
|
+
this.getCsrfToken = () => {
|
|
45
|
+
if (typeof document === "undefined") return null;
|
|
46
|
+
const match = document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]*)/);
|
|
47
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
48
|
+
};
|
|
49
|
+
this.request = async (httpMethod, url, data, options, isRetry) => {
|
|
50
|
+
const requestUrl = url.startsWith("http") ? url : `${this.baseUrl}/${url}`;
|
|
51
|
+
const requestInfo = this.getRequestInfo(httpMethod, data, options);
|
|
52
|
+
const response = await this.convertResponse(this.fetchFn(requestUrl, requestInfo));
|
|
53
|
+
if (response.original.status === 419 && !isRetry) {
|
|
54
|
+
await this.refreshCsrfToken();
|
|
55
|
+
return this.request(httpMethod, url, data, options, true);
|
|
56
|
+
}
|
|
57
|
+
if (response.original.status === 401) this.onUnauthorized(response);
|
|
58
|
+
if (response.original.status === 429) {
|
|
59
|
+
console.warn(`[HTTP 429] ${httpMethod} ${requestUrl}`);
|
|
60
|
+
this.onTooManyRequests(response);
|
|
61
|
+
}
|
|
62
|
+
if (response.original.status === 503) this.onMaintenanceMode(response);
|
|
63
|
+
if (response.ok) this.onSuccessfulResponse(response);
|
|
64
|
+
return response;
|
|
65
|
+
};
|
|
66
|
+
this.refreshCsrfToken = async () => {
|
|
67
|
+
await this.fetchFn(`${this.baseUrl}/sanctum/csrf-cookie`, {
|
|
68
|
+
method: "GET",
|
|
69
|
+
credentials: "include",
|
|
70
|
+
headers: {
|
|
71
|
+
Accept: "application/json",
|
|
72
|
+
...this.headers
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
this.convertResponse = async (apiRes) => {
|
|
77
|
+
try {
|
|
78
|
+
const response = await apiRes;
|
|
79
|
+
const body = response.status === 204 ? {} : await response.json();
|
|
80
|
+
return {
|
|
81
|
+
data: body.data || null,
|
|
82
|
+
meta: body.meta,
|
|
83
|
+
body,
|
|
84
|
+
errors: body.errors || [],
|
|
85
|
+
ok: response.ok && !(body.errors && body.errors.length > 0),
|
|
86
|
+
noContent: response.status === 204,
|
|
87
|
+
paymentRequired: response.status === 402,
|
|
88
|
+
original: response
|
|
89
|
+
};
|
|
90
|
+
} catch (error) {
|
|
91
|
+
this.onNetworkError(error);
|
|
92
|
+
const anyError = error;
|
|
93
|
+
return {
|
|
94
|
+
data: {},
|
|
95
|
+
errors: anyError.response && (anyError.response.data || {}).errors,
|
|
96
|
+
body: {},
|
|
97
|
+
ok: false,
|
|
98
|
+
noContent: false,
|
|
99
|
+
paymentRequired: false,
|
|
100
|
+
original: anyError
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
this.getRequestInfo = (httpMethod = "GET", data, options) => {
|
|
105
|
+
const isJson = options?.isJson !== false;
|
|
106
|
+
const isFormData = data instanceof FormData;
|
|
107
|
+
const situationalHeaders = options?.headers || {};
|
|
108
|
+
const csrfToken = httpMethod !== "GET" ? this.getCsrfToken() : null;
|
|
109
|
+
const headers = {
|
|
110
|
+
Accept: "application/json",
|
|
111
|
+
...this.authToken ? { Authorization: `Bearer ${this.authToken}` } : {},
|
|
112
|
+
...csrfToken ? { "X-XSRF-TOKEN": csrfToken } : {},
|
|
113
|
+
...situationalHeaders,
|
|
114
|
+
...this.headers
|
|
115
|
+
};
|
|
116
|
+
if (isJson && !isFormData) headers["Content-Type"] = "application/json";
|
|
117
|
+
return {
|
|
118
|
+
method: httpMethod,
|
|
119
|
+
credentials: "include",
|
|
120
|
+
headers,
|
|
121
|
+
body: httpMethod !== "GET" ? isJson && !isFormData ? JSON.stringify(data || {}) : data : void 0
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
this.baseUrl = baseUrl;
|
|
125
|
+
this.authToken = authToken || null;
|
|
126
|
+
this.fetchFn = options?.customFetch ?? globalThis.fetch.bind(globalThis);
|
|
127
|
+
this.onNetworkError = () => {};
|
|
128
|
+
this.onSuccessfulResponse = () => {};
|
|
129
|
+
this.onUnauthorized = () => {};
|
|
130
|
+
this.onMaintenanceMode = () => {};
|
|
131
|
+
this.onTooManyRequests = () => {};
|
|
132
|
+
this.headers = {
|
|
133
|
+
...options?.refererUrl ? { Referer: options.refererUrl } : {},
|
|
134
|
+
...cookies ? { Cookie: cookies } : {},
|
|
135
|
+
...options?.headers ?? {}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/helpers/string.ts
|
|
141
|
+
const sanitizeAlphanumeric = (val) => val.replace(/[^a-z0-9]/gi, "");
|
|
142
|
+
const sanitizeNumeric = (val) => {
|
|
143
|
+
return (val.split(".").length > 2 && val.endsWith(".") ? val.slice(0, -1) : val).replace(/[^0-9\.]+/g, "");
|
|
144
|
+
};
|
|
145
|
+
const toNameInitials = (name) => name.split(" ").map((word) => word[0]).join("");
|
|
146
|
+
const toPreviewText = (text, maxChars = 100) => {
|
|
147
|
+
if (!text) return "";
|
|
148
|
+
let sanitized = text.replace(/<\/?(h[1-6])[^>]*>/gi, " - ").replace(/<\/?(p|div|ul|ol|li|br|hr)[^>]*>/gi, " ").replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
|
|
149
|
+
sanitized = sanitized.startsWith("- ") ? sanitized.slice(2) : sanitized;
|
|
150
|
+
sanitized = sanitized.length > maxChars ? sanitized.substring(0, sanitized.lastIndexOf(" ", maxChars)) + "..." : sanitized;
|
|
151
|
+
return sanitized;
|
|
152
|
+
};
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/helpers/number.ts
|
|
155
|
+
const abbreviateNumber = (val) => {
|
|
156
|
+
const SI_SYMBOL = [
|
|
157
|
+
"",
|
|
158
|
+
"k",
|
|
159
|
+
"M",
|
|
160
|
+
"G",
|
|
161
|
+
"T",
|
|
162
|
+
"P",
|
|
163
|
+
"E"
|
|
164
|
+
];
|
|
165
|
+
const tier = Math.log10(Math.abs(val)) / 3 | 0;
|
|
166
|
+
if (tier === 0) return val;
|
|
167
|
+
const suffix = SI_SYMBOL[tier];
|
|
168
|
+
return (val / Math.pow(10, tier * 3)).toFixed(1) + suffix;
|
|
169
|
+
};
|
|
170
|
+
const toHexEncodedNonce = (nonce) => {
|
|
171
|
+
const hex = nonce.toString(16);
|
|
172
|
+
return hex.length % 2 ? "0" + hex : hex;
|
|
173
|
+
};
|
|
174
|
+
const toNftId = (collection, nonce) => `${collection}-${toHexEncodedNonce(nonce)}`;
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/helpers/file.ts
|
|
177
|
+
const isFileFormatImage = (filename) => {
|
|
178
|
+
if (!filename) return false;
|
|
179
|
+
const sanitized = filename.toLowerCase();
|
|
180
|
+
return sanitized.endsWith(".png") || sanitized.endsWith(".jpeg") || sanitized.endsWith(".jpg") || sanitized.endsWith(".gif");
|
|
181
|
+
};
|
|
182
|
+
const isFileFormatVideo = (filename) => {
|
|
183
|
+
if (!filename) return false;
|
|
184
|
+
const sanitized = filename.toLowerCase();
|
|
185
|
+
return sanitized.endsWith(".mov") || sanitized.endsWith(".quicktime") || sanitized.endsWith(".mp4") || sanitized.endsWith(".webm");
|
|
186
|
+
};
|
|
187
|
+
const isFileFormatAudio = (filename) => {
|
|
188
|
+
if (!filename) return false;
|
|
189
|
+
const sanitized = filename.toLowerCase();
|
|
190
|
+
return sanitized.endsWith(".acc") || sanitized.endsWith(".flac") || sanitized.endsWith(".m4a") || sanitized.endsWith(".mp3") || sanitized.endsWith(".wav");
|
|
191
|
+
};
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/helpers/async.ts
|
|
194
|
+
const withRetry = async (fn, validate, { maxRetries = 5, retryDelay = 1e3 } = {}) => {
|
|
195
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
196
|
+
const result = await fn();
|
|
197
|
+
if (validate(result)) return result;
|
|
198
|
+
if (attempt === maxRetries) throw new Error("Max retries reached: validation failed");
|
|
199
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
200
|
+
}
|
|
201
|
+
throw new Error("Unexpected end of retry loop");
|
|
202
|
+
};
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/helpers/asset.ts
|
|
205
|
+
const AssetBaseUrl = "https://raw.githubusercontent.com/JoAiHQ/assets/main";
|
|
206
|
+
const getAssetAppLogoUrl = (name) => `${AssetBaseUrl}/apps/logos/${name}`;
|
|
207
|
+
const getAssetChainLogoUrl = (name) => `${AssetBaseUrl}/chains/logos/${name}`;
|
|
208
|
+
const getAssetTokenLogoUrl = (name) => `${AssetBaseUrl}/tokens/logos/${name}`;
|
|
209
|
+
const getAssetWalletLogoUrl = (name) => `${AssetBaseUrl}/wallets/logos/${name}`;
|
|
210
|
+
const getAssetPartnerLogoUrl = (name) => `${AssetBaseUrl}/partners/logos/${name}`;
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/budget/types.ts
|
|
213
|
+
const PeriodTypeLabels = {
|
|
214
|
+
0: "One-time",
|
|
215
|
+
1: "Daily",
|
|
216
|
+
2: "Weekly",
|
|
217
|
+
3: "Monthly"
|
|
218
|
+
};
|
|
219
|
+
const PeriodTypeOptions = [
|
|
220
|
+
{
|
|
221
|
+
value: 0,
|
|
222
|
+
label: "None (one-time)"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
value: 1,
|
|
226
|
+
label: "Daily"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
value: 2,
|
|
230
|
+
label: "Weekly"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
value: 3,
|
|
234
|
+
label: "Monthly"
|
|
235
|
+
}
|
|
236
|
+
];
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/contract/types.ts
|
|
239
|
+
const DefaultCodeMetadata = {
|
|
240
|
+
isUpgradeable: true,
|
|
241
|
+
isReadable: false,
|
|
242
|
+
isPayable: false,
|
|
243
|
+
isPayableBySmartContract: false
|
|
244
|
+
};
|
|
245
|
+
const ContractCategoryDisplayNames = {
|
|
246
|
+
default: "Popular",
|
|
247
|
+
nft: "NFT",
|
|
248
|
+
marketplace: "Marketplace",
|
|
249
|
+
staking: "Staking",
|
|
250
|
+
governance: "Governance",
|
|
251
|
+
defi: "DeFi",
|
|
252
|
+
game: "Game",
|
|
253
|
+
social: "Social",
|
|
254
|
+
identity: "Identity",
|
|
255
|
+
infra: "Infrastructure"
|
|
256
|
+
};
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/ogimage/types.ts
|
|
259
|
+
const OG_IMAGE_WIDTH = 1200;
|
|
260
|
+
const OG_IMAGE_HEIGHT = 630;
|
|
261
|
+
const OG_IMAGE_CONFIG = {
|
|
262
|
+
width: OG_IMAGE_WIDTH,
|
|
263
|
+
height: 630
|
|
264
|
+
};
|
|
265
|
+
const OgImageColors = {
|
|
266
|
+
primaryDark: "#66CC66",
|
|
267
|
+
primaryLight: "#98FF98",
|
|
268
|
+
secondaryDark: "#CCCC33",
|
|
269
|
+
secondaryLight: "#FFFF66",
|
|
270
|
+
brandBlack: "#181818",
|
|
271
|
+
brandWhite: "#f3f3f3",
|
|
272
|
+
gray: {
|
|
273
|
+
400: "#A3A3A3",
|
|
274
|
+
700: "#404040",
|
|
275
|
+
800: "#262626",
|
|
276
|
+
900: "#171717"
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region src/skill/types.ts
|
|
281
|
+
const SkillType = {
|
|
282
|
+
Knowledge: "knowledge",
|
|
283
|
+
ContentTemplate: "content-template",
|
|
284
|
+
PersonalityClone: "personality-clone",
|
|
285
|
+
BrandKit: "brand-kit",
|
|
286
|
+
VideoTemplate: "video-template",
|
|
287
|
+
VisualKit: "visual-kit"
|
|
288
|
+
};
|
|
289
|
+
const SkillMediaBindingRole = {
|
|
290
|
+
Subject: "subject",
|
|
291
|
+
Style: "style",
|
|
292
|
+
Logo: "logo",
|
|
293
|
+
Product: "product",
|
|
294
|
+
Motion: "motion",
|
|
295
|
+
FirstFrame: "first_frame",
|
|
296
|
+
LastFrame: "last_frame"
|
|
297
|
+
};
|
|
298
|
+
const SkillScope = {
|
|
299
|
+
Agent: "agent",
|
|
300
|
+
Team: "team"
|
|
301
|
+
};
|
|
302
|
+
//#endregion
|
|
303
|
+
export { ContractCategoryDisplayNames, DefaultCodeMetadata, HttpService, JoaiUrls, OG_IMAGE_CONFIG, OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH, OgImageColors, PeriodTypeLabels, PeriodTypeOptions, SkillMediaBindingRole, SkillScope, SkillType, abbreviateNumber, getApiBaseUrl, getAppBaseUrl, getAssetAppLogoUrl, getAssetChainLogoUrl, getAssetPartnerLogoUrl, getAssetTokenLogoUrl, getAssetWalletLogoUrl, getCortexBaseUrl, getMcpServerAgentUrl, isFileFormatAudio, isFileFormatImage, isFileFormatVideo, sanitizeAlphanumeric, sanitizeNumeric, toHexEncodedNonce, toNameInitials, toNftId, toPreviewText, withRetry };
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@joai/core",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "JoAi shared types, HTTP service, and config",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.mjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.mts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsdown",
|
|
19
|
+
"dev": "tsdown --watch",
|
|
20
|
+
"test": "jest",
|
|
21
|
+
"test:watch": "jest --watch"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@joai/warps": ">=4.0.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/jest": "^30.0.0",
|
|
28
|
+
"jest": "^30.3.0",
|
|
29
|
+
"ts-jest": "^29.4.6",
|
|
30
|
+
"tsdown": "^0.21.0",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
|
+
}
|
|
33
|
+
}
|