@todoforai/edge 0.13.13 → 0.13.15
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 +313 -173
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -47695,7 +47695,7 @@ var require_ignore = __commonJS((exports, module) => {
|
|
|
47695
47695
|
makeArray(isString(pattern) ? splitPattern(pattern) : pattern).forEach(this._add, this);
|
|
47696
47696
|
return this._added;
|
|
47697
47697
|
}
|
|
47698
|
-
test(
|
|
47698
|
+
test(path8, checkUnignored, mode) {
|
|
47699
47699
|
let ignored = false;
|
|
47700
47700
|
let unignored = false;
|
|
47701
47701
|
let matchedRule;
|
|
@@ -47704,7 +47704,7 @@ var require_ignore = __commonJS((exports, module) => {
|
|
|
47704
47704
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
47705
47705
|
return;
|
|
47706
47706
|
}
|
|
47707
|
-
const matched = rule[mode].test(
|
|
47707
|
+
const matched = rule[mode].test(path8);
|
|
47708
47708
|
if (!matched) {
|
|
47709
47709
|
return;
|
|
47710
47710
|
}
|
|
@@ -47725,20 +47725,20 @@ var require_ignore = __commonJS((exports, module) => {
|
|
|
47725
47725
|
var throwError = (message, Ctor) => {
|
|
47726
47726
|
throw new Ctor(message);
|
|
47727
47727
|
};
|
|
47728
|
-
var checkPath = (
|
|
47729
|
-
if (!isString(
|
|
47728
|
+
var checkPath = (path8, originalPath, doThrow) => {
|
|
47729
|
+
if (!isString(path8)) {
|
|
47730
47730
|
return doThrow(`path must be a string, but got \`${originalPath}\``, TypeError);
|
|
47731
47731
|
}
|
|
47732
|
-
if (!
|
|
47732
|
+
if (!path8) {
|
|
47733
47733
|
return doThrow(`path must not be empty`, TypeError);
|
|
47734
47734
|
}
|
|
47735
|
-
if (checkPath.isNotRelative(
|
|
47735
|
+
if (checkPath.isNotRelative(path8)) {
|
|
47736
47736
|
const r = "`path.relative()`d";
|
|
47737
47737
|
return doThrow(`path should be a ${r} string, but got "${originalPath}"`, RangeError);
|
|
47738
47738
|
}
|
|
47739
47739
|
return true;
|
|
47740
47740
|
};
|
|
47741
|
-
var isNotRelative = (
|
|
47741
|
+
var isNotRelative = (path8) => REGEX_TEST_INVALID_PATH.test(path8);
|
|
47742
47742
|
checkPath.isNotRelative = isNotRelative;
|
|
47743
47743
|
checkPath.convert = (p10) => p10;
|
|
47744
47744
|
|
|
@@ -47767,15 +47767,15 @@ var require_ignore = __commonJS((exports, module) => {
|
|
|
47767
47767
|
return this.add(pattern);
|
|
47768
47768
|
}
|
|
47769
47769
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
47770
|
-
const
|
|
47771
|
-
checkPath(
|
|
47772
|
-
return this._t(
|
|
47770
|
+
const path8 = originalPath && checkPath.convert(originalPath);
|
|
47771
|
+
checkPath(path8, originalPath, this._strictPathCheck ? throwError : RETURN_FALSE);
|
|
47772
|
+
return this._t(path8, cache, checkUnignored, slices);
|
|
47773
47773
|
}
|
|
47774
|
-
checkIgnore(
|
|
47775
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
47776
|
-
return this.test(
|
|
47774
|
+
checkIgnore(path8) {
|
|
47775
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path8)) {
|
|
47776
|
+
return this.test(path8);
|
|
47777
47777
|
}
|
|
47778
|
-
const slices =
|
|
47778
|
+
const slices = path8.split(SLASH).filter(Boolean);
|
|
47779
47779
|
slices.pop();
|
|
47780
47780
|
if (slices.length) {
|
|
47781
47781
|
const parent = this._t(slices.join(SLASH) + SLASH, this._testCache, true, slices);
|
|
@@ -47783,42 +47783,42 @@ var require_ignore = __commonJS((exports, module) => {
|
|
|
47783
47783
|
return parent;
|
|
47784
47784
|
}
|
|
47785
47785
|
}
|
|
47786
|
-
return this._rules.test(
|
|
47786
|
+
return this._rules.test(path8, false, MODE_CHECK_IGNORE);
|
|
47787
47787
|
}
|
|
47788
|
-
_t(
|
|
47789
|
-
if (
|
|
47790
|
-
return cache[
|
|
47788
|
+
_t(path8, cache, checkUnignored, slices) {
|
|
47789
|
+
if (path8 in cache) {
|
|
47790
|
+
return cache[path8];
|
|
47791
47791
|
}
|
|
47792
47792
|
if (!slices) {
|
|
47793
|
-
slices =
|
|
47793
|
+
slices = path8.split(SLASH).filter(Boolean);
|
|
47794
47794
|
}
|
|
47795
47795
|
slices.pop();
|
|
47796
47796
|
if (!slices.length) {
|
|
47797
|
-
return cache[
|
|
47797
|
+
return cache[path8] = this._rules.test(path8, checkUnignored, MODE_IGNORE);
|
|
47798
47798
|
}
|
|
47799
47799
|
const parent = this._t(slices.join(SLASH) + SLASH, cache, checkUnignored, slices);
|
|
47800
|
-
return cache[
|
|
47800
|
+
return cache[path8] = parent.ignored ? parent : this._rules.test(path8, checkUnignored, MODE_IGNORE);
|
|
47801
47801
|
}
|
|
47802
|
-
ignores(
|
|
47803
|
-
return this._test(
|
|
47802
|
+
ignores(path8) {
|
|
47803
|
+
return this._test(path8, this._ignoreCache, false).ignored;
|
|
47804
47804
|
}
|
|
47805
47805
|
createFilter() {
|
|
47806
|
-
return (
|
|
47806
|
+
return (path8) => !this.ignores(path8);
|
|
47807
47807
|
}
|
|
47808
47808
|
filter(paths) {
|
|
47809
47809
|
return makeArray(paths).filter(this.createFilter());
|
|
47810
47810
|
}
|
|
47811
|
-
test(
|
|
47812
|
-
return this._test(
|
|
47811
|
+
test(path8) {
|
|
47812
|
+
return this._test(path8, this._testCache, true);
|
|
47813
47813
|
}
|
|
47814
47814
|
}
|
|
47815
47815
|
var factory = (options) => new Ignore(options);
|
|
47816
|
-
var isPathValid = (
|
|
47816
|
+
var isPathValid = (path8) => checkPath(path8 && checkPath.convert(path8), path8, RETURN_FALSE);
|
|
47817
47817
|
var setupWindows = () => {
|
|
47818
47818
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
47819
47819
|
checkPath.convert = makePosix;
|
|
47820
47820
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
47821
|
-
checkPath.isNotRelative = (
|
|
47821
|
+
checkPath.isNotRelative = (path8) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path8) || isNotRelative(path8);
|
|
47822
47822
|
};
|
|
47823
47823
|
if (typeof process !== "undefined" && process.platform === "win32") {
|
|
47824
47824
|
setupWindows();
|
|
@@ -48645,9 +48645,9 @@ class BrowserExtensionBridge {
|
|
|
48645
48645
|
}
|
|
48646
48646
|
|
|
48647
48647
|
// src/handlers.ts
|
|
48648
|
-
import
|
|
48648
|
+
import fs12 from "fs";
|
|
48649
48649
|
import { mkdir, rm as rm2, writeFile } from "fs/promises";
|
|
48650
|
-
import
|
|
48650
|
+
import path9 from "path";
|
|
48651
48651
|
|
|
48652
48652
|
// src/path-utils.ts
|
|
48653
48653
|
import path2 from "path";
|
|
@@ -48790,7 +48790,6 @@ var tool_catalog_default = {
|
|
|
48790
48790
|
pkg: "tiktok-uploader",
|
|
48791
48791
|
installer: "pip",
|
|
48792
48792
|
label: "TikTok",
|
|
48793
|
-
statusCmd: "python3 -c 'import tiktok_uploader' 2>&1",
|
|
48794
48793
|
loginCmd: "npx @todoforai/tiktok-cookie-helper",
|
|
48795
48794
|
credentialPaths: [
|
|
48796
48795
|
"~/.tiktok/cookies.txt"
|
|
@@ -48806,7 +48805,6 @@ var tool_catalog_default = {
|
|
|
48806
48805
|
label: "Instagram",
|
|
48807
48806
|
capabilities: "Upload photos & reels, post stories, send DMs, like & comment, manage followers",
|
|
48808
48807
|
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; ..."`.',
|
|
48809
|
-
statusCmd: 'python3 -c "import instagrapi" 2>/dev/null',
|
|
48810
48808
|
versionCmd: "pip show instagrapi 2>/dev/null | grep -oP 'Version: \\K.*'"
|
|
48811
48809
|
},
|
|
48812
48810
|
mudslide: {
|
|
@@ -48828,7 +48826,6 @@ var tool_catalog_default = {
|
|
|
48828
48826
|
pkg: "telegram-send",
|
|
48829
48827
|
installer: "pip",
|
|
48830
48828
|
label: "Telegram",
|
|
48831
|
-
statusCmd: "test -f ~/.config/telegram-send/telegram-send.conf && echo 'configured'",
|
|
48832
48829
|
loginCmd: "telegram-send --configure",
|
|
48833
48830
|
credentialPaths: [
|
|
48834
48831
|
"~/.config/telegram-send/telegram-send.conf"
|
|
@@ -49052,7 +49049,6 @@ var tool_catalog_default = {
|
|
|
49052
49049
|
pkg: "@shopify/cli",
|
|
49053
49050
|
installer: "npm",
|
|
49054
49051
|
label: "Shopify",
|
|
49055
|
-
statusCmd: "test -f ~/.config/shopify/config.json && echo 'authenticated'",
|
|
49056
49052
|
loginCmd: "shopify auth login",
|
|
49057
49053
|
credentialPaths: [
|
|
49058
49054
|
"~/.config/shopify/config.json"
|
|
@@ -49091,7 +49087,6 @@ var tool_catalog_default = {
|
|
|
49091
49087
|
label: "TODOforAI",
|
|
49092
49088
|
capabilities: "Create & manage TODOs, run workflows, API access",
|
|
49093
49089
|
description: "Use to programmatically create/list/update TODOs in TODOforAI, kick off workflows, call the platform API.",
|
|
49094
|
-
statusCmd: "todoai whoami >/dev/null 2>&1",
|
|
49095
49090
|
installCmd: "bun add -g @todoforai/cli",
|
|
49096
49091
|
versionCmd: "todoai --version 2>/dev/null | head -1",
|
|
49097
49092
|
internal: true
|
|
@@ -49153,7 +49148,7 @@ var tool_catalog_default = {
|
|
|
49153
49148
|
"~/.config/rclone/rclone.conf"
|
|
49154
49149
|
],
|
|
49155
49150
|
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).",
|
|
49156
|
-
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
|
|
49151
|
+
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 lsf <remote>:<path>`, `rclone cat <remote>:<file>`, `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>`. Note: FUSE `mount` needs /dev/fuse + CAP_SYS_ADMIN — works on VM sandboxes and bridge devices, but NOT in lite sandboxes; there use `lsf`/`cat`/`copy`/`sync` (HTTPS-only) instead. See `subProviders` for per-provider connect commands.",
|
|
49157
49152
|
subProviders: {
|
|
49158
49153
|
gdrive: {
|
|
49159
49154
|
label: "Google Drive",
|
|
@@ -49199,7 +49194,7 @@ var tool_catalog_default = {
|
|
|
49199
49194
|
installer: "pip",
|
|
49200
49195
|
label: "PDF",
|
|
49201
49196
|
capabilities: "PDF text editing, extraction, merge, split",
|
|
49202
|
-
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)`.
|
|
49197
|
+
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)`.",
|
|
49203
49198
|
versionCmd: "python3 -c 'import pymupdf; print(pymupdf.__version__)' 2>/dev/null",
|
|
49204
49199
|
preinstallCloud: true
|
|
49205
49200
|
},
|
|
@@ -49210,23 +49205,9 @@ var tool_catalog_default = {
|
|
|
49210
49205
|
preinstallCloud: true,
|
|
49211
49206
|
label: "Browser",
|
|
49212
49207
|
capabilities: "Headless browser automation, web scraping, accessibility tree snapshots, screenshots, PDF generation",
|
|
49213
|
-
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
|
|
49208
|
+
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. 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>.",
|
|
49214
49209
|
versionCmd: "agent-browser --version 2>/dev/null | head -1"
|
|
49215
49210
|
},
|
|
49216
|
-
firecrawl: {
|
|
49217
|
-
category: "development",
|
|
49218
|
-
pkg: "firecrawl-cli",
|
|
49219
|
-
installer: "npm",
|
|
49220
|
-
label: "Firecrawl",
|
|
49221
|
-
statusCmd: "firecrawl view-config 2>&1 | grep -q 'Authenticated' && echo authenticated",
|
|
49222
|
-
loginCmd: "firecrawl login",
|
|
49223
|
-
credentialPaths: [
|
|
49224
|
-
"~/.config/firecrawl-cli"
|
|
49225
|
-
],
|
|
49226
|
-
capabilities: "Web scraping, crawling, search, map URLs, parse local HTML/PDF/DOCX to markdown, AI agent extraction",
|
|
49227
|
-
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.",
|
|
49228
|
-
versionCmd: "firecrawl --version 2>/dev/null | head -1"
|
|
49229
|
-
},
|
|
49230
49211
|
"todoforai-browser": {
|
|
49231
49212
|
category: "development",
|
|
49232
49213
|
pkg: "@todoforai/browser",
|
|
@@ -49267,7 +49248,6 @@ var tool_catalog_default = {
|
|
|
49267
49248
|
capabilities: "Explore a codebase as a real TODO: read-only agent maps structure, surfaces relevant files, streams findings to terminal",
|
|
49268
49249
|
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.',
|
|
49269
49250
|
versionCmd: "tfa-explore --version 2>/dev/null | head -1",
|
|
49270
|
-
statusCmd: "tfa-explore whoami",
|
|
49271
49251
|
installCmd: "bun add -g @todoforai/tfa-explore",
|
|
49272
49252
|
preinstall: true,
|
|
49273
49253
|
preinstallCloud: true,
|
|
@@ -49281,7 +49261,6 @@ var tool_catalog_default = {
|
|
|
49281
49261
|
capabilities: "Review a git diff as a real TODO: read-only agent assesses goal, finds issues, suggests simpler approaches",
|
|
49282
49262
|
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.',
|
|
49283
49263
|
versionCmd: "tfa-review --version 2>/dev/null | head -1",
|
|
49284
|
-
statusCmd: "tfa-review whoami",
|
|
49285
49264
|
installCmd: "bun add -g @todoforai/tfa-review",
|
|
49286
49265
|
preinstall: true,
|
|
49287
49266
|
preinstallCloud: true,
|
|
@@ -49295,7 +49274,6 @@ var tool_catalog_default = {
|
|
|
49295
49274
|
capabilities: "Summarize files or piped input as a real TODO with no-tools sub-agent",
|
|
49296
49275
|
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.',
|
|
49297
49276
|
versionCmd: "tfa-summary --version 2>/dev/null | head -1",
|
|
49298
|
-
statusCmd: "tfa-summary whoami",
|
|
49299
49277
|
installCmd: "bun add -g @todoforai/tfa-summary",
|
|
49300
49278
|
internal: true
|
|
49301
49279
|
},
|
|
@@ -51390,12 +51368,7 @@ import { spawn as nodeSpawn } from "child_process";
|
|
|
51390
51368
|
// src/shell-pause-detector.ts
|
|
51391
51369
|
import os5 from "os";
|
|
51392
51370
|
import { readFile, readlink } from "fs/promises";
|
|
51393
|
-
|
|
51394
|
-
class NullDetector {
|
|
51395
|
-
watch() {
|
|
51396
|
-
return { cancel: () => {}, reset: () => {} };
|
|
51397
|
-
}
|
|
51398
|
-
}
|
|
51371
|
+
var ECHO = 8;
|
|
51399
51372
|
var READ_SYSCALL_NR = {
|
|
51400
51373
|
x64: 0,
|
|
51401
51374
|
arm64: 63,
|
|
@@ -51405,23 +51378,29 @@ var READ_SYSCALL_NR = {
|
|
|
51405
51378
|
var READ_NR = READ_SYSCALL_NR[process.arch] ?? -1;
|
|
51406
51379
|
var POLL_MS = 250;
|
|
51407
51380
|
var GRACE_TICKS = 2;
|
|
51381
|
+
var IS_LINUX2 = os5.platform() === "linux" && READ_NR >= 0;
|
|
51408
51382
|
|
|
51409
|
-
class
|
|
51410
|
-
watch(pid, onPaused) {
|
|
51383
|
+
class PtyPauseDetector {
|
|
51384
|
+
watch(pid, onPaused, terminal) {
|
|
51411
51385
|
let pausedTicks = 0;
|
|
51412
51386
|
let signalled = false;
|
|
51413
51387
|
let cancelled = false;
|
|
51414
51388
|
let rootStdin = null;
|
|
51415
|
-
|
|
51416
|
-
|
|
51417
|
-
|
|
51389
|
+
if (IS_LINUX2)
|
|
51390
|
+
readFdTarget(pid, 0).then((t) => {
|
|
51391
|
+
rootStdin = t;
|
|
51392
|
+
});
|
|
51418
51393
|
const tick = async () => {
|
|
51419
51394
|
if (cancelled)
|
|
51420
51395
|
return;
|
|
51421
|
-
|
|
51422
|
-
|
|
51423
|
-
|
|
51424
|
-
|
|
51396
|
+
let blocked = terminal != null && !(terminal.localFlags & ECHO);
|
|
51397
|
+
if (!blocked && IS_LINUX2) {
|
|
51398
|
+
const fgPid = await getForegroundPid(pid);
|
|
51399
|
+
const sc2 = fgPid != null ? await readSyscall(fgPid) : null;
|
|
51400
|
+
const leafTarget = sc2 && sc2.nr === READ_NR && sc2.fd >= 0 && fgPid != null ? await readFdTarget(fgPid, sc2.fd) : null;
|
|
51401
|
+
blocked = isTerminalReadPause(sc2, leafTarget, rootStdin);
|
|
51402
|
+
}
|
|
51403
|
+
if (blocked) {
|
|
51425
51404
|
if (!signalled && ++pausedTicks >= GRACE_TICKS) {
|
|
51426
51405
|
signalled = true;
|
|
51427
51406
|
onPaused();
|
|
@@ -51492,7 +51471,7 @@ async function readFdTarget(pid, fd3) {
|
|
|
51492
51471
|
return null;
|
|
51493
51472
|
}
|
|
51494
51473
|
}
|
|
51495
|
-
var pauseDetector =
|
|
51474
|
+
var pauseDetector = new PtyPauseDetector;
|
|
51496
51475
|
|
|
51497
51476
|
// src/shell.ts
|
|
51498
51477
|
var IS_WIN = os6.platform() === "win32";
|
|
@@ -51652,13 +51631,13 @@ async function executeBlock(blockId, content, send, todoId, messageId, timeout,
|
|
|
51652
51631
|
}
|
|
51653
51632
|
}, timeout * 1000);
|
|
51654
51633
|
let cancelPauseWatch = null;
|
|
51655
|
-
const startPauseWatch = (pid) => {
|
|
51634
|
+
const startPauseWatch = (pid, terminal) => {
|
|
51656
51635
|
if (!keepAliveOnTimeout)
|
|
51657
51636
|
return;
|
|
51658
51637
|
const watcher = pauseDetector.watch(pid, () => {
|
|
51659
51638
|
if (processes.has(blockId))
|
|
51660
51639
|
resolveAlive();
|
|
51661
|
-
});
|
|
51640
|
+
}, terminal);
|
|
51662
51641
|
cancelPauseWatch = watcher.cancel;
|
|
51663
51642
|
const h = processes.get(blockId);
|
|
51664
51643
|
if (h)
|
|
@@ -51708,7 +51687,7 @@ async function executeBlock(blockId, content, send, todoId, messageId, timeout,
|
|
|
51708
51687
|
const handle = { terminal, proc, pid: proc.pid };
|
|
51709
51688
|
processes.set(blockId, handle);
|
|
51710
51689
|
const timer = startTimeout();
|
|
51711
|
-
startPauseWatch(proc.pid);
|
|
51690
|
+
startPauseWatch(proc.pid, terminal);
|
|
51712
51691
|
proc.exited.then((code) => {
|
|
51713
51692
|
terminal.close();
|
|
51714
51693
|
onExit(code ?? -1, timer);
|
|
@@ -51889,9 +51868,9 @@ function consumeExitedOutput(pid) {
|
|
|
51889
51868
|
}
|
|
51890
51869
|
|
|
51891
51870
|
// src/functions.ts
|
|
51892
|
-
import
|
|
51893
|
-
import
|
|
51894
|
-
import
|
|
51871
|
+
import fs11 from "fs";
|
|
51872
|
+
import path8 from "path";
|
|
51873
|
+
import os9 from "os";
|
|
51895
51874
|
|
|
51896
51875
|
// src/skills.ts
|
|
51897
51876
|
import fs9 from "fs";
|
|
@@ -52068,6 +52047,62 @@ function sanitize(s) {
|
|
|
52068
52047
|
return s.split(/\s+/).filter(Boolean).join(" ");
|
|
52069
52048
|
}
|
|
52070
52049
|
|
|
52050
|
+
// src/agent-md.ts
|
|
52051
|
+
import fs10 from "fs";
|
|
52052
|
+
import path7 from "path";
|
|
52053
|
+
import os8 from "os";
|
|
52054
|
+
var MAX_BYTES = 64 * 1024;
|
|
52055
|
+
var FILENAMES = ["AGENT.md", "AGENTS.md"];
|
|
52056
|
+
async function discoverAgentMd(rootPaths, opts = {}) {
|
|
52057
|
+
const includeUserScope = opts.includeUserScope ?? true;
|
|
52058
|
+
const maxBytes = opts.maxBytes ?? MAX_BYTES;
|
|
52059
|
+
const dirs = [
|
|
52060
|
+
...rootPaths.map((p10) => ({ dir: p10, scope: "repo" }))
|
|
52061
|
+
];
|
|
52062
|
+
if (includeUserScope) {
|
|
52063
|
+
dirs.push({ dir: path7.join(os8.homedir(), ".agents"), scope: "user" });
|
|
52064
|
+
}
|
|
52065
|
+
const files = [];
|
|
52066
|
+
const errors = [];
|
|
52067
|
+
const seen = new Set;
|
|
52068
|
+
for (const { dir, scope } of dirs) {
|
|
52069
|
+
for (const name of FILENAMES) {
|
|
52070
|
+
const full = path7.join(dir, name);
|
|
52071
|
+
if (seen.has(full))
|
|
52072
|
+
continue;
|
|
52073
|
+
let stat;
|
|
52074
|
+
try {
|
|
52075
|
+
stat = fs10.statSync(full);
|
|
52076
|
+
} catch {
|
|
52077
|
+
continue;
|
|
52078
|
+
}
|
|
52079
|
+
if (!stat.isFile())
|
|
52080
|
+
continue;
|
|
52081
|
+
seen.add(full);
|
|
52082
|
+
try {
|
|
52083
|
+
const fd3 = fs10.openSync(full, "r");
|
|
52084
|
+
const buf = Buffer.alloc(maxBytes);
|
|
52085
|
+
let bytes;
|
|
52086
|
+
try {
|
|
52087
|
+
bytes = fs10.readSync(fd3, buf, 0, maxBytes, 0);
|
|
52088
|
+
} finally {
|
|
52089
|
+
fs10.closeSync(fd3);
|
|
52090
|
+
}
|
|
52091
|
+
files.push({
|
|
52092
|
+
path: full,
|
|
52093
|
+
scope,
|
|
52094
|
+
content: buf.subarray(0, bytes).toString("utf-8"),
|
|
52095
|
+
bytes: stat.size,
|
|
52096
|
+
truncated: stat.size > maxBytes
|
|
52097
|
+
});
|
|
52098
|
+
} catch (e) {
|
|
52099
|
+
errors.push({ path: full, message: `read failed: ${e?.message ?? e}` });
|
|
52100
|
+
}
|
|
52101
|
+
}
|
|
52102
|
+
}
|
|
52103
|
+
return { files, errors };
|
|
52104
|
+
}
|
|
52105
|
+
|
|
52071
52106
|
// src/functions.ts
|
|
52072
52107
|
var FUNCTION_REGISTRY = new Map;
|
|
52073
52108
|
function register(name, fn2) {
|
|
@@ -52083,12 +52118,12 @@ register("get_environment_variable", async (args) => ({
|
|
|
52083
52118
|
value: process.env[args.var_name] ?? null
|
|
52084
52119
|
}));
|
|
52085
52120
|
register("get_system_info", async () => {
|
|
52086
|
-
let system2 =
|
|
52121
|
+
let system2 = os9.platform();
|
|
52087
52122
|
if (system2 === "darwin")
|
|
52088
52123
|
system2 = "macOS";
|
|
52089
52124
|
else if (system2 === "linux") {
|
|
52090
52125
|
try {
|
|
52091
|
-
const release =
|
|
52126
|
+
const release = fs11.readFileSync("/etc/os-release", "utf-8");
|
|
52092
52127
|
const m = release.match(/PRETTY_NAME="(.+?)"/);
|
|
52093
52128
|
if (m)
|
|
52094
52129
|
system2 = m[1];
|
|
@@ -52098,10 +52133,10 @@ register("get_system_info", async () => {
|
|
|
52098
52133
|
system2 = "Linux";
|
|
52099
52134
|
}
|
|
52100
52135
|
} else if (system2 === "win32") {
|
|
52101
|
-
system2 = `Windows ${
|
|
52136
|
+
system2 = `Windows ${os9.release()}`;
|
|
52102
52137
|
}
|
|
52103
|
-
const shell = process.env.SHELL ?
|
|
52104
|
-
const mount_path =
|
|
52138
|
+
const shell = process.env.SHELL ? path8.basename(process.env.SHELL) : "unknown";
|
|
52139
|
+
const mount_path = path8.join(os9.homedir(), ".todoforai", "mnt", "todoforai");
|
|
52105
52140
|
return { system: system2, shell, mount_path };
|
|
52106
52141
|
});
|
|
52107
52142
|
register("get_available_tools", async () => {
|
|
@@ -52145,11 +52180,11 @@ register("uninstall_tool", async (args) => {
|
|
|
52145
52180
|
});
|
|
52146
52181
|
register("get_workspace_tree", async (args) => {
|
|
52147
52182
|
const { path: p10, max_depth = 2 } = args;
|
|
52148
|
-
const root =
|
|
52149
|
-
if (!
|
|
52183
|
+
const root = path8.resolve(p10.replace(/^~/, process.env.HOME || "~"));
|
|
52184
|
+
if (!fs11.existsSync(root) || !fs11.statSync(root).isDirectory()) {
|
|
52150
52185
|
return { tree: "", is_git: false };
|
|
52151
52186
|
}
|
|
52152
|
-
const isGit =
|
|
52187
|
+
const isGit = fs11.existsSync(path8.join(root, ".git"));
|
|
52153
52188
|
if (process.platform !== "win32") {
|
|
52154
52189
|
try {
|
|
52155
52190
|
const { execSync: execSync2 } = await import("child_process");
|
|
@@ -52172,12 +52207,12 @@ register("get_workspace_tree", async (args) => {
|
|
|
52172
52207
|
const ig2 = ignore();
|
|
52173
52208
|
if (isGit) {
|
|
52174
52209
|
let scanGitignores = function(dir) {
|
|
52175
|
-
const giPath =
|
|
52176
|
-
if (
|
|
52210
|
+
const giPath = path8.join(dir, ".gitignore");
|
|
52211
|
+
if (fs11.existsSync(giPath)) {
|
|
52177
52212
|
try {
|
|
52178
|
-
const relDir =
|
|
52213
|
+
const relDir = path8.relative(root, dir).replace(/\\/g, "/");
|
|
52179
52214
|
const prefix = relDir === "" || relDir === "." ? "" : relDir + "/";
|
|
52180
|
-
for (let line of
|
|
52215
|
+
for (let line of fs11.readFileSync(giPath, "utf-8").split(`
|
|
52181
52216
|
`)) {
|
|
52182
52217
|
line = line.trim();
|
|
52183
52218
|
if (!line || line.startsWith("#"))
|
|
@@ -52191,21 +52226,21 @@ register("get_workspace_tree", async (args) => {
|
|
|
52191
52226
|
} catch {}
|
|
52192
52227
|
}
|
|
52193
52228
|
try {
|
|
52194
|
-
for (const e of
|
|
52229
|
+
for (const e of fs11.readdirSync(dir, { withFileTypes: true })) {
|
|
52195
52230
|
if (e.isDirectory() && e.name !== ".git")
|
|
52196
|
-
scanGitignores(
|
|
52231
|
+
scanGitignores(path8.join(dir, e.name));
|
|
52197
52232
|
}
|
|
52198
52233
|
} catch {}
|
|
52199
52234
|
};
|
|
52200
52235
|
scanGitignores(root);
|
|
52201
52236
|
}
|
|
52202
|
-
const lines = [
|
|
52237
|
+
const lines = [path8.basename(root) + "/"];
|
|
52203
52238
|
function walk(dirPath, prefix, depth) {
|
|
52204
52239
|
if (depth > max_depth)
|
|
52205
52240
|
return;
|
|
52206
52241
|
let entries;
|
|
52207
52242
|
try {
|
|
52208
|
-
entries =
|
|
52243
|
+
entries = fs11.readdirSync(dirPath, { withFileTypes: true });
|
|
52209
52244
|
} catch {
|
|
52210
52245
|
return;
|
|
52211
52246
|
}
|
|
@@ -52213,7 +52248,7 @@ register("get_workspace_tree", async (args) => {
|
|
|
52213
52248
|
if (e.name === ".git")
|
|
52214
52249
|
return false;
|
|
52215
52250
|
if (isGit) {
|
|
52216
|
-
let rel =
|
|
52251
|
+
let rel = path8.relative(root, path8.join(dirPath, e.name)).replace(/\\/g, "/");
|
|
52217
52252
|
if (e.isDirectory())
|
|
52218
52253
|
rel += "/";
|
|
52219
52254
|
if (ig2.ignores(rel))
|
|
@@ -52235,7 +52270,7 @@ register("get_workspace_tree", async (args) => {
|
|
|
52235
52270
|
lines.push(`${prefix}${connector}${entry.name}${suffix}`);
|
|
52236
52271
|
if (entry.isDirectory()) {
|
|
52237
52272
|
const extension2 = isLast ? " " : "│ ";
|
|
52238
|
-
walk(
|
|
52273
|
+
walk(path8.join(dirPath, entry.name), prefix + extension2, depth + 1);
|
|
52239
52274
|
}
|
|
52240
52275
|
}
|
|
52241
52276
|
}
|
|
@@ -52248,25 +52283,30 @@ register("get_skills", async (args) => {
|
|
|
52248
52283
|
const includeUserScope = args?.includeUserScope ?? true;
|
|
52249
52284
|
return await discoverSkills(paths, { includeUserScope });
|
|
52250
52285
|
});
|
|
52286
|
+
register("get_agent_md", async (args) => {
|
|
52287
|
+
const paths = Array.isArray(args?.paths) ? args.paths : [];
|
|
52288
|
+
const includeUserScope = args?.includeUserScope ?? true;
|
|
52289
|
+
return await discoverAgentMd(paths, { includeUserScope });
|
|
52290
|
+
});
|
|
52251
52291
|
register("get_os_aware_default_path", async () => {
|
|
52252
52292
|
let p10 = getPlatformDefaultDirectory();
|
|
52253
|
-
if (!p10.endsWith(
|
|
52254
|
-
p10 +=
|
|
52293
|
+
if (!p10.endsWith(path8.sep))
|
|
52294
|
+
p10 += path8.sep;
|
|
52255
52295
|
return { path: p10 };
|
|
52256
52296
|
});
|
|
52257
52297
|
register("create_directory", async (args) => {
|
|
52258
52298
|
const { name } = args;
|
|
52259
52299
|
if (!name?.trim())
|
|
52260
52300
|
throw new Error("Folder name cannot be empty");
|
|
52261
|
-
const baseDir =
|
|
52262
|
-
let target =
|
|
52263
|
-
if (!
|
|
52264
|
-
target =
|
|
52265
|
-
const existed =
|
|
52266
|
-
|
|
52301
|
+
const baseDir = path8.resolve(getPathOrDefault(args.path).replace(/^~/, process.env.HOME || "~"));
|
|
52302
|
+
let target = path8.resolve(name.replace(/^~/, process.env.HOME || "~"));
|
|
52303
|
+
if (!path8.isAbsolute(name))
|
|
52304
|
+
target = path8.join(baseDir, name.trim());
|
|
52305
|
+
const existed = fs11.existsSync(target);
|
|
52306
|
+
fs11.mkdirSync(target, { recursive: true });
|
|
52267
52307
|
let full = target;
|
|
52268
|
-
if (!full.endsWith(
|
|
52269
|
-
full +=
|
|
52308
|
+
if (!full.endsWith(path8.sep))
|
|
52309
|
+
full += path8.sep;
|
|
52270
52310
|
return { path: full, created: !existed, exists: true };
|
|
52271
52311
|
});
|
|
52272
52312
|
FUNCTION_REGISTRY.set("getOSAwareDefaultPath", FUNCTION_REGISTRY.get("get_os_aware_default_path"));
|
|
@@ -52300,7 +52340,7 @@ register("execute_shell_command", async (args, client) => {
|
|
|
52300
52340
|
if (!canStream) {
|
|
52301
52341
|
const { exec } = await import("child_process");
|
|
52302
52342
|
const result = await new Promise((resolve) => {
|
|
52303
|
-
exec(cmd, { cwd: cwd ||
|
|
52343
|
+
exec(cmd, { cwd: cwd || os9.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) => {
|
|
52304
52344
|
resolve((stdout || "") + (stderr || ""));
|
|
52305
52345
|
});
|
|
52306
52346
|
});
|
|
@@ -52365,17 +52405,17 @@ var LIST_DIR_MAX_ENTRIES = 1e4;
|
|
|
52365
52405
|
register("list_dir", async (args) => {
|
|
52366
52406
|
const { path: p10, rootPath = "", fallbackRootPaths = [] } = args;
|
|
52367
52407
|
const fullPath = resolveFilePath(p10, rootPath, fallbackRootPaths);
|
|
52368
|
-
const st2 =
|
|
52408
|
+
const st2 = fs11.statSync(fullPath);
|
|
52369
52409
|
if (!st2.isDirectory())
|
|
52370
52410
|
throw new Error(`Not a directory: ${fullPath}`);
|
|
52371
|
-
const dirents =
|
|
52411
|
+
const dirents = fs11.readdirSync(fullPath, { withFileTypes: true });
|
|
52372
52412
|
if (dirents.length > LIST_DIR_MAX_ENTRIES) {
|
|
52373
52413
|
throw new Error(`Directory too large: ${dirents.length} entries (max ${LIST_DIR_MAX_ENTRIES})`);
|
|
52374
52414
|
}
|
|
52375
52415
|
const entries = dirents.map((d) => {
|
|
52376
52416
|
let size = 0, mtime = 0, mode = 0, is_dir = d.isDirectory();
|
|
52377
52417
|
try {
|
|
52378
|
-
const s =
|
|
52418
|
+
const s = fs11.lstatSync(path8.join(fullPath, d.name));
|
|
52379
52419
|
size = Number(s.size);
|
|
52380
52420
|
mtime = s.mtimeMs / 1000;
|
|
52381
52421
|
mode = s.mode & 511;
|
|
@@ -52388,23 +52428,123 @@ register("list_dir", async (args) => {
|
|
|
52388
52428
|
register("create_file", async (args) => {
|
|
52389
52429
|
const { path: p10, content, rootPath = "", fallbackRootPaths = [] } = args;
|
|
52390
52430
|
const fullPath = resolveFilePath(p10, rootPath, fallbackRootPaths);
|
|
52391
|
-
const dir =
|
|
52431
|
+
const dir = path8.dirname(fullPath);
|
|
52392
52432
|
if (dir)
|
|
52393
|
-
|
|
52394
|
-
|
|
52433
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
52434
|
+
fs11.writeFileSync(fullPath, content, "utf-8");
|
|
52395
52435
|
return { path: fullPath, bytes: Buffer.byteLength(content, "utf-8") };
|
|
52396
52436
|
});
|
|
52397
52437
|
register("read_file_base64", async (args) => {
|
|
52398
52438
|
const { path: p10, rootPath = "", fallbackRootPaths = [] } = args;
|
|
52399
52439
|
const fullPath = resolveFilePath(p10, rootPath, fallbackRootPaths);
|
|
52400
|
-
if (!
|
|
52440
|
+
if (!fs11.existsSync(fullPath))
|
|
52401
52441
|
throw new Error(`File not found: ${fullPath}`);
|
|
52402
|
-
const stat =
|
|
52442
|
+
const stat = fs11.statSync(fullPath);
|
|
52403
52443
|
if (stat.size > 50000000)
|
|
52404
52444
|
throw new Error(`File too large: ${stat.size.toLocaleString()} bytes (max 50MB)`);
|
|
52405
|
-
const data =
|
|
52445
|
+
const data = fs11.readFileSync(fullPath);
|
|
52406
52446
|
return { path: fullPath, base64: data.toString("base64"), bytes: data.length };
|
|
52407
52447
|
});
|
|
52448
|
+
register("search_files", async (args) => {
|
|
52449
|
+
const { pattern, path: p10 = ".", cwd = args.root_path ?? "", head = 100, max_count = 5, glob: globPattern = "", ignore_case = true } = args;
|
|
52450
|
+
const { execSync: execWhich } = await import("child_process");
|
|
52451
|
+
const whichCmd = process.platform === "win32" ? "where" : "which";
|
|
52452
|
+
const which = (bin) => {
|
|
52453
|
+
try {
|
|
52454
|
+
return execWhich(`${whichCmd} ${bin}`, { encoding: "utf-8" }).trim().split(`
|
|
52455
|
+
`)[0].trim();
|
|
52456
|
+
} catch {
|
|
52457
|
+
return null;
|
|
52458
|
+
}
|
|
52459
|
+
};
|
|
52460
|
+
let rgPath = which("rg");
|
|
52461
|
+
if (!rgPath) {
|
|
52462
|
+
await ensureTool("rg");
|
|
52463
|
+
rgPath = which("rg");
|
|
52464
|
+
}
|
|
52465
|
+
let searchPath = p10.replace(/^~/, process.env.HOME || "~");
|
|
52466
|
+
if (!path8.isAbsolute(searchPath) && cwd)
|
|
52467
|
+
searchPath = path8.join(cwd, searchPath);
|
|
52468
|
+
searchPath = path8.resolve(searchPath);
|
|
52469
|
+
if (!fs11.existsSync(searchPath))
|
|
52470
|
+
throw new Error(`Search path does not exist: ${searchPath}`);
|
|
52471
|
+
let cmd;
|
|
52472
|
+
if (rgPath) {
|
|
52473
|
+
cmd = [rgPath, "--no-heading", "--line-number", "--color=never"];
|
|
52474
|
+
if (ignore_case)
|
|
52475
|
+
cmd.push("--ignore-case");
|
|
52476
|
+
if (max_count > 0)
|
|
52477
|
+
cmd.push(`--max-count=${max_count}`);
|
|
52478
|
+
if (globPattern)
|
|
52479
|
+
cmd.push("--glob", globPattern);
|
|
52480
|
+
cmd.push(pattern, searchPath);
|
|
52481
|
+
} else {
|
|
52482
|
+
console.warn("[search_files] ripgrep (rg) not found, falling back to grep");
|
|
52483
|
+
const grepPath = which("grep") || "grep";
|
|
52484
|
+
cmd = [grepPath, "-rn", "--color=never"];
|
|
52485
|
+
if (ignore_case)
|
|
52486
|
+
cmd.push("-i");
|
|
52487
|
+
if (max_count > 0)
|
|
52488
|
+
cmd.push(`--max-count=${max_count}`);
|
|
52489
|
+
if (globPattern) {
|
|
52490
|
+
cmd.push(`--include=${globPattern}`);
|
|
52491
|
+
}
|
|
52492
|
+
cmd.push(pattern, searchPath);
|
|
52493
|
+
}
|
|
52494
|
+
const { spawn: spawnChild } = await import("child_process");
|
|
52495
|
+
const { stdout, stderr, code } = await new Promise((resolve) => {
|
|
52496
|
+
const child = spawnChild(cmd[0], cmd.slice(1));
|
|
52497
|
+
let out = "", err2 = "";
|
|
52498
|
+
child.stdout?.on("data", (d) => {
|
|
52499
|
+
out += d.toString();
|
|
52500
|
+
});
|
|
52501
|
+
child.stderr?.on("data", (d) => {
|
|
52502
|
+
err2 += d.toString();
|
|
52503
|
+
});
|
|
52504
|
+
child.on("close", (exitCode) => resolve({ stdout: out, stderr: err2, code: exitCode ?? 1 }));
|
|
52505
|
+
});
|
|
52506
|
+
if (code === 0) {
|
|
52507
|
+
let output = stdout;
|
|
52508
|
+
const lines = output.split(`
|
|
52509
|
+
`).filter((l) => l.trim());
|
|
52510
|
+
if (lines.length > head) {
|
|
52511
|
+
output = lines.slice(0, head).join(`
|
|
52512
|
+
`) + `
|
|
52513
|
+
... (${lines.length - head} more matches truncated)`;
|
|
52514
|
+
}
|
|
52515
|
+
if ((cwd || searchPath) && output) {
|
|
52516
|
+
const searchBase = searchPath && fs11.existsSync(searchPath) && fs11.statSync(searchPath).isDirectory() ? searchPath : path8.dirname(searchPath);
|
|
52517
|
+
const bases = Array.from(new Set([cwd, searchBase].filter(Boolean)));
|
|
52518
|
+
const lines2 = output.split(`
|
|
52519
|
+
`).map((line) => {
|
|
52520
|
+
if (line.includes(":")) {
|
|
52521
|
+
const colonIdx = line.indexOf(":");
|
|
52522
|
+
let filePart = line.slice(0, colonIdx);
|
|
52523
|
+
const rest = line.slice(colonIdx);
|
|
52524
|
+
try {
|
|
52525
|
+
const candidates = [filePart, ...bases.map((b) => path8.relative(b, filePart))].filter((p11) => (p11.match(/\.\.\//g) || []).length <= 2);
|
|
52526
|
+
filePart = candidates.reduce((a, b) => a.length <= b.length ? a : b, filePart);
|
|
52527
|
+
} catch {}
|
|
52528
|
+
let fullLine = filePart + rest;
|
|
52529
|
+
if (fullLine.length > 300) {
|
|
52530
|
+
fullLine = fullLine.slice(0, 300) + "...";
|
|
52531
|
+
}
|
|
52532
|
+
return fullLine;
|
|
52533
|
+
}
|
|
52534
|
+
return line;
|
|
52535
|
+
});
|
|
52536
|
+
output = lines2.join(`
|
|
52537
|
+
`);
|
|
52538
|
+
}
|
|
52539
|
+
if (output.length > 1e5)
|
|
52540
|
+
output = output.slice(0, 1e5) + `
|
|
52541
|
+
... (output truncated)`;
|
|
52542
|
+
return { result: output };
|
|
52543
|
+
}
|
|
52544
|
+
if (code === 1)
|
|
52545
|
+
return { result: "No matches found." };
|
|
52546
|
+
throw new Error(`search error (exit ${code}): ${stderr}`);
|
|
52547
|
+
});
|
|
52408
52548
|
register("download_attachment", async (args, client) => {
|
|
52409
52549
|
if (!client)
|
|
52410
52550
|
throw new Error("Client instance required");
|
|
@@ -52419,13 +52559,13 @@ register("download_attachment", async (args, client) => {
|
|
|
52419
52559
|
throw new Error(`Backend responded with ${res.status}`);
|
|
52420
52560
|
const base = getPathOrDefault(rootPath);
|
|
52421
52561
|
let target = p10.replace(/^~/, process.env.HOME || "~");
|
|
52422
|
-
if (!
|
|
52423
|
-
target =
|
|
52424
|
-
target =
|
|
52425
|
-
|
|
52562
|
+
if (!path8.isAbsolute(target))
|
|
52563
|
+
target = path8.join(base, target);
|
|
52564
|
+
target = path8.resolve(target);
|
|
52565
|
+
fs11.mkdirSync(path8.dirname(target), { recursive: true });
|
|
52426
52566
|
try {
|
|
52427
52567
|
const data = Buffer.from(await res.arrayBuffer());
|
|
52428
|
-
|
|
52568
|
+
fs11.writeFileSync(target, data);
|
|
52429
52569
|
return { path: target, bytes: data.length };
|
|
52430
52570
|
} catch (e) {
|
|
52431
52571
|
throw new Error(`Download failed: ${e.message}`);
|
|
@@ -52453,13 +52593,13 @@ register("register_attachment", async (args, client) => {
|
|
|
52453
52593
|
const { filePath, userId = "test-user", isPublic = false, agentSettingsId = "", todoId = "", rootPath = "" } = args;
|
|
52454
52594
|
const base = getPathOrDefault(rootPath);
|
|
52455
52595
|
let target = filePath.replace(/^~/, process.env.HOME || "~");
|
|
52456
|
-
if (!
|
|
52457
|
-
target =
|
|
52458
|
-
target =
|
|
52459
|
-
if (!
|
|
52596
|
+
if (!path8.isAbsolute(target))
|
|
52597
|
+
target = path8.join(base, target);
|
|
52598
|
+
target = path8.resolve(target);
|
|
52599
|
+
if (!fs11.existsSync(target))
|
|
52460
52600
|
throw new Error(`File not found: ${target}`);
|
|
52461
52601
|
const form = new FormData;
|
|
52462
|
-
form.append("file", new Blob([
|
|
52602
|
+
form.append("file", new Blob([fs11.readFileSync(target)]), path8.basename(target));
|
|
52463
52603
|
if (userId)
|
|
52464
52604
|
form.append("userId", userId);
|
|
52465
52605
|
if (agentSettingsId)
|
|
@@ -52503,9 +52643,9 @@ async function handleBlockSave(payload, send) {
|
|
|
52503
52643
|
const { blockId, todoId, filepath, rootPath, fallbackRootPaths = [], content, requestId } = payload;
|
|
52504
52644
|
try {
|
|
52505
52645
|
const resolved = resolveFilePath(filepath, rootPath, fallbackRootPaths);
|
|
52506
|
-
const ext =
|
|
52646
|
+
const ext = path9.extname(resolved).toLowerCase();
|
|
52507
52647
|
if (ext === ".docx" || ext === ".xlsx") {
|
|
52508
|
-
if (!
|
|
52648
|
+
if (!fs12.existsSync(resolved)) {
|
|
52509
52649
|
throw new Error(`Cannot create new ${ext} file from XML — file must already exist: ${filepath}`);
|
|
52510
52650
|
}
|
|
52511
52651
|
if (ext === ".docx")
|
|
@@ -52513,10 +52653,10 @@ async function handleBlockSave(payload, send) {
|
|
|
52513
52653
|
else
|
|
52514
52654
|
saveXlsxContent(resolved, content);
|
|
52515
52655
|
} else {
|
|
52516
|
-
const dir =
|
|
52656
|
+
const dir = path9.dirname(resolved);
|
|
52517
52657
|
if (dir)
|
|
52518
|
-
|
|
52519
|
-
|
|
52658
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
52659
|
+
fs12.writeFileSync(resolved, content, "utf-8");
|
|
52520
52660
|
}
|
|
52521
52661
|
await send(msg.blockSaveResult(blockId, todoId, "SUCCESS", requestId));
|
|
52522
52662
|
} catch (e) {
|
|
@@ -52527,22 +52667,22 @@ async function handleGetFolders(payload, send) {
|
|
|
52527
52667
|
const { requestId, edgeId } = payload;
|
|
52528
52668
|
const rawPath = getPathOrDefault(payload.path);
|
|
52529
52669
|
try {
|
|
52530
|
-
const expandedPath =
|
|
52670
|
+
const expandedPath = path9.resolve(rawPath.replace(/^~/, process.env.HOME || "~"));
|
|
52531
52671
|
let targetPath;
|
|
52532
|
-
if (
|
|
52672
|
+
if (fs12.existsSync(expandedPath) && fs12.statSync(expandedPath).isDirectory()) {
|
|
52533
52673
|
targetPath = expandedPath;
|
|
52534
52674
|
} else {
|
|
52535
|
-
targetPath =
|
|
52675
|
+
targetPath = path9.dirname(expandedPath);
|
|
52536
52676
|
}
|
|
52537
|
-
if (!
|
|
52677
|
+
if (!fs12.existsSync(targetPath) || !fs12.statSync(targetPath).isDirectory()) {
|
|
52538
52678
|
throw new Error(`No existing ancestor for path: ${rawPath}`);
|
|
52539
52679
|
}
|
|
52540
52680
|
const folders = [];
|
|
52541
52681
|
const files = [];
|
|
52542
|
-
for (const item of
|
|
52543
|
-
const full =
|
|
52682
|
+
for (const item of fs12.readdirSync(targetPath)) {
|
|
52683
|
+
const full = path9.join(targetPath, item);
|
|
52544
52684
|
try {
|
|
52545
|
-
if (
|
|
52685
|
+
if (fs12.statSync(full).isDirectory())
|
|
52546
52686
|
folders.push(full);
|
|
52547
52687
|
else
|
|
52548
52688
|
files.push(full);
|
|
@@ -52576,10 +52716,10 @@ async function handleDeletePath(payload, send) {
|
|
|
52576
52716
|
async function handleWriteFile(payload, send, pendingBinaries) {
|
|
52577
52717
|
const { requestId, edgeId, path: dirPath, fileName, binaryId, dataBase64 } = payload;
|
|
52578
52718
|
try {
|
|
52579
|
-
const filePath =
|
|
52580
|
-
const dir =
|
|
52719
|
+
const filePath = path9.join(dirPath, fileName);
|
|
52720
|
+
const dir = path9.dirname(filePath);
|
|
52581
52721
|
if (dir)
|
|
52582
|
-
|
|
52722
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
52583
52723
|
let buffer;
|
|
52584
52724
|
if (binaryId && pendingBinaries?.has(binaryId)) {
|
|
52585
52725
|
buffer = pendingBinaries.get(binaryId);
|
|
@@ -52600,8 +52740,8 @@ async function handleCd(payload, send, edgeConfig, onConfigChange) {
|
|
|
52600
52740
|
const { requestId, edgeId } = payload;
|
|
52601
52741
|
const rawPath = getPathOrDefault(payload.path);
|
|
52602
52742
|
try {
|
|
52603
|
-
const resolved =
|
|
52604
|
-
if (!
|
|
52743
|
+
const resolved = path9.resolve(rawPath.replace(/^~/, process.env.HOME || "~"));
|
|
52744
|
+
if (!fs12.existsSync(resolved) || !fs12.statSync(resolved).isDirectory()) {
|
|
52605
52745
|
throw new Error(`Path does not exist or is not a directory: ${rawPath}`);
|
|
52606
52746
|
}
|
|
52607
52747
|
const normalized = resolved.replace(/\/+$/, "");
|
|
@@ -52656,12 +52796,12 @@ async function handleFunctionCall(payload, send, client) {
|
|
|
52656
52796
|
|
|
52657
52797
|
// src/edge.ts
|
|
52658
52798
|
function generateFingerprint() {
|
|
52659
|
-
const
|
|
52660
|
-
const
|
|
52799
|
+
const os10 = __require("os");
|
|
52800
|
+
const fs13 = __require("fs");
|
|
52661
52801
|
const identifiers = {};
|
|
52662
52802
|
if (process.platform === "linux") {
|
|
52663
52803
|
try {
|
|
52664
|
-
const mid =
|
|
52804
|
+
const mid = fs13.readFileSync("/etc/machine-id", "utf-8").trim();
|
|
52665
52805
|
if (mid)
|
|
52666
52806
|
identifiers.machine_id = mid;
|
|
52667
52807
|
} catch {}
|
|
@@ -52676,8 +52816,8 @@ function generateFingerprint() {
|
|
|
52676
52816
|
}
|
|
52677
52817
|
if (Object.keys(identifiers).length === 0) {
|
|
52678
52818
|
identifiers.platform = process.platform;
|
|
52679
|
-
identifiers.machine =
|
|
52680
|
-
identifiers.node =
|
|
52819
|
+
identifiers.machine = os10.machine?.() || os10.arch();
|
|
52820
|
+
identifiers.node = os10.hostname();
|
|
52681
52821
|
}
|
|
52682
52822
|
const keys = Object.keys(identifiers).sort();
|
|
52683
52823
|
const json = "{" + keys.map((k) => JSON.stringify(k) + ": " + JSON.stringify(identifiers[k])).join(", ") + "}";
|
|
@@ -52863,8 +53003,8 @@ class TODOforAIEdge {
|
|
|
52863
53003
|
if (edgeId && edgeId !== this.edgeId)
|
|
52864
53004
|
return;
|
|
52865
53005
|
if (payload.workspacepaths) {
|
|
52866
|
-
const
|
|
52867
|
-
payload.workspacepaths = payload.workspacepaths.filter((p10) => !FORBIDDEN_PATHS2.has(
|
|
53006
|
+
const path10 = __require("path");
|
|
53007
|
+
payload.workspacepaths = payload.workspacepaths.filter((p10) => !FORBIDDEN_PATHS2.has(path10.normalize(p10).replace(/\/+$/, "")));
|
|
52868
53008
|
}
|
|
52869
53009
|
Object.assign(this.edgeConfig, payload);
|
|
52870
53010
|
if (this.addWorkspacePath) {
|
|
@@ -53146,18 +53286,18 @@ class ServerError extends Error {
|
|
|
53146
53286
|
}
|
|
53147
53287
|
|
|
53148
53288
|
// node_modules/@todoforai/update-notifier/src/index.ts
|
|
53149
|
-
import
|
|
53150
|
-
import
|
|
53151
|
-
import
|
|
53289
|
+
import fs13 from "node:fs";
|
|
53290
|
+
import path10 from "node:path";
|
|
53291
|
+
import os10 from "node:os";
|
|
53152
53292
|
function isLinkedInstall() {
|
|
53153
53293
|
try {
|
|
53154
|
-
return !
|
|
53294
|
+
return !fs13.realpathSync(process.argv[1] || "").includes(`${path10.sep}node_modules${path10.sep}`);
|
|
53155
53295
|
} catch {
|
|
53156
53296
|
return false;
|
|
53157
53297
|
}
|
|
53158
53298
|
}
|
|
53159
53299
|
var TTL_MS = 24 * 60 * 60 * 1000;
|
|
53160
|
-
var CACHE_DIR =
|
|
53300
|
+
var CACHE_DIR = path10.join(os10.homedir(), ".config", "todoforai");
|
|
53161
53301
|
function cmpVer(a, b) {
|
|
53162
53302
|
const pa2 = a.split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
53163
53303
|
const pb2 = b.split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
@@ -53171,10 +53311,10 @@ function cmpVer(a, b) {
|
|
|
53171
53311
|
function checkForUpdates(pkg) {
|
|
53172
53312
|
if (!process.stderr.isTTY || process.env.CI || process.env.NO_UPDATE_NOTIFIER || isLinkedInstall())
|
|
53173
53313
|
return;
|
|
53174
|
-
const cacheFile =
|
|
53314
|
+
const cacheFile = path10.join(CACHE_DIR, `notifier-${encodeURIComponent(pkg.name)}.json`);
|
|
53175
53315
|
let cache = {};
|
|
53176
53316
|
try {
|
|
53177
|
-
cache = JSON.parse(
|
|
53317
|
+
cache = JSON.parse(fs13.readFileSync(cacheFile, "utf8"));
|
|
53178
53318
|
} catch {}
|
|
53179
53319
|
if (cache.latest && cmpVer(cache.latest, pkg.version) > 0) {
|
|
53180
53320
|
process.stderr.write(`
|
|
@@ -53185,42 +53325,42 @@ function checkForUpdates(pkg) {
|
|
|
53185
53325
|
}
|
|
53186
53326
|
if (Date.now() - (cache.ts ?? 0) > TTL_MS) {
|
|
53187
53327
|
try {
|
|
53188
|
-
|
|
53189
|
-
|
|
53328
|
+
fs13.mkdirSync(CACHE_DIR, { recursive: true });
|
|
53329
|
+
fs13.writeFileSync(cacheFile, JSON.stringify({ ...cache, ts: Date.now() }));
|
|
53190
53330
|
} catch {}
|
|
53191
53331
|
fetch(`https://registry.npmjs.org/${pkg.name}/latest`, { signal: AbortSignal.timeout(3000) }).then((r) => r.ok ? r.json() : null).then((j) => {
|
|
53192
53332
|
if (!j?.version)
|
|
53193
53333
|
return;
|
|
53194
|
-
|
|
53334
|
+
fs13.writeFileSync(cacheFile, JSON.stringify({ ts: Date.now(), latest: j.version }));
|
|
53195
53335
|
}).catch(() => {});
|
|
53196
53336
|
}
|
|
53197
53337
|
}
|
|
53198
53338
|
|
|
53199
53339
|
// src/index.ts
|
|
53200
53340
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
53201
|
-
import
|
|
53202
|
-
import
|
|
53203
|
-
import
|
|
53341
|
+
import fs14 from "fs";
|
|
53342
|
+
import path11 from "path";
|
|
53343
|
+
import os11 from "os";
|
|
53204
53344
|
import crypto3 from "crypto";
|
|
53205
53345
|
function readOwnPackage() {
|
|
53206
53346
|
try {
|
|
53207
|
-
const pkgPath =
|
|
53208
|
-
return JSON.parse(
|
|
53347
|
+
const pkgPath = path11.resolve(fileURLToPath2(import.meta.url), "../../package.json");
|
|
53348
|
+
return JSON.parse(fs14.readFileSync(pkgPath, "utf-8"));
|
|
53209
53349
|
} catch {
|
|
53210
53350
|
return null;
|
|
53211
53351
|
}
|
|
53212
53352
|
}
|
|
53213
53353
|
function lockPath(apiUrl, userId) {
|
|
53214
|
-
const dir =
|
|
53215
|
-
|
|
53354
|
+
const dir = path11.join(os11.homedir(), ".todoforai");
|
|
53355
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
53216
53356
|
const hash = crypto3.createHash("sha256").update(`${apiUrl}
|
|
53217
53357
|
${userId}`).digest("hex").slice(0, 12);
|
|
53218
|
-
return
|
|
53358
|
+
return path11.join(dir, `edge-${hash}.lock`);
|
|
53219
53359
|
}
|
|
53220
53360
|
function isEdgeProcess(pid) {
|
|
53221
53361
|
try {
|
|
53222
53362
|
process.kill(pid, 0);
|
|
53223
|
-
const cmdline =
|
|
53363
|
+
const cmdline = fs14.readFileSync(`/proc/${pid}/cmdline`, "utf-8");
|
|
53224
53364
|
return cmdline.includes("index.ts") || cmdline.includes("todoforai-edge");
|
|
53225
53365
|
} catch {
|
|
53226
53366
|
return false;
|
|
@@ -53228,7 +53368,7 @@ function isEdgeProcess(pid) {
|
|
|
53228
53368
|
}
|
|
53229
53369
|
function killExistingEdge(lp2) {
|
|
53230
53370
|
try {
|
|
53231
|
-
const pid = parseInt(
|
|
53371
|
+
const pid = parseInt(fs14.readFileSync(lp2, "utf-8").trim(), 10);
|
|
53232
53372
|
if (!isNaN(pid) && isEdgeProcess(pid)) {
|
|
53233
53373
|
console.log(`\x1B[33mKilling existing edge process (pid ${pid})...\x1B[0m`);
|
|
53234
53374
|
process.kill(pid, "SIGTERM");
|
|
@@ -53251,7 +53391,7 @@ function killExistingEdge(lp2) {
|
|
|
53251
53391
|
}
|
|
53252
53392
|
function acquireLock(lp2, kill = false) {
|
|
53253
53393
|
try {
|
|
53254
|
-
const pid = parseInt(
|
|
53394
|
+
const pid = parseInt(fs14.readFileSync(lp2, "utf-8").trim(), 10);
|
|
53255
53395
|
if (!isNaN(pid) && isEdgeProcess(pid)) {
|
|
53256
53396
|
if (kill) {
|
|
53257
53397
|
killExistingEdge(lp2);
|
|
@@ -53259,13 +53399,13 @@ function acquireLock(lp2, kill = false) {
|
|
|
53259
53399
|
return false;
|
|
53260
53400
|
}
|
|
53261
53401
|
} catch {}
|
|
53262
|
-
|
|
53402
|
+
fs14.writeFileSync(lp2, String(process.pid));
|
|
53263
53403
|
return true;
|
|
53264
53404
|
}
|
|
53265
53405
|
function releaseLock(lp2) {
|
|
53266
53406
|
try {
|
|
53267
|
-
if (
|
|
53268
|
-
|
|
53407
|
+
if (fs14.readFileSync(lp2, "utf-8").trim() === String(process.pid))
|
|
53408
|
+
fs14.unlinkSync(lp2);
|
|
53269
53409
|
} catch {}
|
|
53270
53410
|
}
|
|
53271
53411
|
var MIN_BUN_VERSION = "1.3.14";
|