mirror of
https://github.com/luckyyzh/pi-agent-integrated.git
synced 2026-10-03 02:59:35 +00:00
feat: integrate Pi backend and Pi Web
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import type { SessionEntry } from "./types";
|
||||
|
||||
const SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
||||
export function isValidSessionId(sessionId: string | null): sessionId is string {
|
||||
return !!sessionId && SESSION_ID_RE.test(sessionId);
|
||||
}
|
||||
|
||||
function safeDecode(value: string): string {
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeSlashes(value: string): string {
|
||||
return value.replace(/\\/g, "/");
|
||||
}
|
||||
|
||||
function isPathChar(ch: string): boolean {
|
||||
return /[A-Za-z0-9._~+%@/\\:-]/.test(ch);
|
||||
}
|
||||
|
||||
function hasReferenceBoundaryAfter(text: string, index: number): boolean {
|
||||
if (index >= text.length) return true;
|
||||
const ch = text[index];
|
||||
if (ch === ":") return /\d/.test(text[index + 1] ?? "");
|
||||
return !isPathChar(ch);
|
||||
}
|
||||
|
||||
function containsExactPathReference(text: string, filePath: string): boolean {
|
||||
const target = normalizeSlashes(filePath);
|
||||
const targets = target.startsWith("/") ? [target, `file://${target}`] : [target];
|
||||
const haystacks = new Set([normalizeSlashes(text), normalizeSlashes(safeDecode(text))]);
|
||||
|
||||
for (const haystack of haystacks) {
|
||||
for (const t of targets) {
|
||||
let index = haystack.indexOf(t);
|
||||
while (index !== -1) {
|
||||
const before = index === 0 ? "" : haystack[index - 1];
|
||||
const afterIndex = index + t.length;
|
||||
if ((index === 0 || !isPathChar(before)) && hasReferenceBoundaryAfter(haystack, afterIndex)) {
|
||||
return true;
|
||||
}
|
||||
index = haystack.indexOf(t, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function collectStrings(value: unknown, out: string[]): void {
|
||||
if (typeof value === "string") {
|
||||
out.push(value);
|
||||
return;
|
||||
}
|
||||
if (!value || typeof value !== "object") return;
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) collectStrings(item, out);
|
||||
return;
|
||||
}
|
||||
for (const item of Object.values(value)) collectStrings(item, out);
|
||||
}
|
||||
|
||||
export function isFilePathReferencedByEntries(filePath: string, entries: SessionEntry[]): boolean {
|
||||
for (const entry of entries) {
|
||||
const strings: string[] = [];
|
||||
collectStrings(entry, strings);
|
||||
if (strings.some((text) => containsExactPathReference(text, filePath))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isBashOutputPathReferencedByEntries(filePath: string, entries: SessionEntry[]): boolean {
|
||||
return entries.some((entry) => (
|
||||
entry.type === "message"
|
||||
&& entry.message.role === "bashExecution"
|
||||
&& entry.message.fullOutputPath === filePath
|
||||
));
|
||||
}
|
||||
Reference in New Issue
Block a user