| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import JSZip from "jszip"; |
|
|
| |
| |
| |
|
|
| type AnyObj = Record<string, any>; |
|
|
| export interface BuildAnalysisZipOptions { |
| originalZipBytes: ArrayBuffer | Uint8Array | Buffer; |
| taskPackage: AnyObj; |
| mode?: "api" | "mcp" | string; |
| originalFilename?: string; |
| } |
|
|
| export interface BuildAnalysisZipResult { |
| buffer: Buffer; |
| filename: string; |
| } |
|
|
| |
| |
| |
| export function remapTrajectoryForMode(taskPackage: AnyObj, mode: string): AnyObj { |
| if (!taskPackage || !Array.isArray(taskPackage.golden_trajectory)) return taskPackage; |
| if (mode !== "api") return taskPackage; |
| const remapped = taskPackage.golden_trajectory.map(function (entry: AnyObj) { |
| if (!entry || !entry.subtask) return entry; |
| const sub = entry.subtask; |
| const calls = Array.isArray(sub.mcpCalls) |
| ? sub.mcpCalls |
| : Array.isArray(sub.apiCalls) |
| ? sub.apiCalls |
| : []; |
| return { |
| subtask: { |
| order: sub.order, |
| mode: "api", |
| description: sub.description, |
| apiCalls: calls, |
| }, |
| }; |
| }); |
| return Object.assign({}, taskPackage, { golden_trajectory: remapped }); |
| } |
|
|
| |
| |
| export function normalizeUrlPattern(path: string): string { |
| if (!path) return ""; |
| return path |
| .split("/") |
| .map(function (seg) { |
| if (!seg) return seg; |
| if (/^[A-Z0-9_-]{8,}$/i.test(seg) && /\d/.test(seg)) return "{id}"; |
| if (/^[0-9]+$/.test(seg)) return "{id}"; |
| return seg; |
| }) |
| .join("/"); |
| } |
|
|
| |
| export function extractPath(url: string): string { |
| if (!url) return ""; |
| try { |
| const u = new URL(url); |
| return u.pathname; |
| } catch (_) { |
| |
| const noScheme = url.replace(/^https?:\/\/[^/]+/, ""); |
| const qIdx = noScheme.indexOf("?"); |
| return qIdx >= 0 ? noScheme.slice(0, qIdx) : noScheme; |
| } |
| } |
|
|
| function normalizeRubricScoring(rubric: AnyObj): Record<string, string> { |
| const desc = rubric && rubric.description && typeof rubric.description === "object" |
| ? rubric.description |
| : {}; |
| const scoringSource = |
| rubric && rubric.scoring && typeof rubric.scoring === "object" |
| ? rubric.scoring |
| : desc && desc.scoring && typeof desc.scoring === "object" |
| ? desc.scoring |
| : {}; |
| const scoring: Record<string, string> = {}; |
| if (scoringSource["0"] != null) scoring["0"] = String(scoringSource["0"]); |
| if (scoringSource[0] != null && scoring["0"] == null) scoring["0"] = String(scoringSource[0]); |
| if (scoringSource["1"] != null) scoring["1"] = String(scoringSource["1"]); |
| if (scoringSource[1] != null && scoring["1"] == null) scoring["1"] = String(scoringSource[1]); |
| if (Array.isArray(rubric && rubric.scorePoints)) { |
| rubric.scorePoints.forEach(function (point: AnyObj) { |
| if (!point) return; |
| if (Number(point.score) === 0 && scoring["0"] == null) scoring["0"] = String(point.description || ""); |
| if (Number(point.score) === 1 && scoring["1"] == null) scoring["1"] = String(point.description || ""); |
| }); |
| } |
| if (!scoring["0"]) scoring["0"] = "Reject if the required evidence is missing or incorrect."; |
| if (!scoring["1"]) scoring["1"] = "Accept if the required evidence is present and correct."; |
| return scoring; |
| } |
|
|
| function rubricDescriptionText(rubric: AnyObj): string { |
| if (rubric && rubric.description && typeof rubric.description === "object") { |
| return String(rubric.description.description || ""); |
| } |
| return String((rubric && rubric.description) || ""); |
| } |
|
|
| function rubricCategory(rubric: AnyObj): string { |
| if (rubric && rubric.description && typeof rubric.description === "object" && rubric.description.category) { |
| return String(rubric.description.category); |
| } |
| return String((rubric && rubric.category) || "process"); |
| } |
|
|
| function scorePointsFromScoring(scoring: Record<string, string>): AnyObj[] { |
| return [ |
| { score: 0, description: scoring["0"] }, |
| { score: 1, description: scoring["1"] }, |
| ]; |
| } |
|
|
| |
| |
| |
| export function convertRubricToExtensionFormat(rubric: AnyObj, checkerKeyOverride?: string): AnyObj { |
| rubric = rubric || {}; |
| const scoring = normalizeRubricScoring(rubric || {}); |
| const checkerKey = String(checkerKeyOverride || rubric.checker_key || rubric.name || ""); |
| return { |
| name: rubric.name || "", |
| must_pass: Boolean(rubric.must_pass), |
| checker_key: checkerKey, |
| max_score: |
| rubric.max_score != null |
| ? rubric.max_score |
| : rubric.maxScore != null |
| ? rubric.maxScore |
| : 1, |
| scoring, |
| scorePoints: scorePointsFromScoring(scoring), |
| description: { |
| description: rubricDescriptionText(rubric || {}), |
| category: rubricCategory(rubric || {}), |
| scoring, |
| reject: scoring["0"], |
| accept: scoring["1"], |
| }, |
| }; |
| } |
|
|
| export function parseQueryParams(url: string): AnyObj { |
| const out: AnyObj = {}; |
| if (!url) return out; |
| try { |
| const u = new URL(url, "http://_placeholder"); |
| u.searchParams.forEach(function (value, key) { |
| out[key] = value; |
| }); |
| } catch (_) { |
| const qIdx = String(url).indexOf("?"); |
| if (qIdx < 0) return out; |
| String(url) |
| .slice(qIdx + 1) |
| .split("&") |
| .forEach(function (pair) { |
| if (!pair) return; |
| const eqIdx = pair.indexOf("="); |
| let key = eqIdx >= 0 ? pair.slice(0, eqIdx) : pair; |
| let value = eqIdx >= 0 ? pair.slice(eqIdx + 1) : ""; |
| try { |
| key = decodeURIComponent(key); |
| value = decodeURIComponent(value); |
| } catch (_) {} |
| out[key] = value; |
| }); |
| } |
| return out; |
| } |
|
|
| function objectHasKeys(obj: any): boolean { |
| return obj && typeof obj === "object" && Object.keys(obj).length > 0; |
| } |
|
|
| function getHeader(headers: AnyObj | null | undefined, name: string): any { |
| headers = headers || {}; |
| return headers[name] || headers[name.toLowerCase()] || headers[name.toUpperCase()] || null; |
| } |
|
|
| function getNetworkEntries(networkJson: any): any[] { |
| if (Array.isArray(networkJson)) return networkJson; |
| if (networkJson && Array.isArray(networkJson.requests)) return networkJson.requests; |
| if (networkJson && Array.isArray(networkJson.networkRequests)) return networkJson.networkRequests; |
| if (networkJson && Array.isArray(networkJson.data)) return networkJson.data; |
| return []; |
| } |
|
|
| function buildNetworkEntryIndex(networkJson: any): AnyObj { |
| const index: AnyObj = {}; |
| getNetworkEntries(networkJson).forEach(function (entry) { |
| if (entry && entry.id) index[entry.id] = entry; |
| }); |
| return index; |
| } |
|
|
| export function buildApiCallObject(req: AnyObj | null | undefined, networkEntryIndex: AnyObj): AnyObj { |
| req = req || {}; |
| const raw = req.id && networkEntryIndex ? networkEntryIndex[req.id] : null; |
| const rawRequest = (raw && raw.request) || {}; |
| const rawResponse = (raw && raw.response) || {}; |
| const url = rawRequest.url || req.url || ""; |
| const endpoint = extractPath(url) || extractPath(req.url || "") || ""; |
| const method = String(rawRequest.method || req.method || "").toUpperCase(); |
| const input: AnyObj = {}; |
| const queryParams = parseQueryParams(url || req.url || ""); |
| if (objectHasKeys(queryParams)) input.queryParams = queryParams; |
| if (rawRequest.body !== undefined && rawRequest.body !== null && rawRequest.body !== "") { |
| input.bodyParams = rawRequest.body; |
| } |
| const output: AnyObj = {}; |
| const status = |
| req.status != null |
| ? req.status |
| : req.responseStatus != null |
| ? req.responseStatus |
| : rawResponse.status != null |
| ? rawResponse.status |
| : null; |
| if (status != null) output.statusCode = Number(status); |
| const contentType = req.contentType || getHeader(rawResponse.headers, "content-type"); |
| if (contentType) output.contentType = contentType; |
| return { |
| id: req.id || "", |
| name: endpoint, |
| description: "", |
| time: |
| req.timestamp != null |
| ? req.timestamp |
| : raw && raw.timing && raw.timing.startTime != null |
| ? raw.timing.startTime |
| : undefined, |
| type: "api", |
| input: input, |
| output: output, |
| metadata: { |
| duration: req.duration != null ? req.duration : raw && raw.timing ? raw.timing.duration : undefined, |
| timestamp: req.timestamp != null ? req.timestamp : raw && raw.timing ? raw.timing.startTime : undefined, |
| endpoint: endpoint, |
| method: method, |
| }, |
| }; |
| } |
|
|
| export function hydrateApiCall(call: any, networkRequestsById: AnyObj, networkEntryIndex: AnyObj): AnyObj | null { |
| if (call && typeof call === "object" && call.type === "api" && call.metadata) return call; |
| const id = call && typeof call === "object" ? call.id : String(call || ""); |
| const req = id && networkRequestsById ? networkRequestsById[id] : null; |
| if (req) return buildApiCallObject(req, networkEntryIndex); |
| return id ? { id: id } : null; |
| } |
|
|
| function buildNetworkRequestIndexes(networkRequests: any[]): AnyObj { |
| const byId: AnyObj = {}; |
| (networkRequests || []).forEach(function (req: AnyObj) { |
| if (req && req.id) byId[req.id] = req; |
| }); |
| return byId; |
| } |
|
|
| function cloneJson<T>(value: T): T { |
| if (value == null) return value; |
| try { |
| return JSON.parse(JSON.stringify(value)); |
| } catch (_) { |
| return value; |
| } |
| } |
|
|
| const ENTITY_KEYS = ["channelId", "guildId", "messageId", "userId", "roleId", "webhookId"]; |
|
|
| function getMcpCallName(call: any): string { |
| return String((call && (call.name || call.tool || call.function || call.mcpToolName || call.call)) || ""); |
| } |
|
|
| function getWantedMcpCallName(wanted: any): string { |
| const raw = String(wanted && typeof wanted === "object" ? getMcpCallName(wanted) : wanted || "").trim(); |
| const match = raw.match(/^CALL\s+(\S+)/i); |
| return match ? match[1] : raw; |
| } |
|
|
| function getWantedMcpCallId(wanted: any): string { |
| if (!wanted || typeof wanted !== "object") return ""; |
| return String( |
| wanted.callId || |
| wanted.call_id || |
| wanted.id || |
| wanted.requestId || |
| wanted.request_id || |
| wanted.networkRequestId || |
| wanted.network_request_id || |
| "", |
| ).trim(); |
| } |
|
|
| function getMcpCallTime(call: any): number { |
| return Number(call && (call.time ?? call.metadata?.timestamp)) || 0; |
| } |
|
|
| function collectExtensionMcpCalls(task: AnyObj | null | undefined): AnyObj[] { |
| if (!task || typeof task !== "object") return []; |
| const seen: Record<string, boolean> = {}; |
| const calls: AnyObj[] = []; |
| const push = function (call: any) { |
| if (!call || typeof call !== "object") return; |
| if (!getMcpCallName(call)) return; |
| const id = call.id ? String(call.id) : ""; |
| const key = id || [getMcpCallName(call), getMcpCallTime(call)].filter(Boolean).join(":"); |
| if (key && seen[key]) return; |
| if (key) seen[key] = true; |
| calls.push(call); |
| }; |
| (Array.isArray(task.subtasks) ? task.subtasks : []).forEach(function (subtask: AnyObj) { |
| (Array.isArray(subtask && subtask.mcpCalls) ? subtask.mcpCalls : []).forEach(push); |
| }); |
| (Array.isArray(task.other_mcp_calls) ? task.other_mcp_calls : []).forEach(push); |
| return calls.sort(function (a, b) { |
| return getMcpCallTime(a) - getMcpCallTime(b); |
| }); |
| } |
|
|
| function buildMcpCallIndex(calls: any): AnyObj | null { |
| if (!Array.isArray(calls) || !calls.length) return null; |
| const byId: AnyObj = {}; |
| calls.forEach(function (call: AnyObj) { |
| if (call && call.id) byId[String(call.id)] = call; |
| }); |
| return Object.keys(byId).length ? byId : null; |
| } |
|
|
| interface McpCallPoolEntry { |
| index: number; |
| call: AnyObj; |
| id: string; |
| tool: string; |
| used: boolean; |
| } |
|
|
| function buildMcpCallPool(calls: any[]): McpCallPoolEntry[] { |
| return (Array.isArray(calls) ? calls : []) |
| .map(function (call: AnyObj, index: number) { |
| return { |
| index, |
| call, |
| id: call && call.id != null ? String(call.id) : "", |
| tool: getMcpCallName(call), |
| used: false, |
| }; |
| }) |
| .filter(function (entry) { |
| return Boolean(entry.tool); |
| }); |
| } |
|
|
| function takeMcpCallFromPool(wanted: any, pool?: McpCallPoolEntry[] | null): AnyObj | null { |
| const wantedId = getWantedMcpCallId(wanted); |
| if (wantedId && pool && pool.length) { |
| const exact = pool.find(function (entry) { |
| return !entry.used && entry.id === wantedId; |
| }); |
| if (exact) { |
| exact.used = true; |
| return cloneJson(exact.call); |
| } |
| } |
| const target = getWantedMcpCallName(wanted); |
| if (!target || !pool || !pool.length) return null; |
| const hit = pool.find(function (entry) { |
| return !entry.used && entry.tool === target; |
| }); |
| if (!hit) return null; |
| hit.used = true; |
| return cloneJson(hit.call); |
| } |
|
|
| function taskMentionsReadState(text: unknown): boolean { |
| return /(?:mark(?:ed)?\s+.*read|read\s+(?:state|status|marker|receipt|position)|unread|read-position)/i.test( |
| String(text || ""), |
| ); |
| } |
|
|
| function isReadMarkerTool(tool: unknown): boolean { |
| return /^mark_.*read$/i.test(String(tool || "")) || /read[_-]position/i.test(String(tool || "")); |
| } |
|
|
| function addEntityValue(out: Record<string, string[]>, key: string, value: unknown): void { |
| if (value == null || value === "") return; |
| if (typeof value === "object") return; |
| const str = String(value); |
| if (!out[key]) out[key] = []; |
| if (out[key].indexOf(str) < 0) out[key].push(str); |
| } |
|
|
| function collectEntityIds(value: unknown, out: Record<string, string[]> = {}, depth = 0): Record<string, string[]> { |
| if (!value || typeof value !== "object" || depth > 5) return out; |
| if (Array.isArray(value)) { |
| value.forEach(function (item) { |
| collectEntityIds(item, out, depth + 1); |
| }); |
| return out; |
| } |
| Object.keys(value as AnyObj).forEach(function (key) { |
| const child = (value as AnyObj)[key]; |
| if (ENTITY_KEYS.indexOf(key) >= 0) addEntityValue(out, key, child); |
| if (child && typeof child === "object") collectEntityIds(child, out, depth + 1); |
| }); |
| return out; |
| } |
|
|
| function getExtensionMcpCallEntities(call: AnyObj): Record<string, string[]> { |
| return collectEntityIds(call && (call.input || call.args || {})); |
| } |
|
|
| function entitiesCompatible(entities: Record<string, string[]>, anchors: Record<string, string>): boolean { |
| return ENTITY_KEYS.every(function (key) { |
| const anchor = anchors[key]; |
| const values = entities[key] || []; |
| return !anchor || !values.length || values.indexOf(anchor) >= 0; |
| }); |
| } |
|
|
| function updateEntityAnchors(anchors: Record<string, string>, entities: Record<string, string[]>): void { |
| ENTITY_KEYS.forEach(function (key) { |
| if (anchors[key]) return; |
| const values = entities[key] || []; |
| if (values.length === 1) anchors[key] = values[0]; |
| }); |
| } |
|
|
| function filterMcpSubtasksByEntityConsistency(subtasks: AnyObj[], instructionText: unknown): AnyObj[] { |
| const anchors: Record<string, string> = {}; |
| const keepReadMarkers = taskMentionsReadState(instructionText); |
| return (Array.isArray(subtasks) ? subtasks : []) |
| .map(function (st: AnyObj) { |
| if (!st || st.groupMode !== "mcp" || !Array.isArray(st.mcpCalls)) return st; |
| const mcpCalls = st.mcpCalls.filter(function (call: AnyObj) { |
| const tool = getMcpCallName(call); |
| if (!keepReadMarkers && isReadMarkerTool(tool)) return false; |
| const entities = getExtensionMcpCallEntities(call); |
| if (!entitiesCompatible(entities, anchors)) return false; |
| updateEntityAnchors(anchors, entities); |
| return true; |
| }); |
| return Object.assign({}, st, { mcpCalls }); |
| }) |
| .filter(function (st: AnyObj) { |
| return st.groupMode !== "mcp" || (Array.isArray(st.mcpCalls) && st.mcpCalls.length > 0); |
| }); |
| } |
|
|
| function filterMcpSubtasksByNetworkIds(subtasks: AnyObj[], networkRequests: AnyObj[]): AnyObj[] { |
| const ids: Record<string, boolean> = {}; |
| (Array.isArray(networkRequests) ? networkRequests : []).forEach(function (req: AnyObj) { |
| if (req && req.id) ids[String(req.id)] = true; |
| }); |
| if (!Object.keys(ids).length) return subtasks; |
| return (Array.isArray(subtasks) ? subtasks : []) |
| .map(function (st: AnyObj) { |
| if (!st || st.groupMode !== "mcp" || !Array.isArray(st.mcpCalls)) return st; |
| const mcpCalls = st.mcpCalls.filter(function (call: AnyObj) { |
| const id = call && call.id != null ? String(call.id) : ""; |
| return Boolean(id && ids[id]); |
| }); |
| return Object.assign({}, st, { mcpCalls }); |
| }) |
| .filter(function (st: AnyObj) { |
| return st.groupMode !== "mcp" || (Array.isArray(st.mcpCalls) && st.mcpCalls.length > 0); |
| }); |
| } |
|
|
| function checkerToolsFromKey(key: unknown): string[] { |
| const raw = String(key || "").trim(); |
| if (!raw) return []; |
| const tools: string[] = []; |
| raw.split("->").forEach(function (segment) { |
| const text = segment.trim(); |
| const call = text.match(/^CALL\s+(\S+)/i); |
| if (call) { |
| tools.push(call[1]); |
| return; |
| } |
| const simple = text.match(/^([a-zA-Z][\w:-]*)$/); |
| if (simple) tools.push(simple[1]); |
| }); |
| return tools; |
| } |
|
|
| function buildExportedMcpToolSet(subtasks: AnyObj[]): Record<string, boolean> { |
| const tools: Record<string, boolean> = {}; |
| (Array.isArray(subtasks) ? subtasks : []).forEach(function (st: AnyObj) { |
| (Array.isArray(st && st.mcpCalls) ? st.mcpCalls : []).forEach(function (call: AnyObj) { |
| const tool = getMcpCallName(call); |
| if (tool) tools[tool] = true; |
| }); |
| }); |
| return tools; |
| } |
|
|
| function firstExportedCheckerTool(key: unknown, exportedTools: Record<string, boolean>): string { |
| const tools = checkerToolsFromKey(key); |
| for (let i = 0; i < tools.length; i += 1) { |
| const tool = getWantedMcpCallName(tools[i]); |
| if (tool && exportedTools[tool]) return tool; |
| } |
| const raw = String(key || ""); |
| const names = Object.keys(exportedTools); |
| for (let i = 0; i < names.length; i += 1) { |
| if (new RegExp("(^|[^A-Za-z0-9_:-])" + names[i].replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "([^A-Za-z0-9_:-]|$)").test(raw)) { |
| return names[i]; |
| } |
| } |
| return ""; |
| } |
|
|
| function simplifyRubricCheckerKeysForMcp(rubrics: AnyObj[], subtasks: AnyObj[]): AnyObj[] { |
| const exportedTools = buildExportedMcpToolSet(subtasks); |
| if (!Object.keys(exportedTools).length) return rubrics; |
| return (Array.isArray(rubrics) ? rubrics : []).map(function (rubric: AnyObj) { |
| const next = Object.assign({}, rubric); |
| const simpleKey = firstExportedCheckerTool(next.checker_key, exportedTools) || |
| firstExportedCheckerTool(next.name, exportedTools); |
| if (simpleKey) next.checker_key = simpleKey; |
| return next; |
| }); |
| } |
|
|
| function normalizeCheckerCatalogObject(catalog: any): AnyObj { |
| if (!catalog) return {}; |
| if (Array.isArray(catalog)) { |
| const out: AnyObj = {}; |
| catalog.forEach(function (item: AnyObj) { |
| const key = item && (item.checker_key || item.key || item.name); |
| if (key) out[String(key)] = item; |
| }); |
| return out; |
| } |
| return typeof catalog === "object" ? cloneJson(catalog) : {}; |
| } |
|
|
| function inferCheckerEvidenceSource(checkerKey: string): string { |
| const first = String(checkerKey || "").split("->")[0].trim().replace(/^CALL\s+/i, "").split("|")[0].trim(); |
| return first ? "tool_call:" + first : "tool_call"; |
| } |
|
|
| function remapCheckerCatalogForStrictKeys(originalCatalog: any, originalRubrics: AnyObj[], strictRubrics: AnyObj[]): AnyObj { |
| const catalog = normalizeCheckerCatalogObject(originalCatalog); |
| const out: AnyObj = {}; |
| (strictRubrics || []).forEach(function (rubric: AnyObj, index: number) { |
| const oldKey = String((originalRubrics && originalRubrics[index] && originalRubrics[index].checker_key) || ""); |
| const newKey = String((rubric && rubric.checker_key) || oldKey || (rubric && rubric.name) || ""); |
| if (!newKey) return; |
| const scoring = normalizeRubricScoring(rubric || {}); |
| const existing = catalog[newKey] || catalog[oldKey] || {}; |
| out[newKey] = { |
| evidence_source: String(existing.evidence_source || existing.evidenceSource || inferCheckerEvidenceSource(newKey)), |
| match_field: String(existing.match_field || existing.matchField || newKey), |
| fail_when: String(existing.fail_when || existing.failWhen || scoring["0"]), |
| }; |
| }); |
| return out; |
| } |
|
|
| interface RequestPoolEntry { |
| index: number; |
| req: AnyObj; |
| id: string; |
| method: string; |
| path: string; |
| normalizedPath: string; |
| used: boolean; |
| } |
|
|
| function buildRequestPool(networkRequests: any[]): RequestPoolEntry[] { |
| return (networkRequests || []).map(function (req: AnyObj, index: number) { |
| const path = extractPath(req && req.url ? req.url : ""); |
| return { |
| index, |
| req, |
| id: String((req && req.id) || ""), |
| method: String((req && req.method) || "").toUpperCase(), |
| path, |
| normalizedPath: normalizeUrlPattern(path), |
| used: false, |
| }; |
| }); |
| } |
|
|
| function getCallInput(call: any): AnyObj { |
| if (call && call.input && typeof call.input === "object" && !Array.isArray(call.input)) { |
| return call.input; |
| } |
| if (call && call.args && typeof call.args === "object" && !Array.isArray(call.args)) { |
| return { bodyParams: call.args }; |
| } |
| return {}; |
| } |
|
|
| function stripTransportFields(input: AnyObj): AnyObj { |
| const out = Object.assign({}, input || {}); |
| delete out.method; |
| delete out.endpoint; |
| return out; |
| } |
|
|
| function getCallMethod(call: any): string { |
| const input = getCallInput(call); |
| return String(input.method || call?.method || call?.metadata?.method || "").toUpperCase(); |
| } |
|
|
| function getCallEndpoint(call: any): string { |
| const input = getCallInput(call); |
| return String(input.endpoint || call?.endpoint || call?.metadata?.endpoint || ""); |
| } |
|
|
| function getCallName(call: any, method: string, endpoint: string): string { |
| const explicit = call?.tool || call?.name || call?.function; |
| if (explicit) return String(explicit); |
| return inferNameFromEndpoint(method, endpoint); |
| } |
|
|
| function inferNameFromEndpoint(method: string, endpoint: string): string { |
| const path = extractPath(endpoint) || endpoint || ""; |
| const parts = path.split("/").filter(Boolean); |
| const last = (parts[parts.length - 1] || "call").replace(/-/g, "_"); |
| if (method === "POST" && last === "messages") return "send_message"; |
| if (method === "GET" && last === "me") return "get_current_user"; |
| if (method === "PUT" || method === "PATCH") return "update_" + last; |
| if (method === "POST") return "create_" + last; |
| if (method === "DELETE") return "delete_" + last; |
| if (method === "GET") return "get_" + last; |
| return last || "recorded_call"; |
| } |
|
|
| function parseJsonMaybe(value: any): any { |
| if (typeof value !== "string") return value; |
| const trimmed = value.trim(); |
| if (!trimmed) return value; |
| if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return value; |
| try { |
| return JSON.parse(trimmed); |
| } catch (_) { |
| return value; |
| } |
| } |
|
|
| function scalarEqual(expected: any, actual: any): boolean { |
| if (expected === actual) return true; |
| if (expected == null || actual == null) return expected == null && actual == null; |
| return String(expected) === String(actual); |
| } |
|
|
| function containsValue(expected: any, actual: any): boolean { |
| expected = parseJsonMaybe(expected); |
| actual = parseJsonMaybe(actual); |
| if (Array.isArray(expected)) { |
| if (!Array.isArray(actual) || expected.length !== actual.length) return false; |
| return expected.every(function (value, index) { |
| return containsValue(value, actual[index]); |
| }); |
| } |
| if (expected && typeof expected === "object") { |
| if (!actual || typeof actual !== "object") return false; |
| return Object.keys(expected).every(function (key) { |
| return containsValue(expected[key], actual[key]); |
| }); |
| } |
| return scalarEqual(expected, actual); |
| } |
|
|
| function hasMeaningfulParams(value: any): boolean { |
| if (value == null || value === "") return false; |
| if (typeof value === "object" && !Array.isArray(value)) return Object.keys(value).length > 0; |
| return true; |
| } |
|
|
| function splitPathSegments(path: string): string[] { |
| return (extractPath(path) || path || "").split("/").filter(Boolean); |
| } |
|
|
| function isPathPlaceholder(segment: string): boolean { |
| return segment === "{id}" || /^\{[^}]+\}$/.test(segment) || /^:[A-Za-z_]\w*$/.test(segment); |
| } |
|
|
| function endpointPathMatchesRequest(callPath: string, entryPath: string, normalizedEndpoint: string): boolean { |
| if (!callPath) return true; |
| if (entryPath === callPath) return true; |
| if (normalizeUrlPattern(entryPath) !== normalizedEndpoint) return false; |
|
|
| const callParts = splitPathSegments(callPath); |
| const entryParts = splitPathSegments(entryPath); |
| if (callParts.length !== entryParts.length) return false; |
| return callParts.every(function (part, index) { |
| return isPathPlaceholder(part) || part === entryParts[index]; |
| }); |
| } |
|
|
| const PATH_PARAM_RESOURCE_BY_KEY: Record<string, string> = { |
| channelId: "channels", |
| guildId: "guilds", |
| messageId: "messages", |
| userId: "users", |
| roleId: "roles", |
| memberId: "members", |
| threadId: "threads", |
| webhookId: "webhooks", |
| eventId: "events", |
| }; |
|
|
| function getPathParamAfterResource(path: string, resource: string): string { |
| const parts = splitPathSegments(path); |
| const index = parts.indexOf(resource); |
| return index >= 0 && index + 1 < parts.length ? decodeURIComponent(parts[index + 1]) : ""; |
| } |
|
|
| function pathParamsMatch(input: AnyObj, url: string): boolean { |
| const params = input.pathParams; |
| if (!hasMeaningfulParams(params) || !params || typeof params !== "object" || Array.isArray(params)) { |
| return true; |
| } |
| const path = extractPath(url) || url || ""; |
| return Object.keys(params).every(function (key) { |
| const resource = PATH_PARAM_RESOURCE_BY_KEY[key]; |
| if (!resource) return true; |
| const actual = getPathParamAfterResource(path, resource); |
| return !actual || scalarEqual((params as AnyObj)[key], actual); |
| }); |
| } |
|
|
| function callParamsMatch(call: any, req: AnyObj, networkEntryIndex: AnyObj): boolean { |
| const input = getCallInput(call); |
| const raw = req.id && networkEntryIndex ? networkEntryIndex[req.id] : null; |
| const rawRequest = (raw && raw.request) || {}; |
| const url = rawRequest.url || req.url || ""; |
|
|
| if (!pathParamsMatch(input, url)) return false; |
|
|
| if (hasMeaningfulParams(input.queryParams)) { |
| const actualQuery = parseQueryParams(url); |
| if (!containsValue(input.queryParams, actualQuery)) return false; |
| } |
|
|
| if (hasMeaningfulParams(input.bodyParams)) { |
| const actualBody = rawRequest.body; |
| if (!containsValue(input.bodyParams, actualBody)) return false; |
| } |
|
|
| return true; |
| } |
|
|
| function findNetworkRequestForCall( |
| call: any, |
| requestPool: RequestPoolEntry[], |
| networkEntryIndex: AnyObj, |
| ): RequestPoolEntry | null { |
| const callId = getWantedMcpCallId(call); |
| if (callId) { |
| const direct = requestPool.find(function (entry) { |
| return !entry.used && entry.id === callId; |
| }); |
| if (direct && callParamsMatch(call, direct.req, networkEntryIndex)) return direct; |
| } |
|
|
| const method = getCallMethod(call); |
| const endpoint = getCallEndpoint(call); |
| const callPath = extractPath(endpoint) || endpoint; |
| const normalizedEndpoint = normalizeUrlPattern(callPath); |
| if (!method && !callPath) return null; |
|
|
| for (let i = 0; i < requestPool.length; i += 1) { |
| const entry = requestPool[i]; |
| if (entry.used) continue; |
| if (method && entry.method !== method) continue; |
| if (callPath && !endpointPathMatchesRequest(callPath, entry.path, normalizedEndpoint)) { |
| continue; |
| } |
| if (!callParamsMatch(call, entry.req, networkEntryIndex)) continue; |
| return entry; |
| } |
| return null; |
| } |
|
|
| function markRequestUsed(requestPool: RequestPoolEntry[] | undefined, req: AnyObj): void { |
| if (!requestPool || !req || !req.id) return; |
| const hit = requestPool.find(function (entry) { |
| return !entry.used && entry.id === req.id; |
| }); |
| if (hit) hit.used = true; |
| } |
|
|
| function buildMcpCallObject( |
| req: AnyObj | null | undefined, |
| networkEntryIndex: AnyObj, |
| sourceCall?: any, |
| mcpCallIndex?: AnyObj | null, |
| ): AnyObj | null { |
| if (req && req.id && mcpCallIndex) { |
| const exact = mcpCallIndex[String(req.id)]; |
| return exact ? cloneJson(exact) : null; |
| } |
| return null; |
| } |
|
|
| function hydrateMcpCall( |
| call: any, |
| requestPool: RequestPoolEntry[], |
| networkEntryIndex: AnyObj, |
| mcpCallIndex?: AnyObj | null, |
| ): AnyObj | null { |
| const exactId = getWantedMcpCallId(call); |
| if (exactId && mcpCallIndex && mcpCallIndex[exactId]) { |
| const match = requestPool.find(function (entry) { |
| return !entry.used && entry.id === exactId; |
| }); |
| if (match && callParamsMatch(call, match.req, networkEntryIndex)) match.used = true; |
| return cloneJson(mcpCallIndex[exactId]); |
| } |
| if ( |
| call && |
| typeof call === "object" && |
| call.type === "mcp" && |
| call.id && |
| call.metadata |
| ) { |
| return call; |
| } |
| const match = findNetworkRequestForCall(call, requestPool, networkEntryIndex); |
| if (match) { |
| match.used = true; |
| return buildMcpCallObject(match.req, networkEntryIndex, call, mcpCallIndex); |
| } |
| return null; |
| } |
|
|
| function normalizeMcpCall(call: any): AnyObj | null { |
| if (!call) return null; |
| if (typeof call !== "object") { |
| return { tool: String(call), input: {} }; |
| } |
| const tool = call.tool || call.name || call.function; |
| if (!tool) return call; |
| const input = |
| call.input && typeof call.input === "object" && !Array.isArray(call.input) |
| ? Object.assign({}, call.input) |
| : {}; |
| if ( |
| input.bodyParams === undefined && |
| call.args && |
| typeof call.args === "object" && |
| !Array.isArray(call.args) && |
| Object.keys(call.args).length > 0 |
| ) { |
| input.bodyParams = call.args; |
| } |
| const normalized: AnyObj = { |
| tool: String(tool), |
| input, |
| }; |
| if (call.output !== undefined) normalized.output = call.output; |
| if (call.success !== undefined) normalized.success = call.success; |
| if (call.id !== undefined) normalized.id = call.id; |
| if (call.time !== undefined) normalized.time = call.time; |
| return normalized; |
| } |
|
|
| |
| |
| export function convertSubtaskToExtensionFormat( |
| st: AnyObj, |
| index: number, |
| networkRequests: any[], |
| mode: string | undefined, |
| networkEntryIndex: AnyObj, |
| requestPool?: AnyObj[], |
| mcpCallIndex?: AnyObj | null, |
| mcpCallPool?: McpCallPoolEntry[] | null, |
| ): AnyObj { |
| const requestedMode = |
| mode || st.mode || st.groupMode || (st.apiCalls && st.apiCalls.length ? "api" : "mcp"); |
| const networkRequestsById = buildNetworkRequestIndexes(networkRequests || []); |
| const mcpRequestPool = (requestPool as RequestPoolEntry[] | undefined) || buildRequestPool(networkRequests || []); |
| |
| const instruction = st.name || st.reason || st.description || st.instruction || ""; |
|
|
| let apiCalls: AnyObj[] = []; |
| let mcpCalls: AnyObj[] = []; |
| if (Array.isArray(st._requestIndices) && networkRequests) { |
| st._requestIndices.forEach(function (idx: any) { |
| const req = networkRequests[Number(idx)]; |
| if (!req || !req.id) return; |
| if (requestedMode === "mcp") { |
| const mcpCall = |
| req.id && mcpCallIndex && mcpCallIndex[String(req.id)] |
| ? buildMcpCallObject(req, networkEntryIndex, null, mcpCallIndex) |
| : null; |
| if (mcpCall) markRequestUsed(mcpRequestPool, req); |
| if (mcpCall) mcpCalls.push(mcpCall); |
| } else { |
| apiCalls.push(buildApiCallObject(req, networkEntryIndex)); |
| } |
| }); |
| } |
|
|
| const hasExactCalls = function () { |
| return requestedMode === "mcp" ? mcpCalls.length > 0 : apiCalls.length > 0; |
| }; |
|
|
| if (!hasExactCalls() && requestedMode === "mcp" && Array.isArray(st.callIds) && mcpCallIndex) { |
| st.callIds.forEach(function (id: any) { |
| const exact = mcpCallIndex[String(id || "")]; |
| if (exact) mcpCalls.push(cloneJson(exact)); |
| }); |
| } |
|
|
| if (!hasExactCalls() && Array.isArray(st._callDetails) && networkRequests) { |
| st._callDetails.forEach(function (detail: AnyObj) { |
| if (!detail || detail.index == null) return; |
| const req = networkRequests[Number(detail.index) - 1] || networkRequests[Number(detail.index)]; |
| if (!req || !req.id) return; |
| if (requestedMode === "mcp") { |
| const mcpCall = |
| takeMcpCallFromPool(detail.call || detail.tool || detail.name || detail.mcpToolName, mcpCallPool) || |
| (req.id && mcpCallIndex && mcpCallIndex[String(req.id)] |
| ? buildMcpCallObject(req, networkEntryIndex, detail, mcpCallIndex) |
| : null); |
| if (mcpCall) markRequestUsed(mcpRequestPool, req); |
| if (mcpCall) mcpCalls.push(mcpCall); |
| } else { |
| apiCalls.push(buildApiCallObject(req, networkEntryIndex)); |
| } |
| }); |
| } |
|
|
| if (!apiCalls.length && requestedMode !== "mcp" && Array.isArray(st.apiCalls)) { |
| apiCalls = st.apiCalls |
| .map(function (call: any) { |
| return hydrateApiCall(call, networkRequestsById, networkEntryIndex); |
| }) |
| .filter(Boolean) as AnyObj[]; |
| } |
|
|
| if (!mcpCalls.length && Array.isArray(st.mcpCalls)) { |
| mcpCalls = st.mcpCalls |
| .map(function (call: any) { |
| return hydrateMcpCall(call, mcpRequestPool, networkEntryIndex, mcpCallIndex); |
| }) |
| .filter(Boolean) as AnyObj[]; |
| } |
| if (!mcpCalls.length && requestedMode === "mcp" && Array.isArray(st.calls)) { |
| mcpCalls = st.calls |
| .map(function (call: any) { |
| return takeMcpCallFromPool(call, mcpCallPool); |
| }) |
| .filter(Boolean) as AnyObj[]; |
| } |
|
|
| const effectiveMode = requestedMode === "mcp" ? "mcp" : "api"; |
| return { |
| id: st.id || "subtask_" + (index + 1), |
| order: st.order !== undefined ? st.order : index + 1, |
| groupMode: effectiveMode, |
| instruction: instruction, |
| apiCalls: effectiveMode === "api" ? apiCalls : [], |
| mcpCalls: effectiveMode === "mcp" ? mcpCalls : [], |
| }; |
| } |
|
|
| |
| |
| |
| export function deriveSubtasksFromTrajectory( |
| goldenTrajectory: any[], |
| networkRequests: any[], |
| mode: string | undefined, |
| networkEntryIndex: AnyObj, |
| mcpCallIndex?: AnyObj | null, |
| ): AnyObj[] { |
| if (!Array.isArray(goldenTrajectory) || !Array.isArray(networkRequests)) return []; |
|
|
| |
| const reqPool = buildRequestPool(networkRequests); |
|
|
| const result: AnyObj[] = []; |
| goldenTrajectory.forEach(function (entry: AnyObj, i: number) { |
| if (!entry || !entry.subtask) return; |
| const sub = entry.subtask; |
| const calls = Array.isArray(sub.apiCalls) |
| ? sub.apiCalls |
| : Array.isArray(sub.mcpCalls) |
| ? sub.mcpCalls |
| : []; |
|
|
| const matchedApiCalls: AnyObj[] = []; |
| const matchedMcpCalls: AnyObj[] = []; |
| calls.forEach(function (call: any) { |
| if (!call) return; |
| if (mode === "mcp") { |
| const mcpCall = hydrateMcpCall(call, reqPool, networkEntryIndex, mcpCallIndex); |
| if (mcpCall) matchedMcpCalls.push(mcpCall); |
| return; |
| } |
| const callMethod = getCallMethod(call); |
| const callEndpoint = getCallEndpoint(call); |
| |
| const callPath = extractPath(callEndpoint) || callEndpoint; |
| const normalizedEndpoint = normalizeUrlPattern(callPath); |
|
|
| for (let j = 0; j < reqPool.length; j++) { |
| const req = reqPool[j]; |
| if (req.used) continue; |
| if (callMethod && req.method !== callMethod) continue; |
| |
| if (req.normalizedPath === normalizedEndpoint || req.path === callPath) { |
| req.used = true; |
| if (req.id) matchedApiCalls.push(buildApiCallObject(req.req, networkEntryIndex)); |
| break; |
| } |
| } |
| }); |
|
|
| const effectiveMode = mode === "mcp" ? "mcp" : "api"; |
| result.push({ |
| id: "subtask_" + (i + 1), |
| order: sub.order !== undefined ? sub.order : i + 1, |
| groupMode: effectiveMode, |
| instruction: sub.description || "", |
| apiCalls: effectiveMode === "api" ? matchedApiCalls : [], |
| mcpCalls: effectiveMode === "mcp" ? matchedMcpCalls : [], |
| }); |
| }); |
|
|
| return result; |
| } |
|
|
| function buildStrictGoldenTrajectoryFromExtensionSubtasks(subtasks: AnyObj[], mode: string | undefined): AnyObj[] { |
| const effectiveMode = mode === "api" ? "api" : "mcp"; |
| return (Array.isArray(subtasks) ? subtasks : []) |
| .map(function (st: AnyObj, index: number) { |
| const order = st.order !== undefined ? st.order : index + 1; |
| const description = st.instruction || st.description || st.name || ""; |
| if (effectiveMode === "api") { |
| const apiCalls = (Array.isArray(st.apiCalls) ? st.apiCalls : []).map(function (call: AnyObj) { |
| return { |
| api: call.name || call.metadata?.endpoint || "", |
| description: call.description || "", |
| method: call.metadata?.method || "", |
| endpoint: call.metadata?.endpoint || "", |
| input: call.input || {}, |
| output: call.output || {}, |
| }; |
| }); |
| if (!apiCalls.length) return null; |
| return { subtask: { order, mode: "api", description, apiCalls } }; |
| } |
|
|
| const mcpCalls = (Array.isArray(st.mcpCalls) ? st.mcpCalls : []) |
| .map(function (call: AnyObj) { |
| const tool = getMcpCallName(call); |
| if (!tool) return null; |
| const out: AnyObj = { |
| tool, |
| input: call.input || {}, |
| }; |
| if (call.id != null) out.id = call.id; |
| if (call.name != null) out.name = call.name; |
| if (call.output != null) out.output = call.output; |
| if (call.metadata != null) out.metadata = call.metadata; |
| return out; |
| }) |
| .filter(Boolean); |
| if (!mcpCalls.length) return null; |
| return { subtask: { order, mode: "mcp", description, mcpCalls } }; |
| }) |
| .filter(Boolean) as AnyObj[]; |
| } |
|
|
| |
| |
| |
| |
| |
| export function buildOutputTaskJson( |
| originalTaskJson: AnyObj | null | undefined, |
| taskPackage: AnyObj, |
| mode: string | undefined, |
| networkJson: any, |
| ): AnyObj { |
| |
| let base: AnyObj = {}; |
| if (originalTaskJson && typeof originalTaskJson === "object") { |
| try { |
| base = JSON.parse(JSON.stringify(originalTaskJson)); |
| } catch (_) { |
| base = {}; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| if (!base.metadata || typeof base.metadata !== "object") { |
| base.metadata = {}; |
| } |
| if (!base.task || typeof base.task !== "object") { |
| base.task = {}; |
| } |
|
|
| |
| if (taskPackage.instruction) { |
| base.task.instruction = taskPackage.instruction; |
| } else if (!base.task.instruction) { |
| base.task.instruction = ""; |
| } |
|
|
| |
| |
| |
| if (!Array.isArray(base.task.networkRequests)) { |
| base.task.networkRequests = []; |
| } |
|
|
| const networkRequests = base.task.networkRequests; |
| const networkEntryIndex = buildNetworkEntryIndex(networkJson); |
| const originalMcpCalls = collectExtensionMcpCalls(base.task); |
| const mcpCallIndex = buildMcpCallIndex(originalMcpCalls); |
| const mcpCallPool = buildMcpCallPool(originalMcpCalls); |
|
|
| let outputRubrics: AnyObj[] = Array.isArray(taskPackage.rubrics) ? cloneJson(taskPackage.rubrics) : []; |
|
|
| const subtasksHaveCalls = function (subtasks: AnyObj[]): boolean { |
| return subtasks.some(function (st) { |
| return (st.apiCalls && st.apiCalls.length > 0) || (st.mcpCalls && st.mcpCalls.length > 0); |
| }); |
| }; |
|
|
| |
| |
| |
| |
| let convertedSubtasks: AnyObj[] = []; |
| if (mode === "mcp" && Array.isArray(taskPackage.subtasks) && taskPackage.subtasks.length > 0) { |
| const fallbackRequestPool = buildRequestPool(networkRequests); |
| convertedSubtasks = taskPackage.subtasks.map(function (st: AnyObj, i: number) { |
| return convertSubtaskToExtensionFormat( |
| st, |
| i, |
| networkRequests, |
| mode, |
| networkEntryIndex, |
| fallbackRequestPool, |
| mcpCallIndex, |
| mcpCallPool, |
| ); |
| }); |
| } |
|
|
| if ( |
| mode !== "mcp" && |
| Array.isArray(taskPackage.golden_trajectory) && |
| taskPackage.golden_trajectory.length > 0 |
| ) { |
| const derived = deriveSubtasksFromTrajectory( |
| taskPackage.golden_trajectory, |
| networkRequests, |
| mode, |
| networkEntryIndex, |
| mcpCallIndex, |
| ); |
| if (derived.length > 0 && subtasksHaveCalls(derived)) { |
| convertedSubtasks = derived; |
| } |
| } |
|
|
| |
| |
| if (!convertedSubtasks.length && Array.isArray(taskPackage.subtasks) && taskPackage.subtasks.length > 0) { |
| const fallbackRequestPool = buildRequestPool(networkRequests); |
| convertedSubtasks = taskPackage.subtasks.map(function (st: AnyObj, i: number) { |
| return convertSubtaskToExtensionFormat( |
| st, |
| i, |
| networkRequests, |
| mode, |
| networkEntryIndex, |
| fallbackRequestPool, |
| mcpCallIndex, |
| mcpCallPool, |
| ); |
| }); |
| } |
|
|
| if (mode === "mcp") { |
| convertedSubtasks = convertedSubtasks.filter(function (st) { |
| return Array.isArray(st.mcpCalls) && st.mcpCalls.length > 0; |
| }); |
| convertedSubtasks = filterMcpSubtasksByNetworkIds(convertedSubtasks, networkRequests); |
| convertedSubtasks = filterMcpSubtasksByEntityConsistency(convertedSubtasks, base.task.instruction || ""); |
| } |
|
|
| |
| |
| const hasAnyCalls = subtasksHaveCalls(convertedSubtasks); |
|
|
| if ( |
| mode !== "mcp" && |
| !hasAnyCalls && |
| Array.isArray(taskPackage.golden_trajectory) && |
| taskPackage.golden_trajectory.length > 0 |
| ) { |
| const derived = deriveSubtasksFromTrajectory( |
| taskPackage.golden_trajectory, |
| networkRequests, |
| mode, |
| networkEntryIndex, |
| mcpCallIndex, |
| ); |
| if (derived.length > 0) { |
| convertedSubtasks = derived; |
| } |
| } |
|
|
| if (convertedSubtasks.length > 0) { |
| base.task.subtasks = convertedSubtasks; |
| } |
|
|
| if (mode === "mcp" && outputRubrics.length) { |
| outputRubrics = simplifyRubricCheckerKeysForMcp(outputRubrics, convertedSubtasks); |
| } |
|
|
| |
| |
| if (outputRubrics.length > 0) { |
| base.task.rubrics = outputRubrics.map(function (rubric) { |
| return convertRubricToExtensionFormat(rubric); |
| }); |
| } |
|
|
| |
| base.rule_profile = taskPackage.rule_profile; |
| base.instruction = taskPackage.instruction || base.task.instruction || ""; |
| base.subtasks = cloneJson(taskPackage.subtasks || []); |
| base.rubrics = cloneJson(taskPackage.rubrics || []); |
| base.recommended_groups = cloneJson(taskPackage.subtasks || []); |
| base.recommended_rubrics = cloneJson(taskPackage.rubrics || []); |
| base.rubric_checkers = |
| mode === "mcp" && outputRubrics.length |
| ? remapCheckerCatalogForStrictKeys(taskPackage.rubric_checkers, taskPackage.rubrics || [], outputRubrics) |
| : taskPackage.rubric_checkers; |
| base.dbdiff_criteria = taskPackage.dbdiff_criteria; |
| base.pass_policy = taskPackage.pass_policy; |
| const strictGoldenTrajectory = |
| mode === "mcp" |
| ? buildStrictGoldenTrajectoryFromExtensionSubtasks(base.task.subtasks || [], mode) |
| : taskPackage.golden_trajectory; |
| base.golden_trajectory = |
| mode === "mcp" |
| ? (Array.isArray(strictGoldenTrajectory) ? strictGoldenTrajectory : []) |
| : taskPackage.golden_trajectory; |
| if (taskPackage.dbdiff_canonical !== undefined) base.dbdiff_canonical = taskPackage.dbdiff_canonical; |
| if (taskPackage.validation !== undefined) base.validation = taskPackage.validation; |
| if (taskPackage.network !== undefined) base.network = taskPackage.network; |
|
|
| |
| |
|
|
| return base; |
| } |
|
|
| function findEntryPath(zip: JSZip, re: RegExp): string | null { |
| let found: string | null = null; |
| zip.forEach(function (path, entry) { |
| if (!entry.dir && re.test(path)) found = path; |
| }); |
| return found; |
| } |
|
|
| |
| |
| export async function buildAnalysisZipBuffer( |
| options: BuildAnalysisZipOptions, |
| ): Promise<BuildAnalysisZipResult> { |
| options = options || ({} as BuildAnalysisZipOptions); |
| const originalBytes = options.originalZipBytes; |
| const originalFilename = options.originalFilename || "analysis.zip"; |
| const taskPackage = options.taskPackage; |
| const mode = options.mode === "api" ? "api" : "mcp"; |
| if (!originalBytes) throw new Error("็ผบๅฐๅๅง zip ๅญ่๏ผ่ฏท้ๆฐไธไผ zip ๅๅไธ่ฝฝใ"); |
| if (!taskPackage) throw new Error("็ผบๅฐๅๆ็ปๆ๏ผ่ฏทๅ
ๅฎๆๅๆใ"); |
|
|
| |
| |
| const zip = await JSZip.loadAsync(originalBytes as any); |
|
|
| |
| const packageForOutput = remapTrajectoryForMode(taskPackage, mode); |
|
|
| |
| const taskEntryPath = findEntryPath(zip, /(^|\/)task\.json$/i) || "task.json"; |
|
|
| |
| let originalTaskJson: AnyObj | null = null; |
| let originalNetworkJson: any = null; |
| const taskEntry = zip.file(taskEntryPath); |
| if (taskEntry) { |
| try { |
| const rawText = await taskEntry.async("text"); |
| originalTaskJson = JSON.parse(rawText); |
| } catch (_) { |
| |
| } |
| } |
|
|
| const networkEntryPath = findEntryPath(zip, /(^|\/)network\.json$/i); |
| const networkEntry = networkEntryPath ? zip.file(networkEntryPath) : null; |
| if (networkEntry) { |
| try { |
| originalNetworkJson = JSON.parse(await networkEntry.async("text")); |
| } catch (_) { |
| originalNetworkJson = null; |
| } |
| } |
|
|
| |
| const outputTaskJson = buildOutputTaskJson(originalTaskJson, packageForOutput, mode, originalNetworkJson); |
| zip.file(taskEntryPath, JSON.stringify(outputTaskJson, null, 2)); |
|
|
| const buffer = await zip.generateAsync({ |
| type: "nodebuffer", |
| compression: "DEFLATE", |
| compressionOptions: { level: 6 }, |
| }); |
|
|
| const baseName = String(originalFilename || "analysis.zip").replace(/\.zip$/i, "") || "analysis"; |
| const outName = baseName + ".task-package.zip"; |
| return { buffer: buffer as Buffer, filename: outName }; |
| } |
|
|