From aff52f2c04a26221d566571444790d0ace890671 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 16:05:02 +0800 Subject: [PATCH 01/45] =?UTF-8?q?=E5=A2=9E=E5=8A=A0browser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- .npmignore | 1 + package-lock.json | 637 ++++++++++++++++++++++++++++++++++++++++++- package.json | 6 +- src/tools/browser.ts | 73 +++++ src/tools/index.ts | 4 +- 6 files changed, 720 insertions(+), 4 deletions(-) create mode 100644 src/tools/browser.ts diff --git a/.gitignore b/.gitignore index bdfa6b8..59831f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .env node_modules -dist \ No newline at end of file +dist +.vscode \ No newline at end of file diff --git a/.npmignore b/.npmignore index 759fd62..e825c10 100644 --- a/.npmignore +++ b/.npmignore @@ -6,3 +6,4 @@ install.bat install.sh GEMINI.md node_modules/ +.vscode diff --git a/package-lock.json b/package-lock.json index 6ba9feb..003a1ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,25 +9,67 @@ "version": "1.0.16", "license": "MIT", "dependencies": { + "@mozilla/readability": "^0.6.0", "chalk": "^5.6.2", "commander": "^14.0.3", "dotenv": "^16.4.7", "inquirer": "^13.2.2", + "jsdom": "^28.0.0", "nodemailer": "^8.0.0", "openai": "^6.18.0", - "ora": "^9.3.0" + "ora": "^9.3.0", + "playwright": "^1.58.2" }, "bin": { "autoclaw": "dist/index.js" }, "devDependencies": { "@types/inquirer": "^9.0.9", + "@types/jsdom": "^27.0.0", "@types/node": "^25.2.1", "@types/nodemailer": "^7.0.9", "ts-node": "^10.9.2", "typescript": "^5.9.3" } }, + "node_modules/@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmmirror.com/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", + "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.0.0", + "@csstools/css-color-parser": "^4.0.1", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.5" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.7.8", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/dom-selector/-/dom-selector-6.7.8.tgz", + "integrity": "sha512-stisC1nULNc9oH5lakAj8MH88ZxeGxzyWNDfbdCxvJSJIvDsHNZqYvscGTgy/ysgXWLJPt6K/4t0/GjvtKcFJQ==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.5" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmmirror.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "license": "MIT" + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -41,6 +83,149 @@ "node": ">=12" } }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/@csstools/color-helpers/-/color-helpers-6.0.1.tgz", + "integrity": "sha512-NmXRccUJMk2AWA5A7e5a//3bCIMyOu2hAtdRYrhPPHjDxINuCwX1w6rnIZ4xjLcp0ayv6h8Pc3X0eJUGiAAXHQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/@csstools/css-calc/-/css-calc-3.0.0.tgz", + "integrity": "sha512-q4d82GTl8BIlh/dTnVsWmxnbWJeb3kiU8eUH71UxlxnS+WIaALmtzTL8gR15PkYOexMQYVk0CO4qIG93C1IvPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/@csstools/css-color-parser/-/css-color-parser-4.0.1.tgz", + "integrity": "sha512-vYwO15eRBEkeF6xjAno/KQ61HacNhfQuuU/eGwH67DplL0zD5ZixUa563phQvUelA07yDczIXdtmYojCphKJcw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.1", + "@csstools/css-calc": "^3.0.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.26", + "resolved": "https://registry.npmmirror.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.26.tgz", + "integrity": "sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0" + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.11.0", + "resolved": "https://registry.npmmirror.com/@exodus/bytes/-/bytes-1.11.0.tgz", + "integrity": "sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, "node_modules/@inquirer/ansi": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.3.tgz", @@ -397,6 +582,15 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mozilla/readability": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/@mozilla/readability/-/readability-0.6.0.tgz", + "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", @@ -436,6 +630,31 @@ "rxjs": "^7.2.0" } }, + "node_modules/@types/jsdom": { + "version": "27.0.0", + "resolved": "https://registry.npmmirror.com/@types/jsdom/-/jsdom-27.0.0.tgz", + "integrity": "sha512-NZyFl/PViwKzdEkQg96gtnB8wm+1ljhdDay9ahn4hgb+SfVtPCbm3TlmDUFXTA+MGN3CijicnMhG18SI5H3rFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/jsdom/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/@types/node": { "version": "25.2.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz", @@ -466,6 +685,13 @@ "@types/node": "*" } }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -492,6 +718,15 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -523,6 +758,15 @@ "dev": true, "license": "MIT" }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -593,6 +837,70 @@ "dev": true, "license": "MIT" }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssstyle": { + "version": "5.3.7", + "resolved": "https://registry.npmmirror.com/cssstyle/-/cssstyle-5.3.7.tgz", + "integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.21", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, "node_modules/diff": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", @@ -621,6 +929,32 @@ "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT" }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/get-east-asian-width": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", @@ -633,6 +967,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -687,6 +1059,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "license": "MIT" + }, "node_modules/is-unicode-supported": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", @@ -699,6 +1077,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/jsdom": { + "version": "28.0.0", + "resolved": "https://registry.npmmirror.com/jsdom/-/jsdom-28.0.0.tgz", + "integrity": "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==", + "license": "MIT", + "dependencies": { + "@acemir/cssom": "^0.9.31", + "@asamuzakjp/dom-selector": "^6.7.6", + "@exodus/bytes": "^1.11.0", + "cssstyle": "^5.3.7", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "undici": "^7.20.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/log-symbols": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", @@ -715,6 +1132,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "11.2.5", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -722,6 +1148,12 @@ "dev": true, "license": "ISC" }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "license": "CC0-1.0" + }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", @@ -734,6 +1166,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/mute-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", @@ -810,6 +1248,66 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmmirror.com/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/restore-cursor": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", @@ -850,6 +1348,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -862,6 +1372,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stdin-discarder": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.1.tgz", @@ -905,6 +1424,54 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT" + }, + "node_modules/tldts": { + "version": "7.0.22", + "resolved": "https://registry.npmmirror.com/tldts/-/tldts-7.0.22.tgz", + "integrity": "sha512-nqpKFC53CgopKPjT6Wfb6tpIcZXHcI6G37hesvikhx0EmUGPkZrujRyAjgnmp1SHNgpQfKVanZ+KfpANFt2Hxw==", + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.22" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.22", + "resolved": "https://registry.npmmirror.com/tldts-core/-/tldts-core-7.0.22.tgz", + "integrity": "sha512-KgbTDC5wzlL6j/x6np6wCnDSMUq4kucHNm00KXPbfNzmllCmtmvtykJHfmgdHntwIeupW04y8s1N/43S1PkQDw==", + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -969,6 +1536,15 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "7.21.0", + "resolved": "https://registry.npmmirror.com/undici/-/undici-7.21.0.tgz", + "integrity": "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -983,6 +1559,50 @@ "dev": true, "license": "MIT" }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-16.0.0.tgz", + "integrity": "sha512-9CcxtEKsf53UFwkSUZjG+9vydAsFO4lFHBpJUtjBcoJOCJpKnSJNwCw813zrYJHpCJ7sgfbtOe0V5Ku7Pa1XMQ==", + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/wrap-ansi": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", @@ -1017,6 +1637,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index edc6bb8..04f6e0f 100644 --- a/package.json +++ b/package.json @@ -31,16 +31,20 @@ "license": "MIT", "description": "A lightweight AI agent CLI tool that brings the power of LLMs to your terminal.", "dependencies": { + "@mozilla/readability": "^0.6.0", "chalk": "^5.6.2", "commander": "^14.0.3", "dotenv": "^16.4.7", "inquirer": "^13.2.2", + "jsdom": "^28.0.0", "nodemailer": "^8.0.0", "openai": "^6.18.0", - "ora": "^9.3.0" + "ora": "^9.3.0", + "playwright": "^1.58.2" }, "devDependencies": { "@types/inquirer": "^9.0.9", + "@types/jsdom": "^27.0.0", "@types/node": "^25.2.1", "@types/nodemailer": "^7.0.9", "ts-node": "^10.9.2", diff --git a/src/tools/browser.ts b/src/tools/browser.ts new file mode 100644 index 0000000..d993833 --- /dev/null +++ b/src/tools/browser.ts @@ -0,0 +1,73 @@ +import { chromium } from 'playwright'; +import { Readability } from '@mozilla/readability'; +import { JSDOM } from 'jsdom'; +import { ToolModule } from './interface.js'; + +export const BrowserTool: ToolModule = { + name: "Web Browser", + configKeys: [], + definition: { + type: "function", + function: { + name: "read_website", + description: "Reads and extracts the main content from a website URL. Use this to summarize articles or get information from specific pages.", + parameters: { + type: "object", + properties: { + url: { + type: "string", + description: "The full URL of the website to read (e.g., https://example.com/article)." + } + }, + required: ["url"] + } + } + }, + handler: async (args: any, config: any) => { + let browser; + try { + browser = await chromium.launch({ headless: true }); + } catch (error: any) { + if (error.message.includes("Executable doesn't exist")) { + return "Error: Playwright browsers are not installed. Please run `npx playwright install chromium` to enable this feature."; + } + return `Error launching browser: ${error.message}`; + } + + const context = await browser.newContext({ + userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' + }); + const page = await context.newPage(); + + try { + console.log(`Navigating to ${args.url}...`); + await page.goto(args.url, { waitUntil: 'domcontentloaded', timeout: 30000 }); + + // Get the full HTML content + const html = await page.content(); + + // Use JSDOM to create a virtual DOM for Readability + const dom = new JSDOM(html, { url: args.url }); + const reader = new Readability(dom.window.document); + const article = reader.parse(); + + if (!article) { + // Fallback: just return body text if Readability fails + const bodyText = await page.innerText('body'); + return `Could not parse article content with Readability. Raw text content: + +${bodyText.slice(0, 5000)}... (truncated)`; + } + + return `Title: ${article.title} + +Content: +${(article.textContent || "").trim()}`; + + } catch (error: any) { + return `Error reading website: ${error.message}`; + } finally { + await browser.close(); + } + } +}; diff --git a/src/tools/index.ts b/src/tools/index.ts index bac7386..19e1427 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -3,6 +3,7 @@ import { ShellTool, ReadFileTool, WriteFileTool } from './core.js'; import { EmailTool } from './email.js'; import { SearchTool } from './search.js'; import { NotifyTool } from './notify.js'; +import { BrowserTool } from './browser.js'; // Central Registry of all available tools export const toolRegistry: ToolModule[] = [ @@ -11,7 +12,8 @@ export const toolRegistry: ToolModule[] = [ WriteFileTool, EmailTool, SearchTool, - NotifyTool + NotifyTool, + BrowserTool ]; export function getToolDefinitions() { From a3201b9d50d71b798e040ee096dc896695c638a9 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 16:05:27 +0800 Subject: [PATCH 02/45] 1.0.17 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 003a1ba..181a1a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.16", + "version": "1.0.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.16", + "version": "1.0.17", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 04f6e0f..6fd8290 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.16", + "version": "1.0.17", "type": "module", "main": "dist/index.js", "bin": { From b25614f75c287f7815a662f92d5a7b967890fb3d Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 16:44:41 +0800 Subject: [PATCH 03/45] =?UTF-8?q?=E6=9C=BA=E5=99=A8=E4=BA=BA=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=85=B3=E9=94=AE=E8=AF=8D=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 11 ++++++++++- src/index.ts | 29 ++++++++++++++++++++++++++++- src/tools/notify.ts | 19 +++++++++++++++++-- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 2097364..aab9fbf 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,12 @@ OPENAI_API_KEY=sk-your-key-here OPENAI_BASE_URL=https://api.openai.com/v1 -OPENAI_MODEL=gpt-4o \ No newline at end of file +OPENAI_MODEL=gpt-4o + +# Notifications (Optional) +# Configure Webhook URL and Security Keyword for your IM platform +FEISHU_WEBHOOK= +FEISHU_KEYWORD= +DINGTALK_WEBHOOK= +DINGTALK_KEYWORD= +WECOM_WEBHOOK= +WECOM_KEYWORD= \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index e281dd2..303216d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,8 +38,11 @@ interface AppConfig { tavilyApiKey?: string; autoConfirm?: boolean; feishuWebhook?: string; + feishuKeyword?: string; dingtalkWebhook?: string; + dingtalkKeyword?: string; wecomWebhook?: string; + wecomKeyword?: string; } function loadJsonConfig(filePath: string): AppConfig { @@ -222,6 +225,12 @@ async function runSetup() { : 'Feishu Webhook (Optional):', mask: '*' }, + { + type: 'input', + name: 'feishuKeyword', + message: 'Feishu Security Keyword (Optional):', + default: currentConfig.feishuKeyword + }, { type: 'password', name: 'dingtalkWebhook', @@ -230,6 +239,12 @@ async function runSetup() { : 'DingTalk Webhook (Optional):', mask: '*' }, + { + type: 'input', + name: 'dingtalkKeyword', + message: 'DingTalk Security Keyword (Optional):', + default: currentConfig.dingtalkKeyword + }, { type: 'password', name: 'wecomWebhook', @@ -237,12 +252,21 @@ async function runSetup() { ? `WeCom Webhook (Leave empty to keep ${maskSecret(currentConfig.wecomWebhook)}):` : 'WeCom Webhook (Optional):', mask: '*' + }, + { + type: 'input', + name: 'wecomKeyword', + message: 'WeCom Security Keyword (Optional):', + default: currentConfig.wecomKeyword } ]); notifyConfig = { feishuWebhook: notifyAnswers.feishuWebhook || currentConfig.feishuWebhook, + feishuKeyword: notifyAnswers.feishuKeyword || currentConfig.feishuKeyword, dingtalkWebhook: notifyAnswers.dingtalkWebhook || currentConfig.dingtalkWebhook, - wecomWebhook: notifyAnswers.wecomWebhook || currentConfig.wecomWebhook + dingtalkKeyword: notifyAnswers.dingtalkKeyword || currentConfig.dingtalkKeyword, + wecomWebhook: notifyAnswers.wecomWebhook || currentConfig.wecomWebhook, + wecomKeyword: notifyAnswers.wecomKeyword || currentConfig.wecomKeyword }; } @@ -302,8 +326,11 @@ async function runChat(queryParts: string[], options: any) { if (process.env.SMTP_PASS) fullConfig.smtpPass = process.env.SMTP_PASS; if (process.env.TAVILY_API_KEY) fullConfig.tavilyApiKey = process.env.TAVILY_API_KEY; if (process.env.FEISHU_WEBHOOK) fullConfig.feishuWebhook = process.env.FEISHU_WEBHOOK; + if (process.env.FEISHU_KEYWORD) fullConfig.feishuKeyword = process.env.FEISHU_KEYWORD; if (process.env.DINGTALK_WEBHOOK) fullConfig.dingtalkWebhook = process.env.DINGTALK_WEBHOOK; + if (process.env.DINGTALK_KEYWORD) fullConfig.dingtalkKeyword = process.env.DINGTALK_KEYWORD; if (process.env.WECOM_WEBHOOK) fullConfig.wecomWebhook = process.env.WECOM_WEBHOOK; + if (process.env.WECOM_KEYWORD) fullConfig.wecomKeyword = process.env.WECOM_KEYWORD; if (!apiKey) { console.log(chalk.yellow("API Key not found.")); diff --git a/src/tools/notify.ts b/src/tools/notify.ts index c159ec7..a137f91 100644 --- a/src/tools/notify.ts +++ b/src/tools/notify.ts @@ -2,7 +2,11 @@ import { ToolModule } from './interface.js'; export const NotifyTool: ToolModule = { name: "Group Bot Notification", - configKeys: ["feishuWebhook", "dingtalkWebhook", "wecomWebhook"], + configKeys: [ + "feishuWebhook", "feishuKeyword", + "dingtalkWebhook", "dingtalkKeyword", + "wecomWebhook", "wecomKeyword" + ], definition: { type: "function", function: { @@ -26,14 +30,23 @@ export const NotifyTool: ToolModule = { } }, handler: async (args: any, config: any) => { - const { platform, content } = args; + let { platform, content } = args; let webhookUrl = ''; let payload = {}; + // Helper to ensure security keyword is present + const ensureKeyword = (envKey: string, configKey: string) => { + const keyword = config[configKey] || process.env[envKey]; + if (keyword && !content.includes(keyword)) { + content = `[${keyword}] ${content}`; + } + }; + // 1. Determine Webhook URL and Payload Format switch (platform) { case 'feishu': webhookUrl = config.feishuWebhook || process.env.FEISHU_WEBHOOK; + ensureKeyword('FEISHU_KEYWORD', 'feishuKeyword'); if (!webhookUrl) return "Error: Feishu Webhook URL is not configured."; payload = { msg_type: "text", @@ -43,6 +56,7 @@ export const NotifyTool: ToolModule = { case 'dingtalk': webhookUrl = config.dingtalkWebhook || process.env.DINGTALK_WEBHOOK; + ensureKeyword('DINGTALK_KEYWORD', 'dingtalkKeyword'); if (!webhookUrl) return "Error: DingTalk Webhook URL is not configured."; payload = { msgtype: "text", @@ -52,6 +66,7 @@ export const NotifyTool: ToolModule = { case 'wecom': webhookUrl = config.wecomWebhook || process.env.WECOM_WEBHOOK; + ensureKeyword('WECOM_KEYWORD', 'wecomKeyword'); if (!webhookUrl) return "Error: WeCom Webhook URL is not configured."; payload = { msgtype: "text", From f10a74a66363b5a0b2effc3fe560ab350d3cf2da Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 16:44:57 +0800 Subject: [PATCH 04/45] 1.0.18 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 181a1a5..89852fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.17", + "version": "1.0.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.17", + "version": "1.0.18", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 6fd8290..9110ff7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.17", + "version": "1.0.18", "type": "module", "main": "dist/index.js", "bin": { From 665571aaa110455f921b81e8f94358ff21fe5af0 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 16:52:48 +0800 Subject: [PATCH 05/45] =?UTF-8?q?=E4=BC=98=E5=8C=96readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTING.md | 51 +++++++++++++++++++++ README.md | 116 ++++++++++++++++++++++++++++++++++++++++-------- package.json | 11 ++++- 3 files changed, 158 insertions(+), 20 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..dcbce55 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,51 @@ +# Contributing to AutoClaw šŸ¦ž + +Thank you for your interest in contributing to AutoClaw! We welcome contributions from everyone. + +## Getting Started + +1. **Fork the repository**: Click the "Fork" button on GitHub. +2. **Clone your fork**: + ```bash + git clone https://github.com/YOUR_USERNAME/autoclaw.git + cd autoclaw + ``` +3. **Install dependencies**: + ```bash + npm install + ``` +4. **Create a branch**: + ```bash + git checkout -b feature/my-new-feature + ``` + +## Development Guidelines + +- **Code Style**: We use TypeScript. Please follow the existing code style (formatting, variable naming). +- **Testing**: Before submitting a PR, please run `npm run build` to ensure there are no compilation errors. +- **Commit Messages**: Write clear, descriptive commit messages. We follow the [Conventional Commits](https://www.conventionalcommits.org/) specification (e.g., `feat: add new tool`, `fix: correct typo in README`). + +## Submitting a Pull Request + +1. Push your changes to your fork: + ```bash + git push origin feature/my-new-feature + ``` +2. Go to the [original repository](https://github.com/tsingliuwin/autoclaw) and click "New Pull Request". +3. Describe your changes clearly. What problem does it solve? Why is this change necessary? +4. Wait for review! We will review your PR as soon as possible. + +## Reporting Bugs + +If you find a bug, please open an issue on GitHub with: +- A clear description of the bug. +- Steps to reproduce. +- Expected behavior. +- Screenshots or logs if applicable. + +## Feature Requests + +Have an idea for a new feature? Open an issue with the "enhancement" label and let's discuss it! + +--- +Thank you for helping make AutoClaw better! diff --git a/README.md b/README.md index 767a14b..eea2873 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # AutoClaw šŸ¦ž +[![NPM Version](https://img.shields.io/npm/v/autoclaw.svg?style=flat-square)](https://www.npmjs.com/package/autoclaw) +[![NPM Downloads](https://img.shields.io/npm/dm/autoclaw.svg?style=flat-square)](https://www.npmjs.com/package/autoclaw) +[![License](https://img.shields.io/npm/l/autoclaw.svg?style=flat-square)](https://github.com/tsingliuwin/autoclaw/blob/main/LICENSE) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) + **The Docker-Native Headless Agent for Massive Scale Automation.** AutoClaw is a hyper-lightweight AI agent designed to live inside **Docker containers**. Unlike heavy, GUI-dependent agents, AutoClaw is built for **headless, massive-scale concurrency**. @@ -11,44 +16,87 @@ You can run one instance to fix a local script, or orchestrate **10,000+ instanc - šŸš€ **Massive Scalability**: Text-only, headless design means you can spawn thousands of agents without consuming graphical resources. - šŸ›”ļø **Sandbox Safety**: Ideal for running untrusted code when isolated in Docker. - šŸ”Œ **Swarm Ready**: Stateless design allows for easy orchestration via K8s, Docker Swarm, or simple shell loops. +- 🧩 **Extensible Integrations**: Built-in support for Web Search (Tavily), Email (SMTP), and Notification Webhooks (Feishu, DingTalk, WeCom). ## Features - šŸ“œ **Headless Execution**: No browsers, no GUIs. Pure terminal efficiency. -- šŸ¤– **Non-Interactive**: Intelligent flag handling (`-y`) for zero-touch automation. +- šŸ¤– **Non-Interactive Mode**: Intelligent flag handling (`-y`, `--no-interactive`) for zero-touch automation. - šŸ“‚ **Universal Control**: From simple file I/O to complex system administration. - 🧠 **Context Aware**: Detects container environments to optimize command strategies. +- 🌐 **Web Search**: Integrated with Tavily for real-time information retrieval. +- šŸ“§ **Communication**: Send emails and push notifications to chat groups automatically. + +## Tech Stack +- **Runtime**: Node.js +- **Language**: TypeScript +- **Framework**: Commander.js +- **UI**: Inquirer (interactivity), Chalk (styling), Ora (spinners) +- **AI**: OpenAI SDK (Compatible with DeepSeek, LocalLLM, etc.) ## Installation +### User Installation +Install globally via npm: ```bash npm install -g autoclaw ``` -## Updating - -To update AutoClaw to the latest version: - -```bash -npm update -g autoclaw -``` +### Development Installation +1. Clone the repository: + ```bash + git clone https://github.com/tsingliuwin/autoclaw.git + cd autoclaw + ``` +2. Install dependencies: + ```bash + npm install + ``` +3. Build the project: + ```bash + npm run build + ``` +4. Link globally (optional): + ```bash + npm link + ``` ## Quick Start -1. **Setup**: Run the setup wizard to configure your API key. +1. **Setup**: Run the interactive setup wizard to configure your API keys and integrations. ```bash autoclaw setup ``` -2. **Run**: Start the agent. +2. **Run**: Start the agent in interactive mode. ```bash autoclaw ``` -## Usage Examples +## Usage -- "List all TypeScript files in the src folder." -- "Create a new React component named Button in `components/Button.tsx`." -- "Check my disk usage and tell me which folder is the largest." +### Interactive Mode +Simply run `autoclaw` to enter the chat loop. +```bash +autoclaw +> List all TypeScript files in the src folder. +``` + +### Headless Mode (One-Shot) +Run a single command and exit. +```bash +autoclaw "Check disk usage and save the report to usage.txt" --no-interactive +``` + +### Auto-Confirm (CI/CD) +Automatically approve all tool executions (dangerous, use with caution or in sandboxes). +```bash +autoclaw "Refactor src/index.ts to use ES modules" -y +``` + +### CLI Options +- `-m, --model `: Specify the LLM model (default: `gpt-4o`). +- `-n, --no-interactive`: Exit after processing the initial query (Headless mode). +- `-y, --yes`: Auto-confirm all tool executions (e.g., shell commands). ## Configuration @@ -61,21 +109,51 @@ AutoClaw uses a hierarchical configuration system. 4. **Global Config**: (`~/.autoclaw/setting.json`) ### Supported Configuration Keys (JSON) -- `apiKey`: Your API Key. -- `baseUrl`: Custom Base URL. +- `apiKey`: Your OpenAI API Key. +- `baseUrl`: Custom Base URL (e.g., for DeepSeek or LocalLLM). - `model`: Default model to use. +- `tavilyApiKey`: API Key for Tavily Web Search. +- `smtpHost`, `smtpPort`, `smtpUser`, `smtpPass`, `smtpFrom`: SMTP Email settings. +- `feishuWebhook`, `dingtalkWebhook`, `wecomWebhook`: Notification webhooks. -### Project-Level Config (Example) +### Project-Level Config Example Create a file at `.autoclaw/setting.json`: ```json { "model": "gpt-3.5-turbo", - "baseUrl": "https://api.example.com/v1" + "baseUrl": "https://api.deepseek.com/v1" } ``` -> **āš ļø Security Warning**: If you store your `apiKey` in `.autoclaw/setting.json`, make sure to add `.autoclaw/` to your `.gitignore` file to prevent leaking secrets! +> **āš ļø Security Warning**: If you store your `apiKey` or secrets in `.autoclaw/setting.json`, make sure to add `.autoclaw/` to your `.gitignore` file to prevent leaking secrets! + +## Integrations + +### Web Search (Tavily) +AutoClaw can search the web if you provide a Tavily API Key during setup or in config. +- **Usage**: "Search for the latest Node.js release notes." + +### Email (SMTP) +Configure SMTP settings to let the agent send emails. +- **Usage**: "Send an email to user@example.com with the summary of the log file." + +### Notifications (Feishu/DingTalk/WeCom) +Configure webhooks to receive alerts or reports in your team chat apps. +- **Usage**: "Notify the team on Feishu that the build has finished." ## License MIT + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +1. Fork the Project +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the Branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +--- +GitHub: [https://github.com/tsingliuwin/autoclaw](https://github.com/tsingliuwin/autoclaw) \ No newline at end of file diff --git a/package.json b/package.json index 9110ff7..de116df 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,16 @@ "agent", "automation", "openai", - "tool" + "tool", + "docker", + "headless", + "devops", + "llm", + "gpt-4", + "typescript", + "orchestration", + "infrastructure", + "terminal" ], "author": "AutoClaw Contributor", "license": "MIT", From f48dbc19776ea09bbfa04490766c9ecfeaee9bb2 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 16:52:59 +0800 Subject: [PATCH 06/45] 1.0.19 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89852fa..225eb34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.18", + "version": "1.0.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.18", + "version": "1.0.19", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index de116df..780d1cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.18", + "version": "1.0.19", "type": "module", "main": "dist/index.js", "bin": { From fd38bf5bd743929f9eedf3970943a6414771da4d Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 19:21:08 +0800 Subject: [PATCH 07/45] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 ++++++- src/tools/core.ts | 24 ++++++++++++++++++++++++ src/tools/index.ts | 3 ++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eea2873..33096b7 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,9 @@ You can run one instance to fix a local script, or orchestrate **10,000+ instanc - šŸ“œ **Headless Execution**: No browsers, no GUIs. Pure terminal efficiency. - šŸ¤– **Non-Interactive Mode**: Intelligent flag handling (`-y`, `--no-interactive`) for zero-touch automation. - šŸ“‚ **Universal Control**: From simple file I/O to complex system administration. -- 🧠 **Context Aware**: Detects container environments to optimize command strategies. +- 🧠 **Context Aware**: Detects container environments and provides accurate system time for relative date queries. - 🌐 **Web Search**: Integrated with Tavily for real-time information retrieval. +- šŸ•’ **Time Accuracy**: Built-in tool to get precise system date and time for correct temporal context. - šŸ“§ **Communication**: Send emails and push notifications to chat groups automatically. ## Tech Stack @@ -141,6 +142,10 @@ Configure SMTP settings to let the agent send emails. Configure webhooks to receive alerts or reports in your team chat apps. - **Usage**: "Notify the team on Feishu that the build has finished." +### Date & Time +Built-in utility to provide the agent with the current system time, ensuring accurate handling of relative time requests. +- **Usage**: "What's the date today?" or "Remind me to check the logs next Monday." + ## License MIT diff --git a/src/tools/core.ts b/src/tools/core.ts index 2ece8dd..c0453d7 100644 --- a/src/tools/core.ts +++ b/src/tools/core.ts @@ -107,3 +107,27 @@ export const WriteFileTool: ToolModule = { } } }; + +export const DateTimeTool: ToolModule = { + name: "Date & Time", + definition: { + type: "function", + function: { + name: "get_current_datetime", + description: "Get the current system date and time. Use this when the user refers to relative dates (like 'today', 'next week', 'this March') to ensure accuracy.", + parameters: { + type: "object", + properties: {} + } + } + }, + handler: async () => { + const now = new Date(); + return JSON.stringify({ + iso: now.toISOString(), + local: now.toLocaleString(), + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + weekday: now.toLocaleDateString('en-US', { weekday: 'long' }) + }, null, 2); + } +}; diff --git a/src/tools/index.ts b/src/tools/index.ts index 19e1427..1d09cb7 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,5 +1,5 @@ import { ToolModule } from './interface.js'; -import { ShellTool, ReadFileTool, WriteFileTool } from './core.js'; +import { ShellTool, ReadFileTool, WriteFileTool, DateTimeTool } from './core.js'; import { EmailTool } from './email.js'; import { SearchTool } from './search.js'; import { NotifyTool } from './notify.js'; @@ -10,6 +10,7 @@ export const toolRegistry: ToolModule[] = [ ShellTool, ReadFileTool, WriteFileTool, + DateTimeTool, EmailTool, SearchTool, NotifyTool, From bd5bc44439000f9f1353a3d80035e46b368f15ff Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 19:21:29 +0800 Subject: [PATCH 08/45] 1.0.20 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 225eb34..f0c0aa6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.19", + "version": "1.0.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.19", + "version": "1.0.20", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 780d1cc..353fc25 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.19", + "version": "1.0.20", "type": "module", "main": "dist/index.js", "bin": { From c2e270fc4896cb48da948efb74857d7b95fd9f25 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 20:34:29 +0800 Subject: [PATCH 09/45] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=88=AA=E5=B1=8F?= =?UTF-8?q?=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/core.ts | 3 +- src/tools/email.ts | 14 +++++++-- src/tools/index.ts | 4 ++- src/tools/screenshot.ts | 69 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/tools/screenshot.ts diff --git a/src/tools/core.ts b/src/tools/core.ts index c0453d7..1d48e0b 100644 --- a/src/tools/core.ts +++ b/src/tools/core.ts @@ -117,7 +117,8 @@ export const DateTimeTool: ToolModule = { description: "Get the current system date and time. Use this when the user refers to relative dates (like 'today', 'next week', 'this March') to ensure accuracy.", parameters: { type: "object", - properties: {} + properties: {}, + required: [] } } }, diff --git a/src/tools/email.ts b/src/tools/email.ts index 3bf9417..f968dd4 100644 --- a/src/tools/email.ts +++ b/src/tools/email.ts @@ -8,13 +8,18 @@ export const EmailTool: ToolModule = { type: "function", function: { name: "send_email", - description: "Send an email using configured SMTP settings.", + description: "Send an email using configured SMTP settings. Can include optional file attachments.", parameters: { type: "object", properties: { to: { type: "string", description: "Recipient email address." }, subject: { type: "string", description: "Email subject." }, - body: { type: "string", description: "Email body content (text)." } + body: { type: "string", description: "Email body content (text)." }, + attachments: { + type: "array", + items: { type: "string" }, + description: "Optional list of local file paths to attach to the email." + } }, required: ["to", "subject", "body"] } @@ -37,11 +42,16 @@ export const EmailTool: ToolModule = { }, }); + const emailAttachments = args.attachments?.map((filePath: string) => ({ + path: filePath + })) || []; + const info = await transporter.sendMail({ from: config.smtpFrom || config.smtpUser, // sender address to: args.to, // list of receivers subject: args.subject, // Subject line text: args.body, // plain text body + attachments: emailAttachments }); return `Email sent successfully. Message ID: ${info.messageId}`; diff --git a/src/tools/index.ts b/src/tools/index.ts index 1d09cb7..2b991cc 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -4,6 +4,7 @@ import { EmailTool } from './email.js'; import { SearchTool } from './search.js'; import { NotifyTool } from './notify.js'; import { BrowserTool } from './browser.js'; +import { ScreenshotTool } from './screenshot.js'; // Central Registry of all available tools export const toolRegistry: ToolModule[] = [ @@ -14,7 +15,8 @@ export const toolRegistry: ToolModule[] = [ EmailTool, SearchTool, NotifyTool, - BrowserTool + BrowserTool, + ScreenshotTool ]; export function getToolDefinitions() { diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts new file mode 100644 index 0000000..7dd6fc0 --- /dev/null +++ b/src/tools/screenshot.ts @@ -0,0 +1,69 @@ +import { chromium } from 'playwright'; +import { ToolModule } from './interface.js'; + +export const ScreenshotTool: ToolModule = { + name: "Screenshot Tool", + configKeys: [], + definition: { + type: "function", + function: { + name: "take_screenshot", + description: "Captures a screenshot of a specified website and saves it as an image file.", + parameters: { + type: "object", + properties: { + url: { + type: "string", + description: "The full URL of the website to capture (e.g., https://google.com)." + }, + outputPath: { + type: "string", + description: "The file path where the screenshot should be saved (e.g., 'homepage.png')." + }, + fullPage: { + type: "boolean", + description: "If true, takes a screenshot of the full scrollable page instead of just the viewport." + } + }, + required: ["url", "outputPath"] + } + } + }, + handler: async (args: any, config: any) => { + let browser; + try { + browser = await chromium.launch({ headless: true }); + } catch (error: any) { + if (error.message.includes("Executable doesn't exist")) { + return "Error: Playwright browsers are not installed. Please run `npx playwright install chromium` to enable this feature."; + } + return `Error launching browser: ${error.message}`; + } + + const context = await browser.newContext({ + userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + viewport: { width: 1280, height: 720 } + }); + const page = await context.newPage(); + + try { + console.log(`Navigating to ${args.url} for screenshot...`); + await page.goto(args.url, { waitUntil: 'networkidle', timeout: 30000 }); + + // Wait a bit for animations etc. + await page.waitForTimeout(1000); + + await page.screenshot({ + path: args.outputPath, + fullPage: args.fullPage || false + }); + + return `Successfully captured screenshot of ${args.url} and saved to ${args.outputPath}`; + + } catch (error: any) { + return `Error taking screenshot: ${error.message}`; + } finally { + await browser.close(); + } + } +}; From 6c76527e1d2acc678ef1e1dbf6797eadde42c77b Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 20:34:56 +0800 Subject: [PATCH 10/45] 1.0.21 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f0c0aa6..f61ab2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.20", + "version": "1.0.21", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.20", + "version": "1.0.21", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 353fc25..92c5fa0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.20", + "version": "1.0.21", "type": "module", "main": "dist/index.js", "bin": { From 4a862d1c86396b28a9015997c2d1d6381f42bc63 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 20:51:36 +0800 Subject: [PATCH 11/45] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E6=96=87=E5=AD=97=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/screenshot.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index 7dd6fc0..c884d24 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -32,7 +32,11 @@ export const ScreenshotTool: ToolModule = { handler: async (args: any, config: any) => { let browser; try { - browser = await chromium.launch({ headless: true }); + // Launch with specific args to improve font rendering + browser = await chromium.launch({ + headless: true, + args: ['--font-render-hinting=none'] + }); } catch (error: any) { if (error.message.includes("Executable doesn't exist")) { return "Error: Playwright browsers are not installed. Please run `npx playwright install chromium` to enable this feature."; @@ -41,8 +45,10 @@ export const ScreenshotTool: ToolModule = { } const context = await browser.newContext({ - userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', - viewport: { width: 1280, height: 720 } + userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + viewport: { width: 1280, height: 720 }, + locale: 'zh-CN', // Set locale to Chinese to help with font selection and site localization + deviceScaleFactor: 2 // High DPI for better text clarity }); const page = await context.newPage(); @@ -50,7 +56,10 @@ export const ScreenshotTool: ToolModule = { console.log(`Navigating to ${args.url} for screenshot...`); await page.goto(args.url, { waitUntil: 'networkidle', timeout: 30000 }); - // Wait a bit for animations etc. + // Wait for fonts to be ready + await page.evaluate(() => document.fonts.ready); + + // Additional small delay for dynamic content await page.waitForTimeout(1000); await page.screenshot({ @@ -66,4 +75,4 @@ export const ScreenshotTool: ToolModule = { await browser.close(); } } -}; +}; \ No newline at end of file From 8e761b742c9c0ff7f51d30d00cb83d8044dfead8 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 20:51:54 +0800 Subject: [PATCH 12/45] 1.0.22 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f61ab2d..56e7230 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.21", + "version": "1.0.22", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.21", + "version": "1.0.22", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 92c5fa0..d4d945f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.21", + "version": "1.0.22", "type": "module", "main": "dist/index.js", "bin": { From 4bb8fde3cabce2591d40ef427a5f7ff74b14f70a Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 21:24:37 +0800 Subject: [PATCH 13/45] =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=8E=86=E5=8F=B2=E5=AF=B9=E8=AF=9D=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 21 +++----- src/tools/screenshot.ts | 107 +++++++++++++++++++++++----------------- 2 files changed, 71 insertions(+), 57 deletions(-) diff --git a/src/index.ts b/src/index.ts index 303216d..cef1c9b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,8 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { fileURLToPath } from 'url'; +import { createInterface } from 'node:readline/promises'; +import { stdin as input, stdout as output } from 'node:process'; // Handle Ctrl+C gracefully function handleExit() { @@ -382,15 +384,11 @@ async function runChat(queryParts: string[], options: any) { } // Main chat loop + const rl = createInterface({ input, output }); + try { while (true) { - const { userInput } = await inquirer.prompt([ - { - type: 'input', - name: 'userInput', - message: 'You >' - } - ]); + const userInput = await rl.question(chalk.green('? ') + 'You > '); if (userInput.toLowerCase() === 'exit' || userInput.toLowerCase() === 'quit') { console.log(chalk.cyan("Goodbye!")); @@ -402,12 +400,9 @@ async function runChat(queryParts: string[], options: any) { await agent.chat(userInput); } } catch (err: any) { - // Check for Inquirer interruption error (Ctrl+C often causes this) - if (err.message && (err.message.includes('User force closed') || err.message.includes('Prompt was canceled'))) { - console.log(chalk.cyan("\nGoodbye!")); - process.exit(0); - } - throw err; // Re-throw real errors to be caught by main().catch + console.error(chalk.red("Error in chat loop:"), err); + } finally { + rl.close(); } } diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index c884d24..93efda8 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -29,50 +29,69 @@ export const ScreenshotTool: ToolModule = { } } }, - handler: async (args: any, config: any) => { - let browser; - try { - // Launch with specific args to improve font rendering - browser = await chromium.launch({ - headless: true, - args: ['--font-render-hinting=none'] - }); - } catch (error: any) { - if (error.message.includes("Executable doesn't exist")) { - return "Error: Playwright browsers are not installed. Please run `npx playwright install chromium` to enable this feature."; + handler: async (args: any, config: any) => { + let browser; + const launchOptions: any = { + headless: true, + args: ['--font-render-hinting=none'] + }; + + try { + // Try to launch system Chrome first as it usually has better font support + browser = await chromium.launch({ ...launchOptions, channel: 'chrome' }); + } catch (e) { + // Fallback to bundled Chromium + try { + browser = await chromium.launch(launchOptions); + } catch (error: any) { + if (error.message.includes("Executable doesn't exist")) { + return "Error: Playwright browsers are not installed. Please run `npx playwright install chromium` to enable this feature."; + } + return `Error launching browser: ${error.message}`; + } } - return `Error launching browser: ${error.message}`; - } - - const context = await browser.newContext({ - userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', - viewport: { width: 1280, height: 720 }, - locale: 'zh-CN', // Set locale to Chinese to help with font selection and site localization - deviceScaleFactor: 2 // High DPI for better text clarity - }); - const page = await context.newPage(); - - try { - console.log(`Navigating to ${args.url} for screenshot...`); - await page.goto(args.url, { waitUntil: 'networkidle', timeout: 30000 }); - - // Wait for fonts to be ready - await page.evaluate(() => document.fonts.ready); - - // Additional small delay for dynamic content - await page.waitForTimeout(1000); - - await page.screenshot({ - path: args.outputPath, - fullPage: args.fullPage || false + + const context = await browser.newContext({ + userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + viewport: { width: 1280, height: 720 }, + locale: 'zh-CN', // Set locale to Chinese + deviceScaleFactor: 2 // High DPI }); - - return `Successfully captured screenshot of ${args.url} and saved to ${args.outputPath}`; - - } catch (error: any) { - return `Error taking screenshot: ${error.message}`; - } finally { - await browser.close(); + const page = await context.newPage(); + + try { + console.log(`Navigating to ${args.url} for screenshot...`); + await page.goto(args.url, { waitUntil: 'networkidle', timeout: 30000 }); + + // Inject CSS to force common Chinese fonts + await page.addStyleTag({ + content: ` + body, h1, h2, h3, h4, h5, h6, p, span, div, li, a, button, input, textarea { + font-family: "PingFang SC", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif !important; + } + ` + }); + + // Wait for fonts to be ready + await page.evaluate(() => document.fonts.ready); + + // Additional small delay for dynamic content and font rendering + await page.waitForTimeout(1000); + + await page.screenshot({ + path: args.outputPath, + fullPage: args.fullPage || false + }); + + return `Successfully captured screenshot of ${args.url} and saved to ${args.outputPath}`; + + } catch (error: any) { + return `Error taking screenshot: ${error.message}`; + } finally { + if (browser) { + await browser.close(); + } + } } - } -}; \ No newline at end of file + }; + \ No newline at end of file From 7034e0eff812b84de61341f904515c4242f452a6 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 21:25:06 +0800 Subject: [PATCH 14/45] 1.0.23 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 56e7230..b0fcea6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.22", + "version": "1.0.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.22", + "version": "1.0.23", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index d4d945f..2f4e5ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.22", + "version": "1.0.23", "type": "module", "main": "dist/index.js", "bin": { From 4da1075e36b6d8dc80c3408a308f7ea8d7ed670f Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 21:33:27 +0800 Subject: [PATCH 15/45] =?UTF-8?q?=E6=9B=B4=E6=96=B0RM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GEMINI.md | 2 ++ README.md | 7 +++++++ package.json | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/GEMINI.md b/GEMINI.md index 5d36c2e..c4b64c1 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -4,6 +4,8 @@ **AutoClaw** is a hyper-lightweight AI agent designed for **massive scale automation** in **headless/containerized environments**. It serves as the ideal "runtime" for executing LLM-driven tasks within Docker containers, allowing users to orchestrate thousands of agents simultaneously for complex parallel workflows. +**GitHub**: [https://github.com/tsingliuwin/autoclaw](https://github.com/tsingliuwin/autoclaw) + ## Core Philosophy - **Docker First**: Designed to run inside isolated containers (Alpine/Debian). - **Massive Scalability**: Low resource footprint enables high-concurrency swarms. diff --git a/README.md b/README.md index 33096b7..fa9490d 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![NPM Version](https://img.shields.io/npm/v/autoclaw.svg?style=flat-square)](https://www.npmjs.com/package/autoclaw) [![NPM Downloads](https://img.shields.io/npm/dm/autoclaw.svg?style=flat-square)](https://www.npmjs.com/package/autoclaw) +[![GitHub](https://img.shields.io/badge/GitHub-Repository-blue?logo=github&style=flat-square)](https://github.com/tsingliuwin/autoclaw) [![License](https://img.shields.io/npm/l/autoclaw.svg?style=flat-square)](https://github.com/tsingliuwin/autoclaw/blob/main/LICENSE) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) @@ -9,6 +10,12 @@ AutoClaw is a hyper-lightweight AI agent designed to live inside **Docker containers**. Unlike heavy, GUI-dependent agents, AutoClaw is built for **headless, massive-scale concurrency**. +--- + +šŸ”— **GitHub Repository**: [https://github.com/tsingliuwin/autoclaw](https://github.com/tsingliuwin/autoclaw) + +--- + You can run one instance to fix a local script, or orchestrate **10,000+ instances** in a Kubernetes cluster to refactor codebases, audit servers, or process data streams in parallel. ## Why AutoClaw? diff --git a/package.json b/package.json index 2f4e5ef..96f6828 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,14 @@ ], "author": "AutoClaw Contributor", "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/tsingliuwin/autoclaw.git" + }, + "bugs": { + "url": "https://github.com/tsingliuwin/autoclaw/issues" + }, + "homepage": "https://github.com/tsingliuwin/autoclaw#readme", "description": "A lightweight AI agent CLI tool that brings the power of LLMs to your terminal.", "dependencies": { "@mozilla/readability": "^0.6.0", From ebaa7ce8cd4a4abb71ecd2de7505c9ad7df905df Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 21:34:15 +0800 Subject: [PATCH 16/45] 1.0.24 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b0fcea6..5da94a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.23", + "version": "1.0.24", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.23", + "version": "1.0.24", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 96f6828..0b4eaec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.23", + "version": "1.0.24", "type": "module", "main": "dist/index.js", "bin": { From a7242e6032035316dbdefcdd43afff34d6d0e7a0 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 22:33:40 +0800 Subject: [PATCH 17/45] =?UTF-8?q?=E5=AE=B9=E5=99=A8=E4=B8=8B=E4=B8=AD?= =?UTF-8?q?=E6=96=87=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 +++++++++++++++++ src/tools/screenshot.ts | 3 +++ 2 files changed, 20 insertions(+) diff --git a/README.md b/README.md index fa9490d..e5fdc37 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,23 @@ Configure webhooks to receive alerts or reports in your team chat apps. Built-in utility to provide the agent with the current system time, ensuring accurate handling of relative time requests. - **Usage**: "What's the date today?" or "Remind me to check the logs next Monday." +## Docker Support + +### Chinese Font Issues in Screenshots +When running AutoClaw inside a Docker container (especially Alpine or Debian Slim), screenshots of Chinese websites may display text as square boxes ("tofu") due to missing fonts. + +**Solution:** Install CJK (Chinese/Japanese/Korean) fonts in your container. + +**For Debian/Ubuntu:** +```bash +apt-get update && apt-get install -y fonts-noto-cjk fonts-wqy-zenhei +``` + +**For Alpine Linux:** +```bash +apk add font-noto-cjk +``` + ## License MIT diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index 93efda8..208ca83 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -64,6 +64,9 @@ export const ScreenshotTool: ToolModule = { await page.goto(args.url, { waitUntil: 'networkidle', timeout: 30000 }); // Inject CSS to force common Chinese fonts + // Note: For Docker environments (Alpine/Debian), ensure fonts are installed. + // Alpine: apk add font-noto-cjk + // Debian/Ubuntu: apt-get install fonts-noto-cjk fonts-wqy-zenhei await page.addStyleTag({ content: ` body, h1, h2, h3, h4, h5, h6, p, span, div, li, a, button, input, textarea { From 014065d060de94e65957d1ce6b16a196d3529186 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 22:33:50 +0800 Subject: [PATCH 18/45] 1.0.25 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5da94a7..168e811 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.24", + "version": "1.0.25", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.24", + "version": "1.0.25", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 0b4eaec..b05c475 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.24", + "version": "1.0.25", "type": "module", "main": "dist/index.js", "bin": { From 18f6885d3ea2d47cc428eba4deaa3a886240911b Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 22:41:26 +0800 Subject: [PATCH 19/45] =?UTF-8?q?=E6=88=AA=E5=9B=BE=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E4=B8=AD=E6=96=87=E6=98=BE=E7=A4=BA=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E6=BB=A1=E8=B6=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/screenshot.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index 208ca83..d446800 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -1,5 +1,22 @@ import { chromium } from 'playwright'; import { ToolModule } from './interface.js'; +import * as fs from 'fs'; +import * as os from 'os'; + +// Helper to check for common CJK font paths on Linux +const checkLinuxFonts = () => { + if (os.platform() !== 'linux') return true; + + const commonFontPaths = [ + '/usr/share/fonts/noto', + '/usr/share/fonts/opentype/noto', + '/usr/share/fonts/truetype/wqy', + '/usr/share/fonts/cjk', + '/usr/share/fonts/google-noto-cjk' // Arch/Manjaro sometimes + ]; + + return commonFontPaths.some(path => fs.existsSync(path)); +}; export const ScreenshotTool: ToolModule = { name: "Screenshot Tool", @@ -30,6 +47,12 @@ export const ScreenshotTool: ToolModule = { } }, handler: async (args: any, config: any) => { + // Check for fonts on Linux to prevent "tofu" characters + if (os.platform() === 'linux' && !checkLinuxFonts()) { + console.warn("āš ļø Warning: No CJK fonts detected. Chinese characters may appear as squares (tofu)."); + console.warn(" Run 'apk add font-noto-cjk' (Alpine) or 'apt-get install fonts-noto-cjk' (Debian/Ubuntu)."); + } + let browser; const launchOptions: any = { headless: true, From 5f34a33c5749b11a16be811829c72bbb8638bed4 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 22:48:53 +0800 Subject: [PATCH 20/45] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 18 ++++++++++++++++-- src/tools/screenshot.ts | 25 +++++++++++++++++-------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/index.ts b/src/index.ts index cef1c9b..9452004 100644 --- a/src/index.ts +++ b/src/index.ts @@ -388,7 +388,16 @@ async function runChat(queryParts: string[], options: any) { try { while (true) { - const userInput = await rl.question(chalk.green('? ') + 'You > '); + let userInput: string; + try { + userInput = await rl.question(chalk.green('? ') + 'You > '); + } catch (err: any) { + if (err.code === 'ABORT_ERR') { + console.log(chalk.cyan("\nGoodbye!")); + break; + } + throw err; + } if (userInput.toLowerCase() === 'exit' || userInput.toLowerCase() === 'quit') { console.log(chalk.cyan("Goodbye!")); @@ -400,7 +409,12 @@ async function runChat(queryParts: string[], options: any) { await agent.chat(userInput); } } catch (err: any) { - console.error(chalk.red("Error in chat loop:"), err); + if (err.name === 'AbortError' || err.code === 'ABORT_ERR') { + // Handled inside loop mostly, but just in case + console.log(chalk.cyan("\nGoodbye!")); + } else { + console.error(chalk.red("Error in chat loop:"), err); + } } finally { rl.close(); } diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index d446800..d9aed69 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -7,15 +7,24 @@ import * as os from 'os'; const checkLinuxFonts = () => { if (os.platform() !== 'linux') return true; - const commonFontPaths = [ - '/usr/share/fonts/noto', - '/usr/share/fonts/opentype/noto', - '/usr/share/fonts/truetype/wqy', - '/usr/share/fonts/cjk', - '/usr/share/fonts/google-noto-cjk' // Arch/Manjaro sometimes + // Check for specific font files rather than just directories + const commonFontFiles = [ + '/usr/share/fonts/noto/NotoSansCJK-Regular.ttc', // Alpine / Some Debian + '/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', // Debian / Ubuntu + '/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc', // ZenHei + '/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc' // Arch ]; - return commonFontPaths.some(path => fs.existsSync(path)); + // Also check if fc-list finds any CJK fonts (if available) + try { + const child_process = require('child_process'); + const output = child_process.execSync('fc-list :lang=zh', { stdio: 'pipe' }).toString(); + if (output.length > 0) return true; + } catch (e) { + // fc-list might not be installed or failed, fall back to file check + } + + return commonFontFiles.some(path => fs.existsSync(path)); }; export const ScreenshotTool: ToolModule = { @@ -93,7 +102,7 @@ export const ScreenshotTool: ToolModule = { await page.addStyleTag({ content: ` body, h1, h2, h3, h4, h5, h6, p, span, div, li, a, button, input, textarea { - font-family: "PingFang SC", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif !important; + font-family: "PingFang SC", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", "Noto Sans CJK SC", "Noto Sans SC", sans-serif !important; } ` }); From bfe18f41df699f5cd7cc82837ea0fce62322b302 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 22:50:01 +0800 Subject: [PATCH 21/45] 1.0.26 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 168e811..9394fd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.25", + "version": "1.0.26", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.25", + "version": "1.0.26", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index b05c475..f8b2b05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.25", + "version": "1.0.26", "type": "module", "main": "dist/index.js", "bin": { From 5954810338b387b8fd895ab9e37211c9f36f222b Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 23:06:05 +0800 Subject: [PATCH 22/45] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=AD=E6=96=87emoji?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E9=BB=98=E8=AE=A4=E6=88=AA=E5=B1=8F=E5=B0=BA?= =?UTF-8?q?=E5=AF=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++---- src/tools/screenshot.ts | 52 ++++++++++++++++++++++++++++------------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index e5fdc37..e07e4b5 100644 --- a/README.md +++ b/README.md @@ -156,18 +156,18 @@ Built-in utility to provide the agent with the current system time, ensuring acc ## Docker Support ### Chinese Font Issues in Screenshots -When running AutoClaw inside a Docker container (especially Alpine or Debian Slim), screenshots of Chinese websites may display text as square boxes ("tofu") due to missing fonts. +When running AutoClaw inside a Docker container (especially Alpine or Debian Slim), screenshots of Chinese websites may display text as square boxes ("tofu") due to missing fonts. Emojis (e.g., šŸ”„) may also appear as squares. -**Solution:** Install CJK (Chinese/Japanese/Korean) fonts in your container. +**Solution:** Install CJK (Chinese/Japanese/Korean) and Emoji fonts in your container. **For Debian/Ubuntu:** ```bash -apt-get update && apt-get install -y fonts-noto-cjk fonts-wqy-zenhei +apt-get update && apt-get install -y fonts-noto-cjk fonts-wqy-zenhei fonts-noto-color-emoji ``` **For Alpine Linux:** ```bash -apk add font-noto-cjk +apk add font-noto-cjk font-noto-emoji ``` ## License diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index d9aed69..8e4a25d 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -3,28 +3,41 @@ import { ToolModule } from './interface.js'; import * as fs from 'fs'; import * as os from 'os'; -// Helper to check for common CJK font paths on Linux +// Helper to check for common CJK and Emoji font paths on Linux const checkLinuxFonts = () => { - if (os.platform() !== 'linux') return true; + if (os.platform() !== 'linux') return { cjk: true, emoji: true }; // Check for specific font files rather than just directories - const commonFontFiles = [ + const commonCJKFontFiles = [ '/usr/share/fonts/noto/NotoSansCJK-Regular.ttc', // Alpine / Some Debian '/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc', // Debian / Ubuntu '/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc', // ZenHei '/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc' // Arch ]; - // Also check if fc-list finds any CJK fonts (if available) + const commonEmojiFontFiles = [ + '/usr/share/fonts/noto/NotoColorEmoji.ttf', + '/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf', + '/usr/share/fonts/google-noto-emoji/NotoColorEmoji.ttf' + ]; + + const hasCJK = commonCJKFontFiles.some(path => fs.existsSync(path)); + const hasEmoji = commonEmojiFontFiles.some(path => fs.existsSync(path)); + + // Also check if fc-list finds fonts (if available) - secondary check try { const child_process = require('child_process'); - const output = child_process.execSync('fc-list :lang=zh', { stdio: 'pipe' }).toString(); - if (output.length > 0) return true; + const cjkOutput = child_process.execSync('fc-list :lang=zh', { stdio: 'pipe' }).toString(); + const emojiOutput = child_process.execSync('fc-list :family=Emoji', { stdio: 'pipe' }).toString(); // Approximate check + + return { + cjk: hasCJK || cjkOutput.length > 0, + emoji: hasEmoji || emojiOutput.length > 0 + }; } catch (e) { // fc-list might not be installed or failed, fall back to file check + return { cjk: hasCJK, emoji: hasEmoji }; } - - return commonFontFiles.some(path => fs.existsSync(path)); }; export const ScreenshotTool: ToolModule = { @@ -48,7 +61,7 @@ export const ScreenshotTool: ToolModule = { }, fullPage: { type: "boolean", - description: "If true, takes a screenshot of the full scrollable page instead of just the viewport." + description: "If true (default), takes a screenshot of the full scrollable page. Set to false for viewport only." } }, required: ["url", "outputPath"] @@ -57,9 +70,16 @@ export const ScreenshotTool: ToolModule = { }, handler: async (args: any, config: any) => { // Check for fonts on Linux to prevent "tofu" characters - if (os.platform() === 'linux' && !checkLinuxFonts()) { - console.warn("āš ļø Warning: No CJK fonts detected. Chinese characters may appear as squares (tofu)."); - console.warn(" Run 'apk add font-noto-cjk' (Alpine) or 'apt-get install fonts-noto-cjk' (Debian/Ubuntu)."); + if (os.platform() === 'linux') { + const fonts = checkLinuxFonts(); + if (!fonts.cjk) { + console.warn("āš ļø Warning: No CJK fonts detected. Chinese characters may appear as squares (tofu)."); + console.warn(" Run 'apk add font-noto-cjk' (Alpine) or 'apt-get install fonts-noto-cjk' (Debian/Ubuntu)."); + } + if (!fonts.emoji) { + console.warn("āš ļø Warning: No Emoji fonts detected. Emojis may appear as squares."); + console.warn(" Run 'apk add font-noto-emoji' (Alpine) or 'apt-get install fonts-noto-color-emoji' (Debian/Ubuntu)."); + } } let browser; @@ -97,12 +117,12 @@ export const ScreenshotTool: ToolModule = { // Inject CSS to force common Chinese fonts // Note: For Docker environments (Alpine/Debian), ensure fonts are installed. - // Alpine: apk add font-noto-cjk - // Debian/Ubuntu: apt-get install fonts-noto-cjk fonts-wqy-zenhei + // Alpine: apk add font-noto-cjk font-noto-emoji + // Debian/Ubuntu: apt-get install fonts-noto-cjk fonts-wqy-zenhei fonts-noto-color-emoji await page.addStyleTag({ content: ` body, h1, h2, h3, h4, h5, h6, p, span, div, li, a, button, input, textarea { - font-family: "PingFang SC", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", "Noto Sans CJK SC", "Noto Sans SC", sans-serif !important; + font-family: "PingFang SC", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", "Noto Sans CJK SC", "Noto Sans SC", "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif !important; } ` }); @@ -115,7 +135,7 @@ export const ScreenshotTool: ToolModule = { await page.screenshot({ path: args.outputPath, - fullPage: args.fullPage || false + fullPage: args.fullPage !== false // Default to true if undefined }); return `Successfully captured screenshot of ${args.url} and saved to ${args.outputPath}`; From 89f9cdca8412506aa1c66510226d83b1f3f59994 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 23:06:25 +0800 Subject: [PATCH 23/45] 1.0.27 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9394fd1..a170197 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.26", + "version": "1.0.27", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.26", + "version": "1.0.27", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index f8b2b05..464c25b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.26", + "version": "1.0.27", "type": "module", "main": "dist/index.js", "bin": { From 1ce998f37f46a1b22a37f5fb95ca337968a7139a Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 23:13:16 +0800 Subject: [PATCH 24/45] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=AD=E6=96=87?= =?UTF-8?q?=E5=92=8Cemoji=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/screenshot.ts | 47 +++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index 8e4a25d..e297268 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -40,6 +40,43 @@ const checkLinuxFonts = () => { } }; +const installFonts = (missing: { cjk: boolean, emoji: boolean }) => { + const child_process = require('child_process'); + try { + let installCmd = ''; + if (fs.existsSync('/etc/alpine-release')) { + // Alpine + const pkgs = []; + if (!missing.cjk) pkgs.push('font-noto-cjk'); + if (!missing.emoji) pkgs.push('font-noto-emoji'); + if (pkgs.length > 0) { + installCmd = `apk add --no-cache ${pkgs.join(' ')}`; + } + } else if (fs.existsSync('/etc/debian_version')) { + // Debian/Ubuntu + const pkgs = []; + if (!missing.cjk) pkgs.push('fonts-noto-cjk', 'fonts-wqy-zenhei'); + if (!missing.emoji) pkgs.push('fonts-noto-color-emoji'); + if (pkgs.length > 0) { + // apt-get update is often needed first in clean containers + installCmd = `apt-get update && apt-get install -y ${pkgs.join(' ')}`; + } + } + + if (installCmd) { + console.log(`Creating font environment... (${installCmd})`); + console.log("This may take a few moments..."); + child_process.execSync(installCmd, { stdio: 'inherit' }); + console.log('āœ… Fonts installed successfully.'); + return true; + } + } catch (e: any) { + console.warn(`āš ļø Failed to auto-install fonts: ${e.message}`); + console.warn('Please install them manually to fix "tofu" characters.'); + } + return false; +}; + export const ScreenshotTool: ToolModule = { name: "Screenshot Tool", configKeys: [], @@ -72,13 +109,9 @@ export const ScreenshotTool: ToolModule = { // Check for fonts on Linux to prevent "tofu" characters if (os.platform() === 'linux') { const fonts = checkLinuxFonts(); - if (!fonts.cjk) { - console.warn("āš ļø Warning: No CJK fonts detected. Chinese characters may appear as squares (tofu)."); - console.warn(" Run 'apk add font-noto-cjk' (Alpine) or 'apt-get install fonts-noto-cjk' (Debian/Ubuntu)."); - } - if (!fonts.emoji) { - console.warn("āš ļø Warning: No Emoji fonts detected. Emojis may appear as squares."); - console.warn(" Run 'apk add font-noto-emoji' (Alpine) or 'apt-get install fonts-noto-color-emoji' (Debian/Ubuntu)."); + if (!fonts.cjk || !fonts.emoji) { + console.log("Missing fonts detected. Attempting to fix environment..."); + installFonts(fonts); } } From 25c88316762fc89a8b7f131ade60f50ab4932510 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 23:13:34 +0800 Subject: [PATCH 25/45] 1.0.28 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a170197..a1a2af3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.27", + "version": "1.0.28", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.27", + "version": "1.0.28", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 464c25b..8fe23f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.27", + "version": "1.0.28", "type": "module", "main": "dist/index.js", "bin": { From 9af2849ce634865ef9659a0047332686f82459a9 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 23:15:46 +0800 Subject: [PATCH 26/45] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/screenshot.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index e297268..61b5c52 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -2,6 +2,7 @@ import { chromium } from 'playwright'; import { ToolModule } from './interface.js'; import * as fs from 'fs'; import * as os from 'os'; +import * as child_process from 'child_process'; // Helper to check for common CJK and Emoji font paths on Linux const checkLinuxFonts = () => { @@ -26,7 +27,6 @@ const checkLinuxFonts = () => { // Also check if fc-list finds fonts (if available) - secondary check try { - const child_process = require('child_process'); const cjkOutput = child_process.execSync('fc-list :lang=zh', { stdio: 'pipe' }).toString(); const emojiOutput = child_process.execSync('fc-list :family=Emoji', { stdio: 'pipe' }).toString(); // Approximate check @@ -41,7 +41,6 @@ const checkLinuxFonts = () => { }; const installFonts = (missing: { cjk: boolean, emoji: boolean }) => { - const child_process = require('child_process'); try { let installCmd = ''; if (fs.existsSync('/etc/alpine-release')) { From 64416e913cdf6a66b654aee37872a130ce1187ca Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sat, 7 Feb 2026 23:15:55 +0800 Subject: [PATCH 27/45] 1.0.29 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1a2af3..f25d50a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.28", + "version": "1.0.29", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.28", + "version": "1.0.29", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 8fe23f8..6ea76a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.28", + "version": "1.0.29", "type": "module", "main": "dist/index.js", "bin": { From ea804a2cd7751af2a5b18869bd4da368dc15f0d2 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 13:03:48 +0800 Subject: [PATCH 28/45] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=89=93=E5=BC=80?= =?UTF-8?q?=E7=BD=91=E9=A1=B5=E5=90=8E=E7=9A=84=E7=AD=89=E5=BE=85=E6=97=B6?= =?UTF-8?q?=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/tools/screenshot.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/tools/screenshot.ts b/src/tools/screenshot.ts index 61b5c52..d359045 100644 --- a/src/tools/screenshot.ts +++ b/src/tools/screenshot.ts @@ -98,6 +98,10 @@ export const ScreenshotTool: ToolModule = { fullPage: { type: "boolean", description: "If true (default), takes a screenshot of the full scrollable page. Set to false for viewport only." + }, + waitTime: { + type: "number", + description: "Optional delay in seconds to wait for page resources to load before capturing (default: 1)." } }, required: ["url", "outputPath"] @@ -162,8 +166,12 @@ export const ScreenshotTool: ToolModule = { // Wait for fonts to be ready await page.evaluate(() => document.fonts.ready); - // Additional small delay for dynamic content and font rendering - await page.waitForTimeout(1000); + // Additional delay for dynamic content (user specified or default 1s) + const waitTimeMs = (args.waitTime || 1) * 1000; + if (waitTimeMs > 0) { + console.log(`Waiting for ${waitTimeMs}ms...`); + await page.waitForTimeout(waitTimeMs); + } await page.screenshot({ path: args.outputPath, From a3beeb498da6a5c9d76a1dd286c3603822ba8e90 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 13:04:14 +0800 Subject: [PATCH 29/45] 1.0.30 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f25d50a..40605c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.29", + "version": "1.0.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.29", + "version": "1.0.30", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 6ea76a1..808a089 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.29", + "version": "1.0.30", "type": "module", "main": "dist/index.js", "bin": { From e27bb823c1b58dd4b8e9afaa4f4ba60971465cea Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 13:47:31 +0800 Subject: [PATCH 30/45] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=BA=A7=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/index.ts b/src/index.ts index 9452004..612d708 100644 --- a/src/index.ts +++ b/src/index.ts @@ -86,8 +86,9 @@ program program .command('setup') .description('Run the interactive setup wizard to configure API keys') - .action(async () => { - await runSetup(); + .option('-p, --project', 'Save configuration to project-level (.autoclaw/setting.json)') + .action(async (options) => { + await runSetup(options); }); program @@ -100,11 +101,18 @@ program program.parse(process.argv); -async function runSetup() { - console.log(chalk.bold.cyan("AutoClaw Setup Wizard šŸ¦ž\n")); - console.log(chalk.dim(`Config will be saved to: ${GLOBAL_CONFIG_FILE}`)); +async function runSetup(options: any = {}) { + const isProject = options.project; + const targetFile = isProject ? LOCAL_CONFIG_FILE : GLOBAL_CONFIG_FILE; + const targetDir = isProject ? path.join(process.cwd(), '.autoclaw') : GLOBAL_CONFIG_DIR; - const currentConfig = loadJsonConfig(GLOBAL_CONFIG_FILE); + console.log(chalk.bold.cyan("AutoClaw Setup Wizard šŸ¦ž\n")); + console.log(chalk.dim(`Config will be saved to: ${targetFile}`)); + + // Load both to show current effective values as defaults + const globalConfig = loadJsonConfig(GLOBAL_CONFIG_FILE); + const localConfig = loadJsonConfig(LOCAL_CONFIG_FILE); + const currentConfig = { ...globalConfig, ...localConfig }; function maskSecret(secret?: string): string { if (!secret || secret.length < 8) return '******'; @@ -282,11 +290,11 @@ async function runSetup() { }; try { - if (!fs.existsSync(GLOBAL_CONFIG_DIR)) { - fs.mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true }); + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }); } - fs.writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(newConfig, null, 2), { mode: 0o600 }); - console.log(chalk.green(`\nāœ… Configuration saved to ${GLOBAL_CONFIG_FILE}`)); + fs.writeFileSync(targetFile, JSON.stringify(newConfig, null, 2), { mode: 0o600 }); + console.log(chalk.green(`\nāœ… Configuration saved to ${targetFile}`)); console.log(chalk.cyan("You can now run 'autoclaw' to start using the agent.")); } catch (error: any) { console.error(chalk.red(`Failed to write config: ${error.message}`)); From 91d744aa1e3683bfcccd920b8646fa1d465a75e0 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 13:47:54 +0800 Subject: [PATCH 31/45] 1.0.31 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 40605c4..9da1595 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.30", + "version": "1.0.31", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.30", + "version": "1.0.31", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 808a089..b3b3611 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.30", + "version": "1.0.31", "type": "module", "main": "dist/index.js", "bin": { From eabf0e772a0b02feaa4e3362ca78af5b322e795f Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 14:06:40 +0800 Subject: [PATCH 32/45] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/index.ts b/src/index.ts index 612d708..34a49c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,8 +8,6 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { fileURLToPath } from 'url'; -import { createInterface } from 'node:readline/promises'; -import { stdin as input, stdout as output } from 'node:process'; // Handle Ctrl+C gracefully function handleExit() { @@ -392,20 +390,16 @@ async function runChat(queryParts: string[], options: any) { } // Main chat loop - const rl = createInterface({ input, output }); - try { while (true) { - let userInput: string; - try { - userInput = await rl.question(chalk.green('? ') + 'You > '); - } catch (err: any) { - if (err.code === 'ABORT_ERR') { - console.log(chalk.cyan("\nGoodbye!")); - break; + const { userInput } = await inquirer.prompt([ + { + type: 'input', + name: 'userInput', + message: 'You >', + prefix: chalk.green('?') } - throw err; - } + ]); if (userInput.toLowerCase() === 'exit' || userInput.toLowerCase() === 'quit') { console.log(chalk.cyan("Goodbye!")); @@ -417,14 +411,11 @@ async function runChat(queryParts: string[], options: any) { await agent.chat(userInput); } } catch (err: any) { - if (err.name === 'AbortError' || err.code === 'ABORT_ERR') { - // Handled inside loop mostly, but just in case + if (err.message && (err.message.includes('User force closed') || err.message.includes('Prompt was canceled'))) { console.log(chalk.cyan("\nGoodbye!")); } else { console.error(chalk.red("Error in chat loop:"), err); } - } finally { - rl.close(); } } From 7be373b82e03d36b46640f4bf5d72be5366b74dd Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 14:07:01 +0800 Subject: [PATCH 33/45] 1.0.32 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9da1595..f0dcb2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.31", + "version": "1.0.32", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.31", + "version": "1.0.32", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index b3b3611..c42e067 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.31", + "version": "1.0.32", "type": "module", "main": "dist/index.js", "bin": { From c337eacf4d9c6cfc17e32c555742e784076a9636 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 14:30:07 +0800 Subject: [PATCH 34/45] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=9F=E5=9B=BE?= =?UTF-8?q?=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 41 +++++++ src/tools/image.ts | 262 +++++++++++++++++++++++++++++++++++++++++++++ src/tools/index.ts | 4 +- 3 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 src/tools/image.ts diff --git a/src/index.ts b/src/index.ts index 34a49c0..6a36abc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,6 +36,9 @@ interface AppConfig { smtpPass?: string; smtpFrom?: string; tavilyApiKey?: string; + imageApiKey?: string; + imageBaseUrl?: string; + imageModel?: string; autoConfirm?: boolean; feishuWebhook?: string; feishuKeyword?: string; @@ -143,6 +146,12 @@ async function runSetup(options: any = {}) { message: 'Enter default Model:', default: currentConfig.model || 'gpt-4o' }, + { + type: 'confirm', + name: 'configureImage', + message: 'Do you want to configure a separate Image Generation Service (DALL-E)?', + default: !!currentConfig.imageApiKey + }, { type: 'confirm', name: 'configureEmail', @@ -166,6 +175,37 @@ async function runSetup(options: any = {}) { // Resolve sensitive values (Keep old if empty) const finalApiKey = answers.apiKey || currentConfig.apiKey; + let imageConfig: any = {}; + if (answers.configureImage) { + const imageAnswers = await inquirer.prompt([ + { + type: 'password', + name: 'imageApiKey', + message: currentConfig.imageApiKey + ? `Enter Image Service API Key (Leave empty to keep ${maskSecret(currentConfig.imageApiKey)}, or leave empty to use main API key):` + : 'Enter Image Service API Key (Leave empty to use main API key):', + mask: '*' + }, + { + type: 'input', + name: 'imageBaseUrl', + message: 'Enter Image Service Base URL:', + default: currentConfig.imageBaseUrl || currentConfig.baseUrl || 'https://api.openai.com/v1' + }, + { + type: 'input', + name: 'imageModel', + message: 'Default Image Model:', + default: currentConfig.imageModel || 'dall-e-3' + } + ]); + imageConfig = { + imageApiKey: imageAnswers.imageApiKey || currentConfig.imageApiKey, + imageBaseUrl: imageAnswers.imageBaseUrl, + imageModel: imageAnswers.imageModel + }; + } + let emailConfig: any = {}; if (answers.configureEmail) { const emailAnswers = await inquirer.prompt([ @@ -282,6 +322,7 @@ async function runSetup(options: any = {}) { apiKey: finalApiKey, baseUrl: answers.baseUrl, model: answers.model, + ...imageConfig, ...emailConfig, ...searchConfig, ...notifyConfig diff --git a/src/tools/image.ts b/src/tools/image.ts new file mode 100644 index 0000000..ed89c82 --- /dev/null +++ b/src/tools/image.ts @@ -0,0 +1,262 @@ +import OpenAI from 'openai'; +import * as fs from 'fs'; +import * as path from 'path'; +import { ToolModule } from './interface.js'; + +const toolDefinition = { + type: "function", + function: { + name: "generate_image", + description: "Generates or edits images using AI models (DALL-E 3/2). Supports text-to-image, image variation, and image editing. Allows control over size, resolution (quality), and model selection.", + parameters: { + type: "object", + properties: { + prompt: { + type: "string", + description: "Text description of the desired image. Required for text-to-image and edit modes." + }, + image_path: { + type: "string", + description: "Path to an existing image file (local path). Required for variation and editing modes." + }, + mask_path: { + type: "string", + description: "Path to a mask image file (local path). Optional, used only for editing." + }, + mode: { + type: "string", + enum: ["text-to-image", "variation", "edit"], + description: "Operation mode. Inferred if not provided." + }, + model: { + type: "string", + enum: ["dall-e-3", "dall-e-2"], + description: "The AI model to use. 'dall-e-3' for high quality (default), 'dall-e-2' for faster/smaller generation or editing.", + default: "dall-e-3" + }, + n: { + type: "integer", + description: "Number of images to generate. Default is 1.", + default: 1 + }, + size: { + type: "string", + description: "Image resolution/size. DALL-E 3: '1024x1024', '1024x1792' (Portrait), '1792x1024' (Landscape). DALL-E 2: '256x256', '512x512', '1024x1024'.", + default: "1024x1024" + }, + quality: { + type: "string", + enum: ["standard", "hd"], + description: "Image quality (DALL-E 3 only). 'hd' creates more detailed images. Default is 'standard'.", + default: "standard" + }, + style: { + type: "string", + enum: ["vivid", "natural"], + description: "Image style (DALL-E 3 only). Default is 'vivid'.", + default: "vivid" + }, + output_dir: { + type: "string", + description: "Directory to save the generated images. Defaults to current directory." + } + }, + required: [] + } + } +}; + +async function downloadImage(url: string, destPath: string): Promise { + const response = await fetch(url); + if (!response.ok) throw new Error(`Failed to download image: ${response.statusText}`); + const buffer = await response.arrayBuffer(); + fs.writeFileSync(destPath, Buffer.from(buffer)); +} + +const handler = async (args: any, config: any): Promise => { + const apiKey = config.imageApiKey || config.apiKey || process.env.OPENAI_API_KEY; + const baseURL = config.imageBaseUrl || config.baseUrl || process.env.OPENAI_BASE_URL; + + if (!apiKey) { + return "Error: Image Service API Key is missing. Please configure it in .autoclaw/setting.json (imageApiKey or apiKey)."; + } + + const client = new OpenAI({ + apiKey: apiKey, + baseURL: baseURL + }); + + const { + prompt, + image_path, + mask_path, + n = 1, + size = "1024x1024", + quality = "standard", + style = "vivid", + output_dir = "." + } = args; + + let mode = args.mode; + let model = args.model || config.imageModel || "dall-e-3"; + + // Infer mode if not provided + if (!mode) { + if (image_path && mask_path) mode = "edit"; + else if (image_path) mode = "variation"; + else mode = "text-to-image"; + } + + // Model-specific validations + if (mode === "text-to-image") { + // DALL-E 3 Validation + if (model === "dall-e-3") { + const validSizes = ["1024x1024", "1024x1792", "1792x1024"]; + if (!validSizes.includes(size)) { + return `Error: Invalid size '${size}' for DALL-E 3. Supported sizes are: ${validSizes.join(", ")}.`; + } + } + // DALL-E 2 Validation + else if (model === "dall-e-2") { + const validSizes = ["256x256", "512x512", "1024x1024"]; + if (!validSizes.includes(size)) { + return `Error: Invalid size '${size}' for DALL-E 2. Supported sizes are: ${validSizes.join(", ")}.`; + } + } + } else { + // Variation and Edit only support DALL-E 2 currently + if (model === "dall-e-3") { + console.log("Note: DALL-E 3 does not support variation/edit. Falling back to DALL-E 2."); + model = "dall-e-2"; + } + } + + // Resolve output directory + const resolvedOutputDir = path.resolve(process.cwd(), output_dir); + if (!fs.existsSync(resolvedOutputDir)) { + fs.mkdirSync(resolvedOutputDir, { recursive: true }); + } + + const generatedFiles: string[] = []; + + try { + if (mode === "text-to-image") { + if (!prompt) return "Error: 'prompt' is required for text-to-image mode."; + + console.log(`Generating ${n} image(s) with ${model} (${size}, ${quality})...`); + + if (model === "dall-e-3") { + for (let i = 0; i < n; i++) { + const response = await client.images.generate({ + model: "dall-e-3", + prompt: prompt, + n: 1, // DALL-E 3 constraint + size: size as any, + quality: quality as any, + style: style as any, + response_format: "url" + }); + + const imageUrl = response.data?.[0]?.url; + if (imageUrl) { + const fileName = `generated-${Date.now()}-${i + 1}.png`; + const filePath = path.join(resolvedOutputDir, fileName); + await downloadImage(imageUrl, filePath); + generatedFiles.push(filePath); + } + } + } else { + // DALL-E 2 + const response = await client.images.generate({ + model: "dall-e-2", + prompt: prompt, + n: n, + size: size as any, + response_format: "url" + }); + + const data = response.data || []; + for (let i = 0; i < data.length; i++) { + const imageUrl = data[i].url; + if (imageUrl) { + const fileName = `generated-${Date.now()}-${i + 1}.png`; + const filePath = path.join(resolvedOutputDir, fileName); + await downloadImage(imageUrl, filePath); + generatedFiles.push(filePath); + } + } + } + + } else if (mode === "variation") { + if (!image_path) return "Error: 'image_path' is required for variation mode."; + if (!fs.existsSync(image_path)) return `Error: Image file not found at ${image_path}`; + + console.log(`Generating ${n} variation(s) with ${model}...`); + + const response = await client.images.createVariation({ + image: fs.createReadStream(image_path), + n: n, + model: "dall-e-2", // Explicitly set model just in case, though it's the default/only option + size: size as any, + response_format: "url" + }); + + const data = response.data || []; + for (let i = 0; i < data.length; i++) { + const imageUrl = data[i].url; + if (imageUrl) { + const fileName = `variation-${Date.now()}-${i + 1}.png`; + const filePath = path.join(resolvedOutputDir, fileName); + await downloadImage(imageUrl, filePath); + generatedFiles.push(filePath); + } + } + + } else if (mode === "edit") { + if (!image_path) return "Error: 'image_path' is required for edit mode."; + if (!prompt) return "Error: 'prompt' is required for edit mode."; + if (!fs.existsSync(image_path)) return `Error: Image file not found at ${image_path}`; + + console.log(`Editing image with ${model}...`); + + const params: any = { + image: fs.createReadStream(image_path), + prompt: prompt, + n: n, + model: "dall-e-2", + size: size as any, + response_format: "url" + }; + + if (mask_path && fs.existsSync(mask_path)) { + params.mask = fs.createReadStream(mask_path); + } + + const response = await client.images.edit(params); + + const data = response.data || []; + for (let i = 0; i < data.length; i++) { + const imageUrl = data[i].url; + if (imageUrl) { + const fileName = `edited-${Date.now()}-${i + 1}.png`; + const filePath = path.join(resolvedOutputDir, fileName); + await downloadImage(imageUrl, filePath); + generatedFiles.push(filePath); + } + } + } else { + return `Error: Unknown mode '${mode}'.`; + } + + return `Successfully generated ${generatedFiles.length} image(s):\n${generatedFiles.join('\n')}`; + + } catch (error: any) { + return `Error generating image: ${error.message}`; + } +}; + +export const ImageTool: ToolModule = { + name: "Image Generation", + definition: toolDefinition as any, + handler: handler +}; diff --git a/src/tools/index.ts b/src/tools/index.ts index 2b991cc..6c598fb 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -5,6 +5,7 @@ import { SearchTool } from './search.js'; import { NotifyTool } from './notify.js'; import { BrowserTool } from './browser.js'; import { ScreenshotTool } from './screenshot.js'; +import { ImageTool } from './image.js'; // Central Registry of all available tools export const toolRegistry: ToolModule[] = [ @@ -16,7 +17,8 @@ export const toolRegistry: ToolModule[] = [ SearchTool, NotifyTool, BrowserTool, - ScreenshotTool + ScreenshotTool, + ImageTool ]; export function getToolDefinitions() { From 94dccc0d2b085815942609e714ecf29e61cb76ef Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 14:30:16 +0800 Subject: [PATCH 35/45] 1.0.33 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f0dcb2f..5983715 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.32", + "version": "1.0.33", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.32", + "version": "1.0.33", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index c42e067..3cb843c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.32", + "version": "1.0.33", "type": "module", "main": "dist/index.js", "bin": { From 2704c30131a36ebc94515cae81c5ab2014d19ffc Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 14:39:05 +0800 Subject: [PATCH 36/45] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=8F=96=E5=80=BC=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 6a36abc..410d97a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -113,7 +113,11 @@ async function runSetup(options: any = {}) { // Load both to show current effective values as defaults const globalConfig = loadJsonConfig(GLOBAL_CONFIG_FILE); const localConfig = loadJsonConfig(LOCAL_CONFIG_FILE); - const currentConfig = { ...globalConfig, ...localConfig }; + // If setting up Global (default), prioritize Global values for display, falling back to Local. + // If setting up Project, prioritize Project values (standard effective config). + const currentConfig = isProject + ? { ...globalConfig, ...localConfig } + : { ...localConfig, ...globalConfig }; function maskSecret(secret?: string): string { if (!secret || secret.length < 8) return '******'; From fa82fa89ab3b63f2825a16ef9f0585ddc41a3bb3 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 14:39:12 +0800 Subject: [PATCH 37/45] 1.0.34 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5983715..3ea885c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.33", + "version": "1.0.34", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.33", + "version": "1.0.34", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 3cb843c..047c650 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.33", + "version": "1.0.34", "type": "module", "main": "dist/index.js", "bin": { From 34b5c1c66a4c193fd691014337e77f32c6e16015 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 14:52:17 +0800 Subject: [PATCH 38/45] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=BA=94=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 4 ++++ src/tools/image.ts | 9 +++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 410d97a..ea66772 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,10 @@ interface AppConfig { imageApiKey?: string; imageBaseUrl?: string; imageModel?: string; + imageSize?: string; + imageQuality?: string; + imageStyle?: string; + imageN?: number; autoConfirm?: boolean; feishuWebhook?: string; feishuKeyword?: string; diff --git a/src/tools/image.ts b/src/tools/image.ts index ed89c82..a31e2b1 100644 --- a/src/tools/image.ts +++ b/src/tools/image.ts @@ -90,13 +90,14 @@ const handler = async (args: any, config: any): Promise => { prompt, image_path, mask_path, - n = 1, - size = "1024x1024", - quality = "standard", - style = "vivid", output_dir = "." } = args; + const n = args.n || config.imageN || 1; + const size = args.size || config.imageSize || "1024x1024"; + const quality = args.quality || config.imageQuality || "standard"; + const style = args.style || config.imageStyle || "vivid"; + let mode = args.mode; let model = args.model || config.imageModel || "dall-e-3"; From 8cdef12507bdc054ea249575576c87c8fa1f7bbf Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 14:52:50 +0800 Subject: [PATCH 39/45] 1.0.35 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3ea885c..9208b70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.34", + "version": "1.0.35", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.34", + "version": "1.0.35", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 047c650..5eb79cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.34", + "version": "1.0.35", "type": "module", "main": "dist/index.js", "bin": { From be749d52fb659c2a0aeda2e273a8a2f46e7c1860 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 15:33:40 +0800 Subject: [PATCH 40/45] =?UTF-8?q?=E7=94=9F=E5=9B=BE=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agent.ts | 1 + src/index.ts | 25 ++++++++++++++++--------- src/tools/image.ts | 33 ++++++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/agent.ts b/src/agent.ts index 78ae434..c645f3f 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -27,6 +27,7 @@ System Information: - Current Working Directory: ${process.cwd()} - User: ${os.userInfo().username} - Home Directory: ${os.homedir()} +- Current Date: ${new Date().toLocaleString()} `; this.messages = [ diff --git a/src/index.ts b/src/index.ts index ea66772..974e52e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import { Agent } from './agent.js'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; +import * as readline from 'node:readline/promises'; import { fileURLToPath } from 'url'; // Handle Ctrl+C gracefully @@ -439,16 +440,15 @@ async function runChat(queryParts: string[], options: any) { } // Main chat loop + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: true + }); + try { while (true) { - const { userInput } = await inquirer.prompt([ - { - type: 'input', - name: 'userInput', - message: 'You >', - prefix: chalk.green('?') - } - ]); + const userInput = await rl.question(chalk.green('?') + ' You > '); if (userInput.toLowerCase() === 'exit' || userInput.toLowerCase() === 'quit') { console.log(chalk.cyan("Goodbye!")); @@ -457,7 +457,12 @@ async function runChat(queryParts: string[], options: any) { if (userInput.trim() === '') continue; - await agent.chat(userInput); + rl.pause(); + try { + await agent.chat(userInput); + } finally { + rl.resume(); + } } } catch (err: any) { if (err.message && (err.message.includes('User force closed') || err.message.includes('Prompt was canceled'))) { @@ -465,6 +470,8 @@ async function runChat(queryParts: string[], options: any) { } else { console.error(chalk.red("Error in chat loop:"), err); } + } finally { + rl.close(); } } diff --git a/src/tools/image.ts b/src/tools/image.ts index a31e2b1..e9b6b1f 100644 --- a/src/tools/image.ts +++ b/src/tools/image.ts @@ -1,4 +1,5 @@ import OpenAI from 'openai'; +import chalk from 'chalk'; import * as fs from 'fs'; import * as path from 'path'; import { ToolModule } from './interface.js'; @@ -30,8 +31,7 @@ const toolDefinition = { }, model: { type: "string", - enum: ["dall-e-3", "dall-e-2"], - description: "The AI model to use. 'dall-e-3' for high quality (default), 'dall-e-2' for faster/smaller generation or editing.", + description: "The AI model to use. 'dall-e-3' for high quality (default), 'dall-e-2' for editing, or a custom model like 'doubao-seedream-4-5-251128'.", default: "dall-e-3" }, n: { @@ -41,7 +41,7 @@ const toolDefinition = { }, size: { type: "string", - description: "Image resolution/size. DALL-E 3: '1024x1024', '1024x1792' (Portrait), '1792x1024' (Landscape). DALL-E 2: '256x256', '512x512', '1024x1024'.", + description: "Resolution/Aspect Ratio. YOU should infer the best size based on the prompt content.\n- DALL-E 3: '1024x1024' (Square), '1792x1024' (Landscape), '1024x1792' (Portrait).\n- Doubao/High-Res: MUST be >3.6M pixels. Use '2048x2048' (Square), '2560x1440' (Landscape), '1440x2560' (Portrait).", default: "1024x1024" }, quality: { @@ -94,13 +94,24 @@ const handler = async (args: any, config: any): Promise => { } = args; const n = args.n || config.imageN || 1; - const size = args.size || config.imageSize || "1024x1024"; + + // Model-specific default size + let mode = args.mode; + let model = args.model; + if (config.imageModel && (!model || model === 'dall-e-3')) { + model = config.imageModel; + } + model = model || "dall-e-3"; + + let defaultSize = "1024x1024"; + if (model.toLowerCase().includes("doubao")) { + defaultSize = "2048x2048"; + } + + const size = args.size || config.imageSize || defaultSize; const quality = args.quality || config.imageQuality || "standard"; const style = args.style || config.imageStyle || "vivid"; - let mode = args.mode; - let model = args.model || config.imageModel || "dall-e-3"; - // Infer mode if not provided if (!mode) { if (image_path && mask_path) mode = "edit"; @@ -167,9 +178,9 @@ const handler = async (args: any, config: any): Promise => { } } } else { - // DALL-E 2 + // DALL-E 2 or Custom Model const response = await client.images.generate({ - model: "dall-e-2", + model: model, prompt: prompt, n: n, size: size as any, @@ -252,6 +263,10 @@ const handler = async (args: any, config: any): Promise => { return `Successfully generated ${generatedFiles.length} image(s):\n${generatedFiles.join('\n')}`; } catch (error: any) { + console.error(chalk.red(`Image Generation Failed: ${error.message}`)); + if (error.response && error.response.data) { + console.error(chalk.dim(JSON.stringify(error.response.data))); + } return `Error generating image: ${error.message}`; } }; From 8585d85e128f43512c7b15800607a734fdd84c90 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 15:34:07 +0800 Subject: [PATCH 41/45] 1.0.36 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9208b70..0e6f7eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.35", + "version": "1.0.36", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.35", + "version": "1.0.36", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 5eb79cd..6d51729 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.35", + "version": "1.0.36", "type": "module", "main": "dist/index.js", "bin": { From a375b2ffc8dd30cd795f453e3143454f612054f0 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 16:03:19 +0800 Subject: [PATCH 42/45] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E8=AF=8D=E4=BC=98=E5=8C=96=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agent.ts | 1 + src/tools/index.ts | 2 + src/tools/prompt-optimizer.ts | 69 +++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/tools/prompt-optimizer.ts diff --git a/src/agent.ts b/src/agent.ts index c645f3f..7593138 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -49,6 +49,7 @@ GUIDELINES: 2. ROBUSTNESS: Use standard Linux/Unix tools found in minimal images (Alpine/Debian). 3. TOOLS: Use 'execute_shell_command' for actions, 'write_file' for code generation. 4. CLARITY: Output concise logs. You are a worker unit, not a chat bot. +5. OPTIMIZATION: When asked to generate creative content (images, stories, complex code), use 'optimize_prompt' first to ensure the best possible output quality. ` } ]; diff --git a/src/tools/index.ts b/src/tools/index.ts index 6c598fb..1333730 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -6,6 +6,7 @@ import { NotifyTool } from './notify.js'; import { BrowserTool } from './browser.js'; import { ScreenshotTool } from './screenshot.js'; import { ImageTool } from './image.js'; +import { PromptOptimizerTool } from './prompt-optimizer.js'; // Central Registry of all available tools export const toolRegistry: ToolModule[] = [ @@ -13,6 +14,7 @@ export const toolRegistry: ToolModule[] = [ ReadFileTool, WriteFileTool, DateTimeTool, + PromptOptimizerTool, EmailTool, SearchTool, NotifyTool, diff --git a/src/tools/prompt-optimizer.ts b/src/tools/prompt-optimizer.ts new file mode 100644 index 0000000..da54fb7 --- /dev/null +++ b/src/tools/prompt-optimizer.ts @@ -0,0 +1,69 @@ +import OpenAI from 'openai'; +import { ToolModule } from './interface.js'; + +export const PromptOptimizerTool: ToolModule = { + name: "Prompt Optimizer", + definition: { + type: "function", + function: { + name: "optimize_prompt", + description: "Optimize a user's raw task description or prompt to be more professional, structured, and effective. STRONGLY RECOMMENDED for creative tasks (like image generation) or complex scripts to ensure high-quality results.", + parameters: { + type: "object", + properties: { + raw_prompt: { + type: "string", + description: "The original, raw prompt or task description provided by the user." + }, + context: { + type: "string", + description: "Optional context about the goal, audience, or specific requirements (e.g., 'for an image generator', 'for a code reviewer')." + } + }, + required: ["raw_prompt"] + } + } + }, + handler: async (args: any, config: any) => { + if (!config?.apiKey) { + return "Error: OpenAI API Key is missing in the configuration. Please run 'autoclaw setup' or check your .env file."; + } + + const client = new OpenAI({ + apiKey: config.apiKey, + baseURL: config.baseUrl + }); + + const contextMsg = args.context ? `Context: ${args.context}` : "Context: General AI Assistant interaction."; + + try { + const completion = await client.chat.completions.create({ + model: config.model || 'gpt-4o', + messages: [ + { + role: "system", + content: `You are an expert Prompt Engineer. Your goal is to rewrite the user's raw prompt to be clear, precise, and highly effective for LLMs or professional communication. + +RULES: +1. Preserve the original intent. +2. Structure the prompt logically (e.g., Role, Context, Task, Constraints, Output Format). +3. Use professional and concise language. +4. Return ONLY the optimized prompt. Do not add conversational filler.` + }, + { + role: "user", + content: `Raw Prompt: "${args.raw_prompt}" + +${contextMsg} + +Please optimize this prompt.` + } + ] + }); + + return completion.choices[0].message?.content || "Error: Failed to generate optimized prompt."; + } catch (error: any) { + return `Error optimizing prompt: ${error.message}`; + } + } +}; From b187a6507a87240f69365a4d096ce11826a2c606 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 16:03:37 +0800 Subject: [PATCH 43/45] 1.0.37 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0e6f7eb..e83be5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.36", + "version": "1.0.37", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.36", + "version": "1.0.37", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index 6d51729..e8c995d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.36", + "version": "1.0.37", "type": "module", "main": "dist/index.js", "bin": { From cd1284c72b9061de60826e2e4d184cb8de15af11 Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 16:05:33 +0800 Subject: [PATCH 44/45] fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e8c995d..ccd7def 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "type": "module", "main": "dist/index.js", "bin": { - "autoclaw": "./dist/index.js" + "autoclaw": "dist/index.js" }, "scripts": { "build": "tsc", From a5a91c879217b2677060b6f52805c3f99ffe732d Mon Sep 17 00:00:00 2001 From: tsingliu <410869548@qq.com> Date: Sun, 8 Feb 2026 16:05:40 +0800 Subject: [PATCH 45/45] 1.0.38 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e83be5d..388b057 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "autoclaw", - "version": "1.0.37", + "version": "1.0.38", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "autoclaw", - "version": "1.0.37", + "version": "1.0.38", "license": "MIT", "dependencies": { "@mozilla/readability": "^0.6.0", diff --git a/package.json b/package.json index ccd7def..4473b70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autoclaw", - "version": "1.0.37", + "version": "1.0.38", "type": "module", "main": "dist/index.js", "bin": {