@todoforai/edge 0.13.9 → 0.13.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +299 -180
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -656,6 +656,8 @@ var require_receiver = __commonJS((exports, module) => {
|
|
|
656
656
|
this._binaryType = options.binaryType || BINARY_TYPES[0];
|
|
657
657
|
this._extensions = options.extensions || {};
|
|
658
658
|
this._isServer = !!options.isServer;
|
|
659
|
+
this._maxBufferedChunks = options.maxBufferedChunks | 0;
|
|
660
|
+
this._maxFragments = options.maxFragments | 0;
|
|
659
661
|
this._maxPayload = options.maxPayload | 0;
|
|
660
662
|
this._skipUTF8Validation = !!options.skipUTF8Validation;
|
|
661
663
|
this[kWebSocket] = undefined;
|
|
@@ -678,6 +680,10 @@ var require_receiver = __commonJS((exports, module) => {
|
|
|
678
680
|
_write(chunk, encoding, cb) {
|
|
679
681
|
if (this._opcode === 8 && this._state == GET_INFO)
|
|
680
682
|
return cb();
|
|
683
|
+
if (this._maxBufferedChunks > 0 && this._buffers.length >= this._maxBufferedChunks) {
|
|
684
|
+
cb(this.createError(RangeError, "Too many buffered chunks", false, 1008, "WS_ERR_TOO_MANY_BUFFERED_PARTS"));
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
681
687
|
this._bufferedBytes += chunk.length;
|
|
682
688
|
this._buffers.push(chunk);
|
|
683
689
|
this.startLoop(cb);
|
|
@@ -881,6 +887,11 @@ var require_receiver = __commonJS((exports, module) => {
|
|
|
881
887
|
return;
|
|
882
888
|
}
|
|
883
889
|
if (data.length) {
|
|
890
|
+
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
891
|
+
const error = this.createError(RangeError, "Too many message fragments", false, 1008, "WS_ERR_TOO_MANY_BUFFERED_PARTS");
|
|
892
|
+
cb(error);
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
884
895
|
this._messageLength = this._totalPayloadLength;
|
|
885
896
|
this._fragments.push(data);
|
|
886
897
|
}
|
|
@@ -898,6 +909,11 @@ var require_receiver = __commonJS((exports, module) => {
|
|
|
898
909
|
cb(error);
|
|
899
910
|
return;
|
|
900
911
|
}
|
|
912
|
+
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
913
|
+
const error = this.createError(RangeError, "Too many message fragments", false, 1008, "WS_ERR_TOO_MANY_BUFFERED_PARTS");
|
|
914
|
+
cb(error);
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
901
917
|
this._fragments.push(buf);
|
|
902
918
|
}
|
|
903
919
|
this.dataMessage(cb);
|
|
@@ -1805,6 +1821,8 @@ var require_websocket = __commonJS((exports, module) => {
|
|
|
1805
1821
|
binaryType: this.binaryType,
|
|
1806
1822
|
extensions: this._extensions,
|
|
1807
1823
|
isServer: this._isServer,
|
|
1824
|
+
maxBufferedChunks: options.maxBufferedChunks,
|
|
1825
|
+
maxFragments: options.maxFragments,
|
|
1808
1826
|
maxPayload: options.maxPayload,
|
|
1809
1827
|
skipUTF8Validation: options.skipUTF8Validation
|
|
1810
1828
|
});
|
|
@@ -2047,6 +2065,8 @@ var require_websocket = __commonJS((exports, module) => {
|
|
|
2047
2065
|
autoPong: true,
|
|
2048
2066
|
closeTimeout: CLOSE_TIMEOUT,
|
|
2049
2067
|
protocolVersion: protocolVersions[1],
|
|
2068
|
+
maxBufferedChunks: 1024 * 1024,
|
|
2069
|
+
maxFragments: 128 * 1024,
|
|
2050
2070
|
maxPayload: 100 * 1024 * 1024,
|
|
2051
2071
|
skipUTF8Validation: false,
|
|
2052
2072
|
perMessageDeflate: true,
|
|
@@ -2285,6 +2305,8 @@ var require_websocket = __commonJS((exports, module) => {
|
|
|
2285
2305
|
websocket.setSocket(socket, head, {
|
|
2286
2306
|
allowSynchronousEvents: opts.allowSynchronousEvents,
|
|
2287
2307
|
generateMask: opts.generateMask,
|
|
2308
|
+
maxBufferedChunks: opts.maxBufferedChunks,
|
|
2309
|
+
maxFragments: opts.maxFragments,
|
|
2288
2310
|
maxPayload: opts.maxPayload,
|
|
2289
2311
|
skipUTF8Validation: opts.skipUTF8Validation
|
|
2290
2312
|
});
|
|
@@ -2620,6 +2642,8 @@ var require_websocket_server = __commonJS((exports, module) => {
|
|
|
2620
2642
|
options = {
|
|
2621
2643
|
allowSynchronousEvents: true,
|
|
2622
2644
|
autoPong: true,
|
|
2645
|
+
maxBufferedChunks: 1024 * 1024,
|
|
2646
|
+
maxFragments: 128 * 1024,
|
|
2623
2647
|
maxPayload: 100 * 1024 * 1024,
|
|
2624
2648
|
skipUTF8Validation: false,
|
|
2625
2649
|
perMessageDeflate: false,
|
|
@@ -2846,6 +2870,8 @@ var require_websocket_server = __commonJS((exports, module) => {
|
|
|
2846
2870
|
socket.removeListener("error", socketOnError);
|
|
2847
2871
|
ws.setSocket(socket, head, {
|
|
2848
2872
|
allowSynchronousEvents: this.options.allowSynchronousEvents,
|
|
2873
|
+
maxBufferedChunks: this.options.maxBufferedChunks,
|
|
2874
|
+
maxFragments: this.options.maxFragments,
|
|
2849
2875
|
maxPayload: this.options.maxPayload,
|
|
2850
2876
|
skipUTF8Validation: this.options.skipUTF8Validation
|
|
2851
2877
|
});
|
|
@@ -47843,6 +47869,7 @@ Options:
|
|
|
47843
47869
|
--api-key <key> API key (env: TODOFORAI_API_KEY)
|
|
47844
47870
|
--api-url <url> API URL (env: TODOFORAI_API_URL, default: https://api.todofor.ai)
|
|
47845
47871
|
--add-path <path> Add workspace path to this edge
|
|
47872
|
+
--max-timeout <sec> Floor for shell execution timeout
|
|
47846
47873
|
--kill Replace any existing edge instance for this user+server
|
|
47847
47874
|
--debug Verbose logging (env: TODOFORAI_DEBUG=1)
|
|
47848
47875
|
-v, --version Print version and exit
|
|
@@ -47861,6 +47888,7 @@ function loadConfig() {
|
|
|
47861
47888
|
debug: { type: "boolean", default: false },
|
|
47862
47889
|
kill: { type: "boolean", default: false },
|
|
47863
47890
|
"add-path": { type: "string" },
|
|
47891
|
+
"max-timeout": { type: "string" },
|
|
47864
47892
|
version: { type: "boolean", short: "v", default: false },
|
|
47865
47893
|
help: { type: "boolean", short: "h", default: false }
|
|
47866
47894
|
},
|
|
@@ -47884,17 +47912,33 @@ function loadConfig() {
|
|
|
47884
47912
|
const p = values["add-path"];
|
|
47885
47913
|
addWorkspacePath = path.resolve(p.replace(/^~/, process.env.HOME || "~"));
|
|
47886
47914
|
}
|
|
47887
|
-
|
|
47915
|
+
const maxTimeout = values["max-timeout"] ? Math.max(0, parseInt(values["max-timeout"], 10) || 0) : undefined;
|
|
47916
|
+
return { apiUrl, apiKey, debug, kill, addWorkspacePath, maxTimeout, subcommand };
|
|
47888
47917
|
}
|
|
47889
|
-
|
|
47890
|
-
|
|
47918
|
+
function credentialsPath() {
|
|
47919
|
+
const sys = os.platform();
|
|
47920
|
+
if (sys === "win32") {
|
|
47921
|
+
return path.join(os.homedir(), "AppData", "Roaming", "todoforai", "credentials.json");
|
|
47922
|
+
}
|
|
47923
|
+
if (sys === "darwin") {
|
|
47924
|
+
return path.join(os.homedir(), "Library", "Application Support", "todoforai", "credentials.json");
|
|
47925
|
+
}
|
|
47926
|
+
const xdg = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
47927
|
+
return path.join(xdg, "todoforai", "credentials.json");
|
|
47928
|
+
}
|
|
47929
|
+
var CREDENTIALS_PATH = credentialsPath();
|
|
47930
|
+
var LEGACY_CREDENTIALS_PATH = path.join(os.homedir(), ".todoforai", "credentials.json");
|
|
47931
|
+
function readFileMap(p) {
|
|
47891
47932
|
try {
|
|
47892
|
-
return JSON.parse(fs.readFileSync(
|
|
47933
|
+
return JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
47893
47934
|
} catch {
|
|
47894
47935
|
return {};
|
|
47895
47936
|
}
|
|
47896
47937
|
}
|
|
47897
|
-
function
|
|
47938
|
+
function readCredentials() {
|
|
47939
|
+
return { ...readFileMap(LEGACY_CREDENTIALS_PATH), ...readFileMap(CREDENTIALS_PATH) };
|
|
47940
|
+
}
|
|
47941
|
+
function writeNewFile(creds) {
|
|
47898
47942
|
const dir = path.dirname(CREDENTIALS_PATH);
|
|
47899
47943
|
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
47900
47944
|
fs.writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), { mode: 384 });
|
|
@@ -47906,14 +47950,23 @@ function loadSavedApiKey(apiUrl) {
|
|
|
47906
47950
|
return readCredentials()[apiUrl] || null;
|
|
47907
47951
|
}
|
|
47908
47952
|
function saveApiKey(apiUrl, apiKey) {
|
|
47909
|
-
const creds =
|
|
47953
|
+
const creds = readFileMap(CREDENTIALS_PATH);
|
|
47910
47954
|
creds[apiUrl] = apiKey;
|
|
47911
|
-
|
|
47955
|
+
writeNewFile(creds);
|
|
47912
47956
|
}
|
|
47913
47957
|
function clearApiKey(apiUrl) {
|
|
47914
|
-
const creds =
|
|
47915
|
-
|
|
47916
|
-
|
|
47958
|
+
const creds = readFileMap(CREDENTIALS_PATH);
|
|
47959
|
+
if (apiUrl in creds) {
|
|
47960
|
+
delete creds[apiUrl];
|
|
47961
|
+
writeNewFile(creds);
|
|
47962
|
+
}
|
|
47963
|
+
const legacy = readFileMap(LEGACY_CREDENTIALS_PATH);
|
|
47964
|
+
if (apiUrl in legacy) {
|
|
47965
|
+
delete legacy[apiUrl];
|
|
47966
|
+
try {
|
|
47967
|
+
fs.writeFileSync(LEGACY_CREDENTIALS_PATH, JSON.stringify(legacy, null, 2), { mode: 384 });
|
|
47968
|
+
} catch {}
|
|
47969
|
+
}
|
|
47917
47970
|
}
|
|
47918
47971
|
|
|
47919
47972
|
// node_modules/ws/wrapper.mjs
|
|
@@ -47938,7 +47991,7 @@ function getConnectionEnv() {
|
|
|
47938
47991
|
const { apiUrl, apiKey } = getter();
|
|
47939
47992
|
if (!apiUrl || !apiKey)
|
|
47940
47993
|
return {};
|
|
47941
|
-
return { TODOFORAI_API_URL: apiUrl,
|
|
47994
|
+
return { TODOFORAI_API_URL: apiUrl, TODOFORAI_API_TOKEN: apiKey };
|
|
47942
47995
|
}
|
|
47943
47996
|
|
|
47944
47997
|
// src/constants.ts
|
|
@@ -48710,6 +48763,7 @@ var tool_catalog_default = {
|
|
|
48710
48763
|
"~/.zele/sqlite.db"
|
|
48711
48764
|
],
|
|
48712
48765
|
capabilities: "Read inbox, search/send/reply email, email addresses — multi-account Gmail, OAuth browser login for setup.",
|
|
48766
|
+
description: "Use for anything Gmail: reading the user's inbox, searching mail, sending or replying to email. Multi-account, OAuth login via `zele login`.",
|
|
48713
48767
|
versionCmd: "zele --version 2>/dev/null | head -1"
|
|
48714
48768
|
},
|
|
48715
48769
|
xurl: {
|
|
@@ -48723,6 +48777,7 @@ var tool_catalog_default = {
|
|
|
48723
48777
|
"~/.xurl"
|
|
48724
48778
|
],
|
|
48725
48779
|
capabilities: "Post tweets, reply & quote, read timelines, search tweets, like & repost, follow/unfollow, DMs, media uploads",
|
|
48780
|
+
description: "Use to interact with X/Twitter: post/reply/quote tweets, search, read timelines, like/repost, follow, DMs, media upload. Authenticated raw API access via `xurl <method> <path>`.",
|
|
48726
48781
|
versionCmd: "xurl --version 2>/dev/null | head -1"
|
|
48727
48782
|
},
|
|
48728
48783
|
"tiktok-uploader": {
|
|
@@ -48736,7 +48791,7 @@ var tool_catalog_default = {
|
|
|
48736
48791
|
"~/.tiktok/cookies.txt"
|
|
48737
48792
|
],
|
|
48738
48793
|
capabilities: "Upload videos, batch uploads, schedule posts, custom covers, hashtags & mentions",
|
|
48739
|
-
description: "Cookie-based auth:
|
|
48794
|
+
description: "Use to upload videos to TikTok (single or batch), schedule posts, set covers/hashtags/mentions. Cookie-based auth: run `npx @todoforai/tiktok-cookie-helper` → log in to tiktok.com → export cookies via 'Get cookies.txt LOCALLY' extension → saved to ~/.tiktok/cookies.txt.",
|
|
48740
48795
|
versionCmd: "pip show tiktok-uploader 2>/dev/null | grep -oP 'Version: \\K.*'"
|
|
48741
48796
|
},
|
|
48742
48797
|
instagrapi: {
|
|
@@ -48745,6 +48800,7 @@ var tool_catalog_default = {
|
|
|
48745
48800
|
installer: "pip",
|
|
48746
48801
|
label: "Instagram",
|
|
48747
48802
|
capabilities: "Upload photos & reels, post stories, send DMs, like & comment, manage followers",
|
|
48803
|
+
description: 'Use for Instagram automation: upload photos/reels, post stories, DMs, like/comment, follower management. Python library; call via `python3 -c "from instagrapi import Client; ..."`.',
|
|
48748
48804
|
statusCmd: 'python3 -c "import instagrapi" 2>/dev/null',
|
|
48749
48805
|
versionCmd: "pip show instagrapi 2>/dev/null | grep -oP 'Version: \\K.*'"
|
|
48750
48806
|
},
|
|
@@ -48759,6 +48815,7 @@ var tool_catalog_default = {
|
|
|
48759
48815
|
"~/.local/share/mudslide"
|
|
48760
48816
|
],
|
|
48761
48817
|
capabilities: "Send messages, send images & files, send locations & polls, group management, QR code login",
|
|
48818
|
+
description: "Use to send WhatsApp messages/files/media/locations/polls from CLI. QR-code login via `mudslide login`.",
|
|
48762
48819
|
versionCmd: "mudslide --version 2>/dev/null | head -1"
|
|
48763
48820
|
},
|
|
48764
48821
|
"telegram-send": {
|
|
@@ -48772,6 +48829,7 @@ var tool_catalog_default = {
|
|
|
48772
48829
|
"~/.config/telegram-send/telegram-send.conf"
|
|
48773
48830
|
],
|
|
48774
48831
|
capabilities: "Send messages, send files & images, send video & audio, Markdown/HTML formatting, channel & group support",
|
|
48832
|
+
description: "Use to send Telegram messages/files from CLI (one-way bot output). Configure once with `telegram-send --configure`. For full Telegram automation use a different tool.",
|
|
48775
48833
|
versionCmd: "telegram-send --version 2>/dev/null | head -1"
|
|
48776
48834
|
},
|
|
48777
48835
|
"apollo-api": {
|
|
@@ -48780,6 +48838,7 @@ var tool_catalog_default = {
|
|
|
48780
48838
|
installer: "npm",
|
|
48781
48839
|
label: "Apollo",
|
|
48782
48840
|
capabilities: "Lead search & enrichment, email sequences, CRM sync",
|
|
48841
|
+
description: "Use for B2B prospecting: search Apollo's people/company DB, enrich leads with emails/titles/companies, manage sequences and CRM data. Needs APOLLO_API_KEY.",
|
|
48783
48842
|
versionCmd: "apollo-api --version 2>/dev/null | head -1"
|
|
48784
48843
|
},
|
|
48785
48844
|
"meta-ads": {
|
|
@@ -48798,6 +48857,7 @@ var tool_catalog_default = {
|
|
|
48798
48857
|
installer: "npm",
|
|
48799
48858
|
label: "ElevenLabs",
|
|
48800
48859
|
capabilities: "Text-to-speech, voice cloning, multiple languages",
|
|
48860
|
+
description: "Use to generate speech audio from text, clone voices, or list voices. Needs ELEVENLABS_API_KEY.",
|
|
48801
48861
|
versionCmd: "elevenlabs-api --version 2>/dev/null | head -1"
|
|
48802
48862
|
},
|
|
48803
48863
|
"suno-api": {
|
|
@@ -48806,12 +48866,37 @@ var tool_catalog_default = {
|
|
|
48806
48866
|
installer: "npm",
|
|
48807
48867
|
label: "Suno",
|
|
48808
48868
|
capabilities: "AI music generation, custom lyrics, multiple genres",
|
|
48869
|
+
description: "Use to generate music/songs from a prompt or custom lyrics. Needs SUNO_API_KEY.",
|
|
48809
48870
|
versionCmd: "suno-api --version 2>/dev/null | head -1"
|
|
48810
48871
|
},
|
|
48872
|
+
ntn: {
|
|
48873
|
+
category: "utility",
|
|
48874
|
+
pkg: "ntn",
|
|
48875
|
+
installer: "npm",
|
|
48876
|
+
label: "Notion",
|
|
48877
|
+
statusCmd: "ntn doctor 2>&1 | grep -E 'Default workspace\\s+✔' >/dev/null",
|
|
48878
|
+
loginCmd: "ntn login",
|
|
48879
|
+
credentialPaths: [
|
|
48880
|
+
"~/.config/notion"
|
|
48881
|
+
],
|
|
48882
|
+
capabilities: "Official Notion CLI: workspace-wide OAuth login, raw API calls (`ntn api <path>`), page/datasource management, file uploads, Workers deploy.",
|
|
48883
|
+
description: "Two auth tracks: (1) `ntn login` for Workers/internal commands (`ntn doctor`, `ntn workers`) — workspace OAuth stored in keychain. (2) Public REST API (`ntn api`, `ntn pages`, `ntn datasources`, `ntn files`) requires NOTION_API_TOKEN env var (integration token from https://www.notion.so/my-integrations) — and the target pages must be Connected to that integration via the Notion UI: page `...` → Connections. Examples: `ntn api /v1/users/me`, `ntn api /v1/search -X POST -d '{\"query\":\"...\"}'`, `ntn pages get <id>` (Markdown export), `ntn pages create --parent page:<id> --content '# Title'`, `ntn pages update <id> --content '...'`, `ntn pages trash <id> --yes`. Use `ntn datasources resolve <database-id>` then `ntn datasources query <data-source-id>`.",
|
|
48884
|
+
versionCmd: "ntn --version 2>/dev/null | head -1"
|
|
48885
|
+
},
|
|
48886
|
+
"perplexity-api": {
|
|
48887
|
+
category: "utility",
|
|
48888
|
+
pkg: "@todoforai/perplexity-api",
|
|
48889
|
+
installer: "npm",
|
|
48890
|
+
label: "Perplexity",
|
|
48891
|
+
capabilities: "AI-powered web search, chat completions, async chat, embeddings and agent runs",
|
|
48892
|
+
description: "Use when you need current web-grounded answers with citations (news, prices, docs released after training cutoff). Prefer over plain LLM when freshness/sources matter. Needs PERPLEXITY_API_KEY.",
|
|
48893
|
+
versionCmd: "perplexity-api --version 2>/dev/null | head -1"
|
|
48894
|
+
},
|
|
48811
48895
|
gh: {
|
|
48812
48896
|
category: "development",
|
|
48813
48897
|
pkg: "gh",
|
|
48814
48898
|
installer: "binary",
|
|
48899
|
+
preinstallCloud: true,
|
|
48815
48900
|
label: "GitHub",
|
|
48816
48901
|
statusCmd: "gh auth status",
|
|
48817
48902
|
loginCmd: "gh auth login",
|
|
@@ -48819,6 +48904,7 @@ var tool_catalog_default = {
|
|
|
48819
48904
|
"~/.config/gh/hosts.yml"
|
|
48820
48905
|
],
|
|
48821
48906
|
capabilities: "Repos, PRs & issues, actions & releases, gists & SSH keys",
|
|
48907
|
+
description: "Use for any GitHub operation: create/list/merge PRs, file issues, manage releases, trigger/inspect Actions, clone/fork repos, gists. Prefer `gh` over raw GitHub API calls.",
|
|
48822
48908
|
versionCmd: "gh --version 2>/dev/null | head -1"
|
|
48823
48909
|
},
|
|
48824
48910
|
glab: {
|
|
@@ -48832,6 +48918,7 @@ var tool_catalog_default = {
|
|
|
48832
48918
|
"~/.config/glab-cli/config.yml"
|
|
48833
48919
|
],
|
|
48834
48920
|
capabilities: "Repos, MRs & issues, CI/CD pipelines, releases",
|
|
48921
|
+
description: "Use for any GitLab operation: MRs, issues, pipelines, releases, repo management. GitLab equivalent of `gh`.",
|
|
48835
48922
|
versionCmd: "glab --version 2>/dev/null | head -1"
|
|
48836
48923
|
},
|
|
48837
48924
|
vercel: {
|
|
@@ -48845,6 +48932,7 @@ var tool_catalog_default = {
|
|
|
48845
48932
|
"~/.local/share/com.vercel.cli/auth.json"
|
|
48846
48933
|
],
|
|
48847
48934
|
capabilities: "Deploy & preview, environment variables, domain management",
|
|
48935
|
+
description: "Use to deploy a project to Vercel (`vercel deploy`), manage env vars, domains, and preview URLs.",
|
|
48848
48936
|
versionCmd: "vercel --version 2>/dev/null | head -1"
|
|
48849
48937
|
},
|
|
48850
48938
|
netlify: {
|
|
@@ -48858,6 +48946,7 @@ var tool_catalog_default = {
|
|
|
48858
48946
|
"~/.netlify/config.json"
|
|
48859
48947
|
],
|
|
48860
48948
|
capabilities: "Deploy & preview, serverless functions, forms & identity",
|
|
48949
|
+
description: "Use to deploy sites/functions to Netlify, manage env vars, forms, identity. `netlify deploy --prod` for production.",
|
|
48861
48950
|
versionCmd: "netlify --version 2>/dev/null | head -1"
|
|
48862
48951
|
},
|
|
48863
48952
|
firebase: {
|
|
@@ -48871,6 +48960,7 @@ var tool_catalog_default = {
|
|
|
48871
48960
|
"~/.config/configstore/firebase-tools.json"
|
|
48872
48961
|
],
|
|
48873
48962
|
capabilities: "Hosting & deploy, Firestore & Realtime DB, auth & functions",
|
|
48963
|
+
description: "Use for Firebase: deploy hosting/functions, manage Firestore/Realtime DB rules and data, auth users, emulators.",
|
|
48874
48964
|
versionCmd: "firebase --version 2>/dev/null | head -1"
|
|
48875
48965
|
},
|
|
48876
48966
|
wrangler: {
|
|
@@ -48884,6 +48974,7 @@ var tool_catalog_default = {
|
|
|
48884
48974
|
"~/.config/.wrangler/config/default.toml"
|
|
48885
48975
|
],
|
|
48886
48976
|
capabilities: "Workers & Pages deploy, KV & D1 storage, R2 & Queues",
|
|
48977
|
+
description: "Use for Cloudflare developer platform: deploy Workers/Pages, manage KV/D1/R2/Queues, tail logs. Use `cloudflared` for tunnels.",
|
|
48887
48978
|
versionCmd: "wrangler --version 2>/dev/null | head -1"
|
|
48888
48979
|
},
|
|
48889
48980
|
stripe: {
|
|
@@ -48897,6 +48988,7 @@ var tool_catalog_default = {
|
|
|
48897
48988
|
"~/.config/stripe/config.toml"
|
|
48898
48989
|
],
|
|
48899
48990
|
capabilities: "Payments & subscriptions, webhook testing, invoice management",
|
|
48991
|
+
description: "Use for Stripe: inspect/create customers, charges, subscriptions, invoices, webhooks. `stripe listen` to forward webhooks locally; `stripe get/post /v1/...` for raw API.",
|
|
48900
48992
|
versionCmd: "stripe --version 2>/dev/null | head -1"
|
|
48901
48993
|
},
|
|
48902
48994
|
flyctl: {
|
|
@@ -48910,6 +49002,7 @@ var tool_catalog_default = {
|
|
|
48910
49002
|
"~/.fly/config.yml"
|
|
48911
49003
|
],
|
|
48912
49004
|
capabilities: "Deploy containers globally, Postgres & volumes, auto-scaling",
|
|
49005
|
+
description: "Use to deploy apps/containers to Fly.io edge, manage Postgres/volumes/machines/secrets. `flyctl deploy` from app dir.",
|
|
48913
49006
|
versionCmd: "flyctl version 2>/dev/null | head -1"
|
|
48914
49007
|
},
|
|
48915
49008
|
supabase: {
|
|
@@ -48923,6 +49016,7 @@ var tool_catalog_default = {
|
|
|
48923
49016
|
"~/.config/supabase/access-token"
|
|
48924
49017
|
],
|
|
48925
49018
|
capabilities: "Postgres database, auth & storage, edge functions",
|
|
49019
|
+
description: "Use for Supabase projects: run/migrate local Postgres, deploy edge functions, manage auth/storage, link to cloud project.",
|
|
48926
49020
|
versionCmd: "supabase --version 2>/dev/null | head -1"
|
|
48927
49021
|
},
|
|
48928
49022
|
railway: {
|
|
@@ -48936,6 +49030,7 @@ var tool_catalog_default = {
|
|
|
48936
49030
|
"~/.railway/config.json"
|
|
48937
49031
|
],
|
|
48938
49032
|
capabilities: "Deploy apps & databases, environment management, auto-scaling",
|
|
49033
|
+
description: "Use to deploy apps/services to Railway, provision databases, manage env vars and environments. `railway up` to deploy current dir.",
|
|
48939
49034
|
versionCmd: "railway --version 2>/dev/null | head -1"
|
|
48940
49035
|
},
|
|
48941
49036
|
shopify: {
|
|
@@ -48949,6 +49044,7 @@ var tool_catalog_default = {
|
|
|
48949
49044
|
"~/.config/shopify/config.json"
|
|
48950
49045
|
],
|
|
48951
49046
|
capabilities: "Theme development, app scaffolding, store management",
|
|
49047
|
+
description: "Use for Shopify dev: scaffold/develop themes and apps, push/pull themes, run dev server against a store.",
|
|
48952
49048
|
versionCmd: "shopify version 2>/dev/null | head -1"
|
|
48953
49049
|
},
|
|
48954
49050
|
"datadog-ci": {
|
|
@@ -48957,6 +49053,7 @@ var tool_catalog_default = {
|
|
|
48957
49053
|
installer: "npm",
|
|
48958
49054
|
label: "Datadog",
|
|
48959
49055
|
capabilities: "CI test visibility, sourcemap uploads, deployment tracking",
|
|
49056
|
+
description: "Use in CI to upload test results/sourcemaps/coverage to Datadog and mark deployments. Needs DATADOG_API_KEY.",
|
|
48960
49057
|
versionCmd: "datadog-ci version 2>/dev/null | head -1"
|
|
48961
49058
|
},
|
|
48962
49059
|
"sentry-cli": {
|
|
@@ -48970,18 +49067,20 @@ var tool_catalog_default = {
|
|
|
48970
49067
|
"~/.sentryclirc"
|
|
48971
49068
|
],
|
|
48972
49069
|
capabilities: "Release management, sourcemap uploads, error monitoring",
|
|
49070
|
+
description: "Use to create Sentry releases, upload sourcemaps/debug-symbols, associate commits, send events. `sentry-cli releases new <version>`.",
|
|
48973
49071
|
versionCmd: "sentry-cli --version 2>/dev/null | head -1"
|
|
48974
49072
|
},
|
|
48975
49073
|
todoai: {
|
|
48976
49074
|
category: "utility",
|
|
48977
49075
|
pkg: "@todoforai/cli",
|
|
48978
49076
|
installer: "bun",
|
|
48979
|
-
label: "
|
|
49077
|
+
label: "TODOforAI",
|
|
48980
49078
|
capabilities: "Create & manage TODOs, run workflows, API access",
|
|
49079
|
+
description: "Use to programmatically create/list/update TODOs in TODOforAI, kick off workflows, call the platform API.",
|
|
48981
49080
|
statusCmd: "todoai --help >/dev/null 2>&1",
|
|
48982
49081
|
installCmd: "bun add -g @todoforai/cli",
|
|
48983
49082
|
versionCmd: "todoai --version 2>/dev/null | head -1",
|
|
48984
|
-
|
|
49083
|
+
internal: true
|
|
48985
49084
|
},
|
|
48986
49085
|
newman: {
|
|
48987
49086
|
category: "testing",
|
|
@@ -48990,17 +49089,21 @@ var tool_catalog_default = {
|
|
|
48990
49089
|
label: "Postman",
|
|
48991
49090
|
statusCmd: "newman --version",
|
|
48992
49091
|
capabilities: "Run API test collections, CI/CD integration, HTML reports",
|
|
49092
|
+
description: "Use to run Postman collections from CLI (API smoke tests, CI checks). `newman run <collection.json> -e <env.json>`.",
|
|
48993
49093
|
versionCmd: "newman --version 2>/dev/null | head -1"
|
|
48994
49094
|
},
|
|
48995
49095
|
curl: {
|
|
48996
49096
|
category: "networking",
|
|
48997
49097
|
pkg: "curl",
|
|
48998
49098
|
installer: "system",
|
|
48999
|
-
label: "
|
|
49099
|
+
label: "Web Request",
|
|
49100
|
+
binName: "curl",
|
|
49000
49101
|
statusCmd: "curl --version >/dev/null 2>&1",
|
|
49001
49102
|
capabilities: "HTTP/HTTPS/FTP client: GET/POST/PUT/DELETE, headers, auth, file upload/download, follow redirects, cookies, TLS.",
|
|
49103
|
+
description: "Default tool for any HTTP request: hitting REST APIs, downloading files, testing endpoints. Common flags: `-s` silent, `-L` follow redirects, `-H 'Header: ...'`, `-X POST -d '...'`, `-o file`, `-f` fail on HTTP errors. Pipe into `jq` for JSON.",
|
|
49002
49104
|
versionCmd: "curl --version 2>/dev/null | head -1",
|
|
49003
|
-
|
|
49105
|
+
preinstallCloud: true,
|
|
49106
|
+
internal: true
|
|
49004
49107
|
},
|
|
49005
49108
|
cloudflared: {
|
|
49006
49109
|
category: "networking",
|
|
@@ -49009,25 +49112,29 @@ var tool_catalog_default = {
|
|
|
49009
49112
|
label: "Tunnel",
|
|
49010
49113
|
statusCmd: "cloudflared --version",
|
|
49011
49114
|
capabilities: "Tunnel to localhost, expose local services, zero-trust access",
|
|
49115
|
+
description: "Use to expose a local port to the public internet via a Cloudflare tunnel. Quick: `cloudflared tunnel --url http://localhost:<port>` prints a public https URL.",
|
|
49012
49116
|
versionCmd: "cloudflared --version 2>/dev/null | head -1"
|
|
49013
49117
|
},
|
|
49014
49118
|
vault: {
|
|
49015
49119
|
category: "security",
|
|
49016
49120
|
pkg: "vault",
|
|
49017
49121
|
installer: "binary",
|
|
49018
|
-
|
|
49122
|
+
preinstallCloud: true,
|
|
49123
|
+
label: "HashiCorp Vault",
|
|
49019
49124
|
statusCmd: "test -f ~/.vault-token && echo 'authenticated'",
|
|
49020
49125
|
loginCmd: "vault login",
|
|
49021
49126
|
credentialPaths: [
|
|
49022
49127
|
"~/.vault-token"
|
|
49023
49128
|
],
|
|
49024
49129
|
capabilities: "Secrets management, dynamic credentials",
|
|
49130
|
+
description: "HashiCorp Vault CLI for Terraform/vals/ecosystem integrations. For day-to-day secret read/write in TODOforAI, prefer `tfa-vault` (zero-config). Needs VAULT_ADDR + VAULT_TOKEN.",
|
|
49025
49131
|
versionCmd: "vault --version 2>/dev/null | head -1"
|
|
49026
49132
|
},
|
|
49027
49133
|
rclone: {
|
|
49028
49134
|
category: "cloud",
|
|
49029
49135
|
pkg: "rclone",
|
|
49030
|
-
installer: "
|
|
49136
|
+
installer: "system",
|
|
49137
|
+
preinstallCloud: true,
|
|
49031
49138
|
label: "Cloud Sync",
|
|
49032
49139
|
statusCmd: "rclone listremotes 2>&1 | grep -v 'NOTICE' | head -5",
|
|
49033
49140
|
loginCmd: "rclone config",
|
|
@@ -49078,81 +49185,127 @@ var tool_catalog_default = {
|
|
|
49078
49185
|
label: "PDF",
|
|
49079
49186
|
statusCmd: "python3 -c 'import pymupdf' 2>/dev/null || /usr/bin/python3 -c 'import pymupdf' 2>/dev/null",
|
|
49080
49187
|
capabilities: "PDF text editing, extraction, merge, split",
|
|
49081
|
-
description: "
|
|
49082
|
-
versionCmd: "python3 -c 'import pymupdf; print(pymupdf.__version__)' 2>/dev/null"
|
|
49188
|
+
description: "Use to programmatically read/edit/merge/split PDFs (text + positions). Call from `python3 -c`. Extract text: `pymupdf.open(p)[i].get_text()`; with bbox/font: `.get_text('dict')`. Replace text in place: `page.add_redact_annot(page.search_for('old')[0], text='new'); page.apply_redactions(); doc.save(out)`. For PDF → markdown prefer `firecrawl parse`.",
|
|
49189
|
+
versionCmd: "python3 -c 'import pymupdf; print(pymupdf.__version__)' 2>/dev/null",
|
|
49190
|
+
preinstallCloud: true
|
|
49083
49191
|
},
|
|
49084
49192
|
"agent-browser": {
|
|
49085
49193
|
category: "development",
|
|
49086
49194
|
pkg: "agent-browser",
|
|
49087
49195
|
installer: "npm",
|
|
49196
|
+
preinstallCloud: true,
|
|
49088
49197
|
label: "Browser",
|
|
49089
49198
|
statusCmd: "agent-browser --version",
|
|
49090
49199
|
capabilities: "Headless browser automation, web scraping, accessibility tree snapshots, screenshots, PDF generation",
|
|
49091
|
-
description: "
|
|
49200
|
+
description: "Use when a task needs a real browser: JS-rendered pages, logged-in flows, clicking/filling forms, screenshots, PDF export. Prefer `curl` for plain HTTP and `firecrawl` for bulk scrape/crawl. Commands: open <url>, click/type/fill <selector> <text>, snapshot (accessibility tree with @refs — use these for clicking), screenshot [path], pdf <path>, eval <js>, wait <selector|ms>.",
|
|
49092
49201
|
versionCmd: "agent-browser --version 2>/dev/null | head -1"
|
|
49093
49202
|
},
|
|
49203
|
+
firecrawl: {
|
|
49204
|
+
category: "development",
|
|
49205
|
+
pkg: "firecrawl-cli",
|
|
49206
|
+
installer: "npm",
|
|
49207
|
+
label: "Firecrawl",
|
|
49208
|
+
statusCmd: "firecrawl view-config 2>&1 | grep -q 'Authenticated' && echo authenticated",
|
|
49209
|
+
loginCmd: "firecrawl login",
|
|
49210
|
+
credentialPaths: [
|
|
49211
|
+
"~/.config/firecrawl-cli"
|
|
49212
|
+
],
|
|
49213
|
+
capabilities: "Web scraping, crawling, search, map URLs, parse local HTML/PDF/DOCX to markdown, AI agent extraction",
|
|
49214
|
+
description: "Use for web → markdown at scale: single pages (`firecrawl scrape <url>`), whole sites (`firecrawl crawl <url>`), URL discovery (`firecrawl map <url>`), web search (`firecrawl search <query>`), local doc conversion (`firecrawl parse <file.pdf|docx|html|xlsx>`), AI-guided extraction (`firecrawl agent <prompt>`). Prefer over `curl` when you want markdown not HTML. Auth: `firecrawl login` or FIRECRAWL_API_KEY.",
|
|
49215
|
+
versionCmd: "firecrawl --version 2>/dev/null | head -1"
|
|
49216
|
+
},
|
|
49094
49217
|
"todoforai-browser": {
|
|
49095
49218
|
category: "development",
|
|
49096
49219
|
pkg: "@todoforai/browser",
|
|
49097
49220
|
installer: "npm",
|
|
49098
|
-
label: "
|
|
49221
|
+
label: "Browser (Extension)",
|
|
49099
49222
|
capabilities: "Browser automation via extension (non-headless), web scraping, accessibility tree snapshots, screenshots, PDF generation",
|
|
49100
49223
|
description: "Browser automation via the TODO for AI browser extension (non-headless, uses your real browser). Commands: open <url>, click/type/fill <selector> <text>, snapshot (accessibility tree with refs), screenshot [path], pdf <path>, eval <js>, wait <selector|ms>. Use snapshot to get semantic page structure with @refs for clicking. Supports drag/drop, file uploads, form interactions.",
|
|
49101
|
-
versionCmd: `node -p "require(require('child_process').execSync('which todoforai-browser',{encoding:'utf8'}).trim().replace(/\\/dist\\/index\\.js$/, '/package.json')).version" 2>/dev/null
|
|
49224
|
+
versionCmd: `node -p "require(require('child_process').execSync('which todoforai-browser',{encoding:'utf8'}).trim().replace(/\\/dist\\/index\\.js$/, '/package.json')).version" 2>/dev/null`,
|
|
49225
|
+
internal: true
|
|
49102
49226
|
},
|
|
49103
49227
|
"todoforai-subagent": {
|
|
49104
49228
|
category: "development",
|
|
49105
49229
|
pkg: "@todoforai/subagent",
|
|
49106
49230
|
installer: "npm",
|
|
49107
|
-
label: "
|
|
49231
|
+
label: "Sub-agent",
|
|
49108
49232
|
capabilities: "Spawn a TODO for AI sub-agent (FluidAgent) from the CLI; pipe stdin in, get an answer out",
|
|
49109
49233
|
description: 'Run a sub-agent task from a shell block. Usage: `todoforai-subagent [-m model] [-s :preset|@file|text] [--tools read,grep,bash] "<question>"`. Pipe stdin to include input context. Presets: :review, :explore, :plan, :summarize. Auth via TODOFORAI_API_KEY (auto-injected by edge).',
|
|
49110
49234
|
versionCmd: "todoforai-subagent --version 2>/dev/null | head -1",
|
|
49111
49235
|
installCmd: "bun add -g @todoforai/subagent",
|
|
49112
|
-
|
|
49113
|
-
},
|
|
49114
|
-
"todoforai-review": {
|
|
49115
|
-
category: "development",
|
|
49116
|
-
pkg: "@todoforai/review",
|
|
49117
|
-
installer: "npm",
|
|
49118
|
-
label: "TODO for AI Review",
|
|
49119
|
-
capabilities: "Review a git diff with a sub-agent: assess goal, find issues, suggest simpler approaches",
|
|
49120
|
-
description: 'Capture `git diff` and ask a read-only sub-agent to review it. Usage: `todoforai-review [--repo <path>] [--against <ref>] "<goal>"`. Default diffs uncommitted changes vs HEAD. Wraps `todoforai-subagent --sysmsg :review --tools read,grep,bash`.',
|
|
49121
|
-
versionCmd: "todoforai-review --version 2>/dev/null | head -1",
|
|
49122
|
-
installCmd: "bun add -g @todoforai/review",
|
|
49123
|
-
preinstall: true
|
|
49124
|
-
},
|
|
49125
|
-
"todoforai-explore": {
|
|
49126
|
-
category: "development",
|
|
49127
|
-
pkg: "@todoforai/explore",
|
|
49128
|
-
installer: "npm",
|
|
49129
|
-
label: "TODO for AI Explore",
|
|
49130
|
-
capabilities: "Explore a codebase with a sub-agent: map structure, find patterns, surface relevant files",
|
|
49131
|
-
description: 'Ask a read-only sub-agent to explore a codebase. Usage: `todoforai-explore [--repo <path>] "<question>"`. Sub-agent uses read/grep/bash to investigate and reports findings with file:line citations. Wraps `todoforai-subagent --sysmsg :explore`.',
|
|
49132
|
-
versionCmd: "todoforai-explore --version 2>/dev/null | head -1",
|
|
49133
|
-
installCmd: "bun add -g @todoforai/explore",
|
|
49134
|
-
preinstall: true
|
|
49236
|
+
internal: true
|
|
49135
49237
|
},
|
|
49136
49238
|
"todoforai-summary": {
|
|
49137
49239
|
category: "development",
|
|
49138
49240
|
pkg: "@todoforai/summary",
|
|
49139
49241
|
installer: "npm",
|
|
49140
|
-
label: "
|
|
49242
|
+
label: "Summarize (Lite)",
|
|
49141
49243
|
capabilities: "Summarize files or piped input via a sub-agent",
|
|
49142
49244
|
description: 'Summarize file contents or stdin. Usage: `todoforai-summary [-f <file>]... [<files...>] [<focus>]` or `cat x | todoforai-summary "focus"`. Wraps `todoforai-subagent --sysmsg :summarize`.',
|
|
49143
49245
|
versionCmd: "todoforai-summary --version 2>/dev/null | head -1",
|
|
49144
49246
|
installCmd: "bun add -g @todoforai/summary",
|
|
49145
|
-
|
|
49247
|
+
internal: true
|
|
49248
|
+
},
|
|
49249
|
+
"tfa-explore": {
|
|
49250
|
+
category: "development",
|
|
49251
|
+
pkg: "@todoforai/tfa-explore",
|
|
49252
|
+
installer: "npm",
|
|
49253
|
+
label: "Explore",
|
|
49254
|
+
capabilities: "Explore a codebase as a real TODO: read-only agent maps structure, surfaces relevant files, streams findings to terminal",
|
|
49255
|
+
description: 'Codebase exploration as a real TODO with patched permissions (read/grep/bash only) and live streaming. Usage: `tfa-explore [--repo <path>] "<question>"`. Creates a TODO visible in the UI and streams block events to stdout until DONE.',
|
|
49256
|
+
versionCmd: "tfa-explore --version 2>/dev/null | head -1",
|
|
49257
|
+
installCmd: "bun add -g @todoforai/tfa-explore",
|
|
49258
|
+
preinstall: true,
|
|
49259
|
+
preinstallCloud: true,
|
|
49260
|
+
internal: true
|
|
49261
|
+
},
|
|
49262
|
+
"tfa-review": {
|
|
49263
|
+
category: "development",
|
|
49264
|
+
pkg: "@todoforai/tfa-review",
|
|
49265
|
+
installer: "npm",
|
|
49266
|
+
label: "Review",
|
|
49267
|
+
capabilities: "Review a git diff as a real TODO: read-only agent assesses goal, finds issues, suggests simpler approaches",
|
|
49268
|
+
description: 'Capture `git diff` and ask a read-only sub-agent to review it as a real TODO. Usage: `tfa-review [--repo <path>] [--against <ref>] "<goal>"`. Default diffs uncommitted changes vs HEAD. Streams block events to stdout until DONE.',
|
|
49269
|
+
versionCmd: "tfa-review --version 2>/dev/null | head -1",
|
|
49270
|
+
installCmd: "bun add -g @todoforai/tfa-review",
|
|
49271
|
+
preinstall: true,
|
|
49272
|
+
preinstallCloud: true,
|
|
49273
|
+
internal: true
|
|
49274
|
+
},
|
|
49275
|
+
"tfa-summary": {
|
|
49276
|
+
category: "development",
|
|
49277
|
+
pkg: "@todoforai/tfa-summary",
|
|
49278
|
+
installer: "npm",
|
|
49279
|
+
label: "Summarize",
|
|
49280
|
+
capabilities: "Summarize files or piped input as a real TODO with no-tools sub-agent",
|
|
49281
|
+
description: 'Summarize file contents or stdin as a real TODO. Usage: `tfa-summary [-f <file>]... [<files...>] [<focus>]` or `cat x | tfa-summary "focus"`. No tools (content is inlined); streams block events to stdout until DONE.',
|
|
49282
|
+
versionCmd: "tfa-summary --version 2>/dev/null | head -1",
|
|
49283
|
+
installCmd: "bun add -g @todoforai/tfa-summary",
|
|
49284
|
+
internal: true
|
|
49285
|
+
},
|
|
49286
|
+
"tfa-vault": {
|
|
49287
|
+
category: "security",
|
|
49288
|
+
pkg: "@todoforai/vault",
|
|
49289
|
+
installer: "npm",
|
|
49290
|
+
label: "Vault",
|
|
49291
|
+
capabilities: "Zero-config TODOforAI vault CLI: put/get/patch/list/rm secrets in the user's KV v2 vault. Reuses bridge credentials (TODOFORAI_API_KEY / ~/.config/todoforai/credentials.json); derives vault URL from backend URL.",
|
|
49292
|
+
description: 'Read & write secrets in the user\'s TODOforAI vault — no VAULT_ADDR, no VAULT_TOKEN, no `vault login` needed. Usage: `tfa-vault put <path> k=v [k=v ...]`, `tfa-vault get [-f <field>] <path>`, `tfa-vault patch <path> k=v ...`, `tfa-vault list [<prefix>]`, `tfa-vault rm <path>`. Values: inline (`k=v`), file (`k=@/path`), stdin (`k=-`). `get -f <field>` writes raw value with no trailing newline — safe in `$(...)`. Exit 2 means "not found" (distinct from network errors). Prefer this over the HashiCorp `vault` CLI for daily use; `vault` remains supported for Terraform/vals/ecosystem tools.',
|
|
49293
|
+
versionCmd: "tfa-vault --version 2>/dev/null | head -1",
|
|
49294
|
+
statusCmd: "tfa-vault whoami",
|
|
49295
|
+
installCmd: "bun add -g @todoforai/vault",
|
|
49296
|
+
preinstall: true,
|
|
49297
|
+
preinstallCloud: true,
|
|
49298
|
+
internal: true
|
|
49146
49299
|
},
|
|
49147
49300
|
ripgrep: {
|
|
49148
49301
|
category: "development",
|
|
49149
49302
|
pkg: "ripgrep",
|
|
49150
49303
|
installer: "binary",
|
|
49151
|
-
label: "
|
|
49304
|
+
label: "Search",
|
|
49152
49305
|
capabilities: "Fast recursive grep with regex, .gitignore-aware, parallel, unicode-correct",
|
|
49153
49306
|
description: "Fast code/text search. Usage: `rg <pattern> [path]`, `-i` case-insensitive, `-l` files only, `-t <lang>` filter by language, `-g '<glob>'` filter by glob, `-C N` context. Prefer over `grep -r`.",
|
|
49154
49307
|
versionCmd: "rg --version 2>/dev/null | head -1",
|
|
49155
|
-
|
|
49308
|
+
preinstallCloud: true,
|
|
49156
49309
|
binary: {
|
|
49157
49310
|
"linux-x86_64": { url: "https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-x86_64-unknown-linux-musl.tar.gz", archive: "tar.gz", extract: "ripgrep-14.1.1-x86_64-unknown-linux-musl/rg" },
|
|
49158
49311
|
"linux-aarch64": { url: "https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-aarch64-unknown-linux-gnu.tar.gz", archive: "tar.gz", extract: "ripgrep-14.1.1-aarch64-unknown-linux-gnu/rg" },
|
|
@@ -49160,17 +49313,18 @@ var tool_catalog_default = {
|
|
|
49160
49313
|
"darwin-aarch64": { url: "https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-aarch64-apple-darwin.tar.gz", archive: "tar.gz", extract: "ripgrep-14.1.1-aarch64-apple-darwin/rg" },
|
|
49161
49314
|
"windows-x86_64": { url: "https://github.com/BurntSushi/ripgrep/releases/download/14.1.1/ripgrep-14.1.1-x86_64-pc-windows-msvc.zip", archive: "zip", extract: "ripgrep-14.1.1-x86_64-pc-windows-msvc/rg.exe" }
|
|
49162
49315
|
},
|
|
49163
|
-
binName: "rg"
|
|
49316
|
+
binName: "rg",
|
|
49317
|
+
internal: true
|
|
49164
49318
|
},
|
|
49165
49319
|
fd: {
|
|
49166
49320
|
category: "development",
|
|
49167
49321
|
pkg: "fd-find",
|
|
49168
49322
|
installer: "binary",
|
|
49169
|
-
label: "
|
|
49323
|
+
label: "Find Files",
|
|
49170
49324
|
capabilities: "Fast user-friendly find replacement: regex by default, .gitignore-aware, parallel",
|
|
49171
49325
|
description: "Find files/dirs by name. Usage: `fd <pattern> [path]`, `-e <ext>` filter by extension, `-t f|d` type, `-H` include hidden, `-x <cmd>` exec per match.",
|
|
49172
49326
|
versionCmd: "fd --version 2>/dev/null | head -1",
|
|
49173
|
-
|
|
49327
|
+
preinstallCloud: true,
|
|
49174
49328
|
binary: {
|
|
49175
49329
|
"linux-x86_64": { url: "https://github.com/sharkdp/fd/releases/download/v10.2.0/fd-v10.2.0-x86_64-unknown-linux-musl.tar.gz", archive: "tar.gz", extract: "fd-v10.2.0-x86_64-unknown-linux-musl/fd" },
|
|
49176
49330
|
"linux-aarch64": { url: "https://github.com/sharkdp/fd/releases/download/v10.2.0/fd-v10.2.0-aarch64-unknown-linux-gnu.tar.gz", archive: "tar.gz", extract: "fd-v10.2.0-aarch64-unknown-linux-gnu/fd" },
|
|
@@ -49178,17 +49332,18 @@ var tool_catalog_default = {
|
|
|
49178
49332
|
"darwin-aarch64": { url: "https://github.com/sharkdp/fd/releases/download/v10.2.0/fd-v10.2.0-aarch64-apple-darwin.tar.gz", archive: "tar.gz", extract: "fd-v10.2.0-aarch64-apple-darwin/fd" },
|
|
49179
49333
|
"windows-x86_64": { url: "https://github.com/sharkdp/fd/releases/download/v10.2.0/fd-v10.2.0-x86_64-pc-windows-msvc.zip", archive: "zip", extract: "fd-v10.2.0-x86_64-pc-windows-msvc/fd.exe" }
|
|
49180
49334
|
},
|
|
49181
|
-
binName: "fd"
|
|
49335
|
+
binName: "fd",
|
|
49336
|
+
internal: true
|
|
49182
49337
|
},
|
|
49183
49338
|
jq: {
|
|
49184
49339
|
category: "development",
|
|
49185
49340
|
pkg: "jq",
|
|
49186
49341
|
installer: "binary",
|
|
49187
|
-
label: "
|
|
49342
|
+
label: "JSON",
|
|
49188
49343
|
capabilities: "Command-line JSON processor: query, filter, transform, format JSON streams",
|
|
49189
49344
|
description: "JSON pipeline tool. Usage: `cat x.json | jq '.field'`, `jq -r` raw output, `jq '.[] | select(.k==\"v\")'`, `jq -s` slurp array. Pairs well with curl/rg.",
|
|
49190
49345
|
versionCmd: "jq --version 2>/dev/null | head -1",
|
|
49191
|
-
|
|
49346
|
+
preinstallCloud: true,
|
|
49192
49347
|
binary: {
|
|
49193
49348
|
"linux-x86_64": { url: "https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-amd64", archive: "raw" },
|
|
49194
49349
|
"linux-aarch64": { url: "https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-arm64", archive: "raw" },
|
|
@@ -49196,16 +49351,44 @@ var tool_catalog_default = {
|
|
|
49196
49351
|
"darwin-aarch64": { url: "https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-macos-arm64", archive: "raw" },
|
|
49197
49352
|
"windows-x86_64": { url: "https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-windows-amd64.exe", archive: "raw" }
|
|
49198
49353
|
},
|
|
49199
|
-
binName: "jq"
|
|
49354
|
+
binName: "jq",
|
|
49355
|
+
internal: true
|
|
49200
49356
|
},
|
|
49201
49357
|
patch: {
|
|
49202
49358
|
category: "development",
|
|
49203
49359
|
pkg: "patch",
|
|
49204
49360
|
installer: "system",
|
|
49205
|
-
|
|
49361
|
+
preinstallCloud: true,
|
|
49362
|
+
label: "Apply Diff",
|
|
49206
49363
|
capabilities: "Apply unified/context diff patches to files; reverse, dry-run, fuzzy matching",
|
|
49207
49364
|
description: "Apply diffs. Usage: `patch -p1 < changes.diff` (strip 1 leading path component), `--dry-run` to preview, `-R` to reverse, `-N` skip already-applied. Reads unified (`diff -u`) or context diffs.",
|
|
49208
|
-
versionCmd: "patch --version 2>/dev/null | head -1"
|
|
49365
|
+
versionCmd: "patch --version 2>/dev/null | head -1",
|
|
49366
|
+
binName: "patch",
|
|
49367
|
+
internal: true
|
|
49368
|
+
},
|
|
49369
|
+
sed: {
|
|
49370
|
+
category: "development",
|
|
49371
|
+
pkg: "sed",
|
|
49372
|
+
installer: "system",
|
|
49373
|
+
preinstallCloud: true,
|
|
49374
|
+
label: "Replace Text",
|
|
49375
|
+
capabilities: "Stream editor: in-place text substitution, line filtering, scripted edits",
|
|
49376
|
+
description: "Non-interactive text transformation. Usage: `sed 's/old/new/g' file`, `-i` edit in place (`-i ''` on macOS), `-n '5,10p'` print line range, `-E` extended regex. Pair with `rg -l` to bulk-rewrite matched files.",
|
|
49377
|
+
versionCmd: "sed --version 2>/dev/null | head -1",
|
|
49378
|
+
binName: "sed",
|
|
49379
|
+
internal: true
|
|
49380
|
+
},
|
|
49381
|
+
grep: {
|
|
49382
|
+
category: "development",
|
|
49383
|
+
pkg: "grep",
|
|
49384
|
+
installer: "system",
|
|
49385
|
+
preinstallCloud: true,
|
|
49386
|
+
label: "Search Text",
|
|
49387
|
+
capabilities: "Search plain text for lines matching a regex: recursive, fixed-string, context, invert, count",
|
|
49388
|
+
description: "Line-based text search. Usage: `grep <pattern> file`, `-r` recursive, `-i` case-insensitive, `-n` line numbers, `-E` extended regex, `-F` fixed strings, `-v` invert, `-C N` context, `-l` files only. For large code searches prefer `rg`.",
|
|
49389
|
+
versionCmd: "grep --version 2>/dev/null | head -1",
|
|
49390
|
+
binName: "grep",
|
|
49391
|
+
internal: true
|
|
49209
49392
|
}
|
|
49210
49393
|
};
|
|
49211
49394
|
|
|
@@ -49616,11 +49799,6 @@ function execShellAsync(cmd, env, timeout) {
|
|
|
49616
49799
|
async function scanCatalogTools() {
|
|
49617
49800
|
const result = {};
|
|
49618
49801
|
const env = buildEnvWithTools();
|
|
49619
|
-
const rawPath = process.env.PATH ?? process.env.Path ?? process.env.path ?? "";
|
|
49620
|
-
const pathDirs = rawPath.split(path3.delimiter).filter(Boolean);
|
|
49621
|
-
log2("scan", `platform=${os3.platform()} PATH dirs (${pathDirs.length}):`);
|
|
49622
|
-
for (const d of pathDirs)
|
|
49623
|
-
log2("scan", ` ${d}`);
|
|
49624
49802
|
const entries = Object.entries(TOOL_CATALOG);
|
|
49625
49803
|
const installed = [];
|
|
49626
49804
|
const missing = [];
|
|
@@ -49632,8 +49810,6 @@ async function scanCatalogTools() {
|
|
|
49632
49810
|
installed.push([name, entry]);
|
|
49633
49811
|
}
|
|
49634
49812
|
}
|
|
49635
|
-
log2("scan", `installed: [${installed.map(([n]) => n).join(", ")}]`);
|
|
49636
|
-
log2("scan", `missing: [${missing.join(", ")}]`);
|
|
49637
49813
|
await Promise.all(installed.map(async ([name, entry]) => {
|
|
49638
49814
|
const state = { installed: true };
|
|
49639
49815
|
if (entry.versionCmd) {
|
|
@@ -51054,7 +51230,7 @@ var mimetypes_default = {
|
|
|
51054
51230
|
image: { icon: "mdi:file-image", color: "yellow", prefixRule: "image/", needsObjectUrl: true },
|
|
51055
51231
|
audio: { icon: "mdi:file-music", color: "grey", prefixRule: "audio/", needsObjectUrl: true },
|
|
51056
51232
|
video: { icon: "mdi:file-video", color: "yellow", prefixRule: "video/", needsObjectUrl: true },
|
|
51057
|
-
pdf: { icon: "mdi:
|
|
51233
|
+
pdf: { icon: "mdi:pdf-box", color: "red", needsObjectUrl: true },
|
|
51058
51234
|
document: { icon: "mdi:file-word", color: "lightblue" },
|
|
51059
51235
|
spreadsheet: { icon: "mdi:file-excel", color: "green" },
|
|
51060
51236
|
text: { icon: "mdi:file", color: "white", prefixRule: "text/" },
|
|
@@ -51145,7 +51321,7 @@ var MAX_IMAGE_FILE_SIZE = 5000000;
|
|
|
51145
51321
|
var OFFICE_EXTENSIONS = new Set([".docx", ".xlsx", ".pdf"]);
|
|
51146
51322
|
var EXT_TO_MIME = new Map(Object.entries(mimetypes_default.extensions).map(([ext, e]) => [`.${ext}`, e.mime]));
|
|
51147
51323
|
var EXT_TO_TYPE = new Map(Object.entries(mimetypes_default.extensions).map(([ext, e]) => [`.${ext}`, e.type]));
|
|
51148
|
-
async function readFileContent(filePath, rootPath, fallbackRootPaths) {
|
|
51324
|
+
async function readFileContent(filePath, rootPath, fallbackRootPaths, skipSizeLimit = false) {
|
|
51149
51325
|
try {
|
|
51150
51326
|
const fullPath = path4.resolve(resolveFilePath(filePath, rootPath, fallbackRootPaths));
|
|
51151
51327
|
if (!fs7.existsSync(fullPath)) {
|
|
@@ -51167,7 +51343,7 @@ async function readFileContent(filePath, rootPath, fallbackRootPaths) {
|
|
|
51167
51343
|
const isImage = fileType === "image";
|
|
51168
51344
|
const isOffice = OFFICE_EXTENSIONS.has(ext);
|
|
51169
51345
|
const sizeLimit = isImage ? MAX_IMAGE_FILE_SIZE : isOffice ? MAX_OFFICE_FILE_SIZE : MAX_FILE_SIZE;
|
|
51170
|
-
if (stat.size > sizeLimit) {
|
|
51346
|
+
if (!skipSizeLimit && stat.size > sizeLimit) {
|
|
51171
51347
|
return {
|
|
51172
51348
|
success: false,
|
|
51173
51349
|
error: `File too large: ${fullPath} (${stat.size.toLocaleString()} bytes, max ${sizeLimit.toLocaleString()})`
|
|
@@ -51660,6 +51836,13 @@ async function sendInput(blockId, text) {
|
|
|
51660
51836
|
}
|
|
51661
51837
|
return true;
|
|
51662
51838
|
}
|
|
51839
|
+
function detachBlock(blockId) {
|
|
51840
|
+
const resolver = completionResolvers.get(blockId);
|
|
51841
|
+
if (resolver) {
|
|
51842
|
+
resolver();
|
|
51843
|
+
completionResolvers.delete(blockId);
|
|
51844
|
+
}
|
|
51845
|
+
}
|
|
51663
51846
|
function interruptBlock(blockId) {
|
|
51664
51847
|
const handle = processes.get(blockId);
|
|
51665
51848
|
if (!handle)
|
|
@@ -52147,12 +52330,13 @@ function detectContentType(output, cmd) {
|
|
|
52147
52330
|
return { result: output };
|
|
52148
52331
|
}
|
|
52149
52332
|
register("execute_shell_command", async (args, client) => {
|
|
52150
|
-
const { cmd,
|
|
52333
|
+
const { cmd, cwd = args.root_path ?? "", todoId = "", messageId = "", blockId = "", agentSettingsId = "", pid: resumePid = 0 } = args;
|
|
52334
|
+
const timeout = Math.max(args.timeout ?? 120, client?.maxTimeout ?? 0);
|
|
52151
52335
|
const canStream = !!(todoId && blockId && client);
|
|
52152
52336
|
if (!canStream) {
|
|
52153
52337
|
const { exec } = await import("child_process");
|
|
52154
52338
|
const result = await new Promise((resolve) => {
|
|
52155
|
-
exec(cmd, { cwd: cwd || os8.tmpdir(), encoding: "utf-8", timeout:
|
|
52339
|
+
exec(cmd, { cwd: cwd || os8.tmpdir(), encoding: "utf-8", timeout: timeout * 1000, maxBuffer: 10485760, env: { ...buildEnvWithTools(), ...getConnectionEnv(), TODOFORAI_TODO_ID: todoId, TODOFORAI_MESSAGE_ID: messageId, TODOFORAI_BLOCK_ID: blockId, TODOFORAI_AGENT_SETTINGS_ID: agentSettingsId } }, (_err, stdout, stderr) => {
|
|
52156
52340
|
resolve((stdout || "") + (stderr || ""));
|
|
52157
52341
|
});
|
|
52158
52342
|
});
|
|
@@ -52209,13 +52393,37 @@ register("execute_shell_command", async (args, client) => {
|
|
|
52209
52393
|
}
|
|
52210
52394
|
});
|
|
52211
52395
|
register("read_file", async (args) => {
|
|
52212
|
-
const { path: p10, rootPath = "", fallbackRootPaths = [] } = args;
|
|
52213
|
-
const result = await readFileContent(p10, rootPath, fallbackRootPaths);
|
|
52396
|
+
const { path: p10, rootPath = "", fallbackRootPaths = [], skipSizeLimit = false } = args;
|
|
52397
|
+
const result = await readFileContent(p10, rootPath, fallbackRootPaths, skipSizeLimit);
|
|
52214
52398
|
if (!result.success)
|
|
52215
52399
|
throw new Error(result.error || "Unknown read error");
|
|
52216
52400
|
const { success, ...rest } = result;
|
|
52217
52401
|
return rest;
|
|
52218
52402
|
});
|
|
52403
|
+
var LIST_DIR_MAX_ENTRIES = 1e4;
|
|
52404
|
+
register("list_dir", async (args) => {
|
|
52405
|
+
const { path: p10, rootPath = "", fallbackRootPaths = [] } = args;
|
|
52406
|
+
const fullPath = resolveFilePath(p10, rootPath, fallbackRootPaths);
|
|
52407
|
+
const st2 = fs10.statSync(fullPath);
|
|
52408
|
+
if (!st2.isDirectory())
|
|
52409
|
+
throw new Error(`Not a directory: ${fullPath}`);
|
|
52410
|
+
const dirents = fs10.readdirSync(fullPath, { withFileTypes: true });
|
|
52411
|
+
if (dirents.length > LIST_DIR_MAX_ENTRIES) {
|
|
52412
|
+
throw new Error(`Directory too large: ${dirents.length} entries (max ${LIST_DIR_MAX_ENTRIES})`);
|
|
52413
|
+
}
|
|
52414
|
+
const entries = dirents.map((d) => {
|
|
52415
|
+
let size = 0, mtime = 0, mode = 0, is_dir = d.isDirectory();
|
|
52416
|
+
try {
|
|
52417
|
+
const s = fs10.lstatSync(path7.join(fullPath, d.name));
|
|
52418
|
+
size = Number(s.size);
|
|
52419
|
+
mtime = s.mtimeMs / 1000;
|
|
52420
|
+
mode = s.mode & 511;
|
|
52421
|
+
is_dir = s.isDirectory();
|
|
52422
|
+
} catch {}
|
|
52423
|
+
return { name: d.name, is_dir, size, mtime, mode };
|
|
52424
|
+
});
|
|
52425
|
+
return { entries };
|
|
52426
|
+
});
|
|
52219
52427
|
register("create_file", async (args) => {
|
|
52220
52428
|
const { path: p10, content, rootPath = "", fallbackRootPaths = [] } = args;
|
|
52221
52429
|
const fullPath = resolveFilePath(p10, rootPath, fallbackRootPaths);
|
|
@@ -52236,106 +52444,6 @@ register("read_file_base64", async (args) => {
|
|
|
52236
52444
|
const data = fs10.readFileSync(fullPath);
|
|
52237
52445
|
return { path: fullPath, base64: data.toString("base64"), bytes: data.length };
|
|
52238
52446
|
});
|
|
52239
|
-
register("search_files", async (args) => {
|
|
52240
|
-
const { pattern, path: p10 = ".", cwd = args.root_path ?? "", head = 100, max_count = 5, glob: globPattern = "", ignore_case = true } = args;
|
|
52241
|
-
const { execSync: execWhich } = await import("child_process");
|
|
52242
|
-
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
52243
|
-
const which = (bin) => {
|
|
52244
|
-
try {
|
|
52245
|
-
return execWhich(`${whichCmd} ${bin}`, { encoding: "utf-8" }).trim().split(`
|
|
52246
|
-
`)[0].trim();
|
|
52247
|
-
} catch {
|
|
52248
|
-
return null;
|
|
52249
|
-
}
|
|
52250
|
-
};
|
|
52251
|
-
let rgPath = which("rg");
|
|
52252
|
-
if (!rgPath) {
|
|
52253
|
-
await ensureTool("rg");
|
|
52254
|
-
rgPath = which("rg");
|
|
52255
|
-
}
|
|
52256
|
-
let searchPath = p10.replace(/^~/, process.env.HOME || "~");
|
|
52257
|
-
if (!path7.isAbsolute(searchPath) && cwd)
|
|
52258
|
-
searchPath = path7.join(cwd, searchPath);
|
|
52259
|
-
searchPath = path7.resolve(searchPath);
|
|
52260
|
-
if (!fs10.existsSync(searchPath))
|
|
52261
|
-
throw new Error(`Search path does not exist: ${searchPath}`);
|
|
52262
|
-
let cmd;
|
|
52263
|
-
if (rgPath) {
|
|
52264
|
-
cmd = [rgPath, "--no-heading", "--line-number", "--color=never"];
|
|
52265
|
-
if (ignore_case)
|
|
52266
|
-
cmd.push("--ignore-case");
|
|
52267
|
-
if (max_count > 0)
|
|
52268
|
-
cmd.push(`--max-count=${max_count}`);
|
|
52269
|
-
if (globPattern)
|
|
52270
|
-
cmd.push("--glob", globPattern);
|
|
52271
|
-
cmd.push(pattern, searchPath);
|
|
52272
|
-
} else {
|
|
52273
|
-
console.warn("[search_files] ripgrep (rg) not found, falling back to grep");
|
|
52274
|
-
const grepPath = which("grep") || "grep";
|
|
52275
|
-
cmd = [grepPath, "-rn", "--color=never"];
|
|
52276
|
-
if (ignore_case)
|
|
52277
|
-
cmd.push("-i");
|
|
52278
|
-
if (max_count > 0)
|
|
52279
|
-
cmd.push(`--max-count=${max_count}`);
|
|
52280
|
-
if (globPattern) {
|
|
52281
|
-
cmd.push(`--include=${globPattern}`);
|
|
52282
|
-
}
|
|
52283
|
-
cmd.push(pattern, searchPath);
|
|
52284
|
-
}
|
|
52285
|
-
const { spawn: spawnChild } = await import("child_process");
|
|
52286
|
-
const { stdout, stderr, code } = await new Promise((resolve) => {
|
|
52287
|
-
const child = spawnChild(cmd[0], cmd.slice(1));
|
|
52288
|
-
let out = "", err2 = "";
|
|
52289
|
-
child.stdout?.on("data", (d) => {
|
|
52290
|
-
out += d.toString();
|
|
52291
|
-
});
|
|
52292
|
-
child.stderr?.on("data", (d) => {
|
|
52293
|
-
err2 += d.toString();
|
|
52294
|
-
});
|
|
52295
|
-
child.on("close", (exitCode) => resolve({ stdout: out, stderr: err2, code: exitCode ?? 1 }));
|
|
52296
|
-
});
|
|
52297
|
-
if (code === 0) {
|
|
52298
|
-
let output = stdout;
|
|
52299
|
-
const lines = output.split(`
|
|
52300
|
-
`).filter((l) => l.trim());
|
|
52301
|
-
if (lines.length > head) {
|
|
52302
|
-
output = lines.slice(0, head).join(`
|
|
52303
|
-
`) + `
|
|
52304
|
-
... (${lines.length - head} more matches truncated)`;
|
|
52305
|
-
}
|
|
52306
|
-
if ((cwd || searchPath) && output) {
|
|
52307
|
-
const searchBase = searchPath && fs10.existsSync(searchPath) && fs10.statSync(searchPath).isDirectory() ? searchPath : path7.dirname(searchPath);
|
|
52308
|
-
const bases = Array.from(new Set([cwd, searchBase].filter(Boolean)));
|
|
52309
|
-
const lines2 = output.split(`
|
|
52310
|
-
`).map((line) => {
|
|
52311
|
-
if (line.includes(":")) {
|
|
52312
|
-
const colonIdx = line.indexOf(":");
|
|
52313
|
-
let filePart = line.slice(0, colonIdx);
|
|
52314
|
-
const rest = line.slice(colonIdx);
|
|
52315
|
-
try {
|
|
52316
|
-
const candidates = [filePart, ...bases.map((b) => path7.relative(b, filePart))].filter((p11) => (p11.match(/\.\.\//g) || []).length <= 2);
|
|
52317
|
-
filePart = candidates.reduce((a, b) => a.length <= b.length ? a : b, filePart);
|
|
52318
|
-
} catch {}
|
|
52319
|
-
let fullLine = filePart + rest;
|
|
52320
|
-
if (fullLine.length > 300) {
|
|
52321
|
-
fullLine = fullLine.slice(0, 300) + "...";
|
|
52322
|
-
}
|
|
52323
|
-
return fullLine;
|
|
52324
|
-
}
|
|
52325
|
-
return line;
|
|
52326
|
-
});
|
|
52327
|
-
output = lines2.join(`
|
|
52328
|
-
`);
|
|
52329
|
-
}
|
|
52330
|
-
if (output.length > 1e5)
|
|
52331
|
-
output = output.slice(0, 1e5) + `
|
|
52332
|
-
... (output truncated)`;
|
|
52333
|
-
return { result: output };
|
|
52334
|
-
}
|
|
52335
|
-
if (code === 1)
|
|
52336
|
-
return { result: "No matches found." };
|
|
52337
|
-
throw new Error(`search error (exit ${code}): ${stderr}`);
|
|
52338
|
-
});
|
|
52339
52447
|
register("download_attachment", async (args, client) => {
|
|
52340
52448
|
if (!client)
|
|
52341
52449
|
throw new Error("Client instance required");
|
|
@@ -52411,18 +52519,21 @@ register("register_attachment", async (args, client) => {
|
|
|
52411
52519
|
|
|
52412
52520
|
// src/handlers.ts
|
|
52413
52521
|
var log3 = (level, ...args) => console.log(`[${level}]`, ...args);
|
|
52414
|
-
async function handleBlockExecute(payload, send, edgeId) {
|
|
52522
|
+
async function handleBlockExecute(payload, send, edgeId, maxTimeout = 0) {
|
|
52415
52523
|
const { blockId, messageId = "", content = "", todoId = "", rootPath = "", manual = false } = payload;
|
|
52416
52524
|
await send(msg.shellBlockStart(todoId, blockId, "execute", messageId));
|
|
52417
52525
|
try {
|
|
52418
|
-
const timeout = payload.timeout ?? 120;
|
|
52526
|
+
const timeout = Math.max(payload.timeout ?? 120, maxTimeout);
|
|
52419
52527
|
await executeBlock(blockId, content, send, todoId, messageId, timeout, rootPath, manual, undefined, edgeId);
|
|
52420
52528
|
} catch (e) {
|
|
52421
52529
|
await send(msg.blockError(blockId, todoId, e.message));
|
|
52422
52530
|
}
|
|
52423
52531
|
}
|
|
52424
52532
|
async function handleBlockSignal(payload) {
|
|
52425
|
-
|
|
52533
|
+
if (payload.detach)
|
|
52534
|
+
detachBlock(payload.blockId);
|
|
52535
|
+
else
|
|
52536
|
+
interruptBlock(payload.blockId);
|
|
52426
52537
|
}
|
|
52427
52538
|
async function handleBlockKeyboard(payload) {
|
|
52428
52539
|
await sendInput(payload.blockId, payload.content || "");
|
|
@@ -52543,8 +52654,8 @@ async function handleCd(payload, send, edgeConfig, onConfigChange) {
|
|
|
52543
52654
|
}
|
|
52544
52655
|
}
|
|
52545
52656
|
async function handleFileChunkRequest(payload, send, responseType = EA.FILE_CHUNK_RESULT) {
|
|
52546
|
-
const { path: p10 = "", rootPath = "", fallbackRootPaths = [] } = payload;
|
|
52547
|
-
const result = await readFileContent(p10, rootPath, fallbackRootPaths);
|
|
52657
|
+
const { path: p10 = "", rootPath = "", fallbackRootPaths = [], skipSizeLimit = false } = payload;
|
|
52658
|
+
const result = await readFileContent(p10, rootPath, fallbackRootPaths, skipSizeLimit);
|
|
52548
52659
|
if (result.success) {
|
|
52549
52660
|
await send(msg.fileChunkResult(responseType, { ...payload, full_path: result.fullPath, content: result.content, content_type: result.contentType }));
|
|
52550
52661
|
} else {
|
|
@@ -52622,6 +52733,7 @@ class TODOforAIEdge {
|
|
|
52622
52733
|
edgeId = "";
|
|
52623
52734
|
userId = "";
|
|
52624
52735
|
debug;
|
|
52736
|
+
maxTimeout;
|
|
52625
52737
|
wsUrl;
|
|
52626
52738
|
fingerprint = "";
|
|
52627
52739
|
heartbeatTimer;
|
|
@@ -52641,6 +52753,7 @@ class TODOforAIEdge {
|
|
|
52641
52753
|
constructor(config) {
|
|
52642
52754
|
this.api = new ApiClient(normalizeApiUrl(config.apiUrl), config.apiKey);
|
|
52643
52755
|
this.debug = config.debug;
|
|
52756
|
+
this.maxTimeout = config.maxTimeout ?? 0;
|
|
52644
52757
|
this.wsUrl = getWsUrl(this.api.apiUrl);
|
|
52645
52758
|
this.addWorkspacePath = config.addWorkspacePath;
|
|
52646
52759
|
this.browserExtensionBridge = new BrowserExtensionBridge(this.debug);
|
|
@@ -52699,10 +52812,16 @@ class TODOforAIEdge {
|
|
|
52699
52812
|
try {
|
|
52700
52813
|
console.log("\x1B[36mStarting device login...\x1B[0m");
|
|
52701
52814
|
const { code, url, expiresIn } = await this.api.initDeviceLogin("edge");
|
|
52815
|
+
const userCode = new URL(url).searchParams.get("user_code") || "";
|
|
52816
|
+
const formattedCode = userCode.length === 8 ? `${userCode.slice(0, 4)}-${userCode.slice(4)}` : userCode;
|
|
52702
52817
|
console.log(`
|
|
52703
52818
|
\x1B[1m\uD83D\uDD11 Open this URL to authorize:\x1B[0m`);
|
|
52704
52819
|
console.log(`\x1B[36m${url}\x1B[0m
|
|
52705
52820
|
`);
|
|
52821
|
+
if (formattedCode) {
|
|
52822
|
+
console.log(`\x1B[1m Your code: \x1B[33m${formattedCode}\x1B[0m
|
|
52823
|
+
`);
|
|
52824
|
+
}
|
|
52706
52825
|
try {
|
|
52707
52826
|
const { exec } = __require("child_process");
|
|
52708
52827
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
@@ -52852,7 +52971,7 @@ class TODOforAIEdge {
|
|
|
52852
52971
|
run(() => handleCd(payload, send, this.edgeConfig, (u) => this.updateConfig(u)));
|
|
52853
52972
|
break;
|
|
52854
52973
|
case FE.BLOCK_EXECUTE:
|
|
52855
|
-
run(() => handleBlockExecute(payload, send, this.edgeId));
|
|
52974
|
+
run(() => handleBlockExecute(payload, send, this.edgeId, this.maxTimeout));
|
|
52856
52975
|
break;
|
|
52857
52976
|
case FE.BLOCK_SAVE:
|
|
52858
52977
|
run(() => handleBlockSave(payload, send));
|