@openontology/opencode-palantir 0.1.3 → 0.1.4
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.js +232 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8068,36 +8068,254 @@ var plugin = async (input) => {
|
|
|
8068
8068
|
function pushText(output, text) {
|
|
8069
8069
|
output.parts.push({ type: "text", text });
|
|
8070
8070
|
}
|
|
8071
|
+
function toPathname(inputUrl) {
|
|
8072
|
+
const trimmed = inputUrl.trim();
|
|
8073
|
+
if (trimmed.length === 0)
|
|
8074
|
+
return "";
|
|
8075
|
+
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
|
|
8076
|
+
try {
|
|
8077
|
+
return new URL(trimmed).pathname;
|
|
8078
|
+
} catch {}
|
|
8079
|
+
}
|
|
8080
|
+
if (trimmed.startsWith("www.")) {
|
|
8081
|
+
try {
|
|
8082
|
+
return new URL(`https://${trimmed}`).pathname;
|
|
8083
|
+
} catch {}
|
|
8084
|
+
}
|
|
8085
|
+
const noQueryOrHash = trimmed.split("#")[0].split("?")[0];
|
|
8086
|
+
if (noQueryOrHash.startsWith("/"))
|
|
8087
|
+
return noQueryOrHash;
|
|
8088
|
+
return `/${noQueryOrHash}`;
|
|
8089
|
+
}
|
|
8090
|
+
function makeUrlCandidates(inputUrl) {
|
|
8091
|
+
const raw = inputUrl.trim();
|
|
8092
|
+
const pathOnly = toPathname(raw);
|
|
8093
|
+
const candidates = new Set;
|
|
8094
|
+
function addVariant(u) {
|
|
8095
|
+
const v = u.trim();
|
|
8096
|
+
if (v.length === 0)
|
|
8097
|
+
return;
|
|
8098
|
+
candidates.add(v);
|
|
8099
|
+
if (v.endsWith("/") && v.length > 1)
|
|
8100
|
+
candidates.add(v.slice(0, -1));
|
|
8101
|
+
else
|
|
8102
|
+
candidates.add(`${v}/`);
|
|
8103
|
+
}
|
|
8104
|
+
addVariant(raw);
|
|
8105
|
+
addVariant(pathOnly);
|
|
8106
|
+
if (pathOnly.startsWith("/docs/")) {
|
|
8107
|
+
addVariant(pathOnly.replace(/^\/docs(?=\/)/, ""));
|
|
8108
|
+
} else if (pathOnly.startsWith("/foundry/") || pathOnly.startsWith("/apollo/") || pathOnly.startsWith("/gotham/")) {
|
|
8109
|
+
addVariant(`/docs${pathOnly}`);
|
|
8110
|
+
}
|
|
8111
|
+
return Array.from(candidates);
|
|
8112
|
+
}
|
|
8113
|
+
function parseScope(rawScope) {
|
|
8114
|
+
if (rawScope === undefined)
|
|
8115
|
+
return "foundry";
|
|
8116
|
+
if (rawScope === "foundry" || rawScope === "apollo" || rawScope === "gotham" || rawScope === "all") {
|
|
8117
|
+
return rawScope;
|
|
8118
|
+
}
|
|
8119
|
+
return null;
|
|
8120
|
+
}
|
|
8121
|
+
function isInScope(pageUrl, scope) {
|
|
8122
|
+
if (scope === "all")
|
|
8123
|
+
return true;
|
|
8124
|
+
const path5 = toPathname(pageUrl);
|
|
8125
|
+
return path5.startsWith(`/${scope}/`) || path5.startsWith(`/docs/${scope}/`);
|
|
8126
|
+
}
|
|
8127
|
+
function tokenizeQuery(query) {
|
|
8128
|
+
const tokens = query.toLowerCase().trim().split(/[\s/._-]+/g).map((t) => t.trim()).filter((t) => t.length > 0);
|
|
8129
|
+
return Array.from(new Set(tokens));
|
|
8130
|
+
}
|
|
8131
|
+
function scorePageMatch(page, query) {
|
|
8132
|
+
const q = query.toLowerCase().trim();
|
|
8133
|
+
if (q.length === 0)
|
|
8134
|
+
return 0;
|
|
8135
|
+
const path5 = toPathname(page.url).toLowerCase();
|
|
8136
|
+
const title = page.title.toLowerCase();
|
|
8137
|
+
if (path5 === q)
|
|
8138
|
+
return 2000;
|
|
8139
|
+
if (path5 === toPathname(q).toLowerCase())
|
|
8140
|
+
return 2000;
|
|
8141
|
+
if (path5.includes(q))
|
|
8142
|
+
return 1200;
|
|
8143
|
+
if (title.includes(q))
|
|
8144
|
+
return 1000;
|
|
8145
|
+
const tokens = tokenizeQuery(q);
|
|
8146
|
+
if (tokens.length === 0)
|
|
8147
|
+
return 0;
|
|
8148
|
+
let score = 0;
|
|
8149
|
+
for (const t of tokens) {
|
|
8150
|
+
if (title.includes(t))
|
|
8151
|
+
score += 40;
|
|
8152
|
+
if (path5.includes(t))
|
|
8153
|
+
score += 30;
|
|
8154
|
+
}
|
|
8155
|
+
if (path5.startsWith(q))
|
|
8156
|
+
score += 100;
|
|
8157
|
+
if (title.startsWith(q))
|
|
8158
|
+
score += 100;
|
|
8159
|
+
return score;
|
|
8160
|
+
}
|
|
8071
8161
|
return {
|
|
8072
8162
|
tool: {
|
|
8073
8163
|
get_doc_page: tool({
|
|
8074
|
-
description: "Retrieve a Palantir
|
|
8164
|
+
description: "Retrieve a Palantir documentation page. Provide either a URL path (preferred) or a free-text query; the tool will handle common URL variants (full URLs, missing /docs prefix, trailing slashes).",
|
|
8075
8165
|
args: {
|
|
8076
|
-
url: tool.schema.string().describe("
|
|
8166
|
+
url: tool.schema.string().optional().describe("Doc URL path or full URL, e.g. /foundry/compute-modules/overview/"),
|
|
8167
|
+
query: tool.schema.string().optional().describe('Free-text query to find the most relevant page, e.g. "compute modules".'),
|
|
8168
|
+
scope: tool.schema.enum(["foundry", "apollo", "gotham", "all"]).optional().describe("Scope to search within when using query or fuzzy matching (default: foundry).")
|
|
8077
8169
|
},
|
|
8078
8170
|
async execute(args) {
|
|
8079
8171
|
if (!await dbExists())
|
|
8080
8172
|
return NO_DB_MESSAGE;
|
|
8173
|
+
const scope = parseScope(args.scope);
|
|
8174
|
+
if (!scope) {
|
|
8175
|
+
return [
|
|
8176
|
+
"[ERROR] Invalid scope. Must be one of: foundry, apollo, gotham, all.",
|
|
8177
|
+
'Example: get_doc_page with { "query": "compute modules", "scope": "foundry" }'
|
|
8178
|
+
].join(`
|
|
8179
|
+
`);
|
|
8180
|
+
}
|
|
8181
|
+
const rawUrl = args.url;
|
|
8182
|
+
const rawQuery = args.query;
|
|
8183
|
+
const urlInput = typeof rawUrl === "string" ? rawUrl.trim() : null;
|
|
8184
|
+
const queryInput = typeof rawQuery === "string" ? rawQuery.trim() : null;
|
|
8185
|
+
if ((!urlInput || urlInput.length === 0) && (!queryInput || queryInput.length === 0)) {
|
|
8186
|
+
return [
|
|
8187
|
+
'[ERROR] Missing input. Provide either "url" or "query".',
|
|
8188
|
+
'Example: get_doc_page with { "url": "/foundry/compute-modules/overview/" }',
|
|
8189
|
+
'Example: get_doc_page with { "query": "compute modules", "scope": "foundry" }'
|
|
8190
|
+
].join(`
|
|
8191
|
+
`);
|
|
8192
|
+
}
|
|
8081
8193
|
const db = await getDb();
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8194
|
+
if (urlInput && urlInput.length > 0) {
|
|
8195
|
+
const candidates = makeUrlCandidates(urlInput);
|
|
8196
|
+
for (const c of candidates) {
|
|
8197
|
+
const page = await getPage(db, c);
|
|
8198
|
+
if (page)
|
|
8199
|
+
return page.content;
|
|
8200
|
+
}
|
|
8201
|
+
}
|
|
8202
|
+
const q = (queryInput && queryInput.length > 0 ? queryInput : urlInput) ?? "";
|
|
8203
|
+
const pages = getAllPages(db);
|
|
8204
|
+
const ranked = pages.filter((p) => isInScope(p.url, scope)).map((p) => ({ page: p, score: scorePageMatch(p, q) })).filter((r) => r.score > 0).sort((a, b) => {
|
|
8205
|
+
if (b.score !== a.score)
|
|
8206
|
+
return b.score - a.score;
|
|
8207
|
+
return toPathname(a.page.url).localeCompare(toPathname(b.page.url));
|
|
8208
|
+
}).slice(0, 5);
|
|
8209
|
+
if (ranked.length === 0) {
|
|
8210
|
+
return `Page not found: ${urlInput ?? q}`;
|
|
8211
|
+
}
|
|
8212
|
+
const best = ranked[0];
|
|
8213
|
+
const bestPage = await getPage(db, best.page.url);
|
|
8214
|
+
if (bestPage) {
|
|
8215
|
+
const strong = best.score >= 200 || toPathname(best.page.url).toLowerCase() === toPathname(q).toLowerCase() || tokenizeQuery(q).every((t) => (best.page.title + " " + toPathname(best.page.url)).toLowerCase().includes(t));
|
|
8216
|
+
if (strong) {
|
|
8217
|
+
return `Matched: ${best.page.title} (${best.page.url})
|
|
8218
|
+
|
|
8219
|
+
${bestPage.content}`;
|
|
8220
|
+
}
|
|
8221
|
+
}
|
|
8222
|
+
const suggestions = ranked.map((r) => `- ${r.page.title} (${r.page.url})`).join(`
|
|
8223
|
+
`);
|
|
8224
|
+
return [
|
|
8225
|
+
`Page not found: ${urlInput ?? q}`,
|
|
8226
|
+
"",
|
|
8227
|
+
"Top matches:",
|
|
8228
|
+
suggestions,
|
|
8229
|
+
"",
|
|
8230
|
+
"Tip: call get_doc_page with an exact URL from the list above."
|
|
8231
|
+
].join(`
|
|
8232
|
+
`);
|
|
8086
8233
|
}
|
|
8087
8234
|
}),
|
|
8088
8235
|
list_all_docs: tool({
|
|
8089
|
-
description: "List
|
|
8090
|
-
args: {
|
|
8091
|
-
|
|
8236
|
+
description: "List available Palantir documentation pages with their URLs and titles. Supports pagination and optional scope filtering (default: foundry). Use this to discover what documentation is available.",
|
|
8237
|
+
args: {
|
|
8238
|
+
limit: tool.schema.number().int().min(1).max(200).optional().describe("Max results to return (default: 50, max: 200)."),
|
|
8239
|
+
offset: tool.schema.number().int().min(0).optional().describe("Zero-based offset into the filtered, deterministic listing (default: 0)."),
|
|
8240
|
+
scope: tool.schema.enum(["foundry", "apollo", "gotham", "all"]).optional().describe("Doc scope filter by URL prefix /<scope>/ (default: foundry)."),
|
|
8241
|
+
query: tool.schema.string().optional().describe("Optional query to filter/rank results by title/URL (case-insensitive).")
|
|
8242
|
+
},
|
|
8243
|
+
async execute(args) {
|
|
8092
8244
|
if (!await dbExists())
|
|
8093
8245
|
return NO_DB_MESSAGE;
|
|
8246
|
+
const scope = parseScope(args.scope);
|
|
8247
|
+
if (!scope) {
|
|
8248
|
+
return [
|
|
8249
|
+
"[ERROR] Invalid scope. Must be one of: foundry, apollo, gotham, all.",
|
|
8250
|
+
'Example: list_all_docs with { "scope": "foundry", "offset": 0, "limit": 50 }'
|
|
8251
|
+
].join(`
|
|
8252
|
+
`);
|
|
8253
|
+
}
|
|
8254
|
+
const rawLimit = args.limit;
|
|
8255
|
+
const limit = rawLimit === undefined ? 50 : rawLimit;
|
|
8256
|
+
if (!Number.isFinite(limit) || !Number.isInteger(limit) || limit < 1 || limit > 200) {
|
|
8257
|
+
return [
|
|
8258
|
+
"[ERROR] Invalid limit. Must be an integer between 1 and 200.",
|
|
8259
|
+
'Example: list_all_docs with { "scope": "foundry", "offset": 0, "limit": 50 }'
|
|
8260
|
+
].join(`
|
|
8261
|
+
`);
|
|
8262
|
+
}
|
|
8263
|
+
const rawOffset = args.offset;
|
|
8264
|
+
const offset = rawOffset === undefined ? 0 : rawOffset;
|
|
8265
|
+
if (!Number.isFinite(offset) || !Number.isInteger(offset) || offset < 0) {
|
|
8266
|
+
return [
|
|
8267
|
+
"[ERROR] Invalid offset. Must be an integer >= 0.",
|
|
8268
|
+
'Example: list_all_docs with { "scope": "foundry", "offset": 0, "limit": 50 }'
|
|
8269
|
+
].join(`
|
|
8270
|
+
`);
|
|
8271
|
+
}
|
|
8272
|
+
const rawQuery = args.query;
|
|
8273
|
+
const query = typeof rawQuery === "string" ? rawQuery.trim() : null;
|
|
8274
|
+
if (query && query.length > 200) {
|
|
8275
|
+
return "[ERROR] Query is too long. Please use 200 characters or fewer.";
|
|
8276
|
+
}
|
|
8094
8277
|
const db = await getDb();
|
|
8095
|
-
const pages = getAllPages(db);
|
|
8096
|
-
const
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8278
|
+
const pages = getAllPages(db).slice();
|
|
8279
|
+
const scoped = pages.filter((p) => isInScope(p.url, scope));
|
|
8280
|
+
const filteredWithScores = query && query.length > 0 ? scoped.map((p) => ({ page: p, score: scorePageMatch(p, query) })).filter((r) => r.score > 0).sort((a, b) => {
|
|
8281
|
+
if (b.score !== a.score)
|
|
8282
|
+
return b.score - a.score;
|
|
8283
|
+
return toPathname(a.page.url).localeCompare(toPathname(b.page.url));
|
|
8284
|
+
}) : scoped.map((p) => ({ page: p, score: 0 })).sort((a, b) => toPathname(a.page.url).localeCompare(toPathname(b.page.url)));
|
|
8285
|
+
const total = filteredWithScores.length;
|
|
8286
|
+
if (offset >= total) {
|
|
8287
|
+
const safeOffset = Math.max(0, total - limit);
|
|
8288
|
+
return [
|
|
8289
|
+
`Available Palantir Documentation Pages`,
|
|
8290
|
+
`scope=${scope} query=${query ?? ""} total=${total} returned=0 offset=${offset} limit=${limit}`,
|
|
8291
|
+
"",
|
|
8292
|
+
`Offset ${offset} is beyond total ${total}.`,
|
|
8293
|
+
`Try: list_all_docs with { "scope": "${scope}", "offset": ${safeOffset}, "limit": ${limit} }`
|
|
8294
|
+
].join(`
|
|
8295
|
+
`);
|
|
8296
|
+
}
|
|
8297
|
+
const page = filteredWithScores.slice(offset, offset + limit).map((r) => r.page);
|
|
8298
|
+
const lines = page.map((p) => `- ${p.title} (${p.url})`);
|
|
8299
|
+
const returned = page.length;
|
|
8300
|
+
const nextOffset = offset + returned;
|
|
8301
|
+
const hasMore = nextOffset < total;
|
|
8302
|
+
const header = [
|
|
8303
|
+
`Available Palantir Documentation Pages`,
|
|
8304
|
+
`scope=${scope} query=${query ?? ""} total=${total} returned=${returned} offset=${offset} limit=${limit}`,
|
|
8305
|
+
""
|
|
8306
|
+
].join(`
|
|
8307
|
+
`);
|
|
8308
|
+
if (!hasMore) {
|
|
8309
|
+
return `${header}${lines.join(`
|
|
8100
8310
|
`)}`;
|
|
8311
|
+
}
|
|
8312
|
+
return [
|
|
8313
|
+
header + lines.join(`
|
|
8314
|
+
`),
|
|
8315
|
+
"",
|
|
8316
|
+
`Next: call list_all_docs with { "scope": "${scope}", "offset": ${nextOffset}, "limit": ${limit} }`
|
|
8317
|
+
].join(`
|
|
8318
|
+
`);
|
|
8101
8319
|
}
|
|
8102
8320
|
})
|
|
8103
8321
|
},
|