oh-my-promptfoo 1.22.3 → 1.22.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/providers.js
CHANGED
|
@@ -1320,7 +1320,7 @@ var import_ignore = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
|
|
|
1320
1320
|
factory.default = factory;
|
|
1321
1321
|
module.exports.isPathValid = isPathValid;
|
|
1322
1322
|
define(module.exports, Symbol.for("setupWindows"), setupWindows);
|
|
1323
|
-
})))()
|
|
1323
|
+
})))());
|
|
1324
1324
|
const PACKAGE = "@allagents/promptfoo-integration";
|
|
1325
1325
|
const MARKER = ".allagents-owner.json";
|
|
1326
1326
|
function contained(root, path) {
|
package/dist/workspace-reader.js
CHANGED
|
@@ -10,7 +10,17 @@ if (!root || !isAbsolute(root) || !(await stat(root)).isDirectory()) throw new E
|
|
|
10
10
|
const physicalRoot = await realpath(root);
|
|
11
11
|
const sourceRoots = JSON.parse(process.argv[3] ?? "{}");
|
|
12
12
|
if (!sourceRoots || typeof sourceRoots !== "object" || Array.isArray(sourceRoots)) throw new Error("Workspace reader source roots must be an object");
|
|
13
|
-
|
|
13
|
+
const declared = [];
|
|
14
|
+
for (const [source, target] of Object.entries(sourceRoots)) {
|
|
15
|
+
if (!source || source.split("/").some((part) => !part || part === "." || part === "..") || isAbsolute(source) || source.includes("\\") || typeof target !== "string" || !isAbsolute(target) || !(await lstat(resolve(root, source))).isSymbolicLink() || !(await stat(target)).isDirectory()) throw new Error("Workspace reader source root is not a declared protected directory link");
|
|
16
|
+
const physical = await realpath(target);
|
|
17
|
+
if (await realpath(resolve(root, source)) !== physical) throw new Error("Workspace reader source root is not a declared protected directory link");
|
|
18
|
+
declared.push({
|
|
19
|
+
source,
|
|
20
|
+
target,
|
|
21
|
+
physical
|
|
22
|
+
});
|
|
23
|
+
}
|
|
14
24
|
const MAX_BYTES = 262144;
|
|
15
25
|
const MAX_ENTRIES = 2e3;
|
|
16
26
|
const tools = [{
|
|
@@ -71,11 +81,11 @@ function within(root, path) {
|
|
|
71
81
|
async function contained(path) {
|
|
72
82
|
if (typeof path !== "string" || !path || path.includes("\0")) throw new Error("path must name a workspace or declared protected source entry");
|
|
73
83
|
const target = resolve(root, path);
|
|
74
|
-
const declaredTarget =
|
|
84
|
+
const declaredTarget = declared.some(({ target: allowed, physical }) => within(allowed, target) || within(physical, target));
|
|
75
85
|
if (!within(root, target) && !(isAbsolute(path) && (within(physicalRoot, target) || declaredTarget))) throw new Error("path must stay inside the workspace or declared protected sources");
|
|
76
86
|
const physical = await realpath(target);
|
|
77
87
|
if (within(physicalRoot, physical)) return physical;
|
|
78
|
-
for (const
|
|
88
|
+
for (const { source, target: allowed, physical: allowedPhysical } of declared) if ((within(resolve(root, source), target) || isAbsolute(path) && (within(allowed, target) || within(allowedPhysical, target))) && within(allowedPhysical, physical)) return physical;
|
|
79
89
|
throw new Error("path follows a symlink outside the workspace or declared protected sources");
|
|
80
90
|
}
|
|
81
91
|
function integer(value, fallback, maximum, minimum = 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-reader.js","names":[],"sources":["../src/workspace-reader.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, opendir, realpath, stat } from \"node:fs/promises\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { createInterface } from \"node:readline\";\n\n// Codex normally reads text through its shell. Graders disable that tool and get\n// this deliberately smaller MCP surface instead. No command or writable handle\n// is accepted; protected source links remain readable just like native Read tools.\nconst root = process.argv[2];\nif (!root || !isAbsolute(root) || !(await stat(root)).isDirectory())\n throw new Error(\"Workspace reader requires an absolute live workspace directory\");\nconst physicalRoot = await realpath(root);\nconst sourceRoots = JSON.parse(process.argv[3] ?? \"{}\") as Record<string, string>;\nif (!sourceRoots || typeof sourceRoots !== \"object\" || Array.isArray(sourceRoots))\n throw new Error(\"Workspace reader source roots must be an object\");\nfor (const [source, target] of Object.entries(sourceRoots)) {\n if (\n !source ||\n source.split(\"/\").some((part) => !part || part === \".\" || part === \"..\") ||\n isAbsolute(source) ||\n source.includes(\"\\\\\") ||\n typeof target !== \"string\" ||\n !isAbsolute(target) ||\n !(await lstat(resolve(root, source))).isSymbolicLink() ||\n (await realpath(resolve(root, source))) !== target ||\n !(await stat(target)).isDirectory()\n )\n throw new Error(\"Workspace reader source root is not a declared protected directory link\");\n}\nconst MAX_BYTES = 256 * 1024;\nconst MAX_ENTRIES = 2000;\nconst tools = [\n {\n name: \"read_file\",\n description: \"Read UTF-8 workspace file contents, with bounded byte pagination.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n offset: { type: \"integer\", minimum: 0 },\n length: { type: \"integer\", minimum: 1, maximum: MAX_BYTES },\n },\n required: [\"path\"],\n additionalProperties: false,\n },\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n },\n {\n name: \"list_directory\",\n description: \"List workspace directory entries, with bounded pagination.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n offset: { type: \"integer\", minimum: 0 },\n limit: { type: \"integer\", minimum: 1, maximum: MAX_ENTRIES },\n },\n required: [\"path\"],\n additionalProperties: false,\n },\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n },\n];\n\nfunction within(root: string, path: string): boolean {\n const rel = relative(root, path);\n return rel !== \"..\" && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);\n}\nasync function contained(path: unknown): Promise<string> {\n if (typeof path !== \"string\" || !path || path.includes(\"\\0\"))\n throw new Error(\"path must name a workspace or declared protected source entry\");\n const target = resolve(root, path);\n const declaredTarget = Object.values(sourceRoots).some((allowed) => within(allowed, target));\n if (\n !within(root, target) &&\n !(isAbsolute(path) && (within(physicalRoot, target) || declaredTarget))\n )\n throw new Error(\"path must stay inside the workspace or declared protected sources\");\n const physical = await realpath(target);\n if (within(physicalRoot, physical)) return physical;\n for (const [source, allowed] of Object.entries(sourceRoots))\n if (\n (within(resolve(root, source), target) || (isAbsolute(path) && within(allowed, target))) &&\n within(allowed, physical)\n )\n return physical;\n throw new Error(\"path follows a symlink outside the workspace or declared protected sources\");\n}\nfunction integer(value: unknown, fallback: number, maximum: number, minimum = 0): number {\n if (value === undefined) return fallback;\n if (!Number.isSafeInteger(value) || (value as number) < minimum || (value as number) > maximum)\n throw new Error(\"Pagination must use bounded nonnegative integers\");\n return value as number;\n}\nasync function callTool(name: unknown, args: Record<string, unknown>) {\n const path = await contained(args.path);\n const offset = integer(args.offset, 0, Number.MAX_SAFE_INTEGER);\n if (name === \"read_file\") {\n if (Object.keys(args).some((key) => ![\"path\", \"offset\", \"length\"].includes(key)))\n throw new Error(\"Unsupported read_file argument\");\n const length = integer(args.length, MAX_BYTES, MAX_BYTES, 1);\n const handle = await open(\n path,\n constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW,\n );\n try {\n const info = await handle.stat();\n if (!info.isFile()) throw new Error(\"path must name a regular file\");\n const bytes = Buffer.alloc(Math.min(length, Math.max(0, info.size - offset)));\n const { bytesRead } = await handle.read(bytes, 0, bytes.length, offset);\n return {\n path: args.path,\n offset,\n nextOffset: offset + bytesRead,\n size: info.size,\n truncated: offset + bytesRead < info.size,\n text: bytes.subarray(0, bytesRead).toString(\"utf8\"),\n };\n } finally {\n await handle.close();\n }\n }\n if (name === \"list_directory\") {\n if (Object.keys(args).some((key) => ![\"path\", \"offset\", \"limit\"].includes(key)))\n throw new Error(\"Unsupported list_directory argument\");\n const limit = integer(args.limit, MAX_ENTRIES, MAX_ENTRIES, 1);\n const entries: { name: string; type: string }[] = [];\n let index = 0;\n let truncated = false;\n for await (const entry of await opendir(path)) {\n if (index++ < offset) continue;\n if (entries.length === limit) {\n truncated = true;\n break;\n }\n entries.push({\n name: entry.name,\n type: entry.isDirectory()\n ? \"directory\"\n : entry.isFile()\n ? \"file\"\n : entry.isSymbolicLink()\n ? \"symlink\"\n : \"other\",\n });\n }\n return { path: args.path, offset, nextOffset: offset + entries.length, truncated, entries };\n }\n throw new Error(\"Unknown workspace inspection tool\");\n}\n\nlet pendingBytes = 0;\nprocess.stdin.on(\"data\", (chunk: Buffer) => {\n let start = 0;\n while (start < chunk.length) {\n const newline = chunk.indexOf(10, start);\n pendingBytes += (newline < 0 ? chunk.length : newline) - start;\n if (pendingBytes > 1024 * 1024) {\n process.exitCode = 1;\n input.close();\n process.stdin.destroy();\n return;\n }\n if (newline < 0) break;\n pendingBytes = 0;\n start = newline + 1;\n }\n});\nconst input = createInterface({ input: process.stdin, crlfDelay: Infinity });\nfor await (const line of input) {\n let id: unknown = null;\n try {\n if (Buffer.byteLength(line) > 1024 * 1024) throw new Error(\"MCP request exceeds byte limit\");\n const request = JSON.parse(line);\n if (!request || request.jsonrpc !== \"2.0\" || typeof request.method !== \"string\")\n throw new Error(\"Invalid JSON-RPC request\");\n if (request.id === undefined) continue;\n id = request.id;\n if (typeof id !== \"string\" && typeof id !== \"number\") throw new Error(\"Invalid request id\");\n let result: unknown;\n if (request.method === \"initialize\") {\n result = {\n protocolVersion: \"2024-11-05\",\n capabilities: { tools: {} },\n serverInfo: { name: \"oh-my-promptfoo-workspace-reader\", version: \"1\" },\n };\n } else if (request.method === \"ping\") result = {};\n else if (request.method === \"tools/list\") result = { tools };\n else if (request.method === \"tools/call\") {\n try {\n const args = request.params?.arguments ?? {};\n if (!args || typeof args !== \"object\" || Array.isArray(args))\n throw new Error(\"Invalid tool arguments\");\n result = {\n content: [\n { type: \"text\", text: JSON.stringify(await callTool(request.params?.name, args)) },\n ],\n };\n } catch (error) {\n result = {\n isError: true,\n content: [{ type: \"text\", text: error instanceof Error ? error.message : String(error) }],\n };\n }\n } else throw new Error(\"Unsupported MCP method\");\n process.stdout.write(`${JSON.stringify({ jsonrpc: \"2.0\", id, result })}\\n`);\n } catch (error) {\n process.stdout.write(\n `${JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n error: { code: -32600, message: error instanceof Error ? error.message : String(error) },\n })}\\n`,\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workspace-reader.js","names":[],"sources":["../src/workspace-reader.ts"],"sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, opendir, realpath, stat } from \"node:fs/promises\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { createInterface } from \"node:readline\";\n\n// Codex normally reads text through its shell. Graders disable that tool and get\n// this deliberately smaller MCP surface instead. No command or writable handle\n// is accepted; protected source links remain readable just like native Read tools.\nconst root = process.argv[2];\nif (!root || !isAbsolute(root) || !(await stat(root)).isDirectory())\n throw new Error(\"Workspace reader requires an absolute live workspace directory\");\nconst physicalRoot = await realpath(root);\nconst sourceRoots = JSON.parse(process.argv[3] ?? \"{}\") as Record<string, string>;\nif (!sourceRoots || typeof sourceRoots !== \"object\" || Array.isArray(sourceRoots))\n throw new Error(\"Workspace reader source roots must be an object\");\n// Windows tmpdir() can spell a declared target with 8.3 short names (RUNNER~1)\n// while realpath returns long names, so compare canonical forms of both sides.\n// The link itself must still be a symlink and the target a real directory.\nconst declared: { source: string; target: string; physical: string }[] = [];\nfor (const [source, target] of Object.entries(sourceRoots)) {\n if (\n !source ||\n source.split(\"/\").some((part) => !part || part === \".\" || part === \"..\") ||\n isAbsolute(source) ||\n source.includes(\"\\\\\") ||\n typeof target !== \"string\" ||\n !isAbsolute(target) ||\n !(await lstat(resolve(root, source))).isSymbolicLink() ||\n !(await stat(target)).isDirectory()\n )\n throw new Error(\"Workspace reader source root is not a declared protected directory link\");\n const physical = await realpath(target);\n if ((await realpath(resolve(root, source))) !== physical)\n throw new Error(\"Workspace reader source root is not a declared protected directory link\");\n declared.push({ source, target, physical });\n}\nconst MAX_BYTES = 256 * 1024;\nconst MAX_ENTRIES = 2000;\nconst tools = [\n {\n name: \"read_file\",\n description: \"Read UTF-8 workspace file contents, with bounded byte pagination.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n offset: { type: \"integer\", minimum: 0 },\n length: { type: \"integer\", minimum: 1, maximum: MAX_BYTES },\n },\n required: [\"path\"],\n additionalProperties: false,\n },\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n },\n {\n name: \"list_directory\",\n description: \"List workspace directory entries, with bounded pagination.\",\n inputSchema: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n offset: { type: \"integer\", minimum: 0 },\n limit: { type: \"integer\", minimum: 1, maximum: MAX_ENTRIES },\n },\n required: [\"path\"],\n additionalProperties: false,\n },\n annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },\n },\n];\n\nfunction within(root: string, path: string): boolean {\n const rel = relative(root, path);\n return rel !== \"..\" && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);\n}\nasync function contained(path: unknown): Promise<string> {\n if (typeof path !== \"string\" || !path || path.includes(\"\\0\"))\n throw new Error(\"path must name a workspace or declared protected source entry\");\n const target = resolve(root, path);\n const declaredTarget = declared.some(\n ({ target: allowed, physical }) => within(allowed, target) || within(physical, target),\n );\n if (\n !within(root, target) &&\n !(isAbsolute(path) && (within(physicalRoot, target) || declaredTarget))\n )\n throw new Error(\"path must stay inside the workspace or declared protected sources\");\n const physical = await realpath(target);\n if (within(physicalRoot, physical)) return physical;\n for (const { source, target: allowed, physical: allowedPhysical } of declared)\n if (\n (within(resolve(root, source), target) ||\n (isAbsolute(path) && (within(allowed, target) || within(allowedPhysical, target)))) &&\n within(allowedPhysical, physical)\n )\n return physical;\n throw new Error(\"path follows a symlink outside the workspace or declared protected sources\");\n}\nfunction integer(value: unknown, fallback: number, maximum: number, minimum = 0): number {\n if (value === undefined) return fallback;\n if (!Number.isSafeInteger(value) || (value as number) < minimum || (value as number) > maximum)\n throw new Error(\"Pagination must use bounded nonnegative integers\");\n return value as number;\n}\nasync function callTool(name: unknown, args: Record<string, unknown>) {\n const path = await contained(args.path);\n const offset = integer(args.offset, 0, Number.MAX_SAFE_INTEGER);\n if (name === \"read_file\") {\n if (Object.keys(args).some((key) => ![\"path\", \"offset\", \"length\"].includes(key)))\n throw new Error(\"Unsupported read_file argument\");\n const length = integer(args.length, MAX_BYTES, MAX_BYTES, 1);\n const handle = await open(\n path,\n constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW,\n );\n try {\n const info = await handle.stat();\n if (!info.isFile()) throw new Error(\"path must name a regular file\");\n const bytes = Buffer.alloc(Math.min(length, Math.max(0, info.size - offset)));\n const { bytesRead } = await handle.read(bytes, 0, bytes.length, offset);\n return {\n path: args.path,\n offset,\n nextOffset: offset + bytesRead,\n size: info.size,\n truncated: offset + bytesRead < info.size,\n text: bytes.subarray(0, bytesRead).toString(\"utf8\"),\n };\n } finally {\n await handle.close();\n }\n }\n if (name === \"list_directory\") {\n if (Object.keys(args).some((key) => ![\"path\", \"offset\", \"limit\"].includes(key)))\n throw new Error(\"Unsupported list_directory argument\");\n const limit = integer(args.limit, MAX_ENTRIES, MAX_ENTRIES, 1);\n const entries: { name: string; type: string }[] = [];\n let index = 0;\n let truncated = false;\n for await (const entry of await opendir(path)) {\n if (index++ < offset) continue;\n if (entries.length === limit) {\n truncated = true;\n break;\n }\n entries.push({\n name: entry.name,\n type: entry.isDirectory()\n ? \"directory\"\n : entry.isFile()\n ? \"file\"\n : entry.isSymbolicLink()\n ? \"symlink\"\n : \"other\",\n });\n }\n return { path: args.path, offset, nextOffset: offset + entries.length, truncated, entries };\n }\n throw new Error(\"Unknown workspace inspection tool\");\n}\n\nlet pendingBytes = 0;\nprocess.stdin.on(\"data\", (chunk: Buffer) => {\n let start = 0;\n while (start < chunk.length) {\n const newline = chunk.indexOf(10, start);\n pendingBytes += (newline < 0 ? chunk.length : newline) - start;\n if (pendingBytes > 1024 * 1024) {\n process.exitCode = 1;\n input.close();\n process.stdin.destroy();\n return;\n }\n if (newline < 0) break;\n pendingBytes = 0;\n start = newline + 1;\n }\n});\nconst input = createInterface({ input: process.stdin, crlfDelay: Infinity });\nfor await (const line of input) {\n let id: unknown = null;\n try {\n if (Buffer.byteLength(line) > 1024 * 1024) throw new Error(\"MCP request exceeds byte limit\");\n const request = JSON.parse(line);\n if (!request || request.jsonrpc !== \"2.0\" || typeof request.method !== \"string\")\n throw new Error(\"Invalid JSON-RPC request\");\n if (request.id === undefined) continue;\n id = request.id;\n if (typeof id !== \"string\" && typeof id !== \"number\") throw new Error(\"Invalid request id\");\n let result: unknown;\n if (request.method === \"initialize\") {\n result = {\n protocolVersion: \"2024-11-05\",\n capabilities: { tools: {} },\n serverInfo: { name: \"oh-my-promptfoo-workspace-reader\", version: \"1\" },\n };\n } else if (request.method === \"ping\") result = {};\n else if (request.method === \"tools/list\") result = { tools };\n else if (request.method === \"tools/call\") {\n try {\n const args = request.params?.arguments ?? {};\n if (!args || typeof args !== \"object\" || Array.isArray(args))\n throw new Error(\"Invalid tool arguments\");\n result = {\n content: [\n { type: \"text\", text: JSON.stringify(await callTool(request.params?.name, args)) },\n ],\n };\n } catch (error) {\n result = {\n isError: true,\n content: [{ type: \"text\", text: error instanceof Error ? error.message : String(error) }],\n };\n }\n } else throw new Error(\"Unsupported MCP method\");\n process.stdout.write(`${JSON.stringify({ jsonrpc: \"2.0\", id, result })}\\n`);\n } catch (error) {\n process.stdout.write(\n `${JSON.stringify({\n jsonrpc: \"2.0\",\n id,\n error: { code: -32600, message: error instanceof Error ? error.message : String(error) },\n })}\\n`,\n );\n }\n}\n"],"mappings":"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"}
|