@rubrion/mastra-cloudflare 0.2.1 → 0.3.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/README.md CHANGED
@@ -6,6 +6,7 @@
6
6
  - **`defineAgents`**: agents as plain config, built lazily once per isolate (bindings and secrets are only readable inside a request).
7
7
  - **`d1Memory`**: Mastra Memory on D1, no vector store to pay for.
8
8
  - **`workersAI`**, **`openAICompatible`**, **`providerModel`**: models from Workers AI, any OpenAI-compatible endpoint, or the provider each request carries.
9
+ - **`readFileTool`**, **`browseTool`**, **`jobTools`**: opt-in tools to read attached files, read web pages in a headless browser, and run long work in a Workflow. Each reports what it used, so you can charge whoever asked.
9
10
  - Re-exports `Agent`, `createTool`, `z` and `RequestContext`, so a project needs only this package.
10
11
 
11
12
  The [rubry template](https://github.com/rubrion/rubry) puts it to work: agents plus a middleware with API keys and a settings page.
@@ -100,6 +101,34 @@ providerModel(requestContext, fallback?); // the provider the request carries
100
101
 
101
102
  `workersAI` with `thinking` drops `reasoning_effort` from the binding call: Mastra sends the AI SDK's unified `reasoning` option, and reasoning models such as Gemma 4 stop thinking whenever it is present.
102
103
 
104
+ ## Tools
105
+
106
+ Opt-in, each with `guard(ctx)` (return a sentence to refuse, e.g. no credit left) and `used(ctx, usage)` (charge whoever asked).
107
+
108
+ ```ts
109
+ import { jobTools, readFileTool, z } from '@rubrion/mastra-cloudflare';
110
+ import { browseTool, readPages } from '@rubrion/mastra-cloudflare/browser'; // needs @cloudflare/puppeteer
111
+
112
+ // The text of an attached file (PDF, Word, Excel, CSV, HTML, XML, images), via Workers AI's toMarkdown.
113
+ readFileTool({ ai: () => env.AI, load: async (ref, ctx) => ({ name, bytes }) /* or null: not theirs */, maxChars: 20_000 });
114
+
115
+ // Public pages in a real browser ("browser": { "binding": "BROWSER" }): text and links, up to 3 pages a call.
116
+ // Private addresses, redirects to them and subresources on them are blocked. usage.ms is billed browser time.
117
+ browseTool({ browser: () => env.BROWSER, allow: ['example.com'] /* optional */, used: (ctx, { ms }) => charge(ctx, ms) });
118
+
119
+ // Long work: start_<name> returns a job id at once, check_<name> waits a little and returns the result.
120
+ // The job id carries a tag of its owner, so only they can check it.
121
+ const crawl = jobTools({ name: 'crawl', description: 'Read a list of pages.', input: z.object({ urls: z.array(z.string()) }),
122
+ workflow: () => env.CRAWL, owner: (ctx) => String(ctx?.requestContext?.get('visitor')) });
123
+ // and the Workflow: step.do('read', () => readPages(this.env.BROWSER, event.payload.urls))
124
+ ```
125
+
126
+ Never point the browser at logins or captchas.
127
+
128
+ ## Releases
129
+
130
+ Every push to `main` is released by CI (`.github/workflows/release.yml`, semantic-release): commit messages follow [Conventional Commits](https://www.conventionalcommits.org), and `fix:` publishes a patch, `feat:` a minor, a breaking change (`feat!:` or a `BREAKING CHANGE:` footer) a major. `docs:`, `chore:`, `ci:`, `test:` and `refactor:` publish nothing. npm trusts the workflow (trusted publishing), so there is no token and each version carries provenance. The version lives in git tags (`v0.3.0`) and on npm, not in `package.json`.
131
+
103
132
  ## License
104
133
 
105
134
  GPL-3.0-only
@@ -0,0 +1,54 @@
1
+ import type { BrowserWorker } from '@cloudflare/puppeteer';
2
+ import { type Allow, type ToolCtx } from './tools.js';
3
+ export interface PageText {
4
+ url: string;
5
+ title?: string;
6
+ text?: string;
7
+ truncated?: boolean;
8
+ links?: {
9
+ text: string;
10
+ href: string;
11
+ }[];
12
+ error?: string;
13
+ }
14
+ export interface ReadPagesOptions {
15
+ /** Only these hosts (and their subdomains); any public host when empty. */
16
+ allow?: string[];
17
+ /** Characters of text kept per page. */
18
+ maxChars?: number;
19
+ /** Per page. */
20
+ timeoutMs?: number;
21
+ /** Links kept per page, for the model to follow. */
22
+ maxLinks?: number;
23
+ }
24
+ /**
25
+ * Open each URL in one browser, one after another, and return its readable text.
26
+ * Requests to private addresses (redirects and subresources too) are blocked, and
27
+ * images, media and fonts are skipped. `ms` is how long the browser was open:
28
+ * what Browser Run bills.
29
+ */
30
+ export declare function readPages(binding: BrowserWorker, urls: string[], o?: ReadPagesOptions): Promise<{
31
+ pages: PageText[];
32
+ ms: number;
33
+ }>;
34
+ export interface BrowseOptions extends ReadPagesOptions {
35
+ /** The `browser` binding. */
36
+ browser: (ctx: ToolCtx) => BrowserWorker;
37
+ id?: string;
38
+ description?: string;
39
+ /** Pages per call. */
40
+ maxPages?: number;
41
+ allow?: string[];
42
+ /** Checked before opening the browser; return a message to refuse. */
43
+ guard?: Allow;
44
+ /** After, with the browser time, so the app can charge whoever asked. */
45
+ used?: (ctx: ToolCtx, usage: {
46
+ ms: number;
47
+ pages: number;
48
+ }) => Promise<void> | void;
49
+ }
50
+ /** `browse`: open public web pages in a headless browser and read them. */
51
+ export declare const browseTool: (o: BrowseOptions) => import("@mastra/core/tools").Tool<{
52
+ urls: string[];
53
+ }, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, string, unknown>;
54
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAmB,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAEvE,MAAM,WAAW,QAAQ;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAChC,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAaD;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,GAAE,gBAAqB;;;GA2C/F;AAED,MAAM,WAAW,aAAc,SAAQ,gBAAgB;IACtD,6BAA6B;IAC7B,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,aAAa,CAAC;IACzC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,yEAAyE;IACzE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACpF;AAED,2EAA2E;AAC3E,eAAO,MAAM,UAAU,GAAI,GAAG,aAAa;;4HAkBxC,CAAC"}
@@ -0,0 +1,93 @@
1
+ // Reading web pages in a headless browser (Cloudflare Browser Run, the
2
+ // `browser` binding). Its own entry, `@rubrion/mastra-cloudflare/browser`, so
3
+ // only agents that browse need `@cloudflare/puppeteer` installed; it's loaded on
4
+ // first use, not at startup.
5
+ import { createTool } from '@mastra/core/tools';
6
+ import { z } from 'zod';
7
+ import { clip, publicUrl } from './tools.js';
8
+ // Runs in the page. A string, so this package needs no DOM types.
9
+ const EXTRACT = `(() => {
10
+ const root = document.querySelector('main, article, [role=main]') || document.body;
11
+ for (const el of root.querySelectorAll('script, style, noscript, svg, iframe')) el.remove();
12
+ return {
13
+ title: document.title,
14
+ text: (root.innerText || '').replace(/\\n{3,}/g, '\\n\\n').trim(),
15
+ links: [...document.querySelectorAll('a[href]')].map((a) => ({ text: (a.innerText || '').trim().slice(0, 80), href: a.href })).filter((l) => l.text && /^https?:/.test(l.href)),
16
+ };
17
+ })()`;
18
+ /**
19
+ * Open each URL in one browser, one after another, and return its readable text.
20
+ * Requests to private addresses (redirects and subresources too) are blocked, and
21
+ * images, media and fonts are skipped. `ms` is how long the browser was open:
22
+ * what Browser Run bills.
23
+ */
24
+ export async function readPages(binding, urls, o = {}) {
25
+ const { default: puppeteer } = await import('@cloudflare/puppeteer');
26
+ const began = Date.now();
27
+ const browser = await puppeteer.launch(binding);
28
+ const pages = [];
29
+ try {
30
+ for (const raw of urls) {
31
+ const url = publicUrl(raw, o.allow);
32
+ if (!url) {
33
+ pages.push({ url: raw, error: 'Not a public web address this tool may open.' });
34
+ continue;
35
+ }
36
+ const page = await browser.newPage();
37
+ try {
38
+ await page.setRequestInterception(true);
39
+ page.on('request', (req) => {
40
+ const kind = req.resourceType();
41
+ if (kind === 'image' || kind === 'media' || kind === 'font' || !publicUrl(req.url()))
42
+ req.abort();
43
+ else
44
+ req.continue();
45
+ });
46
+ const res = await page.goto(url.href, { waitUntil: 'domcontentloaded', timeout: o.timeoutMs ?? 15_000 });
47
+ // A moment for scripts that fill the page after load.
48
+ await new Promise((r) => setTimeout(r, 800));
49
+ const got = (await page.evaluate(EXTRACT));
50
+ const { text, truncated } = clip(got.text, o.maxChars ?? 12_000);
51
+ pages.push({
52
+ url: page.url(),
53
+ title: got.title,
54
+ text,
55
+ truncated,
56
+ links: got.links.slice(0, o.maxLinks ?? 30),
57
+ ...(res && !res.ok() ? { error: `HTTP ${res.status()}` } : {}),
58
+ });
59
+ }
60
+ catch (e) {
61
+ pages.push({ url: url.href, error: e.message });
62
+ }
63
+ finally {
64
+ await page.close().catch(() => { });
65
+ }
66
+ }
67
+ }
68
+ finally {
69
+ await browser.close().catch(() => { });
70
+ }
71
+ return { pages, ms: Date.now() - began };
72
+ }
73
+ /** `browse`: open public web pages in a headless browser and read them. */
74
+ export const browseTool = (o) => createTool({
75
+ id: o.id ?? 'browse',
76
+ description: o.description ??
77
+ 'Open public web pages in a real browser (pages that need JavaScript too) and read their text and links. Use for a page the user names or one you need to answer; never for logins or captchas.',
78
+ inputSchema: z.object({ urls: z.array(z.string().url()).min(1).max(o.maxPages ?? 3).describe('Full http(s) addresses') }),
79
+ execute: async ({ urls }, ctx) => {
80
+ const refused = await o.guard?.(ctx);
81
+ if (refused)
82
+ return { error: refused };
83
+ try {
84
+ const { pages, ms } = await readPages(o.browser(ctx), urls, o);
85
+ await o.used?.(ctx, { ms, pages: pages.length });
86
+ return { pages };
87
+ }
88
+ catch (e) {
89
+ return { error: `The browser failed: ${e.message}` };
90
+ }
91
+ },
92
+ });
93
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { d1Memory, type D1MemoryOptions } from './memory.js';
2
2
  export { openAICompatible, providerModel, workersAI, workersAIOptions, PROVIDER, DEFAULT_MODEL, type AiBinding, type Model, type ProviderConfig } from './models.js';
3
3
  export { buildAgent, defineAgents, type AgentRegistry, type AgentSpec, type AgentsOptions } from './agents.js';
4
4
  export { createAgentServer, HEADERS, type AgentServerOptions } from './server.js';
5
+ export { clip, fileToMarkdown, jobTools, publicUrl, readFileTool, type Allow, type FileText, type JobToolsOptions, type LoadedFile, type MarkdownAi, type ReadFileOptions, type ToolCtx, type WorkflowLike } from './tools.js';
5
6
  export { plannerAgent, RoutingPlanSchema, type RoutingPlan } from './routing.js';
6
7
  export { Agent } from '@mastra/core/agent';
7
8
  export { Mastra } from '@mastra/core/mastra';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACrK,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAGjF,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACrK,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/N,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAGjF,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC"}
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export { d1Memory } from './memory.js';
3
3
  export { openAICompatible, providerModel, workersAI, workersAIOptions, PROVIDER, DEFAULT_MODEL } from './models.js';
4
4
  export { buildAgent, defineAgents } from './agents.js';
5
5
  export { createAgentServer, HEADERS } from './server.js';
6
+ export { clip, fileToMarkdown, jobTools, publicUrl, readFileTool } from './tools.js';
6
7
  export { plannerAgent, RoutingPlanSchema } from './routing.js';
7
8
  // So a project needs only this package to define agents and tools.
8
9
  export { Agent } from '@mastra/core/agent';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,OAAO,EAAE,QAAQ,EAAwB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAmD,MAAM,aAAa,CAAC;AACrK,OAAO,EAAE,UAAU,EAAE,YAAY,EAA0D,MAAM,aAAa,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAA2B,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAoB,MAAM,cAAc,CAAC;AAEjF,mEAAmE;AACnE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,OAAO,EAAE,QAAQ,EAAwB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAmD,MAAM,aAAa,CAAC;AACrK,OAAO,EAAE,UAAU,EAAE,YAAY,EAA0D,MAAM,aAAa,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAA2B,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAA4I,MAAM,YAAY,CAAC;AAC/N,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAoB,MAAM,cAAc,CAAC;AAEjF,mEAAmE;AACnE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC"}
@@ -0,0 +1,118 @@
1
+ import { z } from 'zod';
2
+ /** What a tool's execute gets as its second argument, as far as these tools need. */
3
+ export type ToolCtx = {
4
+ requestContext?: {
5
+ get(key: string): unknown;
6
+ };
7
+ } | undefined;
8
+ /** Return a sentence for the model to refuse with, or nothing to go ahead. */
9
+ export type Allow = (ctx: ToolCtx) => Promise<string | void> | string | void;
10
+ /** The Workers AI binding's file conversion, as these tools use it. */
11
+ export interface MarkdownAi {
12
+ toMarkdown(files: {
13
+ name: string;
14
+ blob: Blob;
15
+ }[]): Promise<({
16
+ name: string;
17
+ mimeType: string;
18
+ } & ({
19
+ format: 'markdown';
20
+ tokens: number;
21
+ data: string;
22
+ } | {
23
+ format: 'error';
24
+ error: string;
25
+ }))[]>;
26
+ }
27
+ export interface LoadedFile {
28
+ name: string;
29
+ type?: string;
30
+ bytes: Uint8Array | ArrayBuffer | Blob;
31
+ }
32
+ /** Cut text to `max` characters, at a line break when one is near. */
33
+ export declare function clip(text: string, max: number): {
34
+ text: string;
35
+ truncated: boolean;
36
+ };
37
+ /** A file as Markdown (PDF, Office, spreadsheets, HTML, XML, images…), cut to `maxChars`. */
38
+ export declare function fileToMarkdown(ai: MarkdownAi, file: LoadedFile, maxChars?: number): Promise<FileText | {
39
+ error: string;
40
+ }>;
41
+ export interface FileText {
42
+ name: string;
43
+ type: string;
44
+ markdown: string;
45
+ truncated: boolean;
46
+ tokens: number;
47
+ }
48
+ export interface ReadFileOptions {
49
+ ai: (ctx: ToolCtx) => MarkdownAi;
50
+ /** The file behind a reference the conversation holds (an attachment id, an R2 key); null when it isn't the caller's. */
51
+ load: (ref: string, ctx: ToolCtx) => Promise<LoadedFile | null>;
52
+ id?: string;
53
+ description?: string;
54
+ maxChars?: number;
55
+ guard?: Allow;
56
+ used?: (ctx: ToolCtx, usage: {
57
+ name: string;
58
+ tokens: number;
59
+ chars: number;
60
+ }) => Promise<void> | void;
61
+ }
62
+ /** `read_file`: the text of a file the user attached, for the model to work with. */
63
+ export declare const readFileTool: (o: ReadFileOptions) => import("@mastra/core/tools").Tool<{
64
+ file: string;
65
+ }, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, string, unknown>;
66
+ /** The Workflow binding, as the job tools use it. */
67
+ export interface WorkflowLike<P> {
68
+ create(options: {
69
+ id?: string;
70
+ params?: P;
71
+ }): Promise<{
72
+ id: string;
73
+ }>;
74
+ get(id: string): Promise<{
75
+ status(): Promise<{
76
+ status: string;
77
+ output?: unknown;
78
+ error?: unknown;
79
+ }>;
80
+ }>;
81
+ }
82
+ export interface JobToolsOptions<S extends z.ZodObject> {
83
+ /** `start_<name>` and `check_<name>`. */
84
+ name: string;
85
+ /** What the job does, for the model (the start tool's description). */
86
+ description: string;
87
+ input: S;
88
+ workflow: (ctx: ToolCtx) => WorkflowLike<z.infer<S> & {
89
+ owner: string;
90
+ } & Record<string, unknown>>;
91
+ /** Who asked: only they can check the job. */
92
+ owner: (ctx: ToolCtx) => string;
93
+ /** More for the Workflow from the request, beside the model's input (e.g. whose credit to charge). */
94
+ params?: (ctx: ToolCtx) => Record<string, unknown>;
95
+ /** How long check waits for the job to finish before answering "still running". */
96
+ waitMs?: number;
97
+ guard?: (ctx: ToolCtx, input: z.infer<S>) => Promise<string | void> | string | void;
98
+ /** After the job starts; charge here what it will cost, or charge in the Workflow by what it used. */
99
+ started?: (ctx: ToolCtx, input: z.infer<S>, job: string) => Promise<void> | void;
100
+ }
101
+ /**
102
+ * A start/check pair over a Workflow: the job outlives the chat request, and the
103
+ * model checks on it (now, or in a later message). A job id carries a tag of its
104
+ * owner, so one person can't read another's.
105
+ */
106
+ export declare function jobTools<S extends z.ZodObject>(o: JobToolsOptions<S>): {
107
+ start: import("@mastra/core/tools").Tool<S extends import("@mastra/schema-compat").PublicSchema<any> ? import("@mastra/schema-compat").InferPublicSchema<S> : unknown, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, `start_${string}`, unknown>;
108
+ check: import("@mastra/core/tools").Tool<{
109
+ job: string;
110
+ }, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, `check_${string}`, unknown>;
111
+ };
112
+ /**
113
+ * The URL when it's a public http(s) address (and, with `allow`, on one of those
114
+ * hosts or their subdomains); null otherwise. Keeps a browser or fetch off
115
+ * localhost, private networks and cloud metadata addresses.
116
+ */
117
+ export declare function publicUrl(input: string, allow?: string[]): URL | null;
118
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/dist/tools.js ADDED
@@ -0,0 +1,142 @@
1
+ // Tools an agent opts into: reading a file it was given, and long work that
2
+ // runs in a Workflow while the chat goes on. Reading web pages lives in
3
+ // ./browser.ts (its own entry), so agents without a browser don't bundle one.
4
+ //
5
+ // Every tool takes `guard` (checked before the work; return a message to refuse,
6
+ // e.g. "not enough credit") and `used` (called after, with what the work cost),
7
+ // so the app charges whoever asked rather than absorbing it.
8
+ import { createTool } from '@mastra/core/tools';
9
+ import { z } from 'zod';
10
+ /** Cut text to `max` characters, at a line break when one is near. */
11
+ export function clip(text, max) {
12
+ if (text.length <= max)
13
+ return { text, truncated: false };
14
+ const cut = text.lastIndexOf('\n', max);
15
+ return { text: text.slice(0, cut > max * 0.8 ? cut : max), truncated: true };
16
+ }
17
+ /** A file as Markdown (PDF, Office, spreadsheets, HTML, XML, images…), cut to `maxChars`. */
18
+ export async function fileToMarkdown(ai, file, maxChars = 20_000) {
19
+ const blob = file.bytes instanceof Blob ? file.bytes : new Blob([file.bytes], { type: file.type ?? 'application/octet-stream' });
20
+ const [out] = await ai.toMarkdown([{ name: file.name, blob }]);
21
+ if (!out || out.format === 'error')
22
+ return { error: `Could not read ${file.name}: ${out?.format === 'error' ? out.error : 'no result'}` };
23
+ const { text, truncated } = clip(out.data.trim(), maxChars);
24
+ return { name: file.name, type: out.mimeType, markdown: text, truncated, tokens: out.tokens };
25
+ }
26
+ /** `read_file`: the text of a file the user attached, for the model to work with. */
27
+ export const readFileTool = (o) => createTool({
28
+ id: o.id ?? 'read_file',
29
+ description: o.description ??
30
+ 'Read a file the user attached (PDF, Word, Excel, CSV, HTML, XML, image) and get its text as Markdown. Pass the file reference shown in the conversation.',
31
+ inputSchema: z.object({ file: z.string().min(1).max(300).describe('The attached file reference') }),
32
+ execute: async ({ file }, ctx) => {
33
+ const refused = await o.guard?.(ctx);
34
+ if (refused)
35
+ return { error: refused };
36
+ const loaded = await o.load(file, ctx).catch(() => null);
37
+ if (!loaded)
38
+ return { error: 'No such file in this conversation.' };
39
+ const out = await fileToMarkdown(o.ai(ctx), loaded, o.maxChars).catch((e) => ({ error: `Could not read ${loaded.name}: ${e.message}` }));
40
+ if ('markdown' in out)
41
+ await o.used?.(ctx, { name: out.name, tokens: out.tokens, chars: out.markdown.length });
42
+ return out;
43
+ },
44
+ });
45
+ const DONE = new Set(['complete', 'errored', 'terminated']);
46
+ async function tag(owner) {
47
+ const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(owner));
48
+ return [...new Uint8Array(bytes)].slice(0, 8).map((b) => b.toString(16).padStart(2, '0')).join('');
49
+ }
50
+ /**
51
+ * A start/check pair over a Workflow: the job outlives the chat request, and the
52
+ * model checks on it (now, or in a later message). A job id carries a tag of its
53
+ * owner, so one person can't read another's.
54
+ */
55
+ export function jobTools(o) {
56
+ const start = createTool({
57
+ id: `start_${o.name}`,
58
+ description: `${o.description} Runs in the background: returns a job id at once; then call check_${o.name} with it.`,
59
+ inputSchema: o.input,
60
+ execute: async (input, ctx) => {
61
+ const args = input;
62
+ const refused = await o.guard?.(ctx, args);
63
+ if (refused)
64
+ return { error: refused };
65
+ const owner = o.owner(ctx);
66
+ const id = `${await tag(owner)}-${crypto.randomUUID()}`;
67
+ await o.workflow(ctx).create({ id, params: { ...o.params?.(ctx), ...args, owner } });
68
+ await o.started?.(ctx, args, id);
69
+ return { job: id, status: 'running' };
70
+ },
71
+ });
72
+ const check = createTool({
73
+ id: `check_${o.name}`,
74
+ description: `The state of a job from start_${o.name}, and its result once complete. Waits a little for it.`,
75
+ inputSchema: z.object({ job: z.string().min(10).max(100) }),
76
+ execute: async ({ job }, ctx) => {
77
+ if (!job.startsWith(`${await tag(o.owner(ctx))}-`))
78
+ return { error: 'No such job.' };
79
+ const instance = await o.workflow(ctx).get(job).catch(() => null);
80
+ if (!instance)
81
+ return { error: 'No such job.' };
82
+ const until = Date.now() + (o.waitMs ?? 15_000);
83
+ for (;;) {
84
+ const s = await instance.status();
85
+ if (DONE.has(s.status))
86
+ return s.status === 'complete' ? { job, status: 'complete', result: s.output } : { job, status: 'failed', error: String(s.error?.message ?? s.error ?? s.status) };
87
+ if (Date.now() > until)
88
+ return { job, status: 'running', note: 'Still running: tell the user, and check again when they ask.' };
89
+ await new Promise((r) => setTimeout(r, 1500));
90
+ }
91
+ },
92
+ });
93
+ return { start, check };
94
+ }
95
+ function privateV4(host) {
96
+ const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
97
+ if (!m)
98
+ return false;
99
+ const [a, b] = [Number(m[1]), Number(m[2])];
100
+ if (a === 0 || a === 10 || a === 127 || a >= 224)
101
+ return true;
102
+ if (a === 100 && b >= 64 && b < 128)
103
+ return true;
104
+ if (a === 169 && b === 254)
105
+ return true;
106
+ if (a === 172 && b >= 16 && b < 32)
107
+ return true;
108
+ if (a === 192 && b === 168)
109
+ return true;
110
+ return false;
111
+ }
112
+ /**
113
+ * The URL when it's a public http(s) address (and, with `allow`, on one of those
114
+ * hosts or their subdomains); null otherwise. Keeps a browser or fetch off
115
+ * localhost, private networks and cloud metadata addresses.
116
+ */
117
+ export function publicUrl(input, allow) {
118
+ let url;
119
+ try {
120
+ url = new URL(input);
121
+ }
122
+ catch {
123
+ return null;
124
+ }
125
+ if (url.protocol !== 'https:' && url.protocol !== 'http:')
126
+ return null;
127
+ if (url.username || url.password)
128
+ return null;
129
+ if (url.port && url.port !== '80' && url.port !== '443')
130
+ return null;
131
+ const host = url.hostname.toLowerCase().replace(/\.$/, '');
132
+ if (!host.includes('.') || host.startsWith('['))
133
+ return null; // single labels, IPv6 literals
134
+ if (/(^|\.)(localhost|local|internal|home|lan|corp|intranet)$/.test(host))
135
+ return null;
136
+ if (/^\d+$/.test(host) || /^0x/i.test(host) || privateV4(host))
137
+ return null;
138
+ if (allow?.length && !allow.some((h) => host === h || host.endsWith(`.${h}`)))
139
+ return null;
140
+ return url;
141
+ }
142
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rubrion/mastra-cloudflare",
3
- "version": "0.2.1",
4
- "description": "Mastra agents on Cloudflare Workers: D1 memory, Workers AI and OpenAI-compatible models, and an OpenAI-compatible agent server with multi-agent routing.",
3
+ "version": "0.3.0",
4
+ "description": "Mastra agents on Cloudflare Workers: D1 memory, Workers AI and OpenAI-compatible models, and an OpenAI-compatible agent server with multi-agent routing, and opt-in tools: read files, browse the web, background jobs.",
5
5
  "license": "GPL-3.0-only",
6
6
  "author": "Rubrion",
7
7
  "repository": {
@@ -25,6 +25,10 @@
25
25
  ".": {
26
26
  "types": "./dist/index.d.ts",
27
27
  "import": "./dist/index.js"
28
+ },
29
+ "./browser": {
30
+ "types": "./dist/browser.d.ts",
31
+ "import": "./dist/browser.js"
28
32
  }
29
33
  },
30
34
  "files": [
@@ -36,7 +40,8 @@
36
40
  "scripts": {
37
41
  "build": "rm -rf dist && tsc -p tsconfig.json",
38
42
  "check": "tsc -p tsconfig.json --noEmit",
39
- "prepublishOnly": "npm run build"
43
+ "prepublishOnly": "npm run build",
44
+ "test": "vitest run"
40
45
  },
41
46
  "dependencies": {
42
47
  "@ai-sdk/openai-compatible": "^3.0.74",
@@ -49,10 +54,21 @@
49
54
  "zod": "^4.4.3"
50
55
  },
51
56
  "devDependencies": {
57
+ "@cloudflare/puppeteer": "^1.4.0",
52
58
  "@cloudflare/workers-types": "^4.20250918.0",
53
- "typescript": "^5.9.3"
59
+ "semantic-release": "^25.0.9",
60
+ "typescript": "^5.9.3",
61
+ "vitest": "^4.1.12"
54
62
  },
55
63
  "publishConfig": {
56
64
  "access": "public"
65
+ },
66
+ "peerDependencies": {
67
+ "@cloudflare/puppeteer": "^1.4.0"
68
+ },
69
+ "peerDependenciesMeta": {
70
+ "@cloudflare/puppeteer": {
71
+ "optional": true
72
+ }
57
73
  }
58
74
  }