@agentforge/tools 0.18.4 → 0.18.5
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.cjs +332 -272
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -322
- package/dist/index.d.ts +118 -322
- package/dist/index.js +331 -271
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { toolBuilder, ToolCategory, LogLevel, createLogger, createTool } from '@agentforge/core';
|
|
2
2
|
import { lookup } from 'dns/promises';
|
|
3
3
|
import net from 'net';
|
|
4
|
-
import
|
|
4
|
+
import axios5 from 'axios';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import * as cheerio from 'cheerio';
|
|
7
7
|
import { WebClient } from '@slack/web-api';
|
|
@@ -201,7 +201,7 @@ async function requestWithDestinationPolicy(config, policy = {}) {
|
|
|
201
201
|
for (let redirectCount = 0; ; redirectCount += 1) {
|
|
202
202
|
const validatedAddresses = await validateDestinationAllowed(currentUrl, effectivePolicy);
|
|
203
203
|
currentConfig.lookup = createPinnedLookup(validatedAddresses);
|
|
204
|
-
const response = await
|
|
204
|
+
const response = await axios5(currentConfig);
|
|
205
205
|
if (!isRedirectStatus(response.status)) return response;
|
|
206
206
|
if (effectivePolicy.allowRedirects === false) blockDestination(currentUrl, "redirect");
|
|
207
207
|
if (redirectCount >= maxRedirects) throw new DestinationPolicyError(currentUrl, "redirect", `Maximum redirect count (${maxRedirects}) exceeded for ${currentUrl}`);
|
|
@@ -712,7 +712,7 @@ var DuckDuckGoProvider = class {
|
|
|
712
712
|
async search(query, maxResults, timeout = DEFAULT_TIMEOUT) {
|
|
713
713
|
try {
|
|
714
714
|
return await retryWithBackoff(async () => {
|
|
715
|
-
const response = await
|
|
715
|
+
const response = await axios5.get("https://api.duckduckgo.com/", {
|
|
716
716
|
params: {
|
|
717
717
|
q: query,
|
|
718
718
|
format: "json"
|
|
@@ -813,7 +813,7 @@ var SerperProvider = class {
|
|
|
813
813
|
}
|
|
814
814
|
try {
|
|
815
815
|
return await retryWithBackoff(async () => {
|
|
816
|
-
const response = await
|
|
816
|
+
const response = await axios5.post(
|
|
817
817
|
"https://google.serper.dev/search",
|
|
818
818
|
{
|
|
819
819
|
q: query,
|
|
@@ -1216,38 +1216,91 @@ function createGetConfiguredAuth(apiKey, email, siteUrl) {
|
|
|
1216
1216
|
return { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, ATLASSIAN_SITE_URL };
|
|
1217
1217
|
};
|
|
1218
1218
|
}
|
|
1219
|
-
function createGetConfiguredAuthHeader(getConfiguredAuth) {
|
|
1220
|
-
return function getConfiguredAuthHeader() {
|
|
1221
|
-
const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfiguredAuth();
|
|
1222
|
-
const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
|
|
1223
|
-
return `Basic ${auth2}`;
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
1219
|
function getConfig() {
|
|
1227
1220
|
const ATLASSIAN_API_KEY = process.env.ATLASSIAN_API_KEY || "";
|
|
1228
1221
|
const ATLASSIAN_EMAIL = process.env.ATLASSIAN_EMAIL || "";
|
|
1229
1222
|
const ATLASSIAN_SITE_URL = (process.env.ATLASSIAN_SITE_URL || "").replace(/\/$/, "");
|
|
1230
1223
|
if (!ATLASSIAN_API_KEY || !ATLASSIAN_EMAIL || !ATLASSIAN_SITE_URL) {
|
|
1231
|
-
throw new Error(
|
|
1224
|
+
throw new Error(
|
|
1225
|
+
"Confluence credentials not configured. Set ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, and ATLASSIAN_SITE_URL in .env"
|
|
1226
|
+
);
|
|
1232
1227
|
}
|
|
1233
1228
|
return { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, ATLASSIAN_SITE_URL };
|
|
1234
1229
|
}
|
|
1235
|
-
function
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1230
|
+
function isRecord(value) {
|
|
1231
|
+
return typeof value === "object" && value !== null;
|
|
1232
|
+
}
|
|
1233
|
+
function describeError(error) {
|
|
1234
|
+
const errorRecord = isRecord(error) ? error : void 0;
|
|
1235
|
+
const response = isRecord(errorRecord?.response) ? errorRecord.response : void 0;
|
|
1236
|
+
const responseData = isRecord(response?.data) ? response.data : void 0;
|
|
1237
|
+
const providerMessage = typeof responseData?.message === "string" ? responseData.message : void 0;
|
|
1238
|
+
const errorMessage = typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
|
|
1239
|
+
return {
|
|
1240
|
+
message: providerMessage || errorMessage,
|
|
1241
|
+
status: typeof response?.status === "number" ? response.status : void 0
|
|
1242
|
+
};
|
|
1239
1243
|
}
|
|
1240
|
-
function
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1244
|
+
function authenticatedConfig(auth2, config = {}) {
|
|
1245
|
+
const credentials = Buffer.from(`${auth2.ATLASSIAN_EMAIL}:${auth2.ATLASSIAN_API_KEY}`).toString(
|
|
1246
|
+
"base64"
|
|
1247
|
+
);
|
|
1248
|
+
return {
|
|
1249
|
+
...config,
|
|
1250
|
+
headers: {
|
|
1251
|
+
...config.headers,
|
|
1252
|
+
Authorization: `Basic ${credentials}`
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
function createConfluenceRequest(getAuth) {
|
|
1257
|
+
return {
|
|
1258
|
+
async get(path13, config = {}) {
|
|
1259
|
+
const auth2 = getAuth();
|
|
1260
|
+
const response = await axios5.get(
|
|
1261
|
+
`${auth2.ATLASSIAN_SITE_URL}${path13}`,
|
|
1262
|
+
authenticatedConfig(auth2, config)
|
|
1263
|
+
);
|
|
1264
|
+
return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
|
|
1265
|
+
},
|
|
1266
|
+
async post(path13, data, config = {}) {
|
|
1267
|
+
const auth2 = getAuth();
|
|
1268
|
+
const response = await axios5.post(
|
|
1269
|
+
`${auth2.ATLASSIAN_SITE_URL}${path13}`,
|
|
1270
|
+
data,
|
|
1271
|
+
authenticatedConfig(auth2, config)
|
|
1272
|
+
);
|
|
1273
|
+
return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
|
|
1274
|
+
},
|
|
1275
|
+
async put(path13, data, config = {}) {
|
|
1276
|
+
const auth2 = getAuth();
|
|
1277
|
+
const response = await axios5.put(
|
|
1278
|
+
`${auth2.ATLASSIAN_SITE_URL}${path13}`,
|
|
1279
|
+
data,
|
|
1280
|
+
authenticatedConfig(auth2, config)
|
|
1281
|
+
);
|
|
1282
|
+
return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
|
|
1283
|
+
},
|
|
1284
|
+
describeError
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function createSearchConfluenceTool(request2, logger20) {
|
|
1288
|
+
return toolBuilder().name("search-confluence").description(
|
|
1289
|
+
"Search for pages in Confluence using keywords or CQL (Confluence Query Language). Returns matching pages with titles, IDs, and excerpts."
|
|
1290
|
+
).category(ToolCategory.WEB).tag("confluence").tag("search").tag("knowledge-base").usageNotes(
|
|
1291
|
+
"Use this to find relevant documentation, policies, or information in Confluence. You can search by keywords or use CQL for advanced queries (e.g., 'space=AI AND type=page'). Use get-confluence-page to retrieve full content of specific pages."
|
|
1292
|
+
).suggests(["get-confluence-page"]).schema(
|
|
1293
|
+
z.object({
|
|
1294
|
+
query: z.string().describe(
|
|
1295
|
+
"Search query or CQL expression (e.g., 'payment processing' or 'space=BL3 AND title~payment')"
|
|
1296
|
+
),
|
|
1297
|
+
limit: z.number().optional().describe("Maximum number of results to return (default: 10, max: 25)")
|
|
1298
|
+
})
|
|
1299
|
+
).implement(async ({ query, limit = 10 }) => {
|
|
1245
1300
|
logger20.info("search-confluence called", { query, limit });
|
|
1246
1301
|
try {
|
|
1247
|
-
const
|
|
1248
|
-
const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
|
|
1302
|
+
const response = await request2.get("/wiki/rest/api/content/search", {
|
|
1249
1303
|
headers: {
|
|
1250
|
-
Authorization: getAuthHeader2(),
|
|
1251
1304
|
Accept: "application/json"
|
|
1252
1305
|
},
|
|
1253
1306
|
params: {
|
|
@@ -1256,22 +1309,24 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1256
1309
|
expand: "space,version"
|
|
1257
1310
|
}
|
|
1258
1311
|
});
|
|
1259
|
-
const { ATLASSIAN_SITE_URL: siteUrl } = getAuth();
|
|
1260
1312
|
const results = response.data.results.map((page) => ({
|
|
1261
1313
|
id: page.id,
|
|
1262
1314
|
title: page.title,
|
|
1263
1315
|
type: page.type,
|
|
1264
1316
|
space: page.space?.name || "Unknown",
|
|
1265
1317
|
spaceKey: page.space?.key || "",
|
|
1266
|
-
url: `${siteUrl}/wiki${page._links.webui}`,
|
|
1318
|
+
url: `${response.siteUrl}/wiki${page._links.webui}`,
|
|
1267
1319
|
lastModified: page.version?.when || ""
|
|
1268
1320
|
}));
|
|
1269
1321
|
if (results.length === 0) {
|
|
1270
|
-
logger20.warn(
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1322
|
+
logger20.warn(
|
|
1323
|
+
"search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry",
|
|
1324
|
+
{
|
|
1325
|
+
query,
|
|
1326
|
+
limit,
|
|
1327
|
+
totalSize: response.data.totalSize
|
|
1328
|
+
}
|
|
1329
|
+
);
|
|
1275
1330
|
} else {
|
|
1276
1331
|
logger20.info("search-confluence result", {
|
|
1277
1332
|
query,
|
|
@@ -1288,28 +1343,33 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1288
1343
|
results
|
|
1289
1344
|
});
|
|
1290
1345
|
} catch (error) {
|
|
1346
|
+
const description = request2.describeError(error);
|
|
1291
1347
|
logger20.error("search-confluence error", {
|
|
1292
1348
|
query,
|
|
1293
|
-
error:
|
|
1294
|
-
status:
|
|
1349
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1350
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1295
1351
|
});
|
|
1296
1352
|
return JSON.stringify({
|
|
1297
1353
|
success: false,
|
|
1298
|
-
error:
|
|
1354
|
+
error: description.message
|
|
1299
1355
|
});
|
|
1300
1356
|
}
|
|
1301
1357
|
}).build();
|
|
1302
1358
|
}
|
|
1303
|
-
function createGetConfluencePageTool(
|
|
1304
|
-
return toolBuilder().name("get-confluence-page").description(
|
|
1305
|
-
|
|
1306
|
-
|
|
1359
|
+
function createGetConfluencePageTool(request2, logger20) {
|
|
1360
|
+
return toolBuilder().name("get-confluence-page").description(
|
|
1361
|
+
"Get the full content of a specific Confluence page by its ID. Returns the page title, content (in storage format), space, and metadata."
|
|
1362
|
+
).category(ToolCategory.WEB).tag("confluence").tag("page").tag("content").usageNotes(
|
|
1363
|
+
"Use this after search-confluence to retrieve the full content of a specific page. The page ID can be found in search results."
|
|
1364
|
+
).requires(["search-confluence"]).schema(
|
|
1365
|
+
z.object({
|
|
1366
|
+
page_id: z.string().describe("The Confluence page ID (from search results)")
|
|
1367
|
+
})
|
|
1368
|
+
).implement(async ({ page_id }) => {
|
|
1307
1369
|
logger20.info("get-confluence-page called", { page_id });
|
|
1308
1370
|
try {
|
|
1309
|
-
const
|
|
1310
|
-
const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
|
|
1371
|
+
const response = await request2.get(`/wiki/rest/api/content/${page_id}`, {
|
|
1311
1372
|
headers: {
|
|
1312
|
-
Authorization: getAuthHeader2(),
|
|
1313
1373
|
Accept: "application/json"
|
|
1314
1374
|
},
|
|
1315
1375
|
params: {
|
|
@@ -1332,35 +1392,40 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1332
1392
|
space: page.space?.name || "Unknown",
|
|
1333
1393
|
spaceKey: page.space?.key || "",
|
|
1334
1394
|
content: page.body?.storage?.value || "",
|
|
1335
|
-
url: `${
|
|
1395
|
+
url: `${response.siteUrl}/wiki${page._links.webui}`,
|
|
1336
1396
|
created: page.history?.createdDate || "",
|
|
1337
1397
|
lastModified: page.version?.when || "",
|
|
1338
1398
|
version: page.version?.number || 1
|
|
1339
1399
|
}
|
|
1340
1400
|
});
|
|
1341
1401
|
} catch (error) {
|
|
1402
|
+
const description = request2.describeError(error);
|
|
1342
1403
|
logger20.error("get-confluence-page error", {
|
|
1343
1404
|
page_id,
|
|
1344
|
-
error:
|
|
1345
|
-
status:
|
|
1405
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1406
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1346
1407
|
});
|
|
1347
1408
|
return JSON.stringify({
|
|
1348
1409
|
success: false,
|
|
1349
|
-
error:
|
|
1410
|
+
error: description.message
|
|
1350
1411
|
});
|
|
1351
1412
|
}
|
|
1352
1413
|
}).build();
|
|
1353
1414
|
}
|
|
1354
|
-
function createListConfluenceSpacesTool(
|
|
1355
|
-
return toolBuilder().name("list-confluence-spaces").description(
|
|
1356
|
-
|
|
1357
|
-
|
|
1415
|
+
function createListConfluenceSpacesTool(request2, logger20) {
|
|
1416
|
+
return toolBuilder().name("list-confluence-spaces").description(
|
|
1417
|
+
"List all available Confluence spaces. Returns space names, keys, types, and descriptions to help identify where to search for information."
|
|
1418
|
+
).category(ToolCategory.WEB).tag("confluence").tag("spaces").tag("list").usageNotes(
|
|
1419
|
+
"Use this first to discover available spaces before searching. Helps narrow down searches to specific areas (e.g., 'AI', 'BL3', 'Finance')."
|
|
1420
|
+
).follows(["search-confluence"]).schema(
|
|
1421
|
+
z.object({
|
|
1422
|
+
limit: z.number().optional().describe("Maximum number of spaces to return (default: 25)")
|
|
1423
|
+
})
|
|
1424
|
+
).implement(async ({ limit = 25 }) => {
|
|
1358
1425
|
logger20.info("list-confluence-spaces called", { limit });
|
|
1359
1426
|
try {
|
|
1360
|
-
const
|
|
1361
|
-
const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
|
|
1427
|
+
const response = await request2.get("/wiki/rest/api/space", {
|
|
1362
1428
|
headers: {
|
|
1363
|
-
Authorization: getAuthHeader2(),
|
|
1364
1429
|
Accept: "application/json"
|
|
1365
1430
|
},
|
|
1366
1431
|
params: {
|
|
@@ -1372,7 +1437,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1372
1437
|
name: space.name,
|
|
1373
1438
|
type: space.type,
|
|
1374
1439
|
description: space.description?.plain?.value || "",
|
|
1375
|
-
url: `${
|
|
1440
|
+
url: `${response.siteUrl}/wiki${space._links.webui}`
|
|
1376
1441
|
}));
|
|
1377
1442
|
logger20.info("list-confluence-spaces result", {
|
|
1378
1443
|
spaceCount: spaces.length,
|
|
@@ -1385,28 +1450,33 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1385
1450
|
spaces
|
|
1386
1451
|
});
|
|
1387
1452
|
} catch (error) {
|
|
1453
|
+
const description = request2.describeError(error);
|
|
1388
1454
|
logger20.error("list-confluence-spaces error", {
|
|
1389
|
-
error:
|
|
1390
|
-
status:
|
|
1455
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1456
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1391
1457
|
});
|
|
1392
1458
|
return JSON.stringify({
|
|
1393
1459
|
success: false,
|
|
1394
|
-
error:
|
|
1460
|
+
error: description.message
|
|
1395
1461
|
});
|
|
1396
1462
|
}
|
|
1397
1463
|
}).build();
|
|
1398
1464
|
}
|
|
1399
|
-
function createGetSpacePagesTool(
|
|
1400
|
-
return toolBuilder().name("get-space-pages").description(
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1465
|
+
function createGetSpacePagesTool(request2, logger20) {
|
|
1466
|
+
return toolBuilder().name("get-space-pages").description(
|
|
1467
|
+
"Get all pages from a specific Confluence space by space key. Useful for browsing content in a particular area."
|
|
1468
|
+
).category(ToolCategory.WEB).tag("confluence").tag("space").tag("pages").usageNotes(
|
|
1469
|
+
"Use this to explore all pages in a specific space. Get the space key from list-confluence-spaces first."
|
|
1470
|
+
).requires(["list-confluence-spaces"]).schema(
|
|
1471
|
+
z.object({
|
|
1472
|
+
space_key: z.string().describe("The space key (e.g., 'AI', 'BL3', 'FIN')"),
|
|
1473
|
+
limit: z.number().optional().describe("Maximum number of pages to return (default: 25)")
|
|
1474
|
+
})
|
|
1475
|
+
).implement(async ({ space_key, limit = 25 }) => {
|
|
1404
1476
|
logger20.info("get-space-pages called", { space_key, limit });
|
|
1405
1477
|
try {
|
|
1406
|
-
const
|
|
1407
|
-
const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
|
|
1478
|
+
const response = await request2.get("/wiki/rest/api/content", {
|
|
1408
1479
|
headers: {
|
|
1409
|
-
Authorization: getAuthHeader2(),
|
|
1410
1480
|
Accept: "application/json"
|
|
1411
1481
|
},
|
|
1412
1482
|
params: {
|
|
@@ -1419,14 +1489,17 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1419
1489
|
const pages = response.data.results.map((page) => ({
|
|
1420
1490
|
id: page.id,
|
|
1421
1491
|
title: page.title,
|
|
1422
|
-
url: `${
|
|
1492
|
+
url: `${response.siteUrl}/wiki${page._links.webui}`,
|
|
1423
1493
|
lastModified: page.version?.when || ""
|
|
1424
1494
|
}));
|
|
1425
1495
|
if (pages.length === 0) {
|
|
1426
|
-
logger20.warn(
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1496
|
+
logger20.warn(
|
|
1497
|
+
"get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry",
|
|
1498
|
+
{
|
|
1499
|
+
space_key,
|
|
1500
|
+
limit
|
|
1501
|
+
}
|
|
1502
|
+
);
|
|
1430
1503
|
} else {
|
|
1431
1504
|
logger20.info("get-space-pages result", {
|
|
1432
1505
|
space_key,
|
|
@@ -1442,28 +1515,38 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1442
1515
|
pages
|
|
1443
1516
|
});
|
|
1444
1517
|
} catch (error) {
|
|
1518
|
+
const description = request2.describeError(error);
|
|
1445
1519
|
logger20.error("get-space-pages error", {
|
|
1446
1520
|
space_key,
|
|
1447
|
-
error:
|
|
1448
|
-
status:
|
|
1521
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1522
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1449
1523
|
});
|
|
1450
1524
|
return JSON.stringify({
|
|
1451
1525
|
success: false,
|
|
1452
|
-
error:
|
|
1526
|
+
error: description.message
|
|
1453
1527
|
});
|
|
1454
1528
|
}
|
|
1455
1529
|
}).build();
|
|
1456
1530
|
}
|
|
1457
|
-
function createCreateConfluencePageTool(
|
|
1458
|
-
return toolBuilder().name("create-confluence-page").description(
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1531
|
+
function createCreateConfluencePageTool(request2, logger20) {
|
|
1532
|
+
return toolBuilder().name("create-confluence-page").description(
|
|
1533
|
+
"Create a new page in a Confluence space. Requires space key, page title, and content (in HTML storage format)."
|
|
1534
|
+
).category(ToolCategory.WEB).tag("confluence").tag("create").tag("write").usageNotes(
|
|
1535
|
+
"Use this to create new documentation pages. Content should be in Confluence storage format (HTML). Get the space key from list-confluence-spaces first. Be mindful of creating duplicate pages."
|
|
1536
|
+
).requires(["list-confluence-spaces"]).schema(
|
|
1537
|
+
z.object({
|
|
1538
|
+
space_key: z.string().describe("The space key where the page will be created (e.g., 'AI', 'BL3')"),
|
|
1539
|
+
title: z.string().describe("The title of the new page"),
|
|
1540
|
+
content: z.string().describe("The page content in HTML format (Confluence storage format)"),
|
|
1541
|
+
parent_page_id: z.string().optional().describe("Optional parent page ID to create this as a child page")
|
|
1542
|
+
})
|
|
1543
|
+
).implement(async ({ space_key, title, content, parent_page_id }) => {
|
|
1544
|
+
logger20.info("create-confluence-page called", {
|
|
1545
|
+
space_key,
|
|
1546
|
+
title,
|
|
1547
|
+
hasParent: !!parent_page_id
|
|
1548
|
+
});
|
|
1465
1549
|
try {
|
|
1466
|
-
const { ATLASSIAN_SITE_URL } = getAuth();
|
|
1467
1550
|
const pageData = {
|
|
1468
1551
|
type: "page",
|
|
1469
1552
|
title,
|
|
@@ -1478,16 +1561,11 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1478
1561
|
if (parent_page_id) {
|
|
1479
1562
|
pageData.ancestors = [{ id: parent_page_id }];
|
|
1480
1563
|
}
|
|
1481
|
-
const response = await
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
{
|
|
1485
|
-
headers: {
|
|
1486
|
-
Authorization: getAuthHeader2(),
|
|
1487
|
-
"Content-Type": "application/json"
|
|
1488
|
-
}
|
|
1564
|
+
const response = await request2.post("/wiki/rest/api/content", pageData, {
|
|
1565
|
+
headers: {
|
|
1566
|
+
"Content-Type": "application/json"
|
|
1489
1567
|
}
|
|
1490
|
-
);
|
|
1568
|
+
});
|
|
1491
1569
|
logger20.info("create-confluence-page result", {
|
|
1492
1570
|
page_id: response.data.id,
|
|
1493
1571
|
title: response.data.title,
|
|
@@ -1499,45 +1577,45 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1499
1577
|
id: response.data.id,
|
|
1500
1578
|
title: response.data.title,
|
|
1501
1579
|
space: space_key,
|
|
1502
|
-
url: `${
|
|
1580
|
+
url: `${response.siteUrl}/wiki${response.data._links.webui}`,
|
|
1503
1581
|
version: response.data.version?.number || 1
|
|
1504
1582
|
}
|
|
1505
1583
|
});
|
|
1506
1584
|
} catch (error) {
|
|
1585
|
+
const description = request2.describeError(error);
|
|
1507
1586
|
logger20.error("create-confluence-page error", {
|
|
1508
1587
|
space_key,
|
|
1509
1588
|
title,
|
|
1510
|
-
error:
|
|
1511
|
-
status:
|
|
1589
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1590
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1512
1591
|
});
|
|
1513
1592
|
return JSON.stringify({
|
|
1514
1593
|
success: false,
|
|
1515
|
-
error:
|
|
1594
|
+
error: description.message
|
|
1516
1595
|
});
|
|
1517
1596
|
}
|
|
1518
1597
|
}).build();
|
|
1519
1598
|
}
|
|
1520
|
-
function createUpdateConfluencePageTool(
|
|
1521
|
-
return toolBuilder().name("update-confluence-page").description(
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1599
|
+
function createUpdateConfluencePageTool(request2, logger20) {
|
|
1600
|
+
return toolBuilder().name("update-confluence-page").description(
|
|
1601
|
+
"Update an existing Confluence page's content. Requires page ID, new title, and new content."
|
|
1602
|
+
).category(ToolCategory.WEB).tag("confluence").tag("update").tag("write").usageNotes(
|
|
1603
|
+
"Use this to update existing documentation. You must provide the page ID (from search results). The tool will automatically handle version incrementing. Always get the current page content first to avoid overwriting important information."
|
|
1604
|
+
).requires(["get-confluence-page"]).schema(
|
|
1605
|
+
z.object({
|
|
1606
|
+
page_id: z.string().describe("The ID of the page to update"),
|
|
1607
|
+
title: z.string().describe("The new title for the page"),
|
|
1608
|
+
content: z.string().describe("The new content in HTML format (Confluence storage format)")
|
|
1609
|
+
})
|
|
1610
|
+
).implement(async ({ page_id, title, content }) => {
|
|
1526
1611
|
logger20.info("update-confluence-page called", { page_id, title });
|
|
1527
1612
|
try {
|
|
1528
|
-
const
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
{
|
|
1532
|
-
headers: {
|
|
1533
|
-
Authorization: getAuthHeader2()
|
|
1534
|
-
},
|
|
1535
|
-
params: { expand: "version" }
|
|
1536
|
-
}
|
|
1537
|
-
);
|
|
1613
|
+
const getResponse = await request2.get(`/wiki/rest/api/content/${page_id}`, {
|
|
1614
|
+
params: { expand: "version" }
|
|
1615
|
+
});
|
|
1538
1616
|
const currentVersion = getResponse.data.version.number;
|
|
1539
|
-
const updateResponse = await
|
|
1540
|
-
|
|
1617
|
+
const updateResponse = await request2.put(
|
|
1618
|
+
`/wiki/rest/api/content/${page_id}`,
|
|
1541
1619
|
{
|
|
1542
1620
|
type: "page",
|
|
1543
1621
|
title,
|
|
@@ -1551,7 +1629,6 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1551
1629
|
},
|
|
1552
1630
|
{
|
|
1553
1631
|
headers: {
|
|
1554
|
-
Authorization: getAuthHeader2(),
|
|
1555
1632
|
"Content-Type": "application/json"
|
|
1556
1633
|
}
|
|
1557
1634
|
}
|
|
@@ -1567,49 +1644,49 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1567
1644
|
page: {
|
|
1568
1645
|
id: updateResponse.data.id,
|
|
1569
1646
|
title: updateResponse.data.title,
|
|
1570
|
-
url: `${
|
|
1647
|
+
url: `${updateResponse.siteUrl}/wiki${updateResponse.data._links.webui}`,
|
|
1571
1648
|
version: updateResponse.data.version.number,
|
|
1572
1649
|
previousVersion: currentVersion
|
|
1573
1650
|
}
|
|
1574
1651
|
});
|
|
1575
1652
|
} catch (error) {
|
|
1653
|
+
const description = request2.describeError(error);
|
|
1576
1654
|
logger20.error("update-confluence-page error", {
|
|
1577
1655
|
page_id,
|
|
1578
1656
|
title,
|
|
1579
|
-
error:
|
|
1580
|
-
status:
|
|
1657
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1658
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1581
1659
|
});
|
|
1582
1660
|
return JSON.stringify({
|
|
1583
1661
|
success: false,
|
|
1584
|
-
error:
|
|
1662
|
+
error: description.message
|
|
1585
1663
|
});
|
|
1586
1664
|
}
|
|
1587
1665
|
}).build();
|
|
1588
1666
|
}
|
|
1589
|
-
function createArchiveConfluencePageTool(
|
|
1590
|
-
return toolBuilder().name("archive-confluence-page").description(
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1667
|
+
function createArchiveConfluencePageTool(request2, logger20) {
|
|
1668
|
+
return toolBuilder().name("archive-confluence-page").description(
|
|
1669
|
+
"Archive a Confluence page by moving it to trash. The page can be restored by space admins. Note: UI may require a note explaining why the page was archived."
|
|
1670
|
+
).category(ToolCategory.WEB).tag("confluence").tag("archive").tag("delete").usageNotes(
|
|
1671
|
+
"Use this to archive outdated or obsolete documentation. The page is moved to trash, not permanently deleted. Space admins can restore it if needed. Be very careful - only archive pages that are truly obsolete."
|
|
1672
|
+
).conflicts(["create-confluence-page"]).schema(
|
|
1673
|
+
z.object({
|
|
1674
|
+
page_id: z.string().describe("The ID of the page to archive"),
|
|
1675
|
+
reason: z.string().optional().describe("Optional reason for archiving (for audit trail)")
|
|
1676
|
+
})
|
|
1677
|
+
).implement(async ({ page_id, reason }) => {
|
|
1594
1678
|
logger20.info("archive-confluence-page called", {
|
|
1595
1679
|
page_id,
|
|
1596
1680
|
...reason ? { reason } : {}
|
|
1597
1681
|
});
|
|
1598
1682
|
try {
|
|
1599
|
-
const
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
{
|
|
1603
|
-
headers: {
|
|
1604
|
-
Authorization: getAuthHeader2()
|
|
1605
|
-
},
|
|
1606
|
-
params: { expand: "version,body.storage,space" }
|
|
1607
|
-
}
|
|
1608
|
-
);
|
|
1683
|
+
const getResponse = await request2.get(`/wiki/rest/api/content/${page_id}`, {
|
|
1684
|
+
params: { expand: "version,body.storage,space" }
|
|
1685
|
+
});
|
|
1609
1686
|
const currentVersion = getResponse.data.version.number;
|
|
1610
1687
|
const pageData = getResponse.data;
|
|
1611
|
-
await
|
|
1612
|
-
|
|
1688
|
+
await request2.put(
|
|
1689
|
+
`/wiki/rest/api/content/${page_id}`,
|
|
1613
1690
|
{
|
|
1614
1691
|
version: { number: currentVersion + 1 },
|
|
1615
1692
|
title: pageData.title,
|
|
@@ -1620,7 +1697,6 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1620
1697
|
},
|
|
1621
1698
|
{
|
|
1622
1699
|
headers: {
|
|
1623
|
-
Authorization: getAuthHeader2(),
|
|
1624
1700
|
"Content-Type": "application/json"
|
|
1625
1701
|
}
|
|
1626
1702
|
}
|
|
@@ -1643,14 +1719,15 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1643
1719
|
}
|
|
1644
1720
|
});
|
|
1645
1721
|
} catch (error) {
|
|
1722
|
+
const description = request2.describeError(error);
|
|
1646
1723
|
logger20.error("archive-confluence-page error", {
|
|
1647
1724
|
page_id,
|
|
1648
|
-
error:
|
|
1649
|
-
status:
|
|
1725
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1726
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1650
1727
|
});
|
|
1651
1728
|
return JSON.stringify({
|
|
1652
1729
|
success: false,
|
|
1653
|
-
error:
|
|
1730
|
+
error: description.message
|
|
1654
1731
|
});
|
|
1655
1732
|
}
|
|
1656
1733
|
}).build();
|
|
@@ -1659,13 +1736,14 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1659
1736
|
// src/web/confluence/index.ts
|
|
1660
1737
|
var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
|
|
1661
1738
|
var logger2 = createLogger("[tools:confluence]", { level: logLevel2 });
|
|
1662
|
-
var
|
|
1663
|
-
var
|
|
1664
|
-
var
|
|
1665
|
-
var
|
|
1666
|
-
var
|
|
1667
|
-
var
|
|
1668
|
-
var
|
|
1739
|
+
var request = createConfluenceRequest(getConfig);
|
|
1740
|
+
var searchConfluence = createSearchConfluenceTool(request, logger2);
|
|
1741
|
+
var getConfluencePage = createGetConfluencePageTool(request, logger2);
|
|
1742
|
+
var listConfluenceSpaces = createListConfluenceSpacesTool(request, logger2);
|
|
1743
|
+
var getSpacePages = createGetSpacePagesTool(request, logger2);
|
|
1744
|
+
var createConfluencePage = createCreateConfluencePageTool(request, logger2);
|
|
1745
|
+
var updateConfluencePage = createUpdateConfluencePageTool(request, logger2);
|
|
1746
|
+
var archiveConfluencePage = createArchiveConfluencePageTool(request, logger2);
|
|
1669
1747
|
var confluenceTools = [
|
|
1670
1748
|
// Read tools
|
|
1671
1749
|
searchConfluence,
|
|
@@ -1678,22 +1756,17 @@ var confluenceTools = [
|
|
|
1678
1756
|
archiveConfluencePage
|
|
1679
1757
|
];
|
|
1680
1758
|
function createConfluenceTools(config = {}) {
|
|
1681
|
-
const {
|
|
1682
|
-
apiKey,
|
|
1683
|
-
email,
|
|
1684
|
-
siteUrl,
|
|
1685
|
-
logLevel: customLogLevel
|
|
1686
|
-
} = config;
|
|
1759
|
+
const { apiKey, email, siteUrl, logLevel: customLogLevel } = config;
|
|
1687
1760
|
const getConfiguredAuth = createGetConfiguredAuth(apiKey, email, siteUrl);
|
|
1688
|
-
const
|
|
1761
|
+
const request2 = createConfluenceRequest(getConfiguredAuth);
|
|
1689
1762
|
const toolLogger = customLogLevel ? createLogger("tools:confluence", { level: customLogLevel }) : logger2;
|
|
1690
|
-
const searchConfluence2 = createSearchConfluenceTool(
|
|
1691
|
-
const getConfluencePage2 = createGetConfluencePageTool(
|
|
1692
|
-
const listConfluenceSpaces2 = createListConfluenceSpacesTool(
|
|
1693
|
-
const getSpacePages2 = createGetSpacePagesTool(
|
|
1694
|
-
const createConfluencePage2 = createCreateConfluencePageTool(
|
|
1695
|
-
const updateConfluencePage2 = createUpdateConfluencePageTool(
|
|
1696
|
-
const archiveConfluencePage2 = createArchiveConfluencePageTool(
|
|
1763
|
+
const searchConfluence2 = createSearchConfluenceTool(request2, toolLogger);
|
|
1764
|
+
const getConfluencePage2 = createGetConfluencePageTool(request2, toolLogger);
|
|
1765
|
+
const listConfluenceSpaces2 = createListConfluenceSpacesTool(request2, toolLogger);
|
|
1766
|
+
const getSpacePages2 = createGetSpacePagesTool(request2, toolLogger);
|
|
1767
|
+
const createConfluencePage2 = createCreateConfluencePageTool(request2, toolLogger);
|
|
1768
|
+
const updateConfluencePage2 = createUpdateConfluencePageTool(request2, toolLogger);
|
|
1769
|
+
const archiveConfluencePage2 = createArchiveConfluencePageTool(request2, toolLogger);
|
|
1697
1770
|
return {
|
|
1698
1771
|
searchConfluence: searchConfluence2,
|
|
1699
1772
|
getConfluencePage: getConfluencePage2,
|
|
@@ -1884,14 +1957,14 @@ function createJsonStringifyTool(defaultIndent = 2, defaultPretty = false) {
|
|
|
1884
1957
|
};
|
|
1885
1958
|
}).build();
|
|
1886
1959
|
}
|
|
1887
|
-
function
|
|
1960
|
+
function isRecord2(value) {
|
|
1888
1961
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1889
1962
|
}
|
|
1890
1963
|
function getPathValue(current, part) {
|
|
1891
1964
|
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
|
|
1892
1965
|
if (arrayMatch) {
|
|
1893
1966
|
const [, key, index] = arrayMatch;
|
|
1894
|
-
if (!
|
|
1967
|
+
if (!isRecord2(current)) {
|
|
1895
1968
|
return void 0;
|
|
1896
1969
|
}
|
|
1897
1970
|
const candidate = current[key];
|
|
@@ -1900,7 +1973,7 @@ function getPathValue(current, part) {
|
|
|
1900
1973
|
}
|
|
1901
1974
|
return candidate[parseInt(index, 10)];
|
|
1902
1975
|
}
|
|
1903
|
-
if (!
|
|
1976
|
+
if (!isRecord2(current)) {
|
|
1904
1977
|
return void 0;
|
|
1905
1978
|
}
|
|
1906
1979
|
return current[part];
|
|
@@ -2556,7 +2629,7 @@ async function initializeFromEnv() {
|
|
|
2556
2629
|
// src/data/neo4j/embeddings/utils.ts
|
|
2557
2630
|
function withProviderErrorMetadata(error, message) {
|
|
2558
2631
|
const wrappedError = new Error(message);
|
|
2559
|
-
if (!
|
|
2632
|
+
if (!isRecord3(error)) return wrappedError;
|
|
2560
2633
|
if (typeof error.code === "string") wrappedError.code = error.code;
|
|
2561
2634
|
if (error.response !== void 0) wrappedError.response = error.response;
|
|
2562
2635
|
return wrappedError;
|
|
@@ -2569,12 +2642,12 @@ var DEFAULT_RETRY_CONFIG2 = {
|
|
|
2569
2642
|
// 10 seconds
|
|
2570
2643
|
backoffMultiplier: 2
|
|
2571
2644
|
};
|
|
2572
|
-
function
|
|
2645
|
+
function isRecord3(value) {
|
|
2573
2646
|
return typeof value === "object" && value !== null;
|
|
2574
2647
|
}
|
|
2575
2648
|
function isRetryableError2(error) {
|
|
2576
|
-
if (!
|
|
2577
|
-
const response =
|
|
2649
|
+
if (!isRecord3(error)) return false;
|
|
2650
|
+
const response = isRecord3(error.response) ? error.response : void 0;
|
|
2578
2651
|
const status = typeof response?.status === "number" ? response.status : void 0;
|
|
2579
2652
|
const code = typeof error.code === "string" ? error.code : void 0;
|
|
2580
2653
|
const message = typeof error.message === "string" ? error.message : void 0;
|
|
@@ -2700,7 +2773,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2700
2773
|
const modelToUse = model || this.defaultModel;
|
|
2701
2774
|
return retryWithBackoff2(async () => {
|
|
2702
2775
|
try {
|
|
2703
|
-
const response = await
|
|
2776
|
+
const response = await axios5.post(
|
|
2704
2777
|
`${this.baseURL}/embeddings`,
|
|
2705
2778
|
{
|
|
2706
2779
|
input: text,
|
|
@@ -2730,7 +2803,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2730
2803
|
};
|
|
2731
2804
|
} catch (error) {
|
|
2732
2805
|
let message;
|
|
2733
|
-
const response =
|
|
2806
|
+
const response = axios5.isAxiosError(error) ? error.response : void 0;
|
|
2734
2807
|
if (response?.status === 401) {
|
|
2735
2808
|
message = "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys";
|
|
2736
2809
|
} else if (response?.status === 429) {
|
|
@@ -2757,7 +2830,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2757
2830
|
const modelToUse = model || this.defaultModel;
|
|
2758
2831
|
return retryWithBackoff2(async () => {
|
|
2759
2832
|
try {
|
|
2760
|
-
const response = await
|
|
2833
|
+
const response = await axios5.post(
|
|
2761
2834
|
`${this.baseURL}/embeddings`,
|
|
2762
2835
|
{
|
|
2763
2836
|
input: texts,
|
|
@@ -2788,7 +2861,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2788
2861
|
};
|
|
2789
2862
|
} catch (error) {
|
|
2790
2863
|
let message;
|
|
2791
|
-
const response =
|
|
2864
|
+
const response = axios5.isAxiosError(error) ? error.response : void 0;
|
|
2792
2865
|
if (response?.status === 401) {
|
|
2793
2866
|
message = "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys";
|
|
2794
2867
|
} else if (response?.status === 429) {
|
|
@@ -2830,7 +2903,7 @@ var CohereEmbeddingProvider = class {
|
|
|
2830
2903
|
const modelToUse = model || this.defaultModel;
|
|
2831
2904
|
return retryWithBackoff2(async () => {
|
|
2832
2905
|
try {
|
|
2833
|
-
const response = await
|
|
2906
|
+
const response = await axios5.post(
|
|
2834
2907
|
`${this.baseUrl}/embed`,
|
|
2835
2908
|
{
|
|
2836
2909
|
texts,
|
|
@@ -2858,7 +2931,7 @@ var CohereEmbeddingProvider = class {
|
|
|
2858
2931
|
} : void 0
|
|
2859
2932
|
};
|
|
2860
2933
|
} catch (error) {
|
|
2861
|
-
if (
|
|
2934
|
+
if (axios5.isAxiosError(error)) {
|
|
2862
2935
|
const status = error.response?.status;
|
|
2863
2936
|
const providerMessage = error.response?.data?.message || error.message;
|
|
2864
2937
|
let message;
|
|
@@ -2896,7 +2969,7 @@ var HuggingFaceEmbeddingProvider = class {
|
|
|
2896
2969
|
const modelToUse = model || this.defaultModel;
|
|
2897
2970
|
return retryWithBackoff2(async () => {
|
|
2898
2971
|
try {
|
|
2899
|
-
const response = await
|
|
2972
|
+
const response = await axios5.post(
|
|
2900
2973
|
`${this.baseUrl}/${modelToUse}`,
|
|
2901
2974
|
{
|
|
2902
2975
|
inputs: text,
|
|
@@ -2918,7 +2991,7 @@ var HuggingFaceEmbeddingProvider = class {
|
|
|
2918
2991
|
dimensions: embedding.length
|
|
2919
2992
|
};
|
|
2920
2993
|
} catch (error) {
|
|
2921
|
-
if (
|
|
2994
|
+
if (axios5.isAxiosError(error)) {
|
|
2922
2995
|
const status = error.response?.status;
|
|
2923
2996
|
const message = error.response?.data?.error || error.message;
|
|
2924
2997
|
let messageText;
|
|
@@ -2943,7 +3016,7 @@ var HuggingFaceEmbeddingProvider = class {
|
|
|
2943
3016
|
const modelToUse = model || this.defaultModel;
|
|
2944
3017
|
return retryWithBackoff2(async () => {
|
|
2945
3018
|
try {
|
|
2946
|
-
const response = await
|
|
3019
|
+
const response = await axios5.post(
|
|
2947
3020
|
`${this.baseUrl}/${modelToUse}`,
|
|
2948
3021
|
{
|
|
2949
3022
|
inputs: texts,
|
|
@@ -2966,7 +3039,7 @@ var HuggingFaceEmbeddingProvider = class {
|
|
|
2966
3039
|
dimensions
|
|
2967
3040
|
};
|
|
2968
3041
|
} catch (error) {
|
|
2969
|
-
if (
|
|
3042
|
+
if (axios5.isAxiosError(error)) {
|
|
2970
3043
|
const status = error.response?.status;
|
|
2971
3044
|
const message = error.response?.data?.error || error.message;
|
|
2972
3045
|
let messageText;
|
|
@@ -3015,7 +3088,7 @@ var VoyageEmbeddingProvider = class {
|
|
|
3015
3088
|
const modelToUse = model || this.defaultModel;
|
|
3016
3089
|
return retryWithBackoff2(async () => {
|
|
3017
3090
|
try {
|
|
3018
|
-
const response = await
|
|
3091
|
+
const response = await axios5.post(
|
|
3019
3092
|
`${this.baseUrl}/embeddings`,
|
|
3020
3093
|
{
|
|
3021
3094
|
input: texts,
|
|
@@ -3042,7 +3115,7 @@ var VoyageEmbeddingProvider = class {
|
|
|
3042
3115
|
} : void 0
|
|
3043
3116
|
};
|
|
3044
3117
|
} catch (error) {
|
|
3045
|
-
if (
|
|
3118
|
+
if (axios5.isAxiosError(error)) {
|
|
3046
3119
|
const status = error.response?.status;
|
|
3047
3120
|
const message = error.response?.data?.message || error.message;
|
|
3048
3121
|
let messageText;
|
|
@@ -3079,7 +3152,7 @@ var OllamaEmbeddingProvider = class {
|
|
|
3079
3152
|
const modelToUse = model || this.defaultModel;
|
|
3080
3153
|
return retryWithBackoff2(async () => {
|
|
3081
3154
|
try {
|
|
3082
|
-
const response = await
|
|
3155
|
+
const response = await axios5.post(
|
|
3083
3156
|
`${this.baseUrl}/api/embeddings`,
|
|
3084
3157
|
{
|
|
3085
3158
|
model: modelToUse,
|
|
@@ -3098,7 +3171,7 @@ var OllamaEmbeddingProvider = class {
|
|
|
3098
3171
|
dimensions: embedding.length
|
|
3099
3172
|
};
|
|
3100
3173
|
} catch (error) {
|
|
3101
|
-
if (
|
|
3174
|
+
if (axios5.isAxiosError(error)) {
|
|
3102
3175
|
const status = error.response?.status;
|
|
3103
3176
|
const message = error.response?.data?.error || error.message;
|
|
3104
3177
|
let messageText;
|
|
@@ -3491,6 +3564,16 @@ function formatResults(records) {
|
|
|
3491
3564
|
});
|
|
3492
3565
|
}
|
|
3493
3566
|
|
|
3567
|
+
// src/data/neo4j/tools/session-owner.ts
|
|
3568
|
+
async function withNeo4jSession(database, operation) {
|
|
3569
|
+
const session = neo4jPool.getSession(database);
|
|
3570
|
+
try {
|
|
3571
|
+
return await operation(session);
|
|
3572
|
+
} finally {
|
|
3573
|
+
await session.close();
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3494
3577
|
// src/data/neo4j/tools/neo4j-query.ts
|
|
3495
3578
|
var logger4 = createLogger("agentforge:tools:neo4j:query");
|
|
3496
3579
|
function createNeo4jQueryTool() {
|
|
@@ -3511,8 +3594,7 @@ function createNeo4jQueryTool() {
|
|
|
3511
3594
|
...input.database ? { database: input.database } : {}
|
|
3512
3595
|
});
|
|
3513
3596
|
try {
|
|
3514
|
-
|
|
3515
|
-
try {
|
|
3597
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3516
3598
|
const result = await session.run(input.cypher, input.parameters || {});
|
|
3517
3599
|
const formattedResults = formatResults(result.records);
|
|
3518
3600
|
const duration = Date.now() - startTime;
|
|
@@ -3541,9 +3623,7 @@ function createNeo4jQueryTool() {
|
|
|
3541
3623
|
}
|
|
3542
3624
|
}
|
|
3543
3625
|
};
|
|
3544
|
-
}
|
|
3545
|
-
await session.close();
|
|
3546
|
-
}
|
|
3626
|
+
});
|
|
3547
3627
|
} catch (error) {
|
|
3548
3628
|
const duration = Date.now() - startTime;
|
|
3549
3629
|
const errorMessage = error instanceof Error ? error.message : "Failed to execute query";
|
|
@@ -3571,8 +3651,7 @@ function createNeo4jGetSchemaTool() {
|
|
|
3571
3651
|
};
|
|
3572
3652
|
}
|
|
3573
3653
|
try {
|
|
3574
|
-
|
|
3575
|
-
try {
|
|
3654
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3576
3655
|
const labelsResult = await session.run("CALL db.labels()");
|
|
3577
3656
|
const nodeLabels = labelsResult.records.map((r) => r.get("label"));
|
|
3578
3657
|
const relTypesResult = await session.run("CALL db.relationshipTypes()");
|
|
@@ -3612,9 +3691,7 @@ function createNeo4jGetSchemaTool() {
|
|
|
3612
3691
|
totalIndexes: indexes.length
|
|
3613
3692
|
}
|
|
3614
3693
|
};
|
|
3615
|
-
}
|
|
3616
|
-
await session.close();
|
|
3617
|
-
}
|
|
3694
|
+
});
|
|
3618
3695
|
} catch (error) {
|
|
3619
3696
|
return {
|
|
3620
3697
|
success: false,
|
|
@@ -3693,8 +3770,7 @@ function createNeo4jFindNodesTool() {
|
|
|
3693
3770
|
}
|
|
3694
3771
|
try {
|
|
3695
3772
|
const safeLabel = validateLabel(input.label);
|
|
3696
|
-
|
|
3697
|
-
try {
|
|
3773
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3698
3774
|
let cypher = `MATCH (n:${safeLabel})`;
|
|
3699
3775
|
let parameters = {};
|
|
3700
3776
|
if (input.properties && Object.keys(input.properties).length > 0) {
|
|
@@ -3716,9 +3792,7 @@ function createNeo4jFindNodesTool() {
|
|
|
3716
3792
|
limit: input.limit
|
|
3717
3793
|
}
|
|
3718
3794
|
};
|
|
3719
|
-
}
|
|
3720
|
-
await session.close();
|
|
3721
|
-
}
|
|
3795
|
+
});
|
|
3722
3796
|
} catch (error) {
|
|
3723
3797
|
return {
|
|
3724
3798
|
success: false,
|
|
@@ -3744,8 +3818,7 @@ function createNeo4jTraverseTool() {
|
|
|
3744
3818
|
try {
|
|
3745
3819
|
const safeDirection = validateDirection(input.direction || "outgoing");
|
|
3746
3820
|
const safeRelType = input.relationshipType ? validateRelationshipType(input.relationshipType) : null;
|
|
3747
|
-
|
|
3748
|
-
try {
|
|
3821
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3749
3822
|
let relPattern = "";
|
|
3750
3823
|
if (safeDirection === "OUTGOING") {
|
|
3751
3824
|
relPattern = safeRelType ? `-[r:${safeRelType}*1..${input.maxDepth}]->` : `-[r*1..${input.maxDepth}]->`;
|
|
@@ -3782,9 +3855,7 @@ function createNeo4jTraverseTool() {
|
|
|
3782
3855
|
maxDepth: input.maxDepth
|
|
3783
3856
|
}
|
|
3784
3857
|
};
|
|
3785
|
-
}
|
|
3786
|
-
await session.close();
|
|
3787
|
-
}
|
|
3858
|
+
});
|
|
3788
3859
|
} catch (error) {
|
|
3789
3860
|
return {
|
|
3790
3861
|
success: false,
|
|
@@ -3809,8 +3880,7 @@ function createNeo4jVectorSearchTool() {
|
|
|
3809
3880
|
};
|
|
3810
3881
|
}
|
|
3811
3882
|
try {
|
|
3812
|
-
|
|
3813
|
-
try {
|
|
3883
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3814
3884
|
const cypher = `
|
|
3815
3885
|
CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
|
|
3816
3886
|
YIELD node, score
|
|
@@ -3837,9 +3907,7 @@ function createNeo4jVectorSearchTool() {
|
|
|
3837
3907
|
limit: input.limit
|
|
3838
3908
|
}
|
|
3839
3909
|
};
|
|
3840
|
-
}
|
|
3841
|
-
await session.close();
|
|
3842
|
-
}
|
|
3910
|
+
});
|
|
3843
3911
|
} catch (error) {
|
|
3844
3912
|
const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search";
|
|
3845
3913
|
let helpText = "";
|
|
@@ -3885,8 +3953,7 @@ function createNeo4jVectorSearchWithEmbeddingTool() {
|
|
|
3885
3953
|
});
|
|
3886
3954
|
try {
|
|
3887
3955
|
const embeddingResult = await embeddingManager.generateEmbedding(input.queryText, input.model);
|
|
3888
|
-
|
|
3889
|
-
try {
|
|
3956
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3890
3957
|
const cypher = `
|
|
3891
3958
|
CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
|
|
3892
3959
|
YIELD node, score
|
|
@@ -3928,9 +3995,7 @@ function createNeo4jVectorSearchWithEmbeddingTool() {
|
|
|
3928
3995
|
usage: embeddingResult.usage
|
|
3929
3996
|
}
|
|
3930
3997
|
};
|
|
3931
|
-
}
|
|
3932
|
-
await session.close();
|
|
3933
|
-
}
|
|
3998
|
+
});
|
|
3934
3999
|
} catch (error) {
|
|
3935
4000
|
const duration = Date.now() - startTime;
|
|
3936
4001
|
const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search with embedding";
|
|
@@ -3986,8 +4051,7 @@ function createNeo4jCreateNodeWithEmbeddingTool() {
|
|
|
3986
4051
|
const embeddingResult = await embeddingManager.generateEmbedding(textToEmbed, input.model);
|
|
3987
4052
|
const safeLabel = validateLabel(input.label);
|
|
3988
4053
|
const safeEmbeddingProp = validatePropertyKey(input.embeddingProperty || "embedding");
|
|
3989
|
-
|
|
3990
|
-
try {
|
|
4054
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3991
4055
|
const allProperties = {
|
|
3992
4056
|
...input.properties
|
|
3993
4057
|
};
|
|
@@ -4021,9 +4085,7 @@ function createNeo4jCreateNodeWithEmbeddingTool() {
|
|
|
4021
4085
|
},
|
|
4022
4086
|
message: `Created node with label '${input.label}' and ${embeddingResult.dimensions}-dimensional embedding`
|
|
4023
4087
|
};
|
|
4024
|
-
}
|
|
4025
|
-
await session.close();
|
|
4026
|
-
}
|
|
4088
|
+
});
|
|
4027
4089
|
} catch (error) {
|
|
4028
4090
|
const errorMessage = error instanceof Error ? error.message : "Failed to create node with embedding";
|
|
4029
4091
|
let helpText = "";
|
|
@@ -10310,49 +10372,57 @@ var fileSearchSchema = z.object({
|
|
|
10310
10372
|
recursive: z.boolean().default(true).describe("Search in subdirectories"),
|
|
10311
10373
|
caseSensitive: z.boolean().default(false).describe("Case-sensitive pattern matching")
|
|
10312
10374
|
});
|
|
10375
|
+
async function* traverseDirectory(directory, recursive) {
|
|
10376
|
+
const entries = await promises.readdir(directory, { withFileTypes: true });
|
|
10377
|
+
for (const entry of entries) {
|
|
10378
|
+
const fullPath = path.join(directory, entry.name);
|
|
10379
|
+
const isDirectory = entry.isDirectory();
|
|
10380
|
+
yield {
|
|
10381
|
+
name: entry.name,
|
|
10382
|
+
fullPath,
|
|
10383
|
+
isFile: entry.isFile(),
|
|
10384
|
+
isDirectory
|
|
10385
|
+
};
|
|
10386
|
+
if (recursive && isDirectory) {
|
|
10387
|
+
yield* traverseDirectory(fullPath, true);
|
|
10388
|
+
}
|
|
10389
|
+
}
|
|
10390
|
+
}
|
|
10391
|
+
|
|
10392
|
+
// src/file/directory/tools/directory-list.ts
|
|
10313
10393
|
function createDirectoryListTool(defaultRecursive = false, defaultIncludeDetails = false, policy = DEFAULT_FILE_SYSTEM_POLICY) {
|
|
10314
10394
|
return toolBuilder().name("directory-list").description("List all files and directories in a directory. Can optionally include file details and filter by extension.").category(ToolCategory.FILE_SYSTEM).tags(["directory", "list", "files", "filesystem"]).schema(directoryListSchema).implementSafe(async (input) => {
|
|
10315
10395
|
const safePath = await policy.resolvePath(input.path, "directory listing");
|
|
10316
10396
|
const includeDetails = input.includeDetails ?? defaultIncludeDetails;
|
|
10317
|
-
const
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
|
|
10324
|
-
if (!entry.isDirectory() || !recursive2) {
|
|
10325
|
-
continue;
|
|
10326
|
-
}
|
|
10327
|
-
}
|
|
10328
|
-
if (includeDetails) {
|
|
10329
|
-
const stats = await promises.lstat(fullPath);
|
|
10330
|
-
files2.push({
|
|
10331
|
-
name: entry.name,
|
|
10332
|
-
path: relativePath,
|
|
10333
|
-
fullPath,
|
|
10334
|
-
isFile: entry.isFile(),
|
|
10335
|
-
isDirectory: entry.isDirectory(),
|
|
10336
|
-
size: stats.size,
|
|
10337
|
-
modified: stats.mtime.toISOString()
|
|
10338
|
-
});
|
|
10339
|
-
} else {
|
|
10340
|
-
files2.push({
|
|
10341
|
-
name: entry.name,
|
|
10342
|
-
path: relativePath,
|
|
10343
|
-
isFile: entry.isFile(),
|
|
10344
|
-
isDirectory: entry.isDirectory()
|
|
10345
|
-
});
|
|
10346
|
-
}
|
|
10347
|
-
if (recursive2 && entry.isDirectory()) {
|
|
10348
|
-
const subFiles = await listFiles(fullPath, true);
|
|
10349
|
-
files2.push(...subFiles);
|
|
10397
|
+
const recursive = input.recursive ?? defaultRecursive;
|
|
10398
|
+
const files = [];
|
|
10399
|
+
for await (const entry of traverseDirectory(safePath, recursive)) {
|
|
10400
|
+
const relativePath = path.relative(safePath, entry.fullPath);
|
|
10401
|
+
if (input.extension && !entry.name.endsWith(input.extension)) {
|
|
10402
|
+
if (!entry.isDirectory || !recursive) {
|
|
10403
|
+
continue;
|
|
10350
10404
|
}
|
|
10351
10405
|
}
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10406
|
+
if (includeDetails) {
|
|
10407
|
+
const stats = await promises.lstat(entry.fullPath);
|
|
10408
|
+
files.push({
|
|
10409
|
+
name: entry.name,
|
|
10410
|
+
path: relativePath,
|
|
10411
|
+
fullPath: entry.fullPath,
|
|
10412
|
+
isFile: entry.isFile,
|
|
10413
|
+
isDirectory: entry.isDirectory,
|
|
10414
|
+
size: stats.size,
|
|
10415
|
+
modified: stats.mtime.toISOString()
|
|
10416
|
+
});
|
|
10417
|
+
} else {
|
|
10418
|
+
files.push({
|
|
10419
|
+
name: entry.name,
|
|
10420
|
+
path: relativePath,
|
|
10421
|
+
isFile: entry.isFile,
|
|
10422
|
+
isDirectory: entry.isDirectory
|
|
10423
|
+
});
|
|
10424
|
+
}
|
|
10425
|
+
}
|
|
10356
10426
|
const result = {
|
|
10357
10427
|
path: input.path,
|
|
10358
10428
|
files,
|
|
@@ -10388,24 +10458,14 @@ function createFileSearchTool(defaultRecursive = true, defaultCaseSensitive = fa
|
|
|
10388
10458
|
const recursive = input.recursive ?? defaultRecursive;
|
|
10389
10459
|
const caseSensitive = input.caseSensitive ?? defaultCaseSensitive;
|
|
10390
10460
|
const safeDirectory = await policy.resolvePath(input.directory, "file search");
|
|
10391
|
-
const
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
|
|
10396
|
-
|
|
10397
|
-
const fullPath = path.join(dir, entry.name);
|
|
10398
|
-
if (entry.isFile() && regex.test(entry.name)) {
|
|
10399
|
-
matches2.push(fullPath);
|
|
10400
|
-
}
|
|
10401
|
-
if (recursive && entry.isDirectory()) {
|
|
10402
|
-
const subMatches = await searchFiles(fullPath);
|
|
10403
|
-
matches2.push(...subMatches);
|
|
10404
|
-
}
|
|
10461
|
+
const regexPattern = input.pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
|
|
10462
|
+
const regex = new RegExp(`^${regexPattern}$`, caseSensitive ? "" : "i");
|
|
10463
|
+
const matches = [];
|
|
10464
|
+
for await (const entry of traverseDirectory(safeDirectory, recursive)) {
|
|
10465
|
+
if (entry.isFile && regex.test(entry.name)) {
|
|
10466
|
+
matches.push(entry.fullPath);
|
|
10405
10467
|
}
|
|
10406
|
-
|
|
10407
|
-
};
|
|
10408
|
-
const matches = await searchFiles(safeDirectory);
|
|
10468
|
+
}
|
|
10409
10469
|
return {
|
|
10410
10470
|
directory: input.directory,
|
|
10411
10471
|
pattern: input.pattern,
|
|
@@ -11367,11 +11427,11 @@ var AskHumanInputSchema = z.object({
|
|
|
11367
11427
|
});
|
|
11368
11428
|
var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
|
|
11369
11429
|
var logger19 = createLogger("agentforge:tools:agent:ask-human", { level: logLevel3 });
|
|
11370
|
-
function
|
|
11430
|
+
function isRecord4(value) {
|
|
11371
11431
|
return typeof value === "object" && value !== null;
|
|
11372
11432
|
}
|
|
11373
11433
|
function resolveInterrupt(module) {
|
|
11374
|
-
if (!
|
|
11434
|
+
if (!isRecord4(module)) {
|
|
11375
11435
|
return void 0;
|
|
11376
11436
|
}
|
|
11377
11437
|
const candidate = module.interrupt;
|