Skip to content

Commit 4b82f57

Browse files
committed
v4.0.4
2 parents 054bf23 + e606889 commit 4b82f57

File tree

13 files changed

+373
-33
lines changed

13 files changed

+373
-33
lines changed

example/extension/public/manifest.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
"storage",
3636
"scripting",
3737
"alarms",
38-
"notifications"
38+
"notifications",
39+
"downloads"
3940
],
4041
"host_permissions": ["<all_urls>"],
4142
"web_accessible_resources": [
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { Agent, AgentContext } from "@eko-ai/eko";
2+
import { LanguageModelV2ToolCallPart, ToolResult } from "@eko-ai/eko/types";
3+
4+
export default class WriteFileAgent extends Agent {
5+
constructor() {
6+
super({
7+
name: "WriteFile",
8+
description:
9+
"File writing tool, used for writing content to local files.",
10+
tools: [
11+
{
12+
name: "write_file",
13+
parameters: {
14+
type: "object",
15+
properties: {
16+
filename: {
17+
type: "string",
18+
description:
19+
"File name only, path is not supported. For example: data.md",
20+
},
21+
content: {
22+
type: "string",
23+
description: "The content to write to the file.",
24+
},
25+
},
26+
},
27+
execute: async (
28+
args: Record<string, unknown>,
29+
agentContext: AgentContext,
30+
toolCall: LanguageModelV2ToolCallPart
31+
): Promise<ToolResult> => {
32+
return this.writeFile(
33+
args.filename as string,
34+
args.content as string
35+
);
36+
},
37+
},
38+
],
39+
llms: [],
40+
});
41+
}
42+
43+
private async writeFile(
44+
filename: string,
45+
content: string
46+
): Promise<ToolResult> {
47+
const sanitizedFilename = this.sanitizeFilename(filename);
48+
const encodedContent = encodeURIComponent(content);
49+
const dataUrl = `data:text/plain;charset=utf-8,${encodedContent}`;
50+
await new Promise<void>((resolve, reject) => {
51+
chrome.downloads.download(
52+
{
53+
url: dataUrl,
54+
filename: sanitizedFilename,
55+
saveAs: false,
56+
},
57+
(downloadId) => {
58+
if (chrome.runtime.lastError) {
59+
reject(new Error(chrome.runtime.lastError.message));
60+
} else if (downloadId === undefined) {
61+
reject(new Error("Failed to download: no download ID returned"));
62+
} else {
63+
resolve();
64+
}
65+
}
66+
);
67+
});
68+
69+
return {
70+
content: [
71+
{
72+
type: "text",
73+
text: `File written successfully: ${sanitizedFilename}`,
74+
},
75+
],
76+
};
77+
}
78+
79+
private sanitizeFilename(filename: string): string {
80+
const invalidChars = /[<>:"/\\|?*\x00-\x1f]/g;
81+
let sanitized = filename.replace(invalidChars, "_");
82+
sanitized = sanitized.replace(/^[\s.]+|[\s.]+$/g, "");
83+
if (!sanitized) {
84+
sanitized = "untitled.txt";
85+
}
86+
if (sanitized.length > 255) {
87+
const ext = this.getFileExtension(sanitized);
88+
const nameWithoutExt = sanitized.slice(0, 255 - ext.length);
89+
sanitized = nameWithoutExt + ext;
90+
}
91+
return sanitized;
92+
}
93+
94+
private getFileExtension(filename: string): string {
95+
const lastDot = filename.lastIndexOf(".");
96+
return lastDot !== -1 ? filename.slice(lastDot) : "";
97+
}
98+
}
99+
100+
export { WriteFileAgent };

example/extension/src/background/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
AgentStreamCallback,
1515
} from "@eko-ai/eko/types";
1616
import { initAgentServices } from "./agent";
17+
import WriteFileAgent from "./agent/file-agent";
1718
import { BrowserAgent } from "@eko-ai/eko-extension";
1819

1920
var chatAgent: ChatAgent | null = null;
@@ -177,7 +178,7 @@ export async function init(): Promise<ChatAgent | void> {
177178
},
178179
};
179180

180-
const agents = [new BrowserAgent()];
181+
const agents = [new BrowserAgent(), new WriteFileAgent()];
181182
chatAgent = new ChatAgent({ llms, agents });
182183
chatAgent.initMessages().catch((e) => {
183184
printLog("init messages error: " + e, "error");

example/nodejs/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,21 @@
2020
"@eko-ai/eko-nodejs": "workspace:*",
2121
"canvas": "^3.2.0",
2222
"glob": "^11.0.2",
23+
"keytar": "^7.9.0",
24+
"level": "^10.0.0",
25+
"leveldown": "^6.1.1",
2326
"merge-deep": "^3.0.3",
2427
"playwright": "^1.57.0",
2528
"playwright-extra": "^4.3.6",
26-
"puppeteer-extra-plugin-stealth": "^2.11.2",
2729
"chrome-cookies-secure": "^3.0.0",
28-
"keytar": "^7.9.0"
30+
"puppeteer-extra-plugin-stealth": "^2.11.2"
2931
},
3032
"devDependencies": {
3133
"@rollup/plugin-commonjs": "^28.0.3",
3234
"@rollup/plugin-json": "^6.1.0",
3335
"@rollup/plugin-node-resolve": "^16.0.1",
3436
"@rollup/plugin-typescript": "^12.1.2",
37+
"@types/leveldown": "^4.0.6",
3538
"@types/node": "^22.15.19",
3639
"dotenv": "^16.5.0",
3740
"rollup": "^4.40.0",

example/nodejs/rollup.config.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@ export default {
1212
},
1313
],
1414
external: (id) =>
15-
["chrome-cookies-secure", "sqlite3", "keytar", "bindings"].some(
16-
(mod) => id === mod || id.startsWith(mod + "/")
17-
) || id.endsWith(".node"),
15+
[
16+
"chrome-cookies-secure",
17+
"sqlite3",
18+
"keytar",
19+
"bindings",
20+
"level",
21+
"leveldown",
22+
].some((mod) => id === mod || id.startsWith(mod + "/")) ||
23+
id.endsWith(".node"),
1824
plugins: [
1925
json(),
2026
commonjs({

0 commit comments

Comments
 (0)