@almadar/integrations 2.19.0 → 2.21.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/{BaseIntegration-B9lSTRQm.d.ts → BaseIntegration-MA-b4fh8.d.ts} +1 -1
- package/dist/{contracts-B28G_7ur.d.ts → contracts-CYtxZXrR.d.ts} +101 -2
- package/dist/{factory-TNgVDujm.d.ts → factory-DTdVeyAi.d.ts} +1 -1
- package/dist/index.d.ts +81 -5
- package/dist/index.js +503 -22
- package/dist/index.js.map +1 -1
- package/dist/integrations/github/index.d.ts +1 -1
- package/dist/mocks/index.d.ts +2 -2
- package/dist/runtime/index.d.ts +3 -3
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -11,8 +11,27 @@ import { execSync, spawn } from 'child_process';
|
|
|
11
11
|
import { mkdtempSync, writeFileSync, rmSync, promises } from 'fs';
|
|
12
12
|
import { join } from 'path';
|
|
13
13
|
import { tmpdir } from 'os';
|
|
14
|
+
import { Pool } from 'pg';
|
|
14
15
|
|
|
15
|
-
// src/
|
|
16
|
+
// src/types.ts
|
|
17
|
+
var IntegrationError = class extends Error {
|
|
18
|
+
constructor(message, code = "UNKNOWN_ERROR", details) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "IntegrationError";
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.details = details;
|
|
23
|
+
}
|
|
24
|
+
toJSON() {
|
|
25
|
+
return {
|
|
26
|
+
name: this.name,
|
|
27
|
+
message: this.message,
|
|
28
|
+
code: this.code,
|
|
29
|
+
integration: this.integration,
|
|
30
|
+
action: this.action,
|
|
31
|
+
details: this.details
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
};
|
|
16
35
|
var ConsoleLogger = class {
|
|
17
36
|
constructor(_level = "info") {
|
|
18
37
|
this.log = createLogger("almadar:integrations");
|
|
@@ -1121,26 +1140,6 @@ Return ONLY valid JSON matching the schema.`,
|
|
|
1121
1140
|
};
|
|
1122
1141
|
registerIntegration("llm", LLMIntegration);
|
|
1123
1142
|
|
|
1124
|
-
// src/types.ts
|
|
1125
|
-
var IntegrationError = class extends Error {
|
|
1126
|
-
constructor(message, code = "UNKNOWN_ERROR", details) {
|
|
1127
|
-
super(message);
|
|
1128
|
-
this.name = "IntegrationError";
|
|
1129
|
-
this.code = code;
|
|
1130
|
-
this.details = details;
|
|
1131
|
-
}
|
|
1132
|
-
toJSON() {
|
|
1133
|
-
return {
|
|
1134
|
-
name: this.name,
|
|
1135
|
-
message: this.message,
|
|
1136
|
-
code: this.code,
|
|
1137
|
-
integration: this.integration,
|
|
1138
|
-
action: this.action,
|
|
1139
|
-
details: this.details
|
|
1140
|
-
};
|
|
1141
|
-
}
|
|
1142
|
-
};
|
|
1143
|
-
|
|
1144
1143
|
// src/integrations/ml/index.ts
|
|
1145
1144
|
var INFERRED_EVENT = "INFERRED";
|
|
1146
1145
|
var INFER_FAILED_EVENT = "INFER_FAILED";
|
|
@@ -3172,6 +3171,488 @@ var DockerIntegration = class extends BaseIntegration {
|
|
|
3172
3171
|
};
|
|
3173
3172
|
registerIntegration("docker", DockerIntegration);
|
|
3174
3173
|
|
|
3175
|
-
|
|
3174
|
+
// src/integrations/database/sql-guard.ts
|
|
3175
|
+
var DENIED_KEYWORDS = /* @__PURE__ */ new Set(["INSERT", "UPDATE", "DELETE", "MERGE", "INTO", "FOR"]);
|
|
3176
|
+
function isWordChar(ch) {
|
|
3177
|
+
return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "_" || ch === "$";
|
|
3178
|
+
}
|
|
3179
|
+
function skipSingleQuoted(sql, start, eString) {
|
|
3180
|
+
let i = start + 1;
|
|
3181
|
+
while (i < sql.length) {
|
|
3182
|
+
if (eString && sql[i] === "\\") {
|
|
3183
|
+
i += 2;
|
|
3184
|
+
continue;
|
|
3185
|
+
}
|
|
3186
|
+
if (sql[i] === "'") {
|
|
3187
|
+
if (sql[i + 1] === "'") {
|
|
3188
|
+
i += 2;
|
|
3189
|
+
continue;
|
|
3190
|
+
}
|
|
3191
|
+
return i + 1;
|
|
3192
|
+
}
|
|
3193
|
+
i++;
|
|
3194
|
+
}
|
|
3195
|
+
return sql.length;
|
|
3196
|
+
}
|
|
3197
|
+
function skipDoubleQuoted(sql, start) {
|
|
3198
|
+
let i = start + 1;
|
|
3199
|
+
while (i < sql.length) {
|
|
3200
|
+
if (sql[i] === '"') {
|
|
3201
|
+
if (sql[i + 1] === '"') {
|
|
3202
|
+
i += 2;
|
|
3203
|
+
continue;
|
|
3204
|
+
}
|
|
3205
|
+
return i + 1;
|
|
3206
|
+
}
|
|
3207
|
+
i++;
|
|
3208
|
+
}
|
|
3209
|
+
return sql.length;
|
|
3210
|
+
}
|
|
3211
|
+
function matchDollarTag(sql, start) {
|
|
3212
|
+
let i = start + 1;
|
|
3213
|
+
if (sql[i] === "$") return "$$";
|
|
3214
|
+
const first = sql[i];
|
|
3215
|
+
if (!first || !(first >= "a" && first <= "z" || first >= "A" && first <= "Z" || first === "_")) {
|
|
3216
|
+
return void 0;
|
|
3217
|
+
}
|
|
3218
|
+
i++;
|
|
3219
|
+
while (i < sql.length && isWordChar(sql[i]) && sql[i] !== "$") i++;
|
|
3220
|
+
if (sql[i] !== "$") return void 0;
|
|
3221
|
+
return sql.slice(start, i + 1);
|
|
3222
|
+
}
|
|
3223
|
+
function sanitizeSql(sql) {
|
|
3224
|
+
const out = [];
|
|
3225
|
+
let i = 0;
|
|
3226
|
+
while (i < sql.length) {
|
|
3227
|
+
const ch = sql[i];
|
|
3228
|
+
if (ch === "-" && sql[i + 1] === "-") {
|
|
3229
|
+
while (i < sql.length && sql[i] !== "\n") i++;
|
|
3230
|
+
out.push(" ");
|
|
3231
|
+
continue;
|
|
3232
|
+
}
|
|
3233
|
+
if (ch === "/" && sql[i + 1] === "*") {
|
|
3234
|
+
let depth = 1;
|
|
3235
|
+
i += 2;
|
|
3236
|
+
while (i < sql.length && depth > 0) {
|
|
3237
|
+
if (sql[i] === "/" && sql[i + 1] === "*") {
|
|
3238
|
+
depth++;
|
|
3239
|
+
i += 2;
|
|
3240
|
+
} else if (sql[i] === "*" && sql[i + 1] === "/") {
|
|
3241
|
+
depth--;
|
|
3242
|
+
i += 2;
|
|
3243
|
+
} else {
|
|
3244
|
+
i++;
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
out.push(" ");
|
|
3248
|
+
continue;
|
|
3249
|
+
}
|
|
3250
|
+
if (ch === "'") {
|
|
3251
|
+
let j = i - 1;
|
|
3252
|
+
while (j >= 0 && (sql[j] === " " || sql[j] === " " || sql[j] === "\n" || sql[j] === "\r")) j--;
|
|
3253
|
+
const prev = j >= 0 ? sql[j] : "";
|
|
3254
|
+
const eString = (prev === "e" || prev === "E") && (j === 0 || !isWordChar(sql[j - 1]));
|
|
3255
|
+
i = skipSingleQuoted(sql, i, eString);
|
|
3256
|
+
out.push("''");
|
|
3257
|
+
continue;
|
|
3258
|
+
}
|
|
3259
|
+
if (ch === '"') {
|
|
3260
|
+
i = skipDoubleQuoted(sql, i);
|
|
3261
|
+
out.push('""');
|
|
3262
|
+
continue;
|
|
3263
|
+
}
|
|
3264
|
+
if (ch === "$") {
|
|
3265
|
+
const tag = matchDollarTag(sql, i);
|
|
3266
|
+
if (tag) {
|
|
3267
|
+
const close = sql.indexOf(tag, i + tag.length);
|
|
3268
|
+
i = close === -1 ? sql.length : close + tag.length;
|
|
3269
|
+
out.push("$$");
|
|
3270
|
+
continue;
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
out.push(ch);
|
|
3274
|
+
i++;
|
|
3275
|
+
}
|
|
3276
|
+
return out.join("");
|
|
3277
|
+
}
|
|
3278
|
+
function assertReadOnlySelect(sql) {
|
|
3279
|
+
const sanitized = sanitizeSql(sql);
|
|
3280
|
+
const statements = sanitized.split(";").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3281
|
+
if (statements.length === 0) {
|
|
3282
|
+
return { ok: false, reason: "Empty statement" };
|
|
3283
|
+
}
|
|
3284
|
+
if (statements.length > 1) {
|
|
3285
|
+
return { ok: false, reason: "Only a single statement is allowed" };
|
|
3286
|
+
}
|
|
3287
|
+
const words = (statements[0].match(/\b[A-Za-z_][A-Za-z0-9_]*\b/g) ?? []).map(
|
|
3288
|
+
(w) => w.toUpperCase()
|
|
3289
|
+
);
|
|
3290
|
+
const first = words[0];
|
|
3291
|
+
if (first !== "SELECT" && first !== "WITH") {
|
|
3292
|
+
return { ok: false, reason: "Only SELECT statements are allowed" };
|
|
3293
|
+
}
|
|
3294
|
+
const denied = words.find((w) => DENIED_KEYWORDS.has(w));
|
|
3295
|
+
if (denied) {
|
|
3296
|
+
return { ok: false, reason: `Keyword not allowed in a read-only query: ${denied}` };
|
|
3297
|
+
}
|
|
3298
|
+
return { ok: true };
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
// src/integrations/database/index.ts
|
|
3302
|
+
var DEFAULT_STATEMENT_TIMEOUT_MS = 1e4;
|
|
3303
|
+
var PG_QUERY_CANCELED = "57014";
|
|
3304
|
+
var PostgresDriver = class {
|
|
3305
|
+
constructor(connectionString, statementTimeoutMs) {
|
|
3306
|
+
this.pool = new Pool({
|
|
3307
|
+
connectionString,
|
|
3308
|
+
statement_timeout: statementTimeoutMs,
|
|
3309
|
+
query_timeout: statementTimeoutMs
|
|
3310
|
+
});
|
|
3311
|
+
}
|
|
3312
|
+
async query(sql, params) {
|
|
3313
|
+
const result = await this.pool.query(sql, [...params]);
|
|
3314
|
+
return {
|
|
3315
|
+
rows: result.rows,
|
|
3316
|
+
rowCount: result.rowCount ?? result.rows.length
|
|
3317
|
+
};
|
|
3318
|
+
}
|
|
3319
|
+
async end() {
|
|
3320
|
+
await this.pool.end();
|
|
3321
|
+
}
|
|
3322
|
+
};
|
|
3323
|
+
function mapDriverError(error) {
|
|
3324
|
+
if (error instanceof IntegrationError) return error;
|
|
3325
|
+
const code = error.code;
|
|
3326
|
+
if (code === PG_QUERY_CANCELED) {
|
|
3327
|
+
return new IntegrationError("Statement timeout exceeded", "TIMEOUT_ERROR");
|
|
3328
|
+
}
|
|
3329
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3330
|
+
return new IntegrationError(message, "SERVICE_ERROR");
|
|
3331
|
+
}
|
|
3332
|
+
var DatabaseIntegration = class extends BaseIntegration {
|
|
3333
|
+
constructor(config) {
|
|
3334
|
+
super(config);
|
|
3335
|
+
this.drivers = /* @__PURE__ */ new Map();
|
|
3336
|
+
const fromEnv = config.env.DATABASE_STATEMENT_TIMEOUT_MS;
|
|
3337
|
+
if (fromEnv) {
|
|
3338
|
+
const parsed = Number(fromEnv);
|
|
3339
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
3340
|
+
throw new Error(`Invalid DATABASE_STATEMENT_TIMEOUT_MS: ${fromEnv}`);
|
|
3341
|
+
}
|
|
3342
|
+
this.statementTimeoutMs = parsed;
|
|
3343
|
+
} else {
|
|
3344
|
+
this.statementTimeoutMs = config.timeout ?? DEFAULT_STATEMENT_TIMEOUT_MS;
|
|
3345
|
+
}
|
|
3346
|
+
this.logger.info("Database integration initialized", {
|
|
3347
|
+
statementTimeoutMs: this.statementTimeoutMs
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3350
|
+
async execute(action, params) {
|
|
3351
|
+
const validation = this.validateParams(action, params);
|
|
3352
|
+
if (!validation.valid) {
|
|
3353
|
+
return {
|
|
3354
|
+
success: false,
|
|
3355
|
+
error: {
|
|
3356
|
+
name: "IntegrationError",
|
|
3357
|
+
message: "Validation failed",
|
|
3358
|
+
code: "VALIDATION_ERROR",
|
|
3359
|
+
details: validation.errors
|
|
3360
|
+
},
|
|
3361
|
+
metadata: this.createMetadata(action, 0)
|
|
3362
|
+
};
|
|
3363
|
+
}
|
|
3364
|
+
const startTime = Date.now();
|
|
3365
|
+
try {
|
|
3366
|
+
let data;
|
|
3367
|
+
switch (action) {
|
|
3368
|
+
case "query":
|
|
3369
|
+
data = await this.executeWithRetry(() => this.runQuery(params));
|
|
3370
|
+
break;
|
|
3371
|
+
default:
|
|
3372
|
+
throw new Error(`Unknown action: ${action}`);
|
|
3373
|
+
}
|
|
3374
|
+
return {
|
|
3375
|
+
success: true,
|
|
3376
|
+
data,
|
|
3377
|
+
metadata: this.createMetadata(action, Date.now() - startTime)
|
|
3378
|
+
};
|
|
3379
|
+
} catch (error) {
|
|
3380
|
+
return this.handleError(action, error);
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
async runQuery(params) {
|
|
3384
|
+
const guard = assertReadOnlySelect(params.sql);
|
|
3385
|
+
if (!guard.ok) {
|
|
3386
|
+
throw new IntegrationError(
|
|
3387
|
+
`Read-only violation: ${guard.reason ?? "not a SELECT statement"}`,
|
|
3388
|
+
"VALIDATION_ERROR"
|
|
3389
|
+
);
|
|
3390
|
+
}
|
|
3391
|
+
const driver = this.driverFor(params.connectionRef);
|
|
3392
|
+
this.logger.debug("Database QUERY", { connectionRef: params.connectionRef });
|
|
3393
|
+
try {
|
|
3394
|
+
return await driver.query(params.sql, params.params ?? []);
|
|
3395
|
+
} catch (error) {
|
|
3396
|
+
throw mapDriverError(error);
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
/** Resolve (and cache) the driver for a connection reference. */
|
|
3400
|
+
driverFor(connectionRef) {
|
|
3401
|
+
const connectionString = process.env[connectionRef];
|
|
3402
|
+
if (!connectionString) {
|
|
3403
|
+
throw new IntegrationError(
|
|
3404
|
+
`Connection reference "${connectionRef}" is not set in the environment`,
|
|
3405
|
+
"AUTH_ERROR"
|
|
3406
|
+
);
|
|
3407
|
+
}
|
|
3408
|
+
const cached = this.drivers.get(connectionString);
|
|
3409
|
+
if (cached) return cached;
|
|
3410
|
+
const driver = new PostgresDriver(connectionString, this.statementTimeoutMs);
|
|
3411
|
+
this.drivers.set(connectionString, driver);
|
|
3412
|
+
return driver;
|
|
3413
|
+
}
|
|
3414
|
+
};
|
|
3415
|
+
registerIntegration("database", DatabaseIntegration);
|
|
3416
|
+
|
|
3417
|
+
// src/integrations/wikimedia/index.ts
|
|
3418
|
+
var WIKI_ENDPOINT = "https://en.wikipedia.org/w/api.php";
|
|
3419
|
+
var DEFAULT_UA = "Almadar/1.0 (https://almadar.dev)";
|
|
3420
|
+
var DEFAULT_TIMEOUT_MS2 = 6e3;
|
|
3421
|
+
var EXTRACT_CHAR_CAP = 2e3;
|
|
3422
|
+
var WikimediaIntegration = class extends BaseIntegration {
|
|
3423
|
+
constructor(config) {
|
|
3424
|
+
super(config);
|
|
3425
|
+
this.userAgent = config.env?.WIKIMEDIA_USER_AGENT ?? DEFAULT_UA;
|
|
3426
|
+
this.timeoutMs = Number(config.env?.WIKIMEDIA_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS2);
|
|
3427
|
+
this.logger.info("Wikimedia integration initialized");
|
|
3428
|
+
}
|
|
3429
|
+
async execute(action, params) {
|
|
3430
|
+
const validation = this.validateParams(action, params);
|
|
3431
|
+
if (!validation.valid) {
|
|
3432
|
+
return {
|
|
3433
|
+
success: false,
|
|
3434
|
+
error: {
|
|
3435
|
+
name: "IntegrationError",
|
|
3436
|
+
message: "Validation failed",
|
|
3437
|
+
code: "VALIDATION_ERROR",
|
|
3438
|
+
details: validation.errors
|
|
3439
|
+
},
|
|
3440
|
+
metadata: this.createMetadata(action, 0)
|
|
3441
|
+
};
|
|
3442
|
+
}
|
|
3443
|
+
const startTime = Date.now();
|
|
3444
|
+
try {
|
|
3445
|
+
let data;
|
|
3446
|
+
switch (action) {
|
|
3447
|
+
case "getPage":
|
|
3448
|
+
data = await this.executeWithRetry(() => this.getPage(params));
|
|
3449
|
+
break;
|
|
3450
|
+
default:
|
|
3451
|
+
throw new Error(`Unknown action: ${action}`);
|
|
3452
|
+
}
|
|
3453
|
+
return { success: true, data, metadata: this.createMetadata(action, Date.now() - startTime) };
|
|
3454
|
+
} catch (error) {
|
|
3455
|
+
return this.handleError(action, error);
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3458
|
+
/** Look up a title on Wikipedia: description, portrait, lead extract. Empty fields on miss. */
|
|
3459
|
+
async getPage(params) {
|
|
3460
|
+
const title = String(params.title ?? "").trim();
|
|
3461
|
+
if (!title) return {};
|
|
3462
|
+
const url = `${WIKI_ENDPOINT}?action=query&format=json&redirects=1&prop=pageimages|description|extracts&explaintext=1&exintro=1&piprop=thumbnail&pithumbsize=240&titles=${encodeURIComponent(title)}`;
|
|
3463
|
+
const ctrl = new AbortController();
|
|
3464
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
3465
|
+
try {
|
|
3466
|
+
const res = await fetch(url, { headers: { "User-Agent": this.userAgent }, signal: ctrl.signal });
|
|
3467
|
+
if (!res.ok) return {};
|
|
3468
|
+
const data = await res.json();
|
|
3469
|
+
const pages = data.query?.pages;
|
|
3470
|
+
if (!pages) return {};
|
|
3471
|
+
const page = Object.values(pages)[0];
|
|
3472
|
+
if (!page || page.missing !== void 0) return {};
|
|
3473
|
+
const extract = typeof page.extract === "string" ? page.extract.slice(0, EXTRACT_CHAR_CAP).trim() : void 0;
|
|
3474
|
+
return {
|
|
3475
|
+
title: page.title,
|
|
3476
|
+
description: page.description ?? (typeof page.extract === "string" ? page.extract.split(".")[0] : void 0),
|
|
3477
|
+
portraitUrl: page.thumbnail?.source,
|
|
3478
|
+
extract
|
|
3479
|
+
};
|
|
3480
|
+
} finally {
|
|
3481
|
+
clearTimeout(timer);
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
};
|
|
3485
|
+
registerIntegration("wikimedia", WikimediaIntegration);
|
|
3486
|
+
|
|
3487
|
+
// src/integrations/iconify/index.ts
|
|
3488
|
+
var ICONIFY_API = "https://api.iconify.design";
|
|
3489
|
+
var DEFAULT_UA2 = "Almadar/1.0 (https://almadar.dev)";
|
|
3490
|
+
var DEFAULT_TIMEOUT_MS3 = 6e3;
|
|
3491
|
+
var IconifyIntegration = class extends BaseIntegration {
|
|
3492
|
+
constructor(config) {
|
|
3493
|
+
super(config);
|
|
3494
|
+
this.userAgent = config.env?.ICONIFY_USER_AGENT ?? DEFAULT_UA2;
|
|
3495
|
+
this.timeoutMs = Number(config.env?.ICONIFY_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS3);
|
|
3496
|
+
this.logger.info("Iconify integration initialized");
|
|
3497
|
+
}
|
|
3498
|
+
async execute(action, params) {
|
|
3499
|
+
const validation = this.validateParams(action, params);
|
|
3500
|
+
if (!validation.valid) {
|
|
3501
|
+
return {
|
|
3502
|
+
success: false,
|
|
3503
|
+
error: {
|
|
3504
|
+
name: "IntegrationError",
|
|
3505
|
+
message: "Validation failed",
|
|
3506
|
+
code: "VALIDATION_ERROR",
|
|
3507
|
+
details: validation.errors
|
|
3508
|
+
},
|
|
3509
|
+
metadata: this.createMetadata(action, 0)
|
|
3510
|
+
};
|
|
3511
|
+
}
|
|
3512
|
+
const startTime = Date.now();
|
|
3513
|
+
try {
|
|
3514
|
+
let data;
|
|
3515
|
+
switch (action) {
|
|
3516
|
+
case "svgExists":
|
|
3517
|
+
data = await this.executeWithRetry(() => this.svgExists(params));
|
|
3518
|
+
break;
|
|
3519
|
+
case "search":
|
|
3520
|
+
data = await this.executeWithRetry(() => this.search(params));
|
|
3521
|
+
break;
|
|
3522
|
+
case "getIconBody":
|
|
3523
|
+
data = await this.executeWithRetry(() => this.getIconBody(params));
|
|
3524
|
+
break;
|
|
3525
|
+
default:
|
|
3526
|
+
throw new Error(`Unknown action: ${action}`);
|
|
3527
|
+
}
|
|
3528
|
+
return { success: true, data, metadata: this.createMetadata(action, Date.now() - startTime) };
|
|
3529
|
+
} catch (error) {
|
|
3530
|
+
return this.handleError(action, error);
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
async svgExists(params) {
|
|
3534
|
+
const path = String(params.path ?? "");
|
|
3535
|
+
if (!path) return { exists: false };
|
|
3536
|
+
return { exists: await this.headOk(`${ICONIFY_API}/${path}`) };
|
|
3537
|
+
}
|
|
3538
|
+
async search(params) {
|
|
3539
|
+
const query = String(params.query ?? "");
|
|
3540
|
+
if (!query) return { icons: [] };
|
|
3541
|
+
const limit = Number(params.limit ?? 1);
|
|
3542
|
+
const data = await this.getJson(`${ICONIFY_API}/search?query=${encodeURIComponent(query)}&limit=${limit}`);
|
|
3543
|
+
return { icons: data?.icons ?? [] };
|
|
3544
|
+
}
|
|
3545
|
+
async getIconBody(params) {
|
|
3546
|
+
const iconId = String(params.iconId ?? "");
|
|
3547
|
+
const [prefix, name] = iconId.split(":");
|
|
3548
|
+
if (!prefix || !name) return { body: null };
|
|
3549
|
+
const data = await this.getJson(`${ICONIFY_API}/${prefix}.json?icons=${encodeURIComponent(name)}`);
|
|
3550
|
+
return { body: data?.icons?.[name]?.body ?? data?.aliases?.[name]?.body ?? null };
|
|
3551
|
+
}
|
|
3552
|
+
async headOk(url) {
|
|
3553
|
+
const ctrl = new AbortController();
|
|
3554
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
3555
|
+
try {
|
|
3556
|
+
const res = await fetch(url, { method: "GET", headers: { "User-Agent": this.userAgent }, signal: ctrl.signal });
|
|
3557
|
+
return res.ok;
|
|
3558
|
+
} catch {
|
|
3559
|
+
return false;
|
|
3560
|
+
} finally {
|
|
3561
|
+
clearTimeout(timer);
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
async getJson(url) {
|
|
3565
|
+
const ctrl = new AbortController();
|
|
3566
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
3567
|
+
try {
|
|
3568
|
+
const res = await fetch(url, { headers: { "User-Agent": this.userAgent }, signal: ctrl.signal });
|
|
3569
|
+
if (!res.ok) return null;
|
|
3570
|
+
return await res.json();
|
|
3571
|
+
} catch {
|
|
3572
|
+
return null;
|
|
3573
|
+
} finally {
|
|
3574
|
+
clearTimeout(timer);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
};
|
|
3578
|
+
registerIntegration("iconify", IconifyIntegration);
|
|
3579
|
+
|
|
3580
|
+
// src/integrations/arxiv/index.ts
|
|
3581
|
+
var ARXIV_ENDPOINT = "https://export.arxiv.org/api/query";
|
|
3582
|
+
var DEFAULT_TIMEOUT_MS4 = 1e4;
|
|
3583
|
+
var DEFAULT_MAX_RESULTS = 8;
|
|
3584
|
+
function parseAtom(xml) {
|
|
3585
|
+
const entries = [];
|
|
3586
|
+
for (const block of xml.split("<entry>").slice(1)) {
|
|
3587
|
+
const grab = (tag) => {
|
|
3588
|
+
const m = block.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)</${tag}>`, "i"));
|
|
3589
|
+
return m ? m[1].replace(/<[^>]+>/g, "").trim() : "";
|
|
3590
|
+
};
|
|
3591
|
+
const id = grab("id");
|
|
3592
|
+
const title = grab("title").replace(/\s+/g, " ");
|
|
3593
|
+
const summary = grab("summary").replace(/\s+/g, " ");
|
|
3594
|
+
const published = grab("published");
|
|
3595
|
+
const authors = Array.from(block.matchAll(/<author>\s*<name>([^<]+)<\/name>/g)).map((m) => m[1].trim());
|
|
3596
|
+
const linkMatch = block.match(/<link[^>]*rel="alternate"[^>]*href="([^"]+)"/i);
|
|
3597
|
+
const url = linkMatch ? linkMatch[1] : id;
|
|
3598
|
+
if (id || title) entries.push({ id, title, summary, authors, published, url });
|
|
3599
|
+
}
|
|
3600
|
+
return entries;
|
|
3601
|
+
}
|
|
3602
|
+
var ArxivIntegration = class extends BaseIntegration {
|
|
3603
|
+
constructor(config) {
|
|
3604
|
+
super(config);
|
|
3605
|
+
this.timeoutMs = Number(config.env?.ARXIV_TIMEOUT_MS ?? DEFAULT_TIMEOUT_MS4);
|
|
3606
|
+
this.logger.info("arXiv integration initialized");
|
|
3607
|
+
}
|
|
3608
|
+
async execute(action, params) {
|
|
3609
|
+
const validation = this.validateParams(action, params);
|
|
3610
|
+
if (!validation.valid) {
|
|
3611
|
+
return {
|
|
3612
|
+
success: false,
|
|
3613
|
+
error: {
|
|
3614
|
+
name: "IntegrationError",
|
|
3615
|
+
message: "Validation failed",
|
|
3616
|
+
code: "VALIDATION_ERROR",
|
|
3617
|
+
details: validation.errors
|
|
3618
|
+
},
|
|
3619
|
+
metadata: this.createMetadata(action, 0)
|
|
3620
|
+
};
|
|
3621
|
+
}
|
|
3622
|
+
const startTime = Date.now();
|
|
3623
|
+
try {
|
|
3624
|
+
let data;
|
|
3625
|
+
switch (action) {
|
|
3626
|
+
case "search":
|
|
3627
|
+
data = await this.executeWithRetry(() => this.search(params));
|
|
3628
|
+
break;
|
|
3629
|
+
default:
|
|
3630
|
+
throw new Error(`Unknown action: ${action}`);
|
|
3631
|
+
}
|
|
3632
|
+
return { success: true, data, metadata: this.createMetadata(action, Date.now() - startTime) };
|
|
3633
|
+
} catch (error) {
|
|
3634
|
+
return this.handleError(action, error);
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
async search(params) {
|
|
3638
|
+
const query = String(params.query ?? "").trim();
|
|
3639
|
+
if (!query) return { results: [] };
|
|
3640
|
+
const maxResults = Number(params.maxResults ?? DEFAULT_MAX_RESULTS);
|
|
3641
|
+
const url = `${ARXIV_ENDPOINT}?search_query=${encodeURIComponent(`all:${query}`)}&start=0&max_results=${maxResults}&sortBy=relevance`;
|
|
3642
|
+
const ctrl = new AbortController();
|
|
3643
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
3644
|
+
try {
|
|
3645
|
+
const res = await fetch(url, { headers: { "Accept": "application/atom+xml" }, signal: ctrl.signal });
|
|
3646
|
+
if (!res.ok) return { results: [] };
|
|
3647
|
+
const xml = await res.text();
|
|
3648
|
+
return { results: parseAtom(xml) };
|
|
3649
|
+
} finally {
|
|
3650
|
+
clearTimeout(timer);
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
};
|
|
3654
|
+
registerIntegration("arxiv", ArxivIntegration);
|
|
3655
|
+
|
|
3656
|
+
export { ArxivIntegration, BaseIntegration, CLIIntegration, ConsoleLogger, DatabaseIntegration, DeepAgentIntegration, DockerIntegration, EmailIntegration, GitHubIntegration, IconifyIntegration, IntegrationError, IntegrationFactory, LLMIntegration, MLIntegration, OAuthIntegration, OtelIntegration, QueueIntegration, RedisIntegration, StorageIntegration, StripeIntegration, TwilioIntegration, WikimediaIntegration, YouTubeIntegration, assertReadOnlySelect, getIntegration, getIntegrationFactory, getRegisteredIntegrations, isKnownIntegration, registerIntegration, resetIntegrationFactory, validateParams, verifyAndParseStripeEvent, withRetry };
|
|
3176
3657
|
//# sourceMappingURL=index.js.map
|
|
3177
3658
|
//# sourceMappingURL=index.js.map
|