@rubytech/taskmaster 1.1.0 → 1.1.1

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.
@@ -5,6 +5,7 @@ import { createBrowserTool } from "./tools/browser-tool.js";
5
5
  import { createCanvasTool } from "./tools/canvas-tool.js";
6
6
  import { createCronTool } from "./tools/cron-tool.js";
7
7
  import { createDocumentTool } from "./tools/document-tool.js";
8
+ import { createDocumentToPdfTool } from "./tools/document-to-pdf-tool.js";
8
9
  import { createGatewayTool } from "./tools/gateway-tool.js";
9
10
  import { createImageTool } from "./tools/image-tool.js";
10
11
  import { createMemoryGetTool, createMemorySaveMediaTool, createMemorySearchTool, createMemoryWriteTool, } from "./tools/memory-tool.js";
@@ -133,6 +134,11 @@ export function createTaskmasterTools(options) {
133
134
  workspaceDir: options?.workspaceDir,
134
135
  }),
135
136
  ...(documentTool ? [documentTool] : []),
137
+ createDocumentToPdfTool({
138
+ config: options?.config,
139
+ agentSessionKey: options?.agentSessionKey,
140
+ sandboxRoot: options?.sandboxRoot,
141
+ }),
136
142
  createCurrentTimeTool({ config: options?.config }),
137
143
  createAuthorizeAdminTool(),
138
144
  createRevokeAdminTool(),
@@ -13,6 +13,7 @@ export const TOOL_GROUPS = {
13
13
  "message_history",
14
14
  "document_read",
15
15
  ],
16
+ "group:documents": ["document_read", "document_to_pdf"],
16
17
  "group:web": ["web_search", "web_fetch"],
17
18
  // Time/date utilities
18
19
  "group:time": ["current_time"],
