Files
orquestra/platforms/pi/extensions/orquestra-web-fetch.ts
rikrdo 634525fa21 feat: Orquestra - sequential orchestration runtime
- Context isolation: fresh Pi process per stage (run_stage.py)
- Gate enforcement: blocks close without approved gates
- Auto commit/push on feature close (close_feature.py)
- Write restrictions: only allowed directories (ALLOWED_WRITE_DIRS)
- Pi extension: orquestra-status with /orquestra-stage command
- Documentation: context-handoff.md, updated README
- Scripts: agent_status.py, verify.sh, install.sh updated
2026-08-17 07:39:03 +02:00

57 lines
2.0 KiB
TypeScript

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
function textFromHtml(html: string): { title: string; content: string } {
const title = html.match(/<title[^>]*>([^<]+)<\/title>/i)?.[1]?.trim() || "No title";
const content = html
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, " ")
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, " ")
.replace(/<[^>]+>/g, " ")
.replace(/&nbsp;/g, " ")
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/\s+/g, " ")
.trim();
return { title, content };
}
async function fetchText(url: string, signal?: AbortSignal) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30000);
const activeSignal = signal ?? controller.signal;
try {
const response = await fetch(url, {
signal: activeSignal,
headers: {
"User-Agent": "Mozilla/5.0 Orquestra/1.0",
},
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return textFromHtml(await response.text());
} finally {
clearTimeout(timeout);
}
}
export default function orquestraWebFetch(pi: ExtensionAPI) {
pi.registerTool({
name: "orquestra_web_fetch",
label: "Orquestra Web Fetch",
description: "Fetch a web page and return extracted text using a simple non-browser fetch.",
parameters: Type.Object({
url: Type.String({ description: "URL to fetch" }),
limit: Type.Optional(Type.Integer({ minimum: 500, maximum: 20000, description: "Max characters to return" })),
}),
async execute(_toolCallId, params, signal) {
const { title, content } = await fetchText(params.url, signal);
const limit = params.limit ?? 8000;
return {
content: [{ type: "text", text: `Title: ${title}\n\nContent:\n${content.slice(0, limit)}` }],
details: { title, url: params.url, characters: content.length },
};
},
});
}