@todoforai/edge 0.13.11 → 0.13.12
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 +51 -98
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -47927,7 +47927,6 @@ function credentialsPath() {
|
|
|
47927
47927
|
return path.join(xdg, "todoforai", "credentials.json");
|
|
47928
47928
|
}
|
|
47929
47929
|
var CREDENTIALS_PATH = credentialsPath();
|
|
47930
|
-
var LEGACY_CREDENTIALS_PATH = path.join(os.homedir(), ".todoforai", "credentials.json");
|
|
47931
47930
|
function readFileMap(p) {
|
|
47932
47931
|
try {
|
|
47933
47932
|
return JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
@@ -47935,9 +47934,6 @@ function readFileMap(p) {
|
|
|
47935
47934
|
return {};
|
|
47936
47935
|
}
|
|
47937
47936
|
}
|
|
47938
|
-
function readCredentials() {
|
|
47939
|
-
return { ...readFileMap(LEGACY_CREDENTIALS_PATH), ...readFileMap(CREDENTIALS_PATH) };
|
|
47940
|
-
}
|
|
47941
47937
|
function writeNewFile(creds) {
|
|
47942
47938
|
const dir = path.dirname(CREDENTIALS_PATH);
|
|
47943
47939
|
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
@@ -47947,7 +47943,7 @@ function writeNewFile(creds) {
|
|
|
47947
47943
|
} catch {}
|
|
47948
47944
|
}
|
|
47949
47945
|
function loadSavedApiKey(apiUrl) {
|
|
47950
|
-
return
|
|
47946
|
+
return readFileMap(CREDENTIALS_PATH)[apiUrl] || null;
|
|
47951
47947
|
}
|
|
47952
47948
|
function saveApiKey(apiUrl, apiKey) {
|
|
47953
47949
|
const creds = readFileMap(CREDENTIALS_PATH);
|
|
@@ -47960,13 +47956,6 @@ function clearApiKey(apiUrl) {
|
|
|
47960
47956
|
delete creds[apiUrl];
|
|
47961
47957
|
writeNewFile(creds);
|
|
47962
47958
|
}
|
|
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
|
-
}
|
|
47970
47959
|
}
|
|
47971
47960
|
|
|
47972
47961
|
// node_modules/ws/wrapper.mjs
|
|
@@ -48940,7 +48929,7 @@ var tool_catalog_default = {
|
|
|
48940
48929
|
pkg: "netlify-cli",
|
|
48941
48930
|
installer: "npm",
|
|
48942
48931
|
label: "Netlify",
|
|
48943
|
-
statusCmd: `
|
|
48932
|
+
statusCmd: `netlify api getCurrentUser 2>&1 | grep -oP '"email":\\s*"\\K[^"]+' | head -1`,
|
|
48944
48933
|
loginCmd: "netlify login",
|
|
48945
48934
|
credentialPaths: [
|
|
48946
48935
|
"~/.netlify/config.json"
|
|
@@ -48954,7 +48943,7 @@ var tool_catalog_default = {
|
|
|
48954
48943
|
pkg: "firebase-tools",
|
|
48955
48944
|
installer: "npm",
|
|
48956
48945
|
label: "Firebase",
|
|
48957
|
-
statusCmd: "
|
|
48946
|
+
statusCmd: "firebase login:list 2>&1 | grep '@'",
|
|
48958
48947
|
loginCmd: "firebase login",
|
|
48959
48948
|
credentialPaths: [
|
|
48960
48949
|
"~/.config/configstore/firebase-tools.json"
|
|
@@ -48968,7 +48957,7 @@ var tool_catalog_default = {
|
|
|
48968
48957
|
pkg: "wrangler",
|
|
48969
48958
|
installer: "npm",
|
|
48970
48959
|
label: "Cloudflare",
|
|
48971
|
-
statusCmd: "
|
|
48960
|
+
statusCmd: "wrangler whoami 2>&1 | grep -v 'Failed' | grep '@'",
|
|
48972
48961
|
loginCmd: "wrangler login",
|
|
48973
48962
|
credentialPaths: [
|
|
48974
48963
|
"~/.config/.wrangler/config/default.toml"
|
|
@@ -49010,7 +48999,7 @@ var tool_catalog_default = {
|
|
|
49010
48999
|
pkg: "supabase",
|
|
49011
49000
|
installer: "binary",
|
|
49012
49001
|
label: "Supabase",
|
|
49013
|
-
statusCmd: "
|
|
49002
|
+
statusCmd: "supabase projects list >/dev/null 2>&1 && echo authenticated",
|
|
49014
49003
|
loginCmd: "supabase login",
|
|
49015
49004
|
credentialPaths: [
|
|
49016
49005
|
"~/.config/supabase/access-token"
|
|
@@ -49024,7 +49013,7 @@ var tool_catalog_default = {
|
|
|
49024
49013
|
pkg: "@railway/cli",
|
|
49025
49014
|
installer: "npm",
|
|
49026
49015
|
label: "Railway",
|
|
49027
|
-
statusCmd: "
|
|
49016
|
+
statusCmd: "railway whoami 2>&1 | grep -v 'Unauthorized'",
|
|
49028
49017
|
loginCmd: "railway login",
|
|
49029
49018
|
credentialPaths: [
|
|
49030
49019
|
"~/.railway/config.json"
|
|
@@ -49061,7 +49050,7 @@ var tool_catalog_default = {
|
|
|
49061
49050
|
pkg: "@sentry/cli",
|
|
49062
49051
|
installer: "npm",
|
|
49063
49052
|
label: "Sentry",
|
|
49064
|
-
statusCmd: "
|
|
49053
|
+
statusCmd: "sentry-cli info 2>&1 | grep -q 'Authentication Info' && echo authenticated",
|
|
49065
49054
|
loginCmd: "sentry-cli login",
|
|
49066
49055
|
credentialPaths: [
|
|
49067
49056
|
"~/.sentryclirc"
|
|
@@ -49077,7 +49066,7 @@ var tool_catalog_default = {
|
|
|
49077
49066
|
label: "TODOforAI",
|
|
49078
49067
|
capabilities: "Create & manage TODOs, run workflows, API access",
|
|
49079
49068
|
description: "Use to programmatically create/list/update TODOs in TODOforAI, kick off workflows, call the platform API.",
|
|
49080
|
-
statusCmd: "todoai
|
|
49069
|
+
statusCmd: "todoai whoami >/dev/null 2>&1",
|
|
49081
49070
|
installCmd: "bun add -g @todoforai/cli",
|
|
49082
49071
|
versionCmd: "todoai --version 2>/dev/null | head -1",
|
|
49083
49072
|
internal: true
|
|
@@ -49087,7 +49076,6 @@ var tool_catalog_default = {
|
|
|
49087
49076
|
pkg: "newman",
|
|
49088
49077
|
installer: "npm",
|
|
49089
49078
|
label: "Postman",
|
|
49090
|
-
statusCmd: "newman --version",
|
|
49091
49079
|
capabilities: "Run API test collections, CI/CD integration, HTML reports",
|
|
49092
49080
|
description: "Use to run Postman collections from CLI (API smoke tests, CI checks). `newman run <collection.json> -e <env.json>`.",
|
|
49093
49081
|
versionCmd: "newman --version 2>/dev/null | head -1"
|
|
@@ -49098,7 +49086,6 @@ var tool_catalog_default = {
|
|
|
49098
49086
|
installer: "system",
|
|
49099
49087
|
label: "Web Request",
|
|
49100
49088
|
binName: "curl",
|
|
49101
|
-
statusCmd: "curl --version >/dev/null 2>&1",
|
|
49102
49089
|
capabilities: "HTTP/HTTPS/FTP client: GET/POST/PUT/DELETE, headers, auth, file upload/download, follow redirects, cookies, TLS.",
|
|
49103
49090
|
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.",
|
|
49104
49091
|
versionCmd: "curl --version 2>/dev/null | head -1",
|
|
@@ -49110,7 +49097,6 @@ var tool_catalog_default = {
|
|
|
49110
49097
|
pkg: "cloudflared",
|
|
49111
49098
|
installer: "binary",
|
|
49112
49099
|
label: "Tunnel",
|
|
49113
|
-
statusCmd: "cloudflared --version",
|
|
49114
49100
|
capabilities: "Tunnel to localhost, expose local services, zero-trust access",
|
|
49115
49101
|
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.",
|
|
49116
49102
|
versionCmd: "cloudflared --version 2>/dev/null | head -1"
|
|
@@ -49121,13 +49107,13 @@ var tool_catalog_default = {
|
|
|
49121
49107
|
installer: "binary",
|
|
49122
49108
|
preinstallCloud: true,
|
|
49123
49109
|
label: "HashiCorp Vault",
|
|
49124
|
-
statusCmd: "
|
|
49110
|
+
statusCmd: "vault token lookup >/dev/null 2>&1 && echo authenticated",
|
|
49125
49111
|
loginCmd: "vault login",
|
|
49126
49112
|
credentialPaths: [
|
|
49127
49113
|
"~/.vault-token"
|
|
49128
49114
|
],
|
|
49129
49115
|
capabilities: "Secrets management, dynamic credentials",
|
|
49130
|
-
description: "HashiCorp Vault CLI for Terraform/vals/ecosystem integrations. For
|
|
49116
|
+
description: "HashiCorp Vault CLI — only for Terraform/vals/ecosystem integrations that expect it. For all credential read/write tasks use `tfa-vault` instead. Needs VAULT_ADDR + VAULT_TOKEN.",
|
|
49131
49117
|
versionCmd: "vault --version 2>/dev/null | head -1"
|
|
49132
49118
|
},
|
|
49133
49119
|
rclone: {
|
|
@@ -49136,16 +49122,17 @@ var tool_catalog_default = {
|
|
|
49136
49122
|
installer: "system",
|
|
49137
49123
|
preinstallCloud: true,
|
|
49138
49124
|
label: "Cloud Sync",
|
|
49139
|
-
statusCmd: "rclone listremotes 2>&1 | grep -v 'NOTICE' |
|
|
49125
|
+
statusCmd: "rclone listremotes 2>&1 | grep -v 'NOTICE' | grep -q ':' && echo authenticated",
|
|
49140
49126
|
loginCmd: "rclone config",
|
|
49141
49127
|
credentialPaths: [
|
|
49142
49128
|
"~/.config/rclone/rclone.conf"
|
|
49143
49129
|
],
|
|
49144
49130
|
capabilities: "Access Google Drive, OneDrive, Dropbox, S3 and 40+ cloud providers. List, copy, sync, move, mount files as virtual filesystem (FUSE). Use 'rclone config create <name> <provider>' to connect (opens browser for OAuth).",
|
|
49145
|
-
description: "
|
|
49131
|
+
description: "Use to access cloud storage (Drive/OneDrive/Dropbox/S3/40+). Connect: `rclone config create <name> <provider>` (OAuth via browser). Core ops: `rclone listremotes`, `rclone ls <remote>:<path>`, `rclone copy|sync <src> <dst>`. Mount as FUSE (lazy fetch): `rclone mount <remote>: ~/.todoforai/mnt/<remote> --vfs-cache-mode full --daemon`; unmount with `fusermount -u <path>`. See `subProviders` for per-provider connect commands.",
|
|
49146
49132
|
subProviders: {
|
|
49147
49133
|
gdrive: {
|
|
49148
49134
|
label: "Google Drive",
|
|
49135
|
+
authType: "oauth",
|
|
49149
49136
|
connectCmd: "rclone config create {{REMOTE}} drive",
|
|
49150
49137
|
statusCmd: "rclone lsd {{REMOTE}}: 2>&1 | head -1",
|
|
49151
49138
|
remoteName: "gdrive",
|
|
@@ -49154,6 +49141,7 @@ var tool_catalog_default = {
|
|
|
49154
49141
|
},
|
|
49155
49142
|
onedrive: {
|
|
49156
49143
|
label: "OneDrive",
|
|
49144
|
+
authType: "oauth",
|
|
49157
49145
|
connectCmd: "rclone config create {{REMOTE}} onedrive",
|
|
49158
49146
|
statusCmd: "rclone lsd {{REMOTE}}: 2>&1 | head -1",
|
|
49159
49147
|
remoteName: "onedrive",
|
|
@@ -49162,6 +49150,7 @@ var tool_catalog_default = {
|
|
|
49162
49150
|
},
|
|
49163
49151
|
dropbox: {
|
|
49164
49152
|
label: "Dropbox",
|
|
49153
|
+
authType: "oauth",
|
|
49165
49154
|
connectCmd: "rclone config create {{REMOTE}} dropbox",
|
|
49166
49155
|
statusCmd: "rclone lsd {{REMOTE}}: 2>&1 | head -1",
|
|
49167
49156
|
remoteName: "dropbox",
|
|
@@ -49170,6 +49159,7 @@ var tool_catalog_default = {
|
|
|
49170
49159
|
},
|
|
49171
49160
|
s3: {
|
|
49172
49161
|
label: "Amazon S3",
|
|
49162
|
+
authType: "credentials",
|
|
49173
49163
|
connectCmd: "rclone config create {{REMOTE}} s3",
|
|
49174
49164
|
statusCmd: "rclone lsd {{REMOTE}}: 2>&1 | head -1",
|
|
49175
49165
|
remoteName: "s3",
|
|
@@ -49183,7 +49173,6 @@ var tool_catalog_default = {
|
|
|
49183
49173
|
pkg: "pymupdf",
|
|
49184
49174
|
installer: "pip",
|
|
49185
49175
|
label: "PDF",
|
|
49186
|
-
statusCmd: "python3 -c 'import pymupdf' 2>/dev/null || /usr/bin/python3 -c 'import pymupdf' 2>/dev/null",
|
|
49187
49176
|
capabilities: "PDF text editing, extraction, merge, split",
|
|
49188
49177
|
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
49178
|
versionCmd: "python3 -c 'import pymupdf; print(pymupdf.__version__)' 2>/dev/null",
|
|
@@ -49195,9 +49184,8 @@ var tool_catalog_default = {
|
|
|
49195
49184
|
installer: "npm",
|
|
49196
49185
|
preinstallCloud: true,
|
|
49197
49186
|
label: "Browser",
|
|
49198
|
-
statusCmd: "agent-browser --version",
|
|
49199
49187
|
capabilities: "Headless browser automation, web scraping, accessibility tree snapshots, screenshots, PDF generation",
|
|
49200
|
-
description: "
|
|
49188
|
+
description: "Headless browser for JS-rendered pages, automated flows, screenshots, PDF export. **Default browser choice** — use this unless the user's own session/cookies are required (then use `todoforai-browser`). Prefer `curl` for plain HTTP, `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>.",
|
|
49201
49189
|
versionCmd: "agent-browser --version 2>/dev/null | head -1"
|
|
49202
49190
|
},
|
|
49203
49191
|
firecrawl: {
|
|
@@ -49220,7 +49208,7 @@ var tool_catalog_default = {
|
|
|
49220
49208
|
installer: "npm",
|
|
49221
49209
|
label: "Browser (Extension)",
|
|
49222
49210
|
capabilities: "Browser automation via extension (non-headless), web scraping, accessibility tree snapshots, screenshots, PDF generation",
|
|
49223
|
-
description: "
|
|
49211
|
+
description: "Drives the user's real browser via the TODOforAI extension (non-headless). Use when the task needs the user's actual session — logged-in accounts, saved cookies, extensions, MFA. For anything else prefer headless `agent-browser`. Commands: open <url>, click/type/fill <selector> <text>, snapshot (accessibility tree with @refs for clicking), screenshot [path], pdf <path>, eval <js>, wait <selector|ms>. Supports drag/drop, file uploads, form interactions.",
|
|
49224
49212
|
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
49213
|
internal: true
|
|
49226
49214
|
},
|
|
@@ -49254,6 +49242,7 @@ var tool_catalog_default = {
|
|
|
49254
49242
|
capabilities: "Explore a codebase as a real TODO: read-only agent maps structure, surfaces relevant files, streams findings to terminal",
|
|
49255
49243
|
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
49244
|
versionCmd: "tfa-explore --version 2>/dev/null | head -1",
|
|
49245
|
+
statusCmd: "tfa-explore whoami",
|
|
49257
49246
|
installCmd: "bun add -g @todoforai/tfa-explore",
|
|
49258
49247
|
preinstall: true,
|
|
49259
49248
|
preinstallCloud: true,
|
|
@@ -49267,6 +49256,7 @@ var tool_catalog_default = {
|
|
|
49267
49256
|
capabilities: "Review a git diff as a real TODO: read-only agent assesses goal, finds issues, suggests simpler approaches",
|
|
49268
49257
|
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
49258
|
versionCmd: "tfa-review --version 2>/dev/null | head -1",
|
|
49259
|
+
statusCmd: "tfa-review whoami",
|
|
49270
49260
|
installCmd: "bun add -g @todoforai/tfa-review",
|
|
49271
49261
|
preinstall: true,
|
|
49272
49262
|
preinstallCloud: true,
|
|
@@ -49280,6 +49270,7 @@ var tool_catalog_default = {
|
|
|
49280
49270
|
capabilities: "Summarize files or piped input as a real TODO with no-tools sub-agent",
|
|
49281
49271
|
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
49272
|
versionCmd: "tfa-summary --version 2>/dev/null | head -1",
|
|
49273
|
+
statusCmd: "tfa-summary whoami",
|
|
49283
49274
|
installCmd: "bun add -g @todoforai/tfa-summary",
|
|
49284
49275
|
internal: true
|
|
49285
49276
|
},
|
|
@@ -49289,13 +49280,12 @@ var tool_catalog_default = {
|
|
|
49289
49280
|
installer: "npm",
|
|
49290
49281
|
label: "Vault",
|
|
49291
49282
|
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: '
|
|
49283
|
+
description: 'Native TODOforAI secret store — free, zero-config (reuses bridge credentials; no VAULT_ADDR/VAULT_TOKEN/login). **Default credential source: for any task touching API keys, tokens, service accounts, or third-party auth, check `tfa-vault` before searching repo files, .env, or env vars — unless the user provides another source.** Discover-then-consume pattern: `tfa-vault list accounts/` to find keys, then `KEY=$(tfa-vault get -f api_key accounts/<service>)` in shell. Common paths: `accounts/<service>`, `api/<service>`, `secrets/<service>`. 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).',
|
|
49293
49284
|
versionCmd: "tfa-vault --version 2>/dev/null | head -1",
|
|
49294
49285
|
statusCmd: "tfa-vault whoami",
|
|
49295
49286
|
installCmd: "bun add -g @todoforai/vault",
|
|
49296
49287
|
preinstall: true,
|
|
49297
|
-
preinstallCloud: true
|
|
49298
|
-
internal: true
|
|
49288
|
+
preinstallCloud: true
|
|
49299
49289
|
},
|
|
49300
49290
|
ripgrep: {
|
|
49301
49291
|
category: "development",
|
|
@@ -49531,17 +49521,6 @@ function isToolInstalled(name) {
|
|
|
49531
49521
|
}
|
|
49532
49522
|
return whichWithTools(name) !== null;
|
|
49533
49523
|
}
|
|
49534
|
-
function findReferencedTools(content) {
|
|
49535
|
-
const stripped = content.replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''");
|
|
49536
|
-
return Object.keys(TOOL_CATALOG).filter((name) => {
|
|
49537
|
-
const esc = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
49538
|
-
const re = new RegExp(String.raw`(?:^|[|;&\n]|&&|\|\||` + String.raw`\$\(|` + "`" + String.raw`|xargs\s+|sudo\s+|env\s+)\s*` + esc + String.raw`\b(?!-)`, "m");
|
|
49539
|
-
return re.test(stripped);
|
|
49540
|
-
});
|
|
49541
|
-
}
|
|
49542
|
-
function findMissingTools(content) {
|
|
49543
|
-
return findReferencedTools(content).filter((name) => !isToolInstalled(name));
|
|
49544
|
-
}
|
|
49545
49524
|
async function installBinary(name) {
|
|
49546
49525
|
const urlFunc = BINARY_URL_FUNCS[name];
|
|
49547
49526
|
if (!urlFunc) {
|
|
@@ -51592,7 +51571,6 @@ ${this.lastPart}`;
|
|
|
51592
51571
|
var processes = new Map;
|
|
51593
51572
|
var outputBuffers = new Map;
|
|
51594
51573
|
var completionResolvers = new Map;
|
|
51595
|
-
var pendingToolApprovals = new Map;
|
|
51596
51574
|
var exitedOutputByPid = new Map;
|
|
51597
51575
|
async function executeBlock(blockId, content, send, todoId, messageId, timeout, cwd, manual = false, runMode, edgeId, agentSettingsId = "", keepAliveOnTimeout = false) {
|
|
51598
51576
|
if (processes.has(blockId)) {
|
|
@@ -51611,38 +51589,6 @@ async function executeBlock(blockId, content, send, todoId, messageId, timeout,
|
|
|
51611
51589
|
} else {
|
|
51612
51590
|
cwd = tmpDir;
|
|
51613
51591
|
}
|
|
51614
|
-
const missing = findMissingTools(content);
|
|
51615
|
-
if (missing.length && !pendingToolApprovals.has(blockId)) {
|
|
51616
|
-
pendingToolApprovals.set(blockId, missing);
|
|
51617
|
-
console.log(`[shell] Missing tools ${missing}, requesting approval for block ${blockId}`);
|
|
51618
|
-
await send({
|
|
51619
|
-
type: "BLOCK_UPDATE",
|
|
51620
|
-
payload: {
|
|
51621
|
-
todoId,
|
|
51622
|
-
blockId,
|
|
51623
|
-
messageId,
|
|
51624
|
-
updates: {
|
|
51625
|
-
status: "AWAITING_APPROVAL",
|
|
51626
|
-
approvalContext: { source: "edge", toolInstalls: missing, workspace: cwd, edgeId }
|
|
51627
|
-
}
|
|
51628
|
-
}
|
|
51629
|
-
});
|
|
51630
|
-
return;
|
|
51631
|
-
}
|
|
51632
|
-
if (pendingToolApprovals.has(blockId)) {
|
|
51633
|
-
const tools = pendingToolApprovals.get(blockId);
|
|
51634
|
-
pendingToolApprovals.delete(blockId);
|
|
51635
|
-
const installed = [];
|
|
51636
|
-
for (const t of tools) {
|
|
51637
|
-
if (await ensureTool(t))
|
|
51638
|
-
installed.push(t);
|
|
51639
|
-
}
|
|
51640
|
-
if (installed.length) {
|
|
51641
|
-
const notice = `[installed: ${installed.join(", ")}]
|
|
51642
|
-
`;
|
|
51643
|
-
await send(msg.shellBlockResult(todoId, blockId, notice, messageId));
|
|
51644
|
-
}
|
|
51645
|
-
}
|
|
51646
51592
|
await send({
|
|
51647
51593
|
type: "BLOCK_UPDATE",
|
|
51648
51594
|
payload: { todoId, blockId, messageId, updates: { status: "RUNNING" } }
|
|
@@ -51931,20 +51877,13 @@ var MAX_DIRS_PER_ROOT = 2000;
|
|
|
51931
51877
|
var FRONTMATTER_BYTES = 8 * 1024;
|
|
51932
51878
|
var MAX_NAME_LEN = 64;
|
|
51933
51879
|
var MAX_DESC_LEN = 1024;
|
|
51934
|
-
function shortestPath(filePath, relTo) {
|
|
51935
|
-
const rel = path6.relative(relTo, filePath);
|
|
51936
|
-
const home = os7.homedir();
|
|
51937
|
-
const homeRel = filePath.startsWith(home + path6.sep) ? "~" + filePath.slice(home.length) : filePath;
|
|
51938
|
-
return [rel, homeRel, filePath].reduce((a, b) => b.length < a.length ? b : a);
|
|
51939
|
-
}
|
|
51940
51880
|
async function discoverSkills(rootPaths, opts = {}) {
|
|
51941
51881
|
const includeUserScope = opts.includeUserScope ?? true;
|
|
51942
51882
|
const roots = [
|
|
51943
|
-
...rootPaths.map((p10) => ({ path: path6.join(p10, ".agents", "skills"),
|
|
51883
|
+
...rootPaths.map((p10) => ({ path: path6.join(p10, ".agents", "skills"), scope: "repo" }))
|
|
51944
51884
|
];
|
|
51945
51885
|
if (includeUserScope) {
|
|
51946
|
-
|
|
51947
|
-
roots.push({ path: path6.join(home, ".agents", "skills"), relTo: home, scope: "user" });
|
|
51886
|
+
roots.push({ path: path6.join(os7.homedir(), ".agents", "skills"), scope: "user" });
|
|
51948
51887
|
}
|
|
51949
51888
|
const skills = [];
|
|
51950
51889
|
const errors = [];
|
|
@@ -51962,11 +51901,11 @@ async function discoverSkills(rootPaths, opts = {}) {
|
|
|
51962
51901
|
}
|
|
51963
51902
|
if (!stat.isDirectory())
|
|
51964
51903
|
continue;
|
|
51965
|
-
walkRoot(root.path, root.
|
|
51904
|
+
walkRoot(root.path, root.scope, skills, errors, seenSkillPaths);
|
|
51966
51905
|
}
|
|
51967
51906
|
return { skills, errors };
|
|
51968
51907
|
}
|
|
51969
|
-
function walkRoot(root,
|
|
51908
|
+
function walkRoot(root, scope, skills, errors, seen) {
|
|
51970
51909
|
const queue = [{ dir: root, depth: 0 }];
|
|
51971
51910
|
let dirsVisited = 0;
|
|
51972
51911
|
for (let i10 = 0;i10 < queue.length; i10++) {
|
|
@@ -52002,14 +51941,14 @@ function walkRoot(root, relTo, scope, skills, errors, seen) {
|
|
|
52002
51941
|
if (seen.has(full))
|
|
52003
51942
|
continue;
|
|
52004
51943
|
seen.add(full);
|
|
52005
|
-
const skill = parseSkillFile(full,
|
|
51944
|
+
const skill = parseSkillFile(full, scope, errors);
|
|
52006
51945
|
if (skill)
|
|
52007
51946
|
skills.push(skill);
|
|
52008
51947
|
}
|
|
52009
51948
|
}
|
|
52010
51949
|
}
|
|
52011
51950
|
}
|
|
52012
|
-
function parseSkillFile(filePath,
|
|
51951
|
+
function parseSkillFile(filePath, scope, errors) {
|
|
52013
51952
|
let head;
|
|
52014
51953
|
try {
|
|
52015
51954
|
const fd3 = fs9.openSync(filePath, "r");
|
|
@@ -52042,7 +51981,7 @@ function parseSkillFile(filePath, relTo, scope, errors) {
|
|
|
52042
51981
|
name,
|
|
52043
51982
|
description,
|
|
52044
51983
|
shortDescription: shortDescription && shortDescription.length <= MAX_DESC_LEN ? shortDescription : undefined,
|
|
52045
|
-
path:
|
|
51984
|
+
path: filePath,
|
|
52046
51985
|
scope
|
|
52047
51986
|
};
|
|
52048
51987
|
}
|
|
@@ -52372,9 +52311,6 @@ register("execute_shell_command", async (args, client) => {
|
|
|
52372
52311
|
try {
|
|
52373
52312
|
await send(msg.shellBlockStart(todoId, blockId, "execute", messageId));
|
|
52374
52313
|
await executeBlock(blockId, execCmd, send, todoId, messageId, timeout, cwd, false, "internal", undefined, agentSettingsId, true);
|
|
52375
|
-
if (pendingToolApprovals.has(blockId)) {
|
|
52376
|
-
return { __awaiting_approval__: true };
|
|
52377
|
-
}
|
|
52378
52314
|
await waitForCompletion(blockId, (timeout + 5) * 1000);
|
|
52379
52315
|
const rawOutput = getBlockRawOutput(blockId);
|
|
52380
52316
|
let output = rawOutput ?? getBlockOutput(blockId);
|
|
@@ -52686,8 +52622,6 @@ async function handleFunctionCall(payload, send, client) {
|
|
|
52686
52622
|
throw new Error(`Unknown function: ${functionName}. Available: ${available.join(", ")}`);
|
|
52687
52623
|
}
|
|
52688
52624
|
const result = await fn2(args, client);
|
|
52689
|
-
if (result && result.__awaiting_approval__)
|
|
52690
|
-
return;
|
|
52691
52625
|
await send(makeSuccess(result));
|
|
52692
52626
|
} catch (e) {
|
|
52693
52627
|
log3("error", `Function call '${functionName}' failed:`, e.message);
|
|
@@ -53186,10 +53120,17 @@ class ServerError extends Error {
|
|
|
53186
53120
|
}
|
|
53187
53121
|
}
|
|
53188
53122
|
|
|
53189
|
-
//
|
|
53123
|
+
// node_modules/@todoforai/update-notifier/src/index.ts
|
|
53190
53124
|
import fs12 from "node:fs";
|
|
53191
53125
|
import path9 from "node:path";
|
|
53192
53126
|
import os9 from "node:os";
|
|
53127
|
+
function isLinkedInstall() {
|
|
53128
|
+
try {
|
|
53129
|
+
return !fs12.realpathSync(process.argv[1] || "").includes(`${path9.sep}node_modules${path9.sep}`);
|
|
53130
|
+
} catch {
|
|
53131
|
+
return false;
|
|
53132
|
+
}
|
|
53133
|
+
}
|
|
53193
53134
|
var TTL_MS = 24 * 60 * 60 * 1000;
|
|
53194
53135
|
var CACHE_DIR = path9.join(os9.homedir(), ".config", "todoforai");
|
|
53195
53136
|
function cmpVer(a, b) {
|
|
@@ -53203,7 +53144,7 @@ function cmpVer(a, b) {
|
|
|
53203
53144
|
return 0;
|
|
53204
53145
|
}
|
|
53205
53146
|
function checkForUpdates(pkg) {
|
|
53206
|
-
if (!process.stderr.isTTY || process.env.CI || process.env.NO_UPDATE_NOTIFIER)
|
|
53147
|
+
if (!process.stderr.isTTY || process.env.CI || process.env.NO_UPDATE_NOTIFIER || isLinkedInstall())
|
|
53207
53148
|
return;
|
|
53208
53149
|
const cacheFile = path9.join(CACHE_DIR, `notifier-${encodeURIComponent(pkg.name)}.json`);
|
|
53209
53150
|
let cache = {};
|
|
@@ -53302,7 +53243,19 @@ function releaseLock(lp2) {
|
|
|
53302
53243
|
fs13.unlinkSync(lp2);
|
|
53303
53244
|
} catch {}
|
|
53304
53245
|
}
|
|
53246
|
+
var MIN_BUN_VERSION = "1.3.14";
|
|
53247
|
+
function cmpSemver(a, b) {
|
|
53248
|
+
const pa2 = a.split(".").map(Number), pb2 = b.split(".").map(Number);
|
|
53249
|
+
for (let i10 = 0;i10 < 3; i10++)
|
|
53250
|
+
if ((pa2[i10] ?? 0) !== (pb2[i10] ?? 0))
|
|
53251
|
+
return (pa2[i10] ?? 0) - (pb2[i10] ?? 0);
|
|
53252
|
+
return 0;
|
|
53253
|
+
}
|
|
53305
53254
|
async function main() {
|
|
53255
|
+
if (typeof Bun !== "undefined" && cmpSemver(Bun.version, MIN_BUN_VERSION) < 0) {
|
|
53256
|
+
console.error(`\x1B[31mBun ${Bun.version} is too old — WebSocket upgrade fails after fetch on the same host. Upgrade to >= ${MIN_BUN_VERSION} via \`bun upgrade\`.\x1B[0m`);
|
|
53257
|
+
process.exit(1);
|
|
53258
|
+
}
|
|
53306
53259
|
const ownPkg = readOwnPackage();
|
|
53307
53260
|
if (ownPkg)
|
|
53308
53261
|
checkForUpdates(ownPkg);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@todoforai/edge",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"todoforai-edge": "dist/index.js"
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"prepublishOnly": "npm run build"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@todoforai/update-notifier": "^0.1.0",
|
|
19
20
|
"fflate": "^0.8.2",
|
|
20
21
|
"ignore": "^7.0.5",
|
|
21
22
|
"unpdf": "^1.4.0",
|