@@ -57,6 +58,7 @@ export const TOOL_GROUPS = {
57
58
  "memory_write",
58
59
  "message_history",
59
60
  "document_read",
61
+ "document_to_pdf",
60
62
  "web_search",
61
63
  "web_fetch",
62
64
  "image",
@@ -0,0 +1,128 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { Type } from "@sinclair/typebox";
5
+ import { resolveBrowserConfig } from "../../browser/config.js";
6
+ import { resolveBrowserExecutableForPlatform } from "../../browser/chrome.executables.js";
7
+ import { resolveAgentWorkspaceDir, resolveSessionAgentId } from "../agent-scope.js";
8
+ import { assertSandboxPath } from "../sandbox-paths.js";
9
+ import { jsonResult, readStringParam } from "./common.js";
10
+ const DocumentToPdfSchema = Type.Object({
11
+ path: Type.String({
12
+ description: "Path to an HTML file. Use a memory path (e.g. memory/users/+447.../documents/invoices/INV-001.html) " +
13
+ "or an absolute path.",
14
+ }),
15
+ });
16
+ export function createDocumentToPdfTool(options) {
17
+ const cfg = options?.config;
18
+ const agentId = resolveSessionAgentId({
19
+ sessionKey: options?.agentSessionKey,
20
+ config: cfg,
21
+ });
22
+ // Get workspace dir for memory path resolution
23
+ const workspaceDir = cfg ? resolveAgentWorkspaceDir(cfg, agentId) : null;
24
+ const memoryDir = workspaceDir ? path.join(workspaceDir, "memory") : null;
25
+ return {
26
+ label: "Document to PDF",
27
+ name: "document_to_pdf",
28
+ description: "Convert an HTML file to PDF using a headless browser. " +
29
+ "The PDF is written to the same directory as the source file with a .pdf extension. " +
30
+ "Returns the absolute path to the generated PDF.",
31
+ parameters: DocumentToPdfSchema,
32
+ execute: async (_toolCallId, params) => {
33
+ const inputPath = readStringParam(params, "path", { required: true });
34
+ // ── Resolve the path ──────────────────────────────────────────────
35
+ let htmlPath;
36
+ try {
37
+ if (memoryDir && inputPath.startsWith("memory/")) {
38
+ const relativePath = inputPath.slice("memory/".length);
39
+ htmlPath = path.join(memoryDir, relativePath);
40
+ }
41
+ else if (memoryDir && !path.isAbsolute(inputPath) && !inputPath.startsWith(".")) {
42
+ // Check if it exists in memory root
43
+ const candidatePath = path.join(memoryDir, inputPath);
44
+ try {
45
+ await fs.stat(candidatePath);
46
+ htmlPath = candidatePath;
47
+ }
48
+ catch {
49
+ htmlPath = inputPath;
50
+ }
51
+ }
52
+ else {
53
+ htmlPath = inputPath;
54
+ }
55
+ if (!path.isAbsolute(htmlPath)) {
56
+ htmlPath = path.resolve(htmlPath);
57
+ }
58
+ // Apply sandbox constraints if configured
59
+ const sandboxRoot = options?.sandboxRoot?.trim();
60
+ if (sandboxRoot) {
61
+ const sandboxed = await assertSandboxPath({
62
+ filePath: htmlPath,
63
+ cwd: sandboxRoot,
64
+ root: sandboxRoot,
65
+ });
66
+ htmlPath = sandboxed.resolved;
67
+ }
68
+ }
69
+ catch (err) {
70
+ const message = err instanceof Error ? err.message : String(err);
71
+ return jsonResult({ ok: false, error: `Path not accessible: ${message}` });
72
+ }
73
+ // ── Verify the HTML file exists ───────────────────────────────────
74
+ try {
75
+ await fs.stat(htmlPath);
76
+ }
77
+ catch (err) {
78
+ const anyErr = err;
79
+ if (anyErr.code === "ENOENT") {
80
+ return jsonResult({ ok: false, error: `File not found: ${inputPath}` });
81
+ }
82
+ const message = err instanceof Error ? err.message : String(err);
83
+ return jsonResult({ ok: false, error: `Cannot access file: ${message}` });
84
+ }
85
+ // ── Determine output path ─────────────────────────────────────────
86
+ const parsed = path.parse(htmlPath);
87
+ const pdfPath = path.join(parsed.dir, `${parsed.name}.pdf`);
88
+ // ── Find Chrome executable ────────────────────────────────────────
89
+ const browserConfig = resolveBrowserConfig(cfg?.browser);
90
+ const exe = resolveBrowserExecutableForPlatform(browserConfig, process.platform);
91
+ if (!exe) {
92
+ return jsonResult({
93
+ ok: false,
94
+ error: "No Chrome/Chromium browser found. Install Chrome or configure browser.executablePath.",
95
+ });
96
+ }
97
+ // ── Generate PDF with Playwright ──────────────────────────────────
98
+ let browser = null;
99
+ try {
100
+ const { chromium } = await import("playwright-core");
101
+ browser = await chromium.launch({
102
+ executablePath: exe.path,
103
+ headless: true,
104
+ args: ["--no-sandbox", "--disable-gpu"],
105
+ });
106
+ const page = await browser.newPage();
107
+ await page.goto(pathToFileURL(htmlPath).href, { waitUntil: "networkidle" });
108
+ const pdfBuffer = await page.pdf({ printBackground: true, format: "A4" });
109
+ await fs.writeFile(pdfPath, pdfBuffer);
110
+ return jsonResult({ ok: true, path: pdfPath });
111
+ }
112
+ catch (err) {
113
+ const message = err instanceof Error ? err.message : String(err);
114
+ return jsonResult({ ok: false, error: `PDF generation failed: ${message}` });
115
+ }
116
+ finally {
117
+ if (browser) {
118
+ try {
119
+ await browser.close();
120
+ }
121
+ catch {
122
+ // Ignore close errors
123
+ }
124
+ }
125
+ }
126
+ },
127
+ };
128
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.0",
3
- "commit": "3f8e0229db78dc2244ee3af29903c5e2e9874d62",
4
- "builtAt": "2026-02-24T11:25:47.903Z"
2
+ "version": "1.1.1",
3
+ "commit": "0c8513071e0c9ecf30e573e15ed8cd2bef18e515",
4
+ "builtAt": "2026-02-24T13:26:12.563Z"
5
5
  }
@@ -127,6 +127,7 @@ export function buildDefaultAgentList(workspaceRoot) {
127
127
  "memory_write",
128
128
  "memory_save_media",
129
129
  "document_read",
130
+ "document_to_pdf",
130
131
  "image",
131
132
  "web_search",
132
133
  "web_fetch",
@@ -188,4 +188,32 @@ export const LEGACY_CONFIG_MIGRATIONS_PART_3 = [
188
188
  }
189
189
  },
190
190
  },
191
+ {
192
+ id: "admin-agent-tools-add-document_to_pdf",
193
+ describe: "Add document_to_pdf to admin agent tools.allow",
194
+ apply: (raw, changes) => {
195
+ const agents = getRecord(raw.agents);
196
+ const list = getAgentsList(agents);
197
+ for (const entry of list) {
198
+ if (!isRecord(entry))
199
+ continue;
200
+ const id = typeof entry.id === "string" ? entry.id.trim() : "";
201
+ if (!id)
202
+ continue;
203
+ const isAdmin = id === "admin" || id.endsWith("-admin");
204
+ if (!isAdmin)
205
+ continue;
206
+ const tools = getRecord(entry.tools);
207
+ if (!tools)
208
+ continue;
209
+ if (!Array.isArray(tools.allow))
210
+ continue;
211
+ const allow = tools.allow;
212
+ if (!allow.includes("document_to_pdf")) {
213
+ allow.push("document_to_pdf");
214
+ changes.push(`Added document_to_pdf to agent "${id}" tools.allow.`);
215
+ }
216
+ }
217
+ },
218
+ },
191
219
  ];