@todoforai/cli 0.1.66 → 0.1.68
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/README.md +2 -0
- package/dist/todoforai-cli.js +431 -115
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,6 +39,8 @@ echo "content" | tfa-cli # pipe from stdin
|
|
|
39
39
|
tfa-cli --path /my/project "Fix bug" # explicit workspace
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
Ctrl+C (or closing the terminal) only exits the CLI — the todo keeps running; `tfa-cli --resume <id>` reattaches. Exception: `--isolated` runs are stopped, since they can't outlive their bridge.
|
|
43
|
+
|
|
42
44
|
### Start from a registry template
|
|
43
45
|
|
|
44
46
|
```bash
|
package/dist/todoforai-cli.js
CHANGED
|
@@ -42969,6 +42969,9 @@ class ApiClient {
|
|
|
42969
42969
|
unshareDevice(id, email) {
|
|
42970
42970
|
return this.request("POST", `/api/v1/devices/${id}/unshare`, { id, email });
|
|
42971
42971
|
}
|
|
42972
|
+
leaveDevice(id) {
|
|
42973
|
+
return this.request("POST", `/api/v1/devices/${id}/leave`, { id });
|
|
42974
|
+
}
|
|
42972
42975
|
setDeviceWallpaperMode(id, mode) {
|
|
42973
42976
|
return this.request("POST", `/api/v1/devices/${id}/wallpaper-mode`, { id, mode });
|
|
42974
42977
|
}
|
|
@@ -43008,6 +43011,15 @@ class ApiClient {
|
|
|
43008
43011
|
listProjectGroups(projectId) {
|
|
43009
43012
|
return this.request("GET", `/api/v1/projects/${projectId}/groups`);
|
|
43010
43013
|
}
|
|
43014
|
+
getProjectGroupIntake(projectId, groupTag) {
|
|
43015
|
+
return this.request("GET", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(groupTag)}/intake`);
|
|
43016
|
+
}
|
|
43017
|
+
updateProjectGroupIntake(projectId, groupTag, fields) {
|
|
43018
|
+
return this.request("PUT", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(groupTag)}/intake`, { projectId, groupTag, ...fields });
|
|
43019
|
+
}
|
|
43020
|
+
regenerateProjectGroupIntake(projectId, groupTag) {
|
|
43021
|
+
return this.request("POST", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(groupTag)}/intake/regenerate`, { projectId, groupTag });
|
|
43022
|
+
}
|
|
43011
43023
|
upsertProjectGroup(projectId, slug, fields) {
|
|
43012
43024
|
return this.request("PUT", `/api/v1/projects/${projectId}/groups/${encodeURIComponent(slug)}`, { projectId, slug, ...fields });
|
|
43013
43025
|
}
|
|
@@ -43609,6 +43621,7 @@ var TodoStatus;
|
|
|
43609
43621
|
TodoStatus2["PAUSED"] = "PAUSED";
|
|
43610
43622
|
TodoStatus2["POSTPONED"] = "POSTPONED";
|
|
43611
43623
|
TodoStatus2["REVIEW_REQUESTED"] = "REVIEW_REQUESTED";
|
|
43624
|
+
TodoStatus2["WAITING_AGENT"] = "WAITING_AGENT";
|
|
43612
43625
|
TodoStatus2["RUNNING"] = "RUNNING";
|
|
43613
43626
|
TodoStatus2["WAITING_FOR_USER"] = "WAITING_FOR_USER";
|
|
43614
43627
|
TodoStatus2["COMPACTING"] = "COMPACTING";
|
|
@@ -43623,6 +43636,11 @@ var TodoStatus;
|
|
|
43623
43636
|
TodoStatus2["ARCHIVED"] = "ARCHIVED";
|
|
43624
43637
|
TodoStatus2["DELETED"] = "DELETED";
|
|
43625
43638
|
})(TodoStatus ||= {});
|
|
43639
|
+
var CLOSED_STATUSES = [
|
|
43640
|
+
"DONE" /* DONE */,
|
|
43641
|
+
"ARCHIVED" /* ARCHIVED */,
|
|
43642
|
+
"DELETED" /* DELETED */
|
|
43643
|
+
];
|
|
43626
43644
|
// ../packages/shared-fbe/src/constants.ts
|
|
43627
43645
|
var APPEAL_EMAIL = "support@todofor.ai";
|
|
43628
43646
|
var ACCOUNT_SUSPENDED_MESSAGE = `Account suspended for suspected abuse. If you think this is a mistake, email ${APPEAL_EMAIL}.`;
|
|
@@ -43639,22 +43657,28 @@ var PROVIDER_BY_AUTHOR = {
|
|
|
43639
43657
|
"x-ai": "xai",
|
|
43640
43658
|
"z-ai": "z.ai",
|
|
43641
43659
|
google: "google-ai-studio",
|
|
43642
|
-
moonshotai: "opencode_go"
|
|
43660
|
+
moonshotai: "opencode_go",
|
|
43661
|
+
meta: "meta"
|
|
43643
43662
|
};
|
|
43644
43663
|
var MODEL_PROVIDER_OVERRIDES = {
|
|
43645
43664
|
"openai/gpt-5.5-pro": "openrouter"
|
|
43646
43665
|
};
|
|
43666
|
+
var MODEL_ENDPOINT_OVERRIDES = {
|
|
43667
|
+
"google/gemini-4-argon": { "google-ai-studio": "vertex" }
|
|
43668
|
+
};
|
|
43647
43669
|
function resolveDispatchProvider(modelId, endpointProvider) {
|
|
43648
|
-
|
|
43670
|
+
const id = modelId.toLowerCase();
|
|
43671
|
+
return MODEL_PROVIDER_OVERRIDES[id] ?? MODEL_ENDPOINT_OVERRIDES[id]?.[endpointProvider.toLowerCase()] ?? endpointProvider;
|
|
43649
43672
|
}
|
|
43650
43673
|
var SUBSCRIPTION_PROVIDERS = new Set(["opencode_go"]);
|
|
43651
43674
|
function applyProviderOverride(qualified) {
|
|
43652
43675
|
const colon = qualified.indexOf(":");
|
|
43676
|
+
const provider = qualified.slice(0, colon);
|
|
43653
43677
|
const rest = qualified.slice(colon + 1);
|
|
43654
43678
|
const suffix = rest.match(/\([^)]+\)$/)?.[0] ?? "";
|
|
43655
43679
|
const bare = (suffix ? rest.slice(0, -suffix.length) : rest).toLowerCase();
|
|
43656
|
-
const
|
|
43657
|
-
return
|
|
43680
|
+
const resolved = resolveDispatchProvider(bare, provider);
|
|
43681
|
+
return resolved === provider ? qualified : `${resolved}:${bare}${suffix}`;
|
|
43658
43682
|
}
|
|
43659
43683
|
function isQualifiedModel(model) {
|
|
43660
43684
|
const id = model.trim();
|
|
@@ -43677,9 +43701,9 @@ function qualifyModel(model) {
|
|
|
43677
43701
|
const override = MODEL_PROVIDER_OVERRIDES[lower];
|
|
43678
43702
|
if (override)
|
|
43679
43703
|
return { model: `${override}:${lower}${suffix}` };
|
|
43680
|
-
const
|
|
43681
|
-
if (
|
|
43682
|
-
return { model: `${
|
|
43704
|
+
const author = PROVIDER_BY_AUTHOR[lower.split("/")[0]];
|
|
43705
|
+
if (author && lower.includes("/"))
|
|
43706
|
+
return { model: `${resolveDispatchProvider(lower, author)}:${lower}${suffix}` };
|
|
43683
43707
|
return { model: id };
|
|
43684
43708
|
}
|
|
43685
43709
|
function qualifiedModelIds(listing, filter) {
|
|
@@ -43738,6 +43762,7 @@ var mimetypes_default = {
|
|
|
43738
43762
|
js: { mime: "text/javascript", type: "text" },
|
|
43739
43763
|
ts: { mime: "text/typescript", type: "text" },
|
|
43740
43764
|
html: { mime: "text/html", type: "text" },
|
|
43765
|
+
htm: { mime: "text/html", type: "text" },
|
|
43741
43766
|
css: { mime: "text/css", type: "text" },
|
|
43742
43767
|
xml: { mime: "text/xml", type: "text" },
|
|
43743
43768
|
py: { mime: "text/x-python", type: "text" },
|
|
@@ -43776,7 +43801,7 @@ var mimetypes_default = {
|
|
|
43776
43801
|
docx: { mime: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", type: "document", tags: ["docx"] },
|
|
43777
43802
|
ppt: { mime: "application/vnd.ms-powerpoint", type: "document" },
|
|
43778
43803
|
pptx: { mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation", type: "document", tags: ["pptx"] },
|
|
43779
|
-
xls: { mime: "application/vnd.ms-excel", type: "spreadsheet" },
|
|
43804
|
+
xls: { mime: "application/vnd.ms-excel", type: "spreadsheet", tags: ["xls"] },
|
|
43780
43805
|
xlsx: { mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", type: "spreadsheet", tags: ["xlsx"] },
|
|
43781
43806
|
zip: { mime: "application/zip", type: "archive" },
|
|
43782
43807
|
rar: { mime: "application/vnd.rar", type: "archive" },
|
|
@@ -44169,6 +44194,8 @@ var USER_HEADINGS = new Set([
|
|
|
44169
44194
|
"steps",
|
|
44170
44195
|
"data needed"
|
|
44171
44196
|
]);
|
|
44197
|
+
// ../packages/shared-fbe/src/bridgeProtocol.ts
|
|
44198
|
+
var ADB_DATA_CHUNK = 40 * 1024;
|
|
44172
44199
|
// ../packages/shared-fbe/src/bashPatterns.ts
|
|
44173
44200
|
function splitShellCommands(input) {
|
|
44174
44201
|
const parts = [];
|
|
@@ -44410,7 +44437,6 @@ function getBlockServerId(block) {
|
|
|
44410
44437
|
const prefix = getBlockServerPrefix(block);
|
|
44411
44438
|
return prefix ? prefix.slice(0, -1) : "*";
|
|
44412
44439
|
}
|
|
44413
|
-
|
|
44414
44440
|
// ../packages/shared-fbe/src/permissionUtils.ts
|
|
44415
44441
|
function parsePattern(pattern) {
|
|
44416
44442
|
const colonIndex = pattern.indexOf(":");
|
|
@@ -44640,7 +44666,7 @@ Exactly ONE \`api_recommend_template\` call per proposal: it carries both what t
|
|
|
44640
44666
|
};
|
|
44641
44667
|
var JARVIS_AGENT = {
|
|
44642
44668
|
id: "00000000-0000-0000-0000-202601013152",
|
|
44643
|
-
name: "
|
|
44669
|
+
name: "Tod",
|
|
44644
44670
|
ownerId: "SYSTEM",
|
|
44645
44671
|
systemMessage: "",
|
|
44646
44672
|
model: "",
|
|
@@ -44967,6 +44993,7 @@ var tool_catalog_default = {
|
|
|
44967
44993
|
label: "GitLab",
|
|
44968
44994
|
summary: "Work with your GitLab repos, merge requests and pipelines.",
|
|
44969
44995
|
statusCmd: "glab auth status 2>&1 | grep -o 'Logged in to [^ ]* [a-z]* [^ ]*' | paste -sd ' ' - | grep -m1 .",
|
|
44996
|
+
logoutCmd: "glab auth logout --hostname gitlab.com 2>&1 </dev/null",
|
|
44970
44997
|
loginCmd: "glab auth login --device",
|
|
44971
44998
|
credentialPaths: ["~/.config/glab-cli/config.yml"],
|
|
44972
44999
|
description: "GitLab equivalent of `gh`.",
|
|
@@ -45128,6 +45155,7 @@ var tool_catalog_default = {
|
|
|
45128
45155
|
credentialPaths: ["~/.config/shopify-cli-kit-nodejs/config.json"],
|
|
45129
45156
|
versionCmd: "shopify version 2>/dev/null | tail -n 1",
|
|
45130
45157
|
statusCmd: `python3 -c "import json,os,sys;p=os.path.expanduser('~/.config/shopify-cli-kit-nodejs/config.json');sys.exit(0 if json.load(open(p)).get('session') else 1)" 2>/dev/null && echo authenticated`,
|
|
45158
|
+
logoutCmd: "shopify auth logout",
|
|
45131
45159
|
deviceCodePattern: "verification code: ([A-Z0-9-]+)",
|
|
45132
45160
|
oauthUrlPattern: "(https://accounts\\.shopify\\.com/activate-with-code\\S*)",
|
|
45133
45161
|
canStreamLogin: true,
|
|
@@ -45173,7 +45201,7 @@ var tool_catalog_default = {
|
|
|
45173
45201
|
installer: "npm",
|
|
45174
45202
|
label: "TODOforAI",
|
|
45175
45203
|
summary: "Create and follow up on your TODOs, and show results in the chat.",
|
|
45176
|
-
description: 'Manage the user\'s OWN TODOforAI tasks (not code `// TODO` comments). Delegate/spin off work: `tfa-cli "prompt"` creates a real TODO with its own agent and chat (`-n` wait for completion, `--no-watch` fire-and-forget, `--project/--agent/--model`, `--resume <id>`, `addmessage <id> "text"`). Inspect: `list [--status open]`, `--inspect <id>`. Edit any todo without opening it: `status <id> DONE|READY|ARCHIVED|POSTPONED|CANCELLED|DELETED` (close, reopen, shelve, trash — `todo restore <id>` undoes a delete) and `todo set <id|-> title=… group=… star=true schedule=<unix-ms> agent=… brand=…` (own todo fields). Both take ONE id, so a loop over `list --json | jq -r \'.items[].id\'` is how you close, retitle, refile or archive MANY todos at once; `project set|settings|groups …` (name, settings, todo groups), `agent create|update <agent> model=… sysmsg=…` (permissions/MCP/device wiring and delete are refused from an agent shell). Surface results into chat: `show <file|-> [todo-id]` (renders by mimetype, private to the todo\'s readers; `--card <name>` via project renderer; `--alias <name>` updates in place, `--alias <todoId>:<name>` updates another todo\'s artifact; `show share <ref>` makes it public and prints the link, `show unshare <ref>` makes it private again), `open <url>` embeds a live preview. Pick by where the output lives: read once → plain reply; interactive, this chat only → `html_snippet`; kept/revised later → `show --alias`, then `show share` only if someone outside needs a link; format the user asked for (pptx, docx…) → `show` the file. Notifications: `inbox [needs-you|messages|activity] [--unread]` reads the user\'s bell feed (what finished/failed/needs them while away), `inbox seen [tab]` marks it read; `notify "Title" "message" [--href /t/<id>]` leaves a note in the user\'s Messages tab + phone push (shown as from you); `--to <email>` reaches a member of the current project instead (`project members` lists them, 20/day) — nobody else; same text is delivered once, max 10/day to the user, use for things they must see later (not chat replies). This is the ONLY way to display a local file (image, pdf, html) in the chat: markdown `` with a local/relative path does NOT resolve to workspace files or attachments and 404s in the browser. `--template` for registry workflows.',
|
|
45204
|
+
description: 'Manage the user\'s OWN TODOforAI tasks (not code `// TODO` comments). Delegate/spin off work: `tfa-cli "prompt"` creates a real TODO with its own agent and chat (`-n` wait for completion, `--no-watch` fire-and-forget, `--project/--agent/--model`, `--resume <id>`, `addmessage <id> "text"`). Inspect: `list [--status open]`, `--inspect <id>`. Edit any todo without opening it: `status <id> DONE|READY|ARCHIVED|POSTPONED|CANCELLED|DELETED` (close, reopen, shelve, trash — `todo restore <id>` undoes a delete) and `todo set <id|-> title=… group=… star=true schedule=<unix-ms> agent=… brand=…` (own todo fields). Both take ONE id, so a loop over `list --json | jq -r \'.items[].id\'` is how you close, retitle, refile or archive MANY todos at once; `project set|settings|groups …` (name, settings, todo groups), `agent create|update <agent> model=… sysmsg=…` (permissions/MCP/device wiring and delete are refused from an agent shell). Surface results into chat: `show <file|-> [todo-id]` (renders by mimetype, private to the todo\'s readers; `--card <name>` via project renderer; `--alias <name>` updates in place, `--alias <todoId>:<name>` updates another todo\'s artifact; `show share <ref>` makes it public and prints the link, `show unshare <ref>` makes it private again), `open <url>` embeds a live preview. Show viewer chrome: `--title` appears in the header; a kebab menu holds file actions, versions and sharing when available. Desktop floating previews add minimize (park in a tab), maximize/restore and close (X); inline blocks have no window controls. For authored HTML, do not duplicate the header/controls or add a rounded, bordered root card: the floating viewer owns the rounded frame. Keep content edge-to-edge; internal layout padding is fine. Pick by where the output lives: read once → plain reply; interactive, this chat only → `html_snippet`; kept/revised later → `show --alias`, then `show share` only if someone outside needs a link; format the user asked for (pptx, docx…) → `show` the file. Notifications: `inbox [needs-you|messages|activity] [--unread]` reads the user\'s bell feed (what finished/failed/needs them while away), `inbox seen [tab]` marks it read; `notify "Title" "message" [--href /t/<id>]` leaves a note in the user\'s Messages tab + phone push (shown as from you); `--to <email>` reaches a member of the current project instead (`project members` lists them, 20/day) — nobody else; same text is delivered once, max 10/day to the user, use for things they must see later (not chat replies). This is the ONLY way to display a local file (image, pdf, html) in the chat: markdown `` with a local/relative path does NOT resolve to workspace files or attachments and 404s in the browser. `--template` for registry workflows. Project files: `memory sync|path [--project <id>]` requires a project and checks out only projects/<id>/{memory,context} and projects/<id>/me/{memory,context}; legacy global files are read-only recovery. Voice commands use `--scope mine|project` (default mine, private within this project; project is shared).',
|
|
45177
45205
|
installCmd: "npm install -g @todoforai/cli",
|
|
45178
45206
|
versionCmd: "tfa-cli --version 2>/dev/null | sed -n 1p",
|
|
45179
45207
|
preinstallCloud: true,
|
|
@@ -45321,6 +45349,7 @@ var tool_catalog_default = {
|
|
|
45321
45349
|
summary: "Manage your Microsoft Azure resources.",
|
|
45322
45350
|
binName: "az",
|
|
45323
45351
|
statusCmd: "az account show --query user.name -o tsv 2>/dev/null | tr -d '\\r' | grep -m1 .",
|
|
45352
|
+
logoutCmd: "az logout 2>&1; az account clear 2>&1",
|
|
45324
45353
|
loginCmd: "az login --use-device-code",
|
|
45325
45354
|
oauthUrlPattern: "(https://login\\.microsoft\\.com/device\\S*)",
|
|
45326
45355
|
deviceCodePattern: "enter the code ([A-Z0-9]+)",
|
|
@@ -45406,7 +45435,7 @@ var tool_catalog_default = {
|
|
|
45406
45435
|
preinstallCloud: true,
|
|
45407
45436
|
label: "Browser",
|
|
45408
45437
|
summary: "Browse websites automatically: click, type, read and screenshot.",
|
|
45409
|
-
description: "Browser automation over CDP (`open`/`snapshot`/`click`/`type`/`fill`/`read`/`screenshot`/`eval`). `connect '<url>'` to a listed live browser (prefer the user's real Chrome), verify with `snapshot`; if empty, `close` (own session only) and reconnect once. NEVER `close --all`. Shared browser: `tab new` for your own tab, `tab <id>` before each command. No URL listed: cloud → browser-manager-cli; PC with display → Chrome `--remote-debugging-port=9222`, attach `--cdp 9222`. Never `sleep`: `open` returns after load, but `click`/`fill` fail at once if the element isn't there yet — `wait '<sel>'`, `wait --text 'Done'`, `wait --url '**/dash'`, `wait --load networkidle` or `wait '#spinner' --state hidden` first.",
|
|
45438
|
+
description: "Browser automation over CDP (`open`/`snapshot`/`click`/`type`/`fill`/`read`/`screenshot`/`eval`). `connect '<url>'` to a listed live browser (prefer the user's real Chrome), verify with `snapshot`; if empty, `close` (own session only) and reconnect once. NEVER `close --all`. Shared browser: `tab new` for your own tab, `tab <id>` before each command. No URL listed: cloud → browser-manager-cli; PC with display → Chrome `--remote-debugging-port=9222`, attach `--cdp 9222`. Never `sleep`: `open` returns after load, but `click`/`fill` fail at once if the element isn't there yet — `wait '<sel>'`, `wait --text 'Done'`, `wait --url '**/dash'`, `wait --load networkidle` or `wait '#spinner' --state hidden` first. Extension browsers (WEB_EXTENSION) only: try sign-in with credentials from `tfa-vault`; request human help only for a step you cannot complete yourself (MFA/CAPTCHA/consent). Use Node/Bun WebSocket on the listed CDP URL; `Target.getTargets` or `device_list` gives `tab-N`, whose N is the numeric tabId. On the SAME socket send `{id: 1, method: 'TodoforAI.requestHumanHelp', params: {tabId: N, message: 'what to do'}}` (message 1–500 chars). This focuses and pauses the tab, sets its yellow `?` badge, and shows needs-you in the extension popup. Tell the user to click Resume control and reply here, then end your turn; Resume does not wake the agent. Afterwards reconnect and snapshot fresh. `TodoforAI.humanHelpStatus` with `{tabId}` remains available while paused. If bootstrap fails on a restricted target, direct CDP `Target.createTarget` with an ordinary URL opens your tab on the SAME socket; pass numeric N from returned `tab-N` as `tabId`.",
|
|
45410
45439
|
versionCmd: "agent-browser --version 2>/dev/null | sed -n 1p",
|
|
45411
45440
|
preinstallJarvis: true
|
|
45412
45441
|
},
|
|
@@ -45548,6 +45577,17 @@ var tool_catalog_default = {
|
|
|
45548
45577
|
preinstallCloud: true,
|
|
45549
45578
|
preinstallJarvis: true
|
|
45550
45579
|
},
|
|
45580
|
+
"tfa-find": {
|
|
45581
|
+
internal: true,
|
|
45582
|
+
category: "todoforai",
|
|
45583
|
+
pkg: "@todoforai/tfa-find",
|
|
45584
|
+
installer: "npm",
|
|
45585
|
+
label: "File Search",
|
|
45586
|
+
summary: "Find documents, photos and more by describing what you need.",
|
|
45587
|
+
description: "Search files in folders you choose on your machine. Register folders, build a search index, then find relevant documents, images, audio and video with natural-language queries. CLI: tfa-find.",
|
|
45588
|
+
versionCmd: "tfa-find --version 2>/dev/null | sed -n 1p",
|
|
45589
|
+
installCmd: "npm install -g @todoforai/tfa-find"
|
|
45590
|
+
},
|
|
45551
45591
|
"tfa-memory": {
|
|
45552
45592
|
category: "todoforai",
|
|
45553
45593
|
pkg: "@todoforai/tfa-memory",
|
|
@@ -45935,6 +45975,7 @@ var tool_catalog_default = {
|
|
|
45935
45975
|
loginCmd: "mailchimp-api auth --api-key <api_key>",
|
|
45936
45976
|
oauthBroker: true,
|
|
45937
45977
|
statusCmd: `mailchimp-api whoami 2>/dev/null | sed -n 's/.*"account_name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p' | grep -m1 .`,
|
|
45978
|
+
logoutCmd: "rm -f ~/.config/mailchimp-api/credentials.json",
|
|
45938
45979
|
credentialPaths: [
|
|
45939
45980
|
"~/.config/mailchimp-api/credentials.json"
|
|
45940
45981
|
],
|
|
@@ -45952,6 +45993,7 @@ var tool_catalog_default = {
|
|
|
45952
45993
|
statusCmd: `facebook-pages-api whoami 2>/dev/null | sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p' | grep -m10 .`,
|
|
45953
45994
|
loginCmd: "facebook-pages-api auth --page-id <id> --page-token <token>",
|
|
45954
45995
|
oauthBroker: true,
|
|
45996
|
+
logoutCmd: "rm -f ~/.config/facebook-pages-api/credentials.json",
|
|
45955
45997
|
credentialPaths: ["~/.config/facebook-pages-api/credentials.json"],
|
|
45956
45998
|
description: "Facebook Pages CLI (Meta Graph API). `auth --page-id <id> --page-token <token>` (or env FB_PAGE_ID/FB_PAGE_TOKEN). `feed`, `comments`, `insights`, `conversations`, `media`, and `request <GET|POST|DELETE> <path> [--data <json>]` = any Graph edge with the page token attached (use instead of curl). Writes are live: `feed create --published false` for tests; `comments reply` posts publicly — get human approval. Follower totals: `whoami --fields followers_count,fan_count` (many classic insights metrics are retired). Instagram (linked professional account, same page token): `ig whoami|media|get|stories|tags|limit`, `ig publish --image_url U | --video_url U [--media_type STORIES] | --carousel U1,U2 [--caption …]` (public URLs; container → wait → publish; `--create-only` = dry run; 100 posts/24h), `ig comments list|replies|create|reply|hide|delete|toggle`, `ig insights [media_id]` (needs instagram_manage_insights). `--help` for subcommands.",
|
|
45957
45999
|
versionCmd: "facebook-pages-api --version 2>/dev/null | sed -n 1p"
|
|
@@ -45993,6 +46035,7 @@ var tool_catalog_default = {
|
|
|
45993
46035
|
voiceChannel: "Outlook",
|
|
45994
46036
|
summary: "Read and write your Outlook mail.",
|
|
45995
46037
|
statusCmd: `outlook-api whoami 2>/dev/null | sed -n 's/.*"userPrincipalName"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p' | grep -m10 .`,
|
|
46038
|
+
logoutCmd: "outlook-api logout",
|
|
45996
46039
|
loginCmd: "outlook-api auth",
|
|
45997
46040
|
credentialPaths: ["~/.config/outlook-api/config.json"],
|
|
45998
46041
|
description: "Outlook/Hotmail/M365 mail over Microsoft Graph (on-prem Exchange → ews-api). `auth` = device-code flow; `--tenant consumers` (default, personal) | `organizations`. list/search/read/attachments are read-only; `draft`/`reply` stay in Drafts unless --send; `send <draftId>`. `get <path>` = raw Graph GET. JSON out.",
|
|
@@ -46025,6 +46068,7 @@ var tool_catalog_default = {
|
|
|
46025
46068
|
label: "Microsoft 365",
|
|
46026
46069
|
summary: "Work with Microsoft 365 mail, Teams, SharePoint and users.",
|
|
46027
46070
|
statusCmd: `m365 status --output text 2>/dev/null | tr -d '"' | grep -vi 'logged out' | grep -m1 .`,
|
|
46071
|
+
logoutCmd: "m365 logout",
|
|
46028
46072
|
loginCmd: "m365 login --authType deviceCode --appId 14d82eec-204b-4c2f-b7e8-296a70dab67e",
|
|
46029
46073
|
deviceCodePattern: "enter the code ([A-Z0-9]{8,}) to authenticate",
|
|
46030
46074
|
oauthUrlPattern: "open the page (https://\\S+) and enter",
|
|
@@ -49944,21 +49988,67 @@ var tool_catalog_default = {
|
|
|
49944
49988
|
},
|
|
49945
49989
|
statusCmd: `zoo user view --format json 2>/dev/null | sed -n 's/.*"email": *"\\([^"]*\\)".*/\\1/p' | grep -m1 .`,
|
|
49946
49990
|
loginCmd: "zoo auth login --web",
|
|
49991
|
+
logoutCmd: "printf 'y\\n' | zoo --host api.zoo.dev auth logout",
|
|
49947
49992
|
loginNeedsPty: true,
|
|
49948
49993
|
deviceCodePattern: "user code: ([A-Z0-9]{4}-[A-Z0-9]{4})",
|
|
49949
|
-
oauthUrlPattern: "(https://api\\.zoo\\.dev/oauth2/device/verify
|
|
49994
|
+
oauthUrlPattern: "(https://api\\.zoo\\.dev/oauth2/device/verify\\?[^\\s']+)",
|
|
49950
49995
|
canStreamLogin: true,
|
|
49951
49996
|
credentialPaths: [
|
|
49952
49997
|
"~/.config/zoo/hosts.toml"
|
|
49953
49998
|
],
|
|
49954
49999
|
description: 'Official Zoo (KittyCAD) CLI. Text-to-CAD: `zoo ml text-to-cad export --output-format step "a 40mm gear"`; convert: `zoo file convert in.obj out/ --output-format step`; mass/volume: `zoo file mass|volume <file>`; KCL: `zoo kcl export|snapshot`; raw: `zoo api /user`. Token alternative: `echo <token> | zoo auth login --with-token`.',
|
|
49955
50000
|
versionCmd: "zoo --version 2>/dev/null | sed -n 1p"
|
|
50001
|
+
},
|
|
50002
|
+
"python-docx": {
|
|
50003
|
+
category: "documents",
|
|
50004
|
+
pkg: "python-docx",
|
|
50005
|
+
pipModules: ["docx"],
|
|
50006
|
+
installer: "pip",
|
|
50007
|
+
label: "Word documents",
|
|
50008
|
+
summary: "Create, read and edit Word documents.",
|
|
50009
|
+
versionCmd: `{ "$HOME/.todoforai/tools/venv/bin/python" -c 'import docx; print(docx.__version__)' || python -c 'import docx; print(docx.__version__)' || python3 -c 'import docx; print(docx.__version__)' || py -c 'import docx; print(docx.__version__)'; } 2>/dev/null`,
|
|
50010
|
+
description: "Python python-docx library. Use python3 to create, save, reopen and edit Office files; no Microsoft account required."
|
|
50011
|
+
},
|
|
50012
|
+
openpyxl: {
|
|
50013
|
+
category: "documents",
|
|
50014
|
+
pkg: "openpyxl",
|
|
50015
|
+
pipModules: ["openpyxl"],
|
|
50016
|
+
installer: "pip",
|
|
50017
|
+
label: "Excel workbooks",
|
|
50018
|
+
summary: "Create, read and edit Excel workbooks.",
|
|
50019
|
+
versionCmd: `{ "$HOME/.todoforai/tools/venv/bin/python" -c 'import openpyxl; print(openpyxl.__version__)' || python -c 'import openpyxl; print(openpyxl.__version__)' || python3 -c 'import openpyxl; print(openpyxl.__version__)' || py -c 'import openpyxl; print(openpyxl.__version__)'; } 2>/dev/null`,
|
|
50020
|
+
description: "Python openpyxl library. Use python3 to create, save, reopen and edit Office files; no Microsoft account required."
|
|
50021
|
+
},
|
|
50022
|
+
"python-pptx": {
|
|
50023
|
+
category: "documents",
|
|
50024
|
+
pkg: "python-pptx",
|
|
50025
|
+
pipModules: ["pptx"],
|
|
50026
|
+
installer: "pip",
|
|
50027
|
+
label: "PowerPoint decks",
|
|
50028
|
+
summary: "Create, read and edit PowerPoint decks.",
|
|
50029
|
+
versionCmd: `{ "$HOME/.todoforai/tools/venv/bin/python" -c 'import pptx; print(pptx.__version__)' || python -c 'import pptx; print(pptx.__version__)' || python3 -c 'import pptx; print(pptx.__version__)' || py -c 'import pptx; print(pptx.__version__)'; } 2>/dev/null`,
|
|
50030
|
+
description: "Python python-pptx library. Use python3 to create, save, reopen and edit Office files; no Microsoft account required."
|
|
50031
|
+
},
|
|
50032
|
+
"modal-h3": {
|
|
50033
|
+
category: "creative",
|
|
50034
|
+
pkg: "@todoforai/modal-h3",
|
|
50035
|
+
installer: "npm",
|
|
50036
|
+
label: "MiniMax-H3 on Modal",
|
|
50037
|
+
summary: "Generate videos with sound using MiniMax-H3 on your own Modal GPUs.",
|
|
50038
|
+
versionCmd: "modal-h3 --version 2>/dev/null | sed -n 1p",
|
|
50039
|
+
statusCmd: `modal-h3 whoami 2>/dev/null | grep '"authenticated": true' >/dev/null && echo authenticated`,
|
|
50040
|
+
loginCmd: "printf '%s' <proxy_token> | modal-h3 auth --token-stdin --fl2va-url <fl2va_url> --ref2va-url <ref2va_url>",
|
|
50041
|
+
credentialPaths: ["~/.config/modal-h3/credentials.json"],
|
|
50042
|
+
description: "MiniMax-H3 text/image/reference-to-video+audio on a self-deployed Modal app (SGLang, 4x H200, scale-to-zero). `modal-h3 generate -p '…' [--first @a.png] [--last …] [--ref-image|--ref-video|--ref-audio <url|@file>] [--duration 4-15] [--ratio 16:9|auto] [--quality high] -o out.mp4` (task inferred, retries 503 during cold start, polls, downloads), `warmup`, `status|download|delete <id>`, `list`, `api <METHOD> <path>`. Deploy once with `modal run modal/serve.py::download` + `modal deploy modal/serve.py`. Costs ~$18/h of 4x H200 while a container is up (cold start minutes, ~75 s per 5 s 768p clip warm); every request wakes a container, `whoami` is offline. Jobs live in container memory. 2K needs `minimax-api regenerate`."
|
|
49956
50043
|
}
|
|
49957
50044
|
};
|
|
49958
50045
|
|
|
49959
50046
|
// ../packages/shared-fbe/src/toolCatalog.ts
|
|
49960
50047
|
var TOOL_CATALOG = tool_catalog_default;
|
|
49961
50048
|
// ../packages/shared-fbe/src/toolInstallCommand.ts
|
|
50049
|
+
var NPM_HOME = '"${TFA_NPM_HOME:-$HOME}"';
|
|
50050
|
+
var NPM_TOOLS = NPM_HOME.replace('}"', '}/.todoforai/tools"');
|
|
50051
|
+
var NPM_LOCAL = NPM_HOME.replace('}"', '}/.local"');
|
|
49962
50052
|
var KEY_OF = new Map(Object.entries(TOOL_CATALOG).map(([k, e]) => [e, k]));
|
|
49963
50053
|
var NODE_MIN_MAJOR = 24;
|
|
49964
50054
|
var NODE_OK = `command -v npm >/dev/null 2>&1 && node -e 'process.exit(+process.versions.node.split(".")[0]>=${NODE_MIN_MAJOR}?0:1)' >/dev/null 2>&1`;
|
|
@@ -50200,7 +50290,7 @@ var package_default = {
|
|
|
50200
50290
|
type: "git",
|
|
50201
50291
|
url: "git+https://github.com/todoforai/cli.git"
|
|
50202
50292
|
},
|
|
50203
|
-
version: "0.1.
|
|
50293
|
+
version: "0.1.68",
|
|
50204
50294
|
type: "module",
|
|
50205
50295
|
bin: {
|
|
50206
50296
|
"todoforai-cli": "bin/todoforai-cli.js",
|
|
@@ -50330,6 +50420,13 @@ Usage:
|
|
|
50330
50420
|
|
|
50331
50421
|
todo-id defaults to $TODOFORAI_TODO_ID (agent shell) or the last todo this CLI touched.
|
|
50332
50422
|
|
|
50423
|
+
Viewer chrome:
|
|
50424
|
+
--title appears in the header; a kebab menu holds file actions, versions and sharing when available.
|
|
50425
|
+
Desktop floating previews add minimize (park in a tab), maximize/restore and close (X).
|
|
50426
|
+
Inline blocks have no window controls. Do not draw these controls or a duplicate title bar in authored HTML.
|
|
50427
|
+
The floating viewer owns the rounded frame. Keep the HTML root flat, without a border, radius, shadow or outer margin;
|
|
50428
|
+
internal content padding and spacing are fine. Existing files and websites keep their own styling.
|
|
50429
|
+
|
|
50333
50430
|
When to use what:
|
|
50334
50431
|
read once, move on → plain chat reply
|
|
50335
50432
|
interactive, this chat only → html_snippet (can exec/connect/chat back)
|
|
@@ -51404,15 +51501,20 @@ function blockToAnthropic(block, mode, format) {
|
|
|
51404
51501
|
const msg = block.error_message || block.content || "(unknown error)";
|
|
51405
51502
|
return [{ type: "text", text: `[error] ${msg}` }];
|
|
51406
51503
|
}
|
|
51504
|
+
const name = t === "named_tool" ? block.tool_name === "run_shell" ? "bash" : block.tool_name : t;
|
|
51407
51505
|
const inputFields = [];
|
|
51408
51506
|
for (const [k, v] of Object.entries(block)) {
|
|
51409
|
-
if (BLOCK_META_KEYS.has(k))
|
|
51507
|
+
if (BLOCK_META_KEYS.has(k) || k === "args" || t === "named_tool" && k === "tool_name")
|
|
51410
51508
|
continue;
|
|
51411
51509
|
if (v === undefined || v === null || v === "")
|
|
51412
51510
|
continue;
|
|
51413
51511
|
inputFields.push([k, v]);
|
|
51414
51512
|
}
|
|
51415
|
-
const
|
|
51513
|
+
for (const [k, v] of Object.entries(block.args ?? {})) {
|
|
51514
|
+
if (!inputFields.some(([key]) => key === k))
|
|
51515
|
+
inputFields.push([k, v]);
|
|
51516
|
+
}
|
|
51517
|
+
const use = format === "anthropic" ? { type: "tool_use", id: block.id, name, input: Object.fromEntries(inputFields) } : { type: "tool_use", content: `<${name}${inputFields.map(([k, v]) => ` ${k}=${xmlAttr(v)}`).join("")}/>` };
|
|
51416
51518
|
if (format === "compact" && mode !== "default")
|
|
51417
51519
|
use.id = block.id;
|
|
51418
51520
|
const items = [use];
|
|
@@ -51428,8 +51530,9 @@ function blockToAnthropic(block, mode, format) {
|
|
|
51428
51530
|
source.name = att.originalName;
|
|
51429
51531
|
parts.push({ type: isImage ? "image" : "document", source });
|
|
51430
51532
|
}
|
|
51431
|
-
|
|
51432
|
-
|
|
51533
|
+
const text = r.content || r.meta?.text;
|
|
51534
|
+
if (typeof text === "string" && text)
|
|
51535
|
+
parts.push({ type: "text", text });
|
|
51433
51536
|
return parts;
|
|
51434
51537
|
});
|
|
51435
51538
|
const result = { type: "tool_result", tool_use_id: block.id };
|
|
@@ -51446,8 +51549,9 @@ function blockToAnthropic(block, mode, format) {
|
|
|
51446
51549
|
const nameAttr = att.originalName && !isAutoName ? ` name=${xmlAttr(att.originalName)}` : "";
|
|
51447
51550
|
parts.push(`<attachment uri=${xmlAttr(att.uri)}${nameAttr} size=${att.fileSize}/>`);
|
|
51448
51551
|
}
|
|
51449
|
-
|
|
51450
|
-
|
|
51552
|
+
const text = r.content || r.meta?.text;
|
|
51553
|
+
if (typeof text === "string" && text)
|
|
51554
|
+
parts.push(text);
|
|
51451
51555
|
}
|
|
51452
51556
|
const result = { type: "tool_result", content: parts.join(`
|
|
51453
51557
|
`) };
|
|
@@ -52673,6 +52777,7 @@ function renderDiff(originalContent, modifiedContent, filePath) {
|
|
|
52673
52777
|
|
|
52674
52778
|
// src/shutdown.ts
|
|
52675
52779
|
var activeRun = null;
|
|
52780
|
+
var stopOnExit = false;
|
|
52676
52781
|
var cleanups = [];
|
|
52677
52782
|
var exiting = null;
|
|
52678
52783
|
var cancelling = null;
|
|
@@ -52680,6 +52785,9 @@ var CANCEL_TIMEOUT_MS = 5000;
|
|
|
52680
52785
|
function setActiveRun(run) {
|
|
52681
52786
|
activeRun = run;
|
|
52682
52787
|
}
|
|
52788
|
+
var stopsRunOnExit = () => stopOnExit;
|
|
52789
|
+
var detachNotice = (todoId) => `Todo keeps running — reattach: tfa-cli --resume ${todoId}
|
|
52790
|
+
`;
|
|
52683
52791
|
function withTimeout(p, ms, fallback) {
|
|
52684
52792
|
let timer;
|
|
52685
52793
|
const timeout = new Promise((r) => {
|
|
@@ -52691,7 +52799,7 @@ function cancelActiveRun(timeoutMs = CANCEL_TIMEOUT_MS) {
|
|
|
52691
52799
|
if (cancelling)
|
|
52692
52800
|
return cancelling;
|
|
52693
52801
|
const pending = activeRun;
|
|
52694
|
-
if (!pending)
|
|
52802
|
+
if (!pending || !stopOnExit)
|
|
52695
52803
|
return Promise.resolve({ ok: false, todoId: null });
|
|
52696
52804
|
const res = { ok: false };
|
|
52697
52805
|
const attempt = (async () => {
|
|
@@ -52717,7 +52825,11 @@ function shutdown(code, reason) {
|
|
|
52717
52825
|
process.stderr.write(`
|
|
52718
52826
|
${reason}
|
|
52719
52827
|
`);
|
|
52720
|
-
if (activeRun) {
|
|
52828
|
+
if (activeRun && !stopOnExit) {
|
|
52829
|
+
const run = await withTimeout(Promise.resolve(activeRun), CANCEL_TIMEOUT_MS, null);
|
|
52830
|
+
if (run)
|
|
52831
|
+
process.stderr.write(detachNotice(run.todoId));
|
|
52832
|
+
} else if (activeRun) {
|
|
52721
52833
|
const { ok, todoId } = await cancelActiveRun();
|
|
52722
52834
|
if (ok)
|
|
52723
52835
|
process.stderr.write(`Stopped todo ${todoId}
|
|
@@ -52829,20 +52941,6 @@ async function watchTodo(ws, todoId, projectId, opts = {}) {
|
|
|
52829
52941
|
diffStoreByWs.set(ws, diffStore);
|
|
52830
52942
|
const diffRendered = new Set;
|
|
52831
52943
|
let approveAll = !!opts.autoApprove;
|
|
52832
|
-
let interruptCount = 0;
|
|
52833
|
-
const origHandler = process.listeners("SIGINT").slice();
|
|
52834
|
-
process.removeAllListeners("SIGINT");
|
|
52835
|
-
process.on("SIGINT", () => {
|
|
52836
|
-
interruptCount++;
|
|
52837
|
-
if (opts.exitOnInterrupt || interruptCount >= 2)
|
|
52838
|
-
return void shutdown(130, "Cancelled by user (Ctrl+C)");
|
|
52839
|
-
process.stderr.write(`
|
|
52840
|
-
${YELLOW}Interrupting... (Ctrl+C again to force exit)${RESET}
|
|
52841
|
-
`);
|
|
52842
|
-
if (opts.interruptOnCancel !== false) {
|
|
52843
|
-
ws.sendInterrupt(projectId, todoId);
|
|
52844
|
-
}
|
|
52845
|
-
});
|
|
52846
52944
|
const pendingBlocks = [];
|
|
52847
52945
|
let approvalPromptActive = false;
|
|
52848
52946
|
let activeApprovalBlocks = [];
|
|
@@ -53065,14 +53163,10 @@ ${YELLOW}Connection lost — reconnecting...${RESET}
|
|
|
53065
53163
|
await cancelActiveRun();
|
|
53066
53164
|
process.exitCode = 1;
|
|
53067
53165
|
if (!opts.suppressCancelNotice) {
|
|
53068
|
-
process.stderr.write(`${YELLOW}Interrupted${RESET}
|
|
53069
|
-
`);
|
|
53166
|
+
process.stderr.write(stopsRunOnExit() ? `${YELLOW}Interrupted${RESET}
|
|
53167
|
+
` : detachNotice(todoId));
|
|
53070
53168
|
}
|
|
53071
53169
|
return false;
|
|
53072
|
-
} finally {
|
|
53073
|
-
process.removeAllListeners("SIGINT");
|
|
53074
|
-
for (const fn of origHandler)
|
|
53075
|
-
process.on("SIGINT", fn);
|
|
53076
53170
|
}
|
|
53077
53171
|
}
|
|
53078
53172
|
|
|
@@ -53641,6 +53735,8 @@ Usage:
|
|
|
53641
53735
|
tfa-cli project settings <field=value>… Project settings (PUT /settings/projects/{id})
|
|
53642
53736
|
tfa-cli project groups List todo groups
|
|
53643
53737
|
tfa-cli project groups set <slug> <field=value>… Create/update a group: name description pinned archived order
|
|
53738
|
+
tfa-cli project groups intake <slug> [on|off|regenerate] [field=value]…
|
|
53739
|
+
Public intake: autoStart maxSubmissions postIpLimit placeholder agentSettingsId
|
|
53644
53740
|
tfa-cli project groups reorder <slug>… Set wall order
|
|
53645
53741
|
tfa-cli project surfaces List the board's card surfaces (live refs shown as cards)
|
|
53646
53742
|
tfa-cli project surfaces set <id> ref=<ref> [title=…] [group=<slug>] [order=N]
|
|
@@ -53805,6 +53901,25 @@ async function projectCommand(api, positionals, args, projectId) {
|
|
|
53805
53901
|
`);
|
|
53806
53902
|
return;
|
|
53807
53903
|
}
|
|
53904
|
+
if (verb === "intake") {
|
|
53905
|
+
const [slug, ...options] = gargs;
|
|
53906
|
+
if (!slug)
|
|
53907
|
+
fail("Usage: tfa-cli project groups intake <slug> [on|off|regenerate] [field=value]…");
|
|
53908
|
+
const action = ["on", "off", "regenerate"].includes(options[0]) ? options.shift() : undefined;
|
|
53909
|
+
if (options.length && action === "regenerate")
|
|
53910
|
+
fail("regenerate does not accept settings");
|
|
53911
|
+
const fields = parseAssignments2(options);
|
|
53912
|
+
const intake = action === "regenerate" ? await api.regenerateProjectGroupIntake(projectId, slug) : action || options.length ? await api.updateProjectGroupIntake(projectId, slug, { ...fields, ...action && { enabled: action === "on" } }) : await api.getProjectGroupIntake(projectId, slug);
|
|
53913
|
+
if (args.json) {
|
|
53914
|
+
console.log(JSON.stringify(intake, null, 2));
|
|
53915
|
+
return;
|
|
53916
|
+
}
|
|
53917
|
+
process.stderr.write(`${slug}: public ${intake.enabled ? "on" : "off"}, ${intake.submissionCount}/${intake.maxSubmissions}, autoStart=${intake.autoStart}
|
|
53918
|
+
`);
|
|
53919
|
+
if (intake.url)
|
|
53920
|
+
console.log(intake.url);
|
|
53921
|
+
return;
|
|
53922
|
+
}
|
|
53808
53923
|
if (verb === "reorder") {
|
|
53809
53924
|
if (!gargs.length)
|
|
53810
53925
|
fail("Usage: tfa-cli project groups reorder <slug>…");
|
|
@@ -53880,11 +53995,10 @@ Usage:
|
|
|
53880
53995
|
tfa-cli brand voice correct "<what is off>" Tell the learner what it got wrong; profile is updated
|
|
53881
53996
|
in one pass and the correction is kept for every re-learn
|
|
53882
53997
|
|
|
53883
|
-
Every voice verb takes --scope
|
|
53884
|
-
|
|
53885
|
-
|
|
53886
|
-
|
|
53887
|
-
Agents read all three; on the same voice name the narrower wins: mine > project > personal.
|
|
53998
|
+
Every voice verb takes --scope mine|project — which voice to teach:
|
|
53999
|
+
mine yours, only in this project (default)
|
|
54000
|
+
project the project's, shared with its members (needs write access)
|
|
54001
|
+
On the same voice name the narrower wins: mine > project.
|
|
53888
54002
|
|
|
53889
54003
|
|
|
53890
54004
|
Questions the UI asks (keys for 'answers'):
|
|
@@ -53984,9 +54098,9 @@ async function voiceDeviceCommand(rest, args) {
|
|
|
53984
54098
|
}
|
|
53985
54099
|
async function voiceCommand(api, projectId, rest, args) {
|
|
53986
54100
|
const [verb, ...vargs] = rest;
|
|
53987
|
-
const scope = args.scope;
|
|
53988
|
-
if (
|
|
53989
|
-
fail(`--scope must be
|
|
54101
|
+
const scope = args.scope ?? "mine";
|
|
54102
|
+
if (!["project", "mine"].includes(scope))
|
|
54103
|
+
fail(`--scope must be mine or project, got '${scope}'`);
|
|
53990
54104
|
if (verb === "collect" && !vargs[0])
|
|
53991
54105
|
fail("Usage: tfa-cli brand voice collect <channel> [--url U | --account <page-id> | --max N] [--dry-run]");
|
|
53992
54106
|
if (verb === "answers") {
|
|
@@ -54113,6 +54227,7 @@ Usage:
|
|
|
54113
54227
|
tfa-cli device access <device> who you shared the device with
|
|
54114
54228
|
tfa-cli device share <device> <email> let a teammate use it (their runs act as THEM)
|
|
54115
54229
|
tfa-cli device unshare <device> <email>
|
|
54230
|
+
tfa-cli device leave <device> remove your own access to a shared device
|
|
54116
54231
|
|
|
54117
54232
|
<device> is an id, name or hostname (unique partial works).
|
|
54118
54233
|
Devices shared with you are marked "shared"; only the owner can rename/share them.
|
|
@@ -54149,6 +54264,12 @@ async function deviceCommand(api, positionals, args) {
|
|
|
54149
54264
|
fail("Usage: tfa-cli device rename <device> <name>");
|
|
54150
54265
|
await api.renameDevice(found.id, value);
|
|
54151
54266
|
process.stderr.write(`${GREEN}✅ ${found.name} → ${value}${RESET}
|
|
54267
|
+
`);
|
|
54268
|
+
return;
|
|
54269
|
+
}
|
|
54270
|
+
if (sub === "leave") {
|
|
54271
|
+
await api.leaveDevice(found.id);
|
|
54272
|
+
process.stderr.write(`${GREEN}✅ Removed ${found.name} from your devices${RESET}
|
|
54152
54273
|
`);
|
|
54153
54274
|
return;
|
|
54154
54275
|
}
|
|
@@ -54198,15 +54319,7 @@ var STATUS_COLOR = {
|
|
|
54198
54319
|
POSTPONED: CYAN,
|
|
54199
54320
|
PAUSED: CYAN
|
|
54200
54321
|
};
|
|
54201
|
-
var CLOSED = new Set(
|
|
54202
|
-
"DONE",
|
|
54203
|
-
"READY_CHECKED",
|
|
54204
|
-
"CANCELLED",
|
|
54205
|
-
"CANCELLED_CHECKED",
|
|
54206
|
-
"ERROR_CHECKED",
|
|
54207
|
-
"ARCHIVED",
|
|
54208
|
-
"DELETED"
|
|
54209
|
-
]);
|
|
54322
|
+
var CLOSED = new Set(CLOSED_STATUSES);
|
|
54210
54323
|
var VALID_STATUS = new Set(Object.values(TodoStatus));
|
|
54211
54324
|
var isOpen = (s) => !CLOSED.has(s);
|
|
54212
54325
|
function printListTodosHelp() {
|
|
@@ -54218,11 +54331,16 @@ Usage:
|
|
|
54218
54331
|
|
|
54219
54332
|
Flags:
|
|
54220
54333
|
-n, --limit <n> Max rows to show (default: 30)
|
|
54221
|
-
--cursor <n> Fetch todos older than this cursor (
|
|
54222
|
-
--
|
|
54334
|
+
--cursor <n> Fetch todos older than this cursor (sort field, epoch ms)
|
|
54335
|
+
--since <t> Only todos with sort field >= t (inclusive)
|
|
54336
|
+
--until <t> Only todos with sort field < t (exclusive)
|
|
54337
|
+
t = epoch ms | YYYY-MM-DD (local midnight) | ISO datetime | 12h / 3d ago
|
|
54338
|
+
--sort <key> activity (lastActivityAt, default) | created (createdAt)
|
|
54339
|
+
--contributor <u> Only todos a member opened/wrote in (id or email; comma = any)
|
|
54340
|
+
--page-size <n> Backend page size per request (default: min(limit, 400), max: 400)
|
|
54223
54341
|
--search <text> Search content/status on the backend
|
|
54224
54342
|
-s, --status <S[,S2]> Filter by status (comma-separated, union).
|
|
54225
|
-
-A, --all Include
|
|
54343
|
+
-A, --all Include closed (DONE, ARCHIVED, DELETED)
|
|
54226
54344
|
--project <id> Project ID (default: current default project)
|
|
54227
54345
|
--json Output { items, nextCursor } as JSON
|
|
54228
54346
|
-h, --help Show this help
|
|
@@ -54233,6 +54351,8 @@ Examples:
|
|
|
54233
54351
|
todoforai-cli list --cursor 1719234567890 # next page
|
|
54234
54352
|
todoforai-cli list --all # include DONE
|
|
54235
54353
|
todoforai-cli list -s RUNNING,REVIEW_REQUESTED
|
|
54354
|
+
todoforai-cli list -A --since 2026-10-11 --until 2026-10-12 # touched that day
|
|
54355
|
+
todoforai-cli list -A --sort created --since 7d --contributor a@b.com
|
|
54236
54356
|
todoforai-cli list --search bug --json | jq '.items[].id'
|
|
54237
54357
|
`);
|
|
54238
54358
|
}
|
|
@@ -54258,6 +54378,25 @@ function cursorValue(value) {
|
|
|
54258
54378
|
}
|
|
54259
54379
|
return n;
|
|
54260
54380
|
}
|
|
54381
|
+
function parseTime(value, name, now = Date.now()) {
|
|
54382
|
+
if (value === undefined)
|
|
54383
|
+
return;
|
|
54384
|
+
const s = String(value).trim();
|
|
54385
|
+
const rel = s.match(/^(\d+)([mhd])$/);
|
|
54386
|
+
let n;
|
|
54387
|
+
if (/^\d+$/.test(s))
|
|
54388
|
+
n = Number(s);
|
|
54389
|
+
else if (rel)
|
|
54390
|
+
n = now - Number(rel[1]) * { m: 60000, h: 3600000, d: 86400000 }[rel[2]];
|
|
54391
|
+
else
|
|
54392
|
+
n = Date.parse(/^\d{4}-\d{2}-\d{2}$/.test(s) ? `${s}T00:00` : s);
|
|
54393
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
54394
|
+
process.stderr.write(`${RED}Error: ${name} must be epoch ms, YYYY-MM-DD, ISO datetime or e.g. 12h / 3d${RESET}
|
|
54395
|
+
`);
|
|
54396
|
+
process.exit(2);
|
|
54397
|
+
}
|
|
54398
|
+
return n;
|
|
54399
|
+
}
|
|
54261
54400
|
function toItemsPage(response) {
|
|
54262
54401
|
if (Array.isArray(response))
|
|
54263
54402
|
return { items: response };
|
|
@@ -54266,8 +54405,25 @@ function toItemsPage(response) {
|
|
|
54266
54405
|
nextCursor: typeof response?.nextCursor === "number" ? response.nextCursor : undefined
|
|
54267
54406
|
};
|
|
54268
54407
|
}
|
|
54269
|
-
function rowTime(t) {
|
|
54270
|
-
return Number(t.lastActivityAt ?? t.createdAt ?? 0);
|
|
54408
|
+
function rowTime(t, sort = "activity") {
|
|
54409
|
+
return Number((sort === "created" ? t.createdAt : t.lastActivityAt ?? t.createdAt) ?? 0);
|
|
54410
|
+
}
|
|
54411
|
+
async function resolveContributors(api, projectId, raw) {
|
|
54412
|
+
const wanted = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
54413
|
+
if (!wanted.some((w) => w.includes("@")))
|
|
54414
|
+
return wanted;
|
|
54415
|
+
const members = await api.getProjectMembers(projectId);
|
|
54416
|
+
return wanted.map((w) => {
|
|
54417
|
+
if (!w.includes("@"))
|
|
54418
|
+
return w;
|
|
54419
|
+
const m = members.find((m2) => m2.email?.toLowerCase() === w.toLowerCase());
|
|
54420
|
+
if (!m) {
|
|
54421
|
+
process.stderr.write(`${RED}Error: no project member with email ${w}${RESET}
|
|
54422
|
+
`);
|
|
54423
|
+
process.exit(2);
|
|
54424
|
+
}
|
|
54425
|
+
return m.id;
|
|
54426
|
+
});
|
|
54271
54427
|
}
|
|
54272
54428
|
function printStatusError(unknown) {
|
|
54273
54429
|
process.stderr.write(`${RED}Error: unknown status ${unknown.map((s) => `'${s}'`).join(", ")}${RESET}
|
|
@@ -54290,6 +54446,10 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54290
54446
|
cursor: { type: "string" },
|
|
54291
54447
|
"page-size": { type: "string" },
|
|
54292
54448
|
search: { type: "string" },
|
|
54449
|
+
since: { type: "string" },
|
|
54450
|
+
until: { type: "string" },
|
|
54451
|
+
sort: { type: "string" },
|
|
54452
|
+
contributor: { type: "string" },
|
|
54293
54453
|
status: { type: "string", short: "s" },
|
|
54294
54454
|
all: { type: "boolean", short: "A", default: false },
|
|
54295
54455
|
project: { type: "string" },
|
|
@@ -54309,9 +54469,20 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54309
54469
|
process.exit(1);
|
|
54310
54470
|
}
|
|
54311
54471
|
const limit = positiveInt(values.limit, "--limit") ?? 30;
|
|
54312
|
-
const pageSize = Math.min(positiveInt(values["page-size"], "--page-size") ?? Math.min(Math.max(limit, 1),
|
|
54472
|
+
const pageSize = Math.min(positiveInt(values["page-size"], "--page-size") ?? Math.min(Math.max(limit, 1), 400), 400);
|
|
54473
|
+
const sort = values.sort === undefined ? "activity" : String(values.sort);
|
|
54474
|
+
if (sort !== "activity" && sort !== "created") {
|
|
54475
|
+
process.stderr.write(`${RED}Error: --sort must be activity or created${RESET}
|
|
54476
|
+
`);
|
|
54477
|
+
process.exit(2);
|
|
54478
|
+
}
|
|
54479
|
+
const since = parseTime(values.since, "--since");
|
|
54480
|
+
const until = parseTime(values.until, "--until");
|
|
54313
54481
|
let cursor = cursorValue(values.cursor);
|
|
54482
|
+
if (until !== undefined)
|
|
54483
|
+
cursor = cursor === undefined ? until : Math.min(cursor, until);
|
|
54314
54484
|
const search = values.search ? String(values.search) : undefined;
|
|
54485
|
+
const contributorIds = values.contributor ? await resolveContributors(api, projectId, String(values.contributor)) : undefined;
|
|
54315
54486
|
const requested = values.status ? String(values.status).split(",").map((s) => s.trim().toUpperCase()).filter(Boolean) : null;
|
|
54316
54487
|
const unknown = requested?.filter((s) => s !== "OPEN" && !VALID_STATUS.has(s)) ?? [];
|
|
54317
54488
|
if (unknown.length)
|
|
@@ -54323,9 +54494,24 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54323
54494
|
let backendNextCursor;
|
|
54324
54495
|
while (rows.length < limit) {
|
|
54325
54496
|
const requestLimit = Math.min(pageSize, limit - rows.length);
|
|
54326
|
-
const page = toItemsPage(await api.listTodos(projectId, {
|
|
54497
|
+
const page = toItemsPage(await api.listTodos(projectId, {
|
|
54498
|
+
limit: requestLimit,
|
|
54499
|
+
cursor,
|
|
54500
|
+
search,
|
|
54501
|
+
since,
|
|
54502
|
+
contributorIds,
|
|
54503
|
+
...!includeClosed && { excludeClosed: true },
|
|
54504
|
+
...sort !== "activity" && { sort }
|
|
54505
|
+
}));
|
|
54327
54506
|
backendNextCursor = page.nextCursor;
|
|
54507
|
+
let exhausted = false;
|
|
54328
54508
|
for (const t of page.items) {
|
|
54509
|
+
if (since !== undefined && rowTime(t) < since) {
|
|
54510
|
+
exhausted = true;
|
|
54511
|
+
break;
|
|
54512
|
+
}
|
|
54513
|
+
if (since !== undefined && rowTime(t, sort) < since)
|
|
54514
|
+
continue;
|
|
54329
54515
|
const status = String(t.status).toUpperCase();
|
|
54330
54516
|
if (!includeClosed && CLOSED.has(status))
|
|
54331
54517
|
continue;
|
|
@@ -54335,14 +54521,18 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54335
54521
|
if (rows.length >= limit)
|
|
54336
54522
|
break;
|
|
54337
54523
|
}
|
|
54524
|
+
if (exhausted) {
|
|
54525
|
+
backendNextCursor = undefined;
|
|
54526
|
+
break;
|
|
54527
|
+
}
|
|
54338
54528
|
if (!backendNextCursor || page.items.length === 0)
|
|
54339
54529
|
break;
|
|
54340
54530
|
cursor = backendNextCursor;
|
|
54341
54531
|
}
|
|
54342
|
-
rows.sort((a, b) => rowTime(b) - rowTime(a));
|
|
54532
|
+
rows.sort((a, b) => rowTime(b, sort) - rowTime(a, sort));
|
|
54343
54533
|
const visible = rows.slice(0, limit);
|
|
54344
54534
|
const hasBufferedRows = rows.length > visible.length;
|
|
54345
|
-
const nextCursor = visible.length && (backendNextCursor !== undefined || hasBufferedRows) ? rowTime(visible[visible.length - 1]) : undefined;
|
|
54535
|
+
const nextCursor = visible.length && (backendNextCursor !== undefined || hasBufferedRows) ? rowTime(visible[visible.length - 1], sort) : undefined;
|
|
54346
54536
|
if (values.json) {
|
|
54347
54537
|
process.stdout.write(JSON.stringify({ items: visible, nextCursor }, null, 2) + `
|
|
54348
54538
|
`);
|
|
@@ -54360,7 +54550,7 @@ async function listTodosCommand(api, defaultProjectId, argv) {
|
|
|
54360
54550
|
for (const t of visible) {
|
|
54361
54551
|
const status = String(t.status).toUpperCase();
|
|
54362
54552
|
const sc = STATUS_COLOR[status] || DIM;
|
|
54363
|
-
const ts = new Date(rowTime(t)).toISOString().slice(0, 16).replace("T", " ");
|
|
54553
|
+
const ts = new Date(rowTime(t, sort)).toISOString().slice(0, 16).replace("T", " ");
|
|
54364
54554
|
const title = String(t.content ?? "").split(`
|
|
54365
54555
|
`)[0].slice(0, 100);
|
|
54366
54556
|
process.stdout.write(`${sc}${status.padEnd(statusW)}${RESET} ${DIM}${ts}${RESET} ${t.id} ${title}
|
|
@@ -55248,7 +55438,6 @@ tfa-cli memory — memory and context folders as git checkouts
|
|
|
55248
55438
|
|
|
55249
55439
|
Usage:
|
|
55250
55440
|
tfa-cli memory sync [--project <id>] Clone, or pull, into:
|
|
55251
|
-
~/.todoforai/{memory,context} yours (todoforai:memory, todoforai:context)
|
|
55252
55441
|
~/.todoforai/projects/<id>/{memory,context} the project's (todoforai:projects/<id>/…)
|
|
55253
55442
|
~/.todoforai/projects/<id>/me/{memory,context} yours in it (todoforai:projects/<id>/me/…)
|
|
55254
55443
|
tfa-cli memory path [--project <id>] Print the checkout paths
|
|
@@ -55258,14 +55447,17 @@ use plain git: log, diff, show, revert, commit, then \`git push\` — the push u
|
|
|
55258
55447
|
folder. A push behind the server is refused: \`git pull\` first. Files over 5 MB live outside
|
|
55259
55448
|
history (upload them instead). Project folders: members with write access push; your own in
|
|
55260
55449
|
a project (me/) is private to you. The project
|
|
55261
|
-
defaults to $TODOFORAI_PROJECT_ID.
|
|
55450
|
+
defaults to $TODOFORAI_PROJECT_ID and is required. Legacy global folders are read-only
|
|
55451
|
+
recovery, not synced.
|
|
55262
55452
|
`);
|
|
55263
55453
|
}
|
|
55264
55454
|
var FOLDERS = ["memory", "context"];
|
|
55265
55455
|
function checkouts(projectId) {
|
|
55266
55456
|
const root = join5(homedir5(), ".todoforai");
|
|
55267
|
-
|
|
55268
|
-
|
|
55457
|
+
if (!projectId)
|
|
55458
|
+
throw new Error("Select a project with --project <id> or $TODOFORAI_PROJECT_ID.");
|
|
55459
|
+
const project = join5(root, "projects", projectId);
|
|
55460
|
+
const scopes = [[project, `projects/${projectId}/`], [join5(project, "me"), `projects/${projectId}/me/`]];
|
|
55269
55461
|
return scopes.flatMap(([dir, remote]) => FOLDERS.map((f) => [join5(dir, f), `${remote}${f}.git`]));
|
|
55270
55462
|
}
|
|
55271
55463
|
function git(args, cwd) {
|
|
@@ -56226,6 +56418,7 @@ __export(exports_util, {
|
|
|
56226
56418
|
toZod: () => toZod,
|
|
56227
56419
|
stringifyPrimitive: () => stringifyPrimitive,
|
|
56228
56420
|
slugify: () => slugify,
|
|
56421
|
+
sizeCaps: () => sizeCaps,
|
|
56229
56422
|
shallowClone: () => shallowClone,
|
|
56230
56423
|
safeExtend: () => safeExtend,
|
|
56231
56424
|
required: () => required,
|
|
@@ -56800,6 +56993,18 @@ function prefixIssues(path4, issues) {
|
|
|
56800
56993
|
function unwrapMessage(message) {
|
|
56801
56994
|
return typeof message === "string" ? message : message?.message;
|
|
56802
56995
|
}
|
|
56996
|
+
function sizeCaps(defChecks) {
|
|
56997
|
+
const caps = [];
|
|
56998
|
+
for (const ch of defChecks ?? []) {
|
|
56999
|
+
const d = ch._zod.def;
|
|
57000
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
57001
|
+
break;
|
|
57002
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
57003
|
+
if (max >= 0)
|
|
57004
|
+
caps.push([max, ch]);
|
|
57005
|
+
}
|
|
57006
|
+
return caps;
|
|
57007
|
+
}
|
|
56803
57008
|
function attachSchema(issues, start, inst) {
|
|
56804
57009
|
var _a;
|
|
56805
57010
|
for (let i = start;i < issues.length; i++) {
|
|
@@ -58219,7 +58424,7 @@ ${content.join(`
|
|
|
58219
58424
|
var version = {
|
|
58220
58425
|
major: 4,
|
|
58221
58426
|
minor: 6,
|
|
58222
|
-
patch:
|
|
58427
|
+
patch: 7
|
|
58223
58428
|
};
|
|
58224
58429
|
|
|
58225
58430
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -58968,6 +59173,31 @@ var $ZodDate = /* @__PURE__ */ $constructor("$ZodDate", (inst, def) => {
|
|
|
58968
59173
|
return payload;
|
|
58969
59174
|
};
|
|
58970
59175
|
});
|
|
59176
|
+
function capSize(inst, size) {
|
|
59177
|
+
const caps = sizeCaps(inst._zod.def.checks);
|
|
59178
|
+
if (!caps.length)
|
|
59179
|
+
return;
|
|
59180
|
+
const cap = Math.min(...caps.map((c3) => c3[0]));
|
|
59181
|
+
const parse2 = inst._zod.parse;
|
|
59182
|
+
inst._zod.parse = (payload, ctx) => {
|
|
59183
|
+
const n = size(payload.value);
|
|
59184
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
59185
|
+
for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
|
|
59186
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
59187
|
+
const start = payload.issues.length;
|
|
59188
|
+
ch._zod.check(payload);
|
|
59189
|
+
payload.issues[start].continue = false;
|
|
59190
|
+
attachSchema(payload.issues, start, inst);
|
|
59191
|
+
return payload;
|
|
59192
|
+
}
|
|
59193
|
+
}
|
|
59194
|
+
}
|
|
59195
|
+
return parse2(payload, ctx);
|
|
59196
|
+
};
|
|
59197
|
+
}
|
|
59198
|
+
var arraySize = (input) => Array.isArray(input) ? input.length : -1;
|
|
59199
|
+
var mapSize = (input) => input instanceof Map ? input.size : -1;
|
|
59200
|
+
var setSize = (input) => input instanceof Set ? input.size : -1;
|
|
58971
59201
|
function handleArrayResult(result, final, index) {
|
|
58972
59202
|
if (result.issues.length) {
|
|
58973
59203
|
final.issues.push(...prefixIssues(index, result.issues));
|
|
@@ -59011,6 +59241,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
|
|
|
59011
59241
|
}
|
|
59012
59242
|
return payload;
|
|
59013
59243
|
};
|
|
59244
|
+
capSize(inst, arraySize);
|
|
59014
59245
|
});
|
|
59015
59246
|
function handlePropertyResult(result, final, key, input, optin, optout) {
|
|
59016
59247
|
const isPresent = key in input;
|
|
@@ -59965,6 +60196,7 @@ var $ZodMap = /* @__PURE__ */ $constructor("$ZodMap", (inst, def) => {
|
|
|
59965
60196
|
return Promise.all(proms).then(() => payload);
|
|
59966
60197
|
return payload;
|
|
59967
60198
|
};
|
|
60199
|
+
capSize(inst, mapSize);
|
|
59968
60200
|
});
|
|
59969
60201
|
function handleMapResult(keyResult, valueResult, final, key, input, inst, ctx) {
|
|
59970
60202
|
if (keyResult.issues.length) {
|
|
@@ -60031,6 +60263,7 @@ var $ZodSet = /* @__PURE__ */ $constructor("$ZodSet", (inst, def) => {
|
|
|
60031
60263
|
return Promise.all(proms).then(() => payload);
|
|
60032
60264
|
return payload;
|
|
60033
60265
|
};
|
|
60266
|
+
capSize(inst, setSize);
|
|
60034
60267
|
});
|
|
60035
60268
|
function handleSetResult(result, final) {
|
|
60036
60269
|
if (result.issues.length) {
|
|
@@ -69480,9 +69713,15 @@ function generateNullableCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
|
69480
69713
|
doc.write(`}`);
|
|
69481
69714
|
return outputVar;
|
|
69482
69715
|
}
|
|
69716
|
+
function generateSizeGate(doc, schema, size) {
|
|
69717
|
+
for (const [max, check] of sizeCaps(schema._zod.def.checks)) {
|
|
69718
|
+
doc.write(`if (${size} > ${numericOperand(max, check._zod.def.check)}) return INVALID;`);
|
|
69719
|
+
}
|
|
69720
|
+
}
|
|
69483
69721
|
function generateArrayCheck(doc, ctx, schema, accessor, buildsValue = true) {
|
|
69484
69722
|
const def = schema._zod.def;
|
|
69485
69723
|
doc.write(`if (!Array.isArray(${accessor})) return INVALID;`);
|
|
69724
|
+
generateSizeGate(doc, schema, `${accessor}.length`);
|
|
69486
69725
|
const outputVar = buildsValue ? newVar(ctx) : null;
|
|
69487
69726
|
const iVar = newVar(ctx);
|
|
69488
69727
|
const elemVar = newVar(ctx);
|
|
@@ -69882,6 +70121,7 @@ function literalPropertyKey(ctx, key) {
|
|
|
69882
70121
|
function generateMapCheck(doc, ctx, schema, accessor) {
|
|
69883
70122
|
const def = schema._zod.def;
|
|
69884
70123
|
doc.write(`if (!(${accessor} instanceof Map)) return INVALID;`);
|
|
70124
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
69885
70125
|
const outputVar = newVar(ctx);
|
|
69886
70126
|
const kVar = newVar(ctx);
|
|
69887
70127
|
const valVar = newVar(ctx);
|
|
@@ -69898,6 +70138,7 @@ function generateMapCheck(doc, ctx, schema, accessor) {
|
|
|
69898
70138
|
function generateSetCheck(doc, ctx, schema, accessor) {
|
|
69899
70139
|
const def = schema._zod.def;
|
|
69900
70140
|
doc.write(`if (!(${accessor} instanceof Set)) return INVALID;`);
|
|
70141
|
+
generateSizeGate(doc, schema, `${accessor}.size`);
|
|
69901
70142
|
const outputVar = newVar(ctx);
|
|
69902
70143
|
const valVar = newVar(ctx);
|
|
69903
70144
|
doc.write(`const ${outputVar} = new Set();`);
|
|
@@ -75065,6 +75306,21 @@ function preserveCustomPayload(schema, key, knownTags) {
|
|
|
75065
75306
|
return output2;
|
|
75066
75307
|
});
|
|
75067
75308
|
}
|
|
75309
|
+
function defaultOnErrorOptionalStringTag(schema, key) {
|
|
75310
|
+
return exports_external.preprocess((value) => {
|
|
75311
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
75312
|
+
return value;
|
|
75313
|
+
}
|
|
75314
|
+
const record2 = value;
|
|
75315
|
+
const tag = record2[key];
|
|
75316
|
+
if (tag === undefined || tag === null || typeof tag === "string") {
|
|
75317
|
+
return value;
|
|
75318
|
+
}
|
|
75319
|
+
const normalized = { ...record2 };
|
|
75320
|
+
delete normalized[key];
|
|
75321
|
+
return normalized;
|
|
75322
|
+
}, schema);
|
|
75323
|
+
}
|
|
75068
75324
|
function vecSkipError(itemSchema) {
|
|
75069
75325
|
return exports_external.array(itemSchema.catch(skippedItem)).transform((items) => items.filter((item) => item !== skippedItem));
|
|
75070
75326
|
}
|
|
@@ -75083,16 +75339,12 @@ var zWriteTextFileRequest = object({
|
|
|
75083
75339
|
var zReadTextFileRequest = object({
|
|
75084
75340
|
sessionId: zSessionId,
|
|
75085
75341
|
path: string2(),
|
|
75086
|
-
line:
|
|
75342
|
+
line: int().gte(0).max(4294967295, {
|
|
75087
75343
|
error: "Invalid value: Expected uint32 to be <= 4294967295"
|
|
75088
|
-
}).nullish(),
|
|
75089
|
-
|
|
75090
|
-
}),
|
|
75091
|
-
limit: defaultOnError(int().gte(0).max(4294967295, {
|
|
75344
|
+
}).nullish(),
|
|
75345
|
+
limit: int().gte(0).max(4294967295, {
|
|
75092
75346
|
error: "Invalid value: Expected uint32 to be <= 4294967295"
|
|
75093
|
-
}).nullish(),
|
|
75094
|
-
return;
|
|
75095
|
-
}),
|
|
75347
|
+
}).nullish(),
|
|
75096
75348
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
75097
75349
|
return;
|
|
75098
75350
|
})
|
|
@@ -75343,14 +75595,10 @@ var zEnvVariable = object({
|
|
|
75343
75595
|
var zCreateTerminalRequest = object({
|
|
75344
75596
|
sessionId: zSessionId,
|
|
75345
75597
|
command: string2(),
|
|
75346
|
-
args:
|
|
75347
|
-
env:
|
|
75348
|
-
cwd:
|
|
75349
|
-
|
|
75350
|
-
}),
|
|
75351
|
-
outputByteLimit: defaultOnError(number2().nullish(), () => {
|
|
75352
|
-
return;
|
|
75353
|
-
}),
|
|
75598
|
+
args: array(string2()).optional(),
|
|
75599
|
+
env: array(zEnvVariable).optional(),
|
|
75600
|
+
cwd: string2().nullish(),
|
|
75601
|
+
outputByteLimit: number2().nullish(),
|
|
75354
75602
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
75355
75603
|
return;
|
|
75356
75604
|
})
|
|
@@ -75876,10 +76124,8 @@ var zAuthMethodTerminal = object({
|
|
|
75876
76124
|
description: defaultOnError(string2().nullish(), () => {
|
|
75877
76125
|
return;
|
|
75878
76126
|
}),
|
|
75879
|
-
args:
|
|
75880
|
-
env:
|
|
75881
|
-
return;
|
|
75882
|
-
}),
|
|
76127
|
+
args: array(string2()).optional(),
|
|
76128
|
+
env: record(string2(), string2()).optional(),
|
|
75883
76129
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
75884
76130
|
return;
|
|
75885
76131
|
})
|
|
@@ -76577,17 +76823,61 @@ var zRunningStateUpdate = object({
|
|
|
76577
76823
|
return;
|
|
76578
76824
|
})
|
|
76579
76825
|
});
|
|
76580
|
-
var
|
|
76581
|
-
|
|
76826
|
+
var zErrorStopReason = object({
|
|
76827
|
+
error: defaultOnError(zError.nullish(), () => {
|
|
76582
76828
|
return;
|
|
76829
|
+
})
|
|
76830
|
+
});
|
|
76831
|
+
var zIdleStateUpdate = defaultOnErrorOptionalStringTag(preserveCustomPayload(intersection(union([
|
|
76832
|
+
object({
|
|
76833
|
+
stopReason: literal("end_turn")
|
|
76834
|
+
}),
|
|
76835
|
+
object({
|
|
76836
|
+
stopReason: literal("max_tokens")
|
|
76583
76837
|
}),
|
|
76838
|
+
object({
|
|
76839
|
+
stopReason: literal("max_turn_requests")
|
|
76840
|
+
}),
|
|
76841
|
+
object({
|
|
76842
|
+
stopReason: literal("refusal")
|
|
76843
|
+
}),
|
|
76844
|
+
object({
|
|
76845
|
+
stopReason: literal("cancelled")
|
|
76846
|
+
}),
|
|
76847
|
+
zErrorStopReason.and(object({
|
|
76848
|
+
stopReason: literal("error")
|
|
76849
|
+
})),
|
|
76850
|
+
excludeKnownTags(object({
|
|
76851
|
+
stopReason: string2()
|
|
76852
|
+
}), "stopReason", [
|
|
76853
|
+
"cancelled",
|
|
76854
|
+
"end_turn",
|
|
76855
|
+
"error",
|
|
76856
|
+
"max_tokens",
|
|
76857
|
+
"max_turn_requests",
|
|
76858
|
+
"refusal"
|
|
76859
|
+
]),
|
|
76860
|
+
object({
|
|
76861
|
+
stopReason: _null3().optional()
|
|
76862
|
+
})
|
|
76863
|
+
]), object({
|
|
76584
76864
|
usage: defaultOnError(zUsage.nullish(), () => {
|
|
76585
76865
|
return;
|
|
76586
76866
|
}),
|
|
76587
76867
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76588
76868
|
return;
|
|
76869
|
+
}),
|
|
76870
|
+
stopReason: defaultOnError(string2().nullish(), () => {
|
|
76871
|
+
return;
|
|
76589
76872
|
})
|
|
76590
|
-
})
|
|
76873
|
+
})), "stopReason", [
|
|
76874
|
+
"cancelled",
|
|
76875
|
+
"end_turn",
|
|
76876
|
+
"error",
|
|
76877
|
+
"max_tokens",
|
|
76878
|
+
"max_turn_requests",
|
|
76879
|
+
"refusal"
|
|
76880
|
+
]), "stopReason");
|
|
76591
76881
|
var zRequiresActionStateUpdate = object({
|
|
76592
76882
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76593
76883
|
return;
|
|
@@ -76970,16 +77260,16 @@ var zMcpServer = union([
|
|
|
76970
77260
|
]);
|
|
76971
77261
|
var zNewSessionRequest = object({
|
|
76972
77262
|
cwd: string2(),
|
|
76973
|
-
additionalDirectories:
|
|
76974
|
-
mcpServers:
|
|
77263
|
+
additionalDirectories: array(string2()).optional(),
|
|
77264
|
+
mcpServers: array(zMcpServer),
|
|
76975
77265
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76976
77266
|
return;
|
|
76977
77267
|
})
|
|
76978
77268
|
});
|
|
76979
77269
|
var zLoadSessionRequest = object({
|
|
76980
|
-
mcpServers:
|
|
77270
|
+
mcpServers: array(zMcpServer),
|
|
76981
77271
|
cwd: string2(),
|
|
76982
|
-
additionalDirectories:
|
|
77272
|
+
additionalDirectories: array(string2()).optional(),
|
|
76983
77273
|
sessionId: zSessionId,
|
|
76984
77274
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
76985
77275
|
return;
|
|
@@ -77001,8 +77291,8 @@ var zDeleteSessionRequest = object({
|
|
|
77001
77291
|
var zForkSessionRequest = object({
|
|
77002
77292
|
sessionId: zSessionId,
|
|
77003
77293
|
cwd: string2(),
|
|
77004
|
-
additionalDirectories:
|
|
77005
|
-
mcpServers:
|
|
77294
|
+
additionalDirectories: array(string2()).optional(),
|
|
77295
|
+
mcpServers: array(zMcpServer).optional(),
|
|
77006
77296
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
77007
77297
|
return;
|
|
77008
77298
|
})
|
|
@@ -77010,8 +77300,8 @@ var zForkSessionRequest = object({
|
|
|
77010
77300
|
var zResumeSessionRequest = object({
|
|
77011
77301
|
sessionId: zSessionId,
|
|
77012
77302
|
cwd: string2(),
|
|
77013
|
-
additionalDirectories:
|
|
77014
|
-
mcpServers:
|
|
77303
|
+
additionalDirectories: array(string2()).optional(),
|
|
77304
|
+
mcpServers: array(zMcpServer).optional(),
|
|
77015
77305
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
77016
77306
|
return;
|
|
77017
77307
|
})
|
|
@@ -77392,7 +77682,7 @@ var zDidChangeDocumentNotification = object({
|
|
|
77392
77682
|
sessionId: zSessionId,
|
|
77393
77683
|
uri: string2(),
|
|
77394
77684
|
version: number2(),
|
|
77395
|
-
contentChanges:
|
|
77685
|
+
contentChanges: array(zTextDocumentContentChangeEvent),
|
|
77396
77686
|
_meta: defaultOnError(record(string2(), unknown()).nullish(), () => {
|
|
77397
77687
|
return;
|
|
77398
77688
|
})
|
|
@@ -78471,9 +78761,26 @@ var zGuardElicitationPropertySchemaBoolean = zBooleanPropertySchema.and(object({
|
|
|
78471
78761
|
var zGuardElicitationPropertySchemaArray = zMultiSelectPropertySchema.and(object({ type: literal("array") }));
|
|
78472
78762
|
var zGuardMultiSelectItemsString = zStringMultiSelectItems.and(object({ type: literal("string") }));
|
|
78473
78763
|
var zGuardStateUpdateRunning = zRunningStateUpdate.and(object({ state: literal("running") }));
|
|
78474
|
-
var zGuardStateUpdateIdle = zIdleStateUpdate.and(object({ state: literal("idle") }));
|
|
78764
|
+
var zGuardStateUpdateIdle = zIdleStateUpdate.and(object({ usage: zUsage.nullish() })).and(object({ state: literal("idle") }));
|
|
78475
78765
|
var zGuardStateUpdateRequiresAction = zRequiresActionStateUpdate.and(object({ state: literal("requires_action") }));
|
|
78476
78766
|
var zGuardStateUpdateUnknown = zUnknownStateUpdate.and(object({ state: literal("unknown") }));
|
|
78767
|
+
var zGuardIdleStateUpdateEndTurn = object({
|
|
78768
|
+
stopReason: literal("end_turn")
|
|
78769
|
+
});
|
|
78770
|
+
var zGuardIdleStateUpdateMaxTokens = object({
|
|
78771
|
+
stopReason: literal("max_tokens")
|
|
78772
|
+
});
|
|
78773
|
+
var zGuardIdleStateUpdateMaxTurnRequests = object({
|
|
78774
|
+
stopReason: literal("max_turn_requests")
|
|
78775
|
+
});
|
|
78776
|
+
var zGuardIdleStateUpdateRefusal = object({
|
|
78777
|
+
stopReason: literal("refusal")
|
|
78778
|
+
});
|
|
78779
|
+
var zGuardIdleStateUpdateCancelled = object({
|
|
78780
|
+
stopReason: literal("cancelled")
|
|
78781
|
+
});
|
|
78782
|
+
var zGuardIdleStateUpdateError = zErrorStopReason.and(object({ error: zError.nullish() })).and(object({ stopReason: literal("error") }));
|
|
78783
|
+
var zGuardIdleStateUpdateNone = object({ stopReason: _null3().optional() });
|
|
78477
78784
|
var zGuardCreateElicitationResponseAccept = zElicitationAcceptAction.and(object({ action: literal("accept") }));
|
|
78478
78785
|
var zGuardCreateElicitationResponseDecline = object({
|
|
78479
78786
|
action: literal("decline")
|
|
@@ -79832,6 +80139,7 @@ async function runAcp(apiUrl, apiKey, opts) {
|
|
|
79832
80139
|
|
|
79833
80140
|
// src/shutdown.ts
|
|
79834
80141
|
var activeRun2 = null;
|
|
80142
|
+
var stopOnExit2 = false;
|
|
79835
80143
|
var cleanups2 = [];
|
|
79836
80144
|
var exiting2 = null;
|
|
79837
80145
|
var cancelling2 = null;
|
|
@@ -79839,6 +80147,11 @@ var CANCEL_TIMEOUT_MS2 = 5000;
|
|
|
79839
80147
|
function setActiveRun2(run2) {
|
|
79840
80148
|
activeRun2 = run2;
|
|
79841
80149
|
}
|
|
80150
|
+
function stopRunOnExit() {
|
|
80151
|
+
stopOnExit2 = true;
|
|
80152
|
+
}
|
|
80153
|
+
var detachNotice2 = (todoId) => `Todo keeps running — reattach: tfa-cli --resume ${todoId}
|
|
80154
|
+
`;
|
|
79842
80155
|
function trackStart(ws, projectId, started) {
|
|
79843
80156
|
setActiveRun2(started.then((todoId) => ({ ws, projectId, todoId }), () => null));
|
|
79844
80157
|
}
|
|
@@ -79856,7 +80169,7 @@ function cancelActiveRun2(timeoutMs = CANCEL_TIMEOUT_MS2) {
|
|
|
79856
80169
|
if (cancelling2)
|
|
79857
80170
|
return cancelling2;
|
|
79858
80171
|
const pending = activeRun2;
|
|
79859
|
-
if (!pending)
|
|
80172
|
+
if (!pending || !stopOnExit2)
|
|
79860
80173
|
return Promise.resolve({ ok: false, todoId: null });
|
|
79861
80174
|
const res = { ok: false };
|
|
79862
80175
|
const attempt = (async () => {
|
|
@@ -79882,7 +80195,11 @@ function shutdown2(code, reason) {
|
|
|
79882
80195
|
process.stderr.write(`
|
|
79883
80196
|
${reason}
|
|
79884
80197
|
`);
|
|
79885
|
-
if (activeRun2) {
|
|
80198
|
+
if (activeRun2 && !stopOnExit2) {
|
|
80199
|
+
const run2 = await withTimeout2(Promise.resolve(activeRun2), CANCEL_TIMEOUT_MS2, null);
|
|
80200
|
+
if (run2)
|
|
80201
|
+
process.stderr.write(detachNotice2(run2.todoId));
|
|
80202
|
+
} else if (activeRun2) {
|
|
79886
80203
|
const { ok, todoId } = await cancelActiveRun2();
|
|
79887
80204
|
if (ok)
|
|
79888
80205
|
process.stderr.write(`Stopped todo ${todoId}
|
|
@@ -79912,7 +80229,7 @@ function finish(code) {
|
|
|
79912
80229
|
});
|
|
79913
80230
|
}
|
|
79914
80231
|
function installSignalHandlers() {
|
|
79915
|
-
process.on("SIGINT", () => void shutdown2(130, "
|
|
80232
|
+
process.on("SIGINT", () => void shutdown2(130, "Exited (Ctrl+C)"));
|
|
79916
80233
|
process.on("SIGTERM", () => void shutdown2(143, "Terminated (SIGTERM)"));
|
|
79917
80234
|
process.on("SIGHUP", () => void shutdown2(129, "Hangup (SIGHUP)"));
|
|
79918
80235
|
}
|
|
@@ -80632,8 +80949,7 @@ async function main() {
|
|
|
80632
80949
|
await watchTodo(ws2, todoId, projectId2, {
|
|
80633
80950
|
json: !!args.json,
|
|
80634
80951
|
autoApprove,
|
|
80635
|
-
agentSettings: agent3
|
|
80636
|
-
exitOnInterrupt: !!args["non-interactive"]
|
|
80952
|
+
agentSettings: agent3
|
|
80637
80953
|
});
|
|
80638
80954
|
if (!args["non-interactive"]) {
|
|
80639
80955
|
process.stderr.write(`
|
|
@@ -80684,7 +81000,7 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
80684
81000
|
cfg.addToHistory(followUp);
|
|
80685
81001
|
await appendTracked(api2, ws2, projectId2, followUp, agent3, todoId);
|
|
80686
81002
|
await linkToSpawningBlock(api2, todoId);
|
|
80687
|
-
await watchTodo(ws2, todoId, projectId2, { json: !!args.json, autoApprove, agentSettings: agent3
|
|
81003
|
+
await watchTodo(ws2, todoId, projectId2, { json: !!args.json, autoApprove, agentSettings: agent3 });
|
|
80688
81004
|
}
|
|
80689
81005
|
if (!args["non-interactive"]) {
|
|
80690
81006
|
await interactiveLoop(ws2, api2, todoId, projectId2, agent3, !!args.json, autoApprove, cfg);
|
|
@@ -80706,6 +81022,7 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
80706
81022
|
if (mayfly) {
|
|
80707
81023
|
process.stderr.write(`${DIM2}Isolated bridge:${RESET2} ${CYAN2}mayfly-${isolatedTodoId}${RESET2}
|
|
80708
81024
|
`);
|
|
81025
|
+
stopRunOnExit();
|
|
80709
81026
|
onShutdown(mayfly.stop);
|
|
80710
81027
|
mayfly.child.on("exit", (code, sig) => {
|
|
80711
81028
|
if (!mayfly.stopped())
|
|
@@ -80845,8 +81162,7 @@ Resumed: ${CYAN2}${getFrontendUrl(apiUrl, todoId)}${RESET2}
|
|
|
80845
81162
|
await watchTodo(ws, actualTodoId, projectId, {
|
|
80846
81163
|
json: !!args.json,
|
|
80847
81164
|
autoApprove,
|
|
80848
|
-
agentSettings: agent2
|
|
80849
|
-
exitOnInterrupt: !!args["non-interactive"]
|
|
81165
|
+
agentSettings: agent2
|
|
80850
81166
|
});
|
|
80851
81167
|
if (!args["non-interactive"]) {
|
|
80852
81168
|
process.stderr.write(`
|