@render-harness/cap-filesystem 0.1.4 → 0.2.0

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 CHANGED
@@ -3,13 +3,19 @@ import { dirname, join, resolve, sep } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { definePack } from '@render-harness/registry';
5
5
 
6
+ // src/index.ts
7
+
8
+ // package.json
9
+ var package_default = {
10
+ version: "0.2.0"};
11
+
6
12
  // src/index.ts
7
13
  var HERE = dirname(fileURLToPath(import.meta.url));
8
14
  var SKILLS_DIR = join(HERE, "..", "skills");
9
15
  var DEFAULT_MAX_BYTES = 1048576;
10
16
  var pack = definePack({
11
17
  name: "cap-filesystem",
12
- version: "0.1.0",
18
+ version: package_default.version,
13
19
  envSchema: [],
14
20
  async localTools(ctx) {
15
21
  const cfg = ctx.config ?? {};
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["urlDirname","urlJoin"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * cap-filesystem — path-scoped filesystem tools for the agent.\n *\n * Provides four tools, all hard-scoped to a configured `root` directory:\n *\n * - `fs.read_file({ path })` — read a UTF-8 text file.\n * - `fs.list_dir({ path })` — list a directory's entries.\n * - `fs.write_file({ path, content })` — write a file (creates parents).\n * - `fs.delete_file({ path })` — delete a file.\n *\n * Tool names get prefixed with the pack name on registration, so the\n * model sees them as `cap-filesystem.fs.read_file` etc.\n *\n * Why this is NOT a core builtin: the production worker pserv is\n * multi-tenant. Filesystem access on a process holding every tenant's\n * env vars and disk-mounted user data is unsafe by default. This pack\n * makes it explicit and per-agent: you opt in, you choose the root, and\n * you decide whether writes are allowed.\n *\n * Usage in render-harness.yaml:\n *\n * capabilities:\n * - pack: \"@render-harness/cap-filesystem\"\n * config:\n * root: \"/var/data/agent-workspace\" # required; absolute path\n * readOnly: false # optional; default false\n * maxBytes: 1048576 # optional read/write cap\n *\n * Path scoping enforces:\n * - Inputs are joined to root and resolved.\n * - The resolved path must remain under root (`..` traversal blocked).\n * - Symlinks are followed via realpath and re-checked against root, so\n * a symlink under root pointing at /etc/passwd cannot escape.\n */\n\nimport { mkdir, readdir, readFile, realpath, rm, stat, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve, sep, dirname as urlDirname, join as urlJoin } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { LocalToolHandler, SkillMetadata } from \"@render-harness/core\";\nimport { definePack, type PackContext } from \"@render-harness/registry\";\n\nconst HERE = urlDirname(fileURLToPath(import.meta.url));\nconst SKILLS_DIR = urlJoin(HERE, \"..\", \"skills\");\n\ninterface FsConfig {\n root?: string;\n readOnly?: boolean;\n maxBytes?: number;\n}\n\nconst DEFAULT_MAX_BYTES = 1_048_576; // 1 MB\n\nconst pack = definePack({\n name: \"cap-filesystem\",\n version: \"0.1.0\",\n envSchema: [],\n async localTools(ctx: PackContext): Promise<LocalToolHandler[]> {\n const cfg = (ctx.config ?? {}) as FsConfig;\n if (!cfg.root || typeof cfg.root !== \"string\") {\n throw new Error(\n \"cap-filesystem: `config.root` is required (absolute path the agent can read/write).\",\n );\n }\n // Resolve symlinks on the root once so subsequent realpath() checks of\n // children can be compared apples-to-apples. On macOS in particular,\n // /var → /private/var, and without this every legit path would look\n // like a symlink-escape.\n const resolvedRoot = resolve(cfg.root);\n let root: string;\n try {\n root = await realpath(resolvedRoot);\n } catch {\n // If the root doesn't exist yet, fall back to the literal path. The\n // tools will still try realpath on each access; once the dir gets\n // created the comparison will work.\n root = resolvedRoot;\n }\n const readOnly = cfg.readOnly === true;\n const maxBytes = clampPositive(cfg.maxBytes, DEFAULT_MAX_BYTES);\n\n const tools: LocalToolHandler[] = [readFileTool(root, maxBytes), listDirTool(root)];\n if (!readOnly) {\n tools.push(writeFileTool(root, maxBytes), deleteFileTool(root));\n }\n return tools;\n },\n skills(_ctx: PackContext): SkillMetadata[] {\n return [\n {\n name: \"filesystem\",\n description: \"Use the path-scoped filesystem tools to read, list, write, and delete files.\",\n whenToUse:\n \"When the user asks you to inspect, edit, or save files. All paths are scoped to a configured root; you cannot escape it.\",\n contentPath: urlJoin(SKILLS_DIR, \"filesystem.md\"),\n },\n ];\n },\n});\n\nexport default pack;\n\n// --------------------------------------------------------------------\n// Path scoping\n// --------------------------------------------------------------------\n\ninterface ResolveOk {\n ok: true;\n abs: string;\n}\ninterface ResolveErr {\n ok: false;\n message: string;\n}\ntype ResolveResult = ResolveOk | ResolveErr;\n\nfunction resolveScoped(root: string, requested: string): ResolveResult {\n if (typeof requested !== \"string\" || requested.length === 0) {\n return { ok: false, message: \"path must be a non-empty string\" };\n }\n // Normalize and join. resolve() collapses `..` segments deterministically.\n const joined = resolve(root, requested);\n if (!isUnder(joined, root)) {\n return { ok: false, message: `path \"${requested}\" escapes the configured root` };\n }\n return { ok: true, abs: joined };\n}\n\nfunction isUnder(child: string, parent: string): boolean {\n const rel = parent.endsWith(sep) ? parent : parent + sep;\n return child === parent || child.startsWith(rel);\n}\n\n/**\n * Resolve symlinks and re-check the result is still under root. Used after\n * a successful read or before an existing-file write — a symlink under\n * root pointing at /etc/passwd otherwise escapes the scope.\n */\nasync function realpathScoped(root: string, abs: string): Promise<ResolveResult> {\n let real: string;\n try {\n real = await realpath(abs);\n } catch (err) {\n if (isNotFound(err)) {\n // For writes against new files, realpath fails — caller must check\n // the parent dir instead. Surface a sentinel.\n return { ok: false, message: \"ENOENT\" };\n }\n return { ok: false, message: (err as Error).message };\n }\n if (!isUnder(real, root)) {\n return { ok: false, message: `symlink target \"${real}\" escapes the configured root` };\n }\n return { ok: true, abs: real };\n}\n\nfunction isNotFound(err: unknown): boolean {\n return err instanceof Error && (err as NodeJS.ErrnoException).code === \"ENOENT\";\n}\n\n// --------------------------------------------------------------------\n// Tools\n// --------------------------------------------------------------------\n\nfunction readFileTool(root: string, maxBytes: number): LocalToolHandler {\n return {\n definition: {\n name: \"fs.read_file\",\n description: `Read a UTF-8 text file under the configured root. Returns the file contents (capped at ${maxBytes} bytes).`,\n source: \"pack:cap-filesystem\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n path: {\n type: \"string\",\n description: \"Path relative to (or absolute under) the configured root.\",\n },\n },\n required: [\"path\"],\n },\n },\n handler: async ({ input }) => {\n const path = (input as { path?: string } | null)?.path ?? \"\";\n const r = resolveScoped(root, path);\n if (!r.ok) return { content: `fs.read_file: ${r.message}`, isError: true };\n\n const real = await realpathScoped(root, r.abs);\n if (!real.ok) {\n if (real.message === \"ENOENT\") {\n return { content: `fs.read_file: file not found: ${path}`, isError: true };\n }\n return { content: `fs.read_file: ${real.message}`, isError: true };\n }\n\n try {\n const buf = await readFile(real.abs);\n if (buf.byteLength > maxBytes) {\n const truncated = buf.subarray(0, maxBytes).toString(\"utf8\");\n return {\n content: `${truncated}\\n\\n[... truncated at ${maxBytes} bytes; full file was ${buf.byteLength} bytes ...]`,\n };\n }\n return { content: buf.toString(\"utf8\") };\n } catch (err) {\n return {\n content: `fs.read_file: ${(err as Error).message}`,\n isError: true,\n };\n }\n },\n };\n}\n\nfunction listDirTool(root: string): LocalToolHandler {\n return {\n definition: {\n name: \"fs.list_dir\",\n description: \"List entries in a directory under the configured root.\",\n source: \"pack:cap-filesystem\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n path: {\n type: \"string\",\n description: \"Path relative to the configured root. Use '.' for root itself.\",\n },\n },\n },\n },\n handler: async ({ input }) => {\n const path = (input as { path?: string } | null)?.path ?? \".\";\n const r = resolveScoped(root, path);\n if (!r.ok) return { content: `fs.list_dir: ${r.message}`, isError: true };\n\n const real = await realpathScoped(root, r.abs);\n if (!real.ok) {\n if (real.message === \"ENOENT\") {\n return { content: `fs.list_dir: directory not found: ${path}`, isError: true };\n }\n return { content: `fs.list_dir: ${real.message}`, isError: true };\n }\n\n try {\n const entries = await readdir(real.abs, { withFileTypes: true });\n if (entries.length === 0) return { content: \"(empty directory)\" };\n const lines = await Promise.all(\n entries.map(async (e) => {\n const full = join(real.abs, e.name);\n const kind = e.isDirectory()\n ? \"dir\"\n : e.isSymbolicLink()\n ? \"link\"\n : e.isFile()\n ? \"file\"\n : \"other\";\n if (kind === \"file\") {\n try {\n const s = await stat(full);\n return `${kind}\\t${e.name}\\t${s.size}b`;\n } catch {\n return `${kind}\\t${e.name}`;\n }\n }\n return `${kind}\\t${e.name}`;\n }),\n );\n return { content: lines.join(\"\\n\") };\n } catch (err) {\n return { content: `fs.list_dir: ${(err as Error).message}`, isError: true };\n }\n },\n };\n}\n\nfunction writeFileTool(root: string, maxBytes: number): LocalToolHandler {\n return {\n definition: {\n name: \"fs.write_file\",\n description: `Write a UTF-8 text file under the configured root. Creates parent directories. Cap: ${maxBytes} bytes.`,\n source: \"pack:cap-filesystem\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n path: { type: \"string\", description: \"Path relative to the configured root.\" },\n content: { type: \"string\", description: \"File content (UTF-8).\" },\n },\n required: [\"path\", \"content\"],\n },\n },\n handler: async ({ input }) => {\n const args = (input ?? {}) as { path?: string; content?: string };\n if (!args.path || typeof args.path !== \"string\") {\n return { content: \"fs.write_file: missing `path`\", isError: true };\n }\n if (typeof args.content !== \"string\") {\n return { content: \"fs.write_file: missing `content` (must be a string)\", isError: true };\n }\n if (Buffer.byteLength(args.content, \"utf8\") > maxBytes) {\n return {\n content: `fs.write_file: content exceeds cap of ${maxBytes} bytes`,\n isError: true,\n };\n }\n\n const r = resolveScoped(root, args.path);\n if (!r.ok) return { content: `fs.write_file: ${r.message}`, isError: true };\n\n // If the file already exists, follow symlinks and re-check.\n // For new files, check the parent directory.\n const existing = await realpathScoped(root, r.abs);\n let target = r.abs;\n if (existing.ok) {\n target = existing.abs;\n } else if (existing.message !== \"ENOENT\") {\n return { content: `fs.write_file: ${existing.message}`, isError: true };\n } else {\n const parent = await realpathScoped(root, dirname(r.abs));\n if (!parent.ok && parent.message !== \"ENOENT\") {\n return { content: `fs.write_file: ${parent.message}`, isError: true };\n }\n }\n\n try {\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, args.content, \"utf8\");\n return {\n content: `fs.write_file: wrote ${Buffer.byteLength(args.content, \"utf8\")} bytes to ${args.path}`,\n };\n } catch (err) {\n return { content: `fs.write_file: ${(err as Error).message}`, isError: true };\n }\n },\n };\n}\n\nfunction deleteFileTool(root: string): LocalToolHandler {\n return {\n definition: {\n name: \"fs.delete_file\",\n description: \"Delete a file under the configured root. Refuses to delete directories.\",\n source: \"pack:cap-filesystem\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n path: { type: \"string\", description: \"Path relative to the configured root.\" },\n },\n required: [\"path\"],\n },\n },\n handler: async ({ input }) => {\n const path = (input as { path?: string } | null)?.path ?? \"\";\n const r = resolveScoped(root, path);\n if (!r.ok) return { content: `fs.delete_file: ${r.message}`, isError: true };\n\n const real = await realpathScoped(root, r.abs);\n if (!real.ok) {\n if (real.message === \"ENOENT\") {\n return { content: `fs.delete_file: file not found: ${path}`, isError: true };\n }\n return { content: `fs.delete_file: ${real.message}`, isError: true };\n }\n\n try {\n const s = await stat(real.abs);\n if (s.isDirectory()) {\n return {\n content: `fs.delete_file: refusing to delete directory: ${path}`,\n isError: true,\n };\n }\n await rm(real.abs);\n return { content: `fs.delete_file: deleted ${path}` };\n } catch (err) {\n return { content: `fs.delete_file: ${(err as Error).message}`, isError: true };\n }\n },\n };\n}\n\nfunction clampPositive(raw: number | undefined, fallback: number): number {\n if (typeof raw !== \"number\" || !Number.isFinite(raw) || raw <= 0) return fallback;\n return Math.floor(raw);\n}\n"]}
1
+ {"version":3,"sources":["../package.json","../src/index.ts"],"names":["urlDirname","urlJoin"],"mappings":"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","file":"index.js","sourcesContent":["{\n \"name\": \"@render-harness/cap-filesystem\",\n \"version\": \"0.2.0\",\n \"description\": \"Path-scoped filesystem tools for the Render agent harness. Opt-in per agent; the worker pserv is multi-tenant by default so this is NOT a core builtin.\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"skills\"\n ],\n \"keywords\": [\n \"render-harness-cap\",\n \"render-harness\",\n \"filesystem\"\n ],\n \"renderHarness\": {\n \"gallery\": {\n \"label\": \"Filesystem (path-scoped fs tools)\",\n \"envHint\": null\n }\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\"\n },\n \"dependencies\": {\n \"@render-harness/core\": \"workspace:*\",\n \"@render-harness/registry\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.6.2\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^6.0.3\",\n \"vitest\": \"^4.1.5\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/render-lab/render-agent-harness.git\",\n \"directory\": \"packages/capabilities/cap-filesystem\"\n }\n}\n","/**\n * cap-filesystem — path-scoped filesystem tools for the agent.\n *\n * Provides four tools, all hard-scoped to a configured `root` directory:\n *\n * - `fs.read_file({ path })` — read a UTF-8 text file.\n * - `fs.list_dir({ path })` — list a directory's entries.\n * - `fs.write_file({ path, content })` — write a file (creates parents).\n * - `fs.delete_file({ path })` — delete a file.\n *\n * Tool names get prefixed with the pack name on registration, so the\n * model sees them as `cap-filesystem.fs.read_file` etc.\n *\n * Why this is NOT a core builtin: the production worker pserv is\n * multi-tenant. Filesystem access on a process holding every tenant's\n * env vars and disk-mounted user data is unsafe by default. This pack\n * makes it explicit and per-agent: you opt in, you choose the root, and\n * you decide whether writes are allowed.\n *\n * Usage in render-harness.yaml:\n *\n * capabilities:\n * - pack: \"@render-harness/cap-filesystem\"\n * config:\n * root: \"/var/data/agent-workspace\" # required; absolute path\n * readOnly: false # optional; default false\n * maxBytes: 1048576 # optional read/write cap\n *\n * Path scoping enforces:\n * - Inputs are joined to root and resolved.\n * - The resolved path must remain under root (`..` traversal blocked).\n * - Symlinks are followed via realpath and re-checked against root, so\n * a symlink under root pointing at /etc/passwd cannot escape.\n */\n\nimport { mkdir, readdir, readFile, realpath, rm, stat, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve, sep, dirname as urlDirname, join as urlJoin } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { LocalToolHandler, SkillMetadata } from \"@render-harness/core\";\nimport { definePack, type PackContext } from \"@render-harness/registry\";\nimport pkg from \"../package.json\" with { type: \"json\" };\n\nconst HERE = urlDirname(fileURLToPath(import.meta.url));\nconst SKILLS_DIR = urlJoin(HERE, \"..\", \"skills\");\n\ninterface FsConfig {\n root?: string;\n readOnly?: boolean;\n maxBytes?: number;\n}\n\nconst DEFAULT_MAX_BYTES = 1_048_576; // 1 MB\n\nconst pack = definePack({\n name: \"cap-filesystem\",\n version: pkg.version,\n envSchema: [],\n async localTools(ctx: PackContext): Promise<LocalToolHandler[]> {\n const cfg = (ctx.config ?? {}) as FsConfig;\n if (!cfg.root || typeof cfg.root !== \"string\") {\n throw new Error(\n \"cap-filesystem: `config.root` is required (absolute path the agent can read/write).\",\n );\n }\n // Resolve symlinks on the root once so subsequent realpath() checks of\n // children can be compared apples-to-apples. On macOS in particular,\n // /var → /private/var, and without this every legit path would look\n // like a symlink-escape.\n const resolvedRoot = resolve(cfg.root);\n let root: string;\n try {\n root = await realpath(resolvedRoot);\n } catch {\n // If the root doesn't exist yet, fall back to the literal path. The\n // tools will still try realpath on each access; once the dir gets\n // created the comparison will work.\n root = resolvedRoot;\n }\n const readOnly = cfg.readOnly === true;\n const maxBytes = clampPositive(cfg.maxBytes, DEFAULT_MAX_BYTES);\n\n const tools: LocalToolHandler[] = [readFileTool(root, maxBytes), listDirTool(root)];\n if (!readOnly) {\n tools.push(writeFileTool(root, maxBytes), deleteFileTool(root));\n }\n return tools;\n },\n skills(_ctx: PackContext): SkillMetadata[] {\n return [\n {\n name: \"filesystem\",\n description: \"Use the path-scoped filesystem tools to read, list, write, and delete files.\",\n whenToUse:\n \"When the user asks you to inspect, edit, or save files. All paths are scoped to a configured root; you cannot escape it.\",\n contentPath: urlJoin(SKILLS_DIR, \"filesystem.md\"),\n },\n ];\n },\n});\n\nexport default pack;\n\n// --------------------------------------------------------------------\n// Path scoping\n// --------------------------------------------------------------------\n\ninterface ResolveOk {\n ok: true;\n abs: string;\n}\ninterface ResolveErr {\n ok: false;\n message: string;\n}\ntype ResolveResult = ResolveOk | ResolveErr;\n\nfunction resolveScoped(root: string, requested: string): ResolveResult {\n if (typeof requested !== \"string\" || requested.length === 0) {\n return { ok: false, message: \"path must be a non-empty string\" };\n }\n // Normalize and join. resolve() collapses `..` segments deterministically.\n const joined = resolve(root, requested);\n if (!isUnder(joined, root)) {\n return { ok: false, message: `path \"${requested}\" escapes the configured root` };\n }\n return { ok: true, abs: joined };\n}\n\nfunction isUnder(child: string, parent: string): boolean {\n const rel = parent.endsWith(sep) ? parent : parent + sep;\n return child === parent || child.startsWith(rel);\n}\n\n/**\n * Resolve symlinks and re-check the result is still under root. Used after\n * a successful read or before an existing-file write — a symlink under\n * root pointing at /etc/passwd otherwise escapes the scope.\n */\nasync function realpathScoped(root: string, abs: string): Promise<ResolveResult> {\n let real: string;\n try {\n real = await realpath(abs);\n } catch (err) {\n if (isNotFound(err)) {\n // For writes against new files, realpath fails — caller must check\n // the parent dir instead. Surface a sentinel.\n return { ok: false, message: \"ENOENT\" };\n }\n return { ok: false, message: (err as Error).message };\n }\n if (!isUnder(real, root)) {\n return { ok: false, message: `symlink target \"${real}\" escapes the configured root` };\n }\n return { ok: true, abs: real };\n}\n\nfunction isNotFound(err: unknown): boolean {\n return err instanceof Error && (err as NodeJS.ErrnoException).code === \"ENOENT\";\n}\n\n// --------------------------------------------------------------------\n// Tools\n// --------------------------------------------------------------------\n\nfunction readFileTool(root: string, maxBytes: number): LocalToolHandler {\n return {\n definition: {\n name: \"fs.read_file\",\n description: `Read a UTF-8 text file under the configured root. Returns the file contents (capped at ${maxBytes} bytes).`,\n source: \"pack:cap-filesystem\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n path: {\n type: \"string\",\n description: \"Path relative to (or absolute under) the configured root.\",\n },\n },\n required: [\"path\"],\n },\n },\n handler: async ({ input }) => {\n const path = (input as { path?: string } | null)?.path ?? \"\";\n const r = resolveScoped(root, path);\n if (!r.ok) return { content: `fs.read_file: ${r.message}`, isError: true };\n\n const real = await realpathScoped(root, r.abs);\n if (!real.ok) {\n if (real.message === \"ENOENT\") {\n return { content: `fs.read_file: file not found: ${path}`, isError: true };\n }\n return { content: `fs.read_file: ${real.message}`, isError: true };\n }\n\n try {\n const buf = await readFile(real.abs);\n if (buf.byteLength > maxBytes) {\n const truncated = buf.subarray(0, maxBytes).toString(\"utf8\");\n return {\n content: `${truncated}\\n\\n[... truncated at ${maxBytes} bytes; full file was ${buf.byteLength} bytes ...]`,\n };\n }\n return { content: buf.toString(\"utf8\") };\n } catch (err) {\n return {\n content: `fs.read_file: ${(err as Error).message}`,\n isError: true,\n };\n }\n },\n };\n}\n\nfunction listDirTool(root: string): LocalToolHandler {\n return {\n definition: {\n name: \"fs.list_dir\",\n description: \"List entries in a directory under the configured root.\",\n source: \"pack:cap-filesystem\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n path: {\n type: \"string\",\n description: \"Path relative to the configured root. Use '.' for root itself.\",\n },\n },\n },\n },\n handler: async ({ input }) => {\n const path = (input as { path?: string } | null)?.path ?? \".\";\n const r = resolveScoped(root, path);\n if (!r.ok) return { content: `fs.list_dir: ${r.message}`, isError: true };\n\n const real = await realpathScoped(root, r.abs);\n if (!real.ok) {\n if (real.message === \"ENOENT\") {\n return { content: `fs.list_dir: directory not found: ${path}`, isError: true };\n }\n return { content: `fs.list_dir: ${real.message}`, isError: true };\n }\n\n try {\n const entries = await readdir(real.abs, { withFileTypes: true });\n if (entries.length === 0) return { content: \"(empty directory)\" };\n const lines = await Promise.all(\n entries.map(async (e) => {\n const full = join(real.abs, e.name);\n const kind = e.isDirectory()\n ? \"dir\"\n : e.isSymbolicLink()\n ? \"link\"\n : e.isFile()\n ? \"file\"\n : \"other\";\n if (kind === \"file\") {\n try {\n const s = await stat(full);\n return `${kind}\\t${e.name}\\t${s.size}b`;\n } catch {\n return `${kind}\\t${e.name}`;\n }\n }\n return `${kind}\\t${e.name}`;\n }),\n );\n return { content: lines.join(\"\\n\") };\n } catch (err) {\n return { content: `fs.list_dir: ${(err as Error).message}`, isError: true };\n }\n },\n };\n}\n\nfunction writeFileTool(root: string, maxBytes: number): LocalToolHandler {\n return {\n definition: {\n name: \"fs.write_file\",\n description: `Write a UTF-8 text file under the configured root. Creates parent directories. Cap: ${maxBytes} bytes.`,\n source: \"pack:cap-filesystem\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n path: { type: \"string\", description: \"Path relative to the configured root.\" },\n content: { type: \"string\", description: \"File content (UTF-8).\" },\n },\n required: [\"path\", \"content\"],\n },\n },\n handler: async ({ input }) => {\n const args = (input ?? {}) as { path?: string; content?: string };\n if (!args.path || typeof args.path !== \"string\") {\n return { content: \"fs.write_file: missing `path`\", isError: true };\n }\n if (typeof args.content !== \"string\") {\n return { content: \"fs.write_file: missing `content` (must be a string)\", isError: true };\n }\n if (Buffer.byteLength(args.content, \"utf8\") > maxBytes) {\n return {\n content: `fs.write_file: content exceeds cap of ${maxBytes} bytes`,\n isError: true,\n };\n }\n\n const r = resolveScoped(root, args.path);\n if (!r.ok) return { content: `fs.write_file: ${r.message}`, isError: true };\n\n // If the file already exists, follow symlinks and re-check.\n // For new files, check the parent directory.\n const existing = await realpathScoped(root, r.abs);\n let target = r.abs;\n if (existing.ok) {\n target = existing.abs;\n } else if (existing.message !== \"ENOENT\") {\n return { content: `fs.write_file: ${existing.message}`, isError: true };\n } else {\n const parent = await realpathScoped(root, dirname(r.abs));\n if (!parent.ok && parent.message !== \"ENOENT\") {\n return { content: `fs.write_file: ${parent.message}`, isError: true };\n }\n }\n\n try {\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, args.content, \"utf8\");\n return {\n content: `fs.write_file: wrote ${Buffer.byteLength(args.content, \"utf8\")} bytes to ${args.path}`,\n };\n } catch (err) {\n return { content: `fs.write_file: ${(err as Error).message}`, isError: true };\n }\n },\n };\n}\n\nfunction deleteFileTool(root: string): LocalToolHandler {\n return {\n definition: {\n name: \"fs.delete_file\",\n description: \"Delete a file under the configured root. Refuses to delete directories.\",\n source: \"pack:cap-filesystem\",\n inputSchema: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n path: { type: \"string\", description: \"Path relative to the configured root.\" },\n },\n required: [\"path\"],\n },\n },\n handler: async ({ input }) => {\n const path = (input as { path?: string } | null)?.path ?? \"\";\n const r = resolveScoped(root, path);\n if (!r.ok) return { content: `fs.delete_file: ${r.message}`, isError: true };\n\n const real = await realpathScoped(root, r.abs);\n if (!real.ok) {\n if (real.message === \"ENOENT\") {\n return { content: `fs.delete_file: file not found: ${path}`, isError: true };\n }\n return { content: `fs.delete_file: ${real.message}`, isError: true };\n }\n\n try {\n const s = await stat(real.abs);\n if (s.isDirectory()) {\n return {\n content: `fs.delete_file: refusing to delete directory: ${path}`,\n isError: true,\n };\n }\n await rm(real.abs);\n return { content: `fs.delete_file: deleted ${path}` };\n } catch (err) {\n return { content: `fs.delete_file: ${(err as Error).message}`, isError: true };\n }\n },\n };\n}\n\nfunction clampPositive(raw: number | undefined, fallback: number): number {\n if (typeof raw !== \"number\" || !Number.isFinite(raw) || raw <= 0) return fallback;\n return Math.floor(raw);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@render-harness/cap-filesystem",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Path-scoped filesystem tools for the Render agent harness. Opt-in per agent; the worker pserv is multi-tenant by default so this is NOT a core builtin.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -10,7 +10,8 @@
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.js"
13
- }
13
+ },
14
+ "./package.json": "./package.json"
14
15
  },
15
16
  "files": [
16
17
  "dist",
@@ -28,8 +29,8 @@
28
29
  }
29
30
  },
30
31
  "dependencies": {
31
- "@render-harness/core": "0.1.1",
32
- "@render-harness/registry": "0.1.1"
32
+ "@render-harness/registry": "0.2.2",
33
+ "@render-harness/core": "0.2.1"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@types/node": "^25.6.2",
@@ -40,6 +41,11 @@
40
41
  "publishConfig": {
41
42
  "access": "public"
42
43
  },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/render-lab/render-agent-harness.git",
47
+ "directory": "packages/capabilities/cap-filesystem"
48
+ },
43
49
  "scripts": {
44
50
  "build": "tsup",
45
51
  "typecheck": "tsc --noEmit",