@todoforai/cli 0.1.69 → 0.1.70

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2026 TODOforAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -137,3 +137,7 @@ No global install needed — `"command": "npx", "args": ["-y", "@todoforai/cli",
137
137
  --reset-config Reset config file
138
138
  --help, -h Show this help
139
139
  ```
140
+
141
+ ## License
142
+
143
+ MIT, see [LICENSE](LICENSE).
@@ -43132,6 +43132,18 @@ class ApiClient {
43132
43132
  throw new Error(`API POST /resources/open failed: ${res.status} ${await res.text()}`);
43133
43133
  return res.json();
43134
43134
  }
43135
+ async showConnect(todoId, keys, opts = {}) {
43136
+ const endpoint = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/connect`;
43137
+ const res = await fetch(endpoint, {
43138
+ method: "POST",
43139
+ headers: { "x-api-key": this.apiKey, "Content-Type": "application/json" },
43140
+ body: JSON.stringify({ todoId, keys, ...opts }),
43141
+ signal: AbortSignal.timeout(30000)
43142
+ });
43143
+ if (!res.ok)
43144
+ throw new Error(`API POST /resources/connect failed: ${res.status} ${await res.text()}`);
43145
+ return res.json();
43146
+ }
43135
43147
  async listShows(opts = {}) {
43136
43148
  const qs = new URLSearchParams;
43137
43149
  if (opts.todoId)
@@ -43976,8 +43988,7 @@ var ToolType;
43976
43988
  ToolType2["Question"] = "question";
43977
43989
  ToolType2["Login"] = "login_prompt";
43978
43990
  ToolType2["BusinessOnboarding"] = "business_onboarding";
43979
- ToolType2["RequestMcpAccess"] = "request_mcp_access";
43980
- ToolType2["EdgeNotRunning"] = "edge_not_running";
43991
+ ToolType2["ConnectDevice"] = "connect_device";
43981
43992
  ToolType2["WebFetch"] = "webfetch";
43982
43993
  ToolType2["GoogleSearch"] = "google_search";
43983
43994
  ToolType2["WebSearch"] = "web_search";
@@ -43998,7 +44009,8 @@ var USER_INPUT_BLOCK_TYPES = new Set([
43998
44009
  "question" /* Question */,
43999
44010
  "login_prompt" /* Login */,
44000
44011
  "business_onboarding" /* BusinessOnboarding */,
44001
- "open_browser" /* OpenBrowser */
44012
+ "open_browser" /* OpenBrowser */,
44013
+ "connect_device" /* ConnectDevice */
44002
44014
  ]);
44003
44015
  var EDGE_TOOL_TYPES = new Set([
44004
44016
  "read" /* Read */,
@@ -44711,6 +44723,7 @@ var tool_catalog_default = {
44711
44723
  voiceChannel: "Gmail",
44712
44724
  summary: "Read and send Gmail from your own account.",
44713
44725
  statusCmd: "zele whoami 2>/dev/null | grep -o 'email: *[^ ]*' | tr -d ' ' | paste -sd ' ' - | grep -m1 .",
44726
+ logoutCmd: "zele logout {{ACCOUNT}}",
44714
44727
  loginNeedsPty: true,
44715
44728
  loginCmd: "zele login --method google",
44716
44729
  oauthUrlPattern: "(https://accounts\\.google\\.com\\S+)",
@@ -44722,6 +44735,7 @@ var tool_catalog_default = {
44722
44735
  description: "Gmail (multi-account, OAuth login): inbox, search, send, reply, contacts. Google Calendar via `zele cal`: list/create/update/delete events (Meet links, recurrence), respond to invites, free/busy."
44723
44736
  },
44724
44737
  "zele-calendar": {
44738
+ subcommands: ["cal"],
44725
44739
  preinstallCloud: true,
44726
44740
  category: "productivity",
44727
44741
  pkg: "zele",
@@ -44730,6 +44744,7 @@ var tool_catalog_default = {
44730
44744
  label: "Google Calendar",
44731
44745
  summary: "See your Google Calendar and book, move or cancel events.",
44732
44746
  statusCmd: "zele whoami 2>/dev/null | grep -o 'email: *[^ ]*' | tr -d ' ' | paste -sd ' ' - | grep -m1 .",
44747
+ logoutCmd: "zele logout {{ACCOUNT}}",
44733
44748
  loginNeedsPty: true,
44734
44749
  loginCmd: "zele login --method google",
44735
44750
  oauthUrlPattern: "(https://accounts\\.google\\.com\\S+)",
@@ -44940,6 +44955,24 @@ var tool_catalog_default = {
44940
44955
  versionCmd: "ntn --version 2>/dev/null | sed -n 1p",
44941
44956
  description: "Official Notion CLI: raw API calls (`ntn api <path>`), page/data-source management, file uploads, Workers deploy."
44942
44957
  },
44958
+ treg: {
44959
+ preinstallCloud: true,
44960
+ packages: ["tools-registry==0.27.0"],
44961
+ category: "research",
44962
+ pkg: "tools-registry",
44963
+ pipModules: ["treg"],
44964
+ installer: "pip",
44965
+ label: "treg",
44966
+ summary: "Call 4000+ paid data/SEO/enrichment APIs through one prepaid key, no vendor accounts.",
44967
+ statusCmd: `python3 -c "import json,os;d=json.load(open(os.path.expanduser('~/.treg/config.json')));print(d.get('email') or '' if d.get('token') else '')" 2>/dev/null | grep -m1 .`,
44968
+ loginCmd: "treg login",
44969
+ oauthUrlPattern: "(https://treg\\.to/login\\?\\S+)",
44970
+ sessionIdPattern: "#code=([A-Z0-9]{4,8})",
44971
+ logoutCmd: "treg logout",
44972
+ credentialPaths: ["~/.treg/config.json"],
44973
+ versionCmd: "treg --version 2>/dev/null | sed -n 1p",
44974
+ description: 'treg.to ("OpenRouter for agent tools"): one prepaid balance, 100+ providers (SE Ranking, DataForSEO, Perplexity, CompanyEnrich, ScrapeCreators, TikHub, cloro AI-search scrapes…) metered per call, credentials injected server-side. Find by intent: `treg catalog search "backlinks for a domain" --json` (each result shows price + a `call_template`); run: `treg call <endpoint.id> --query k=v --json` or `treg call <endpoint.id> --method POST --data \'<json>\' --json` (prints one `{result,_treg}` line); money: `treg balance`, `treg topup`; `treg review` rates a call. Own keys/CLIs: `treg secret|tool|skill`, `treg cli run <vendor> …` and `treg <cmd>` inject the team\'s stored credentials. Team: `treg org ls|use|invite|members`, `treg audit`. `--org <slug>` targets another team. Every catalog call spends real money — check the `cost` field and prefer a connected first-party connector when one exists. Docs: https://treg.to/llms.txt'
44975
+ },
44943
44976
  "perplexity-api": {
44944
44977
  preinstallCloud: true,
44945
44978
  category: "research",
@@ -45201,7 +45234,7 @@ var tool_catalog_default = {
45201
45234
  installer: "npm",
45202
45235
  label: "TODOforAI",
45203
45236
  summary: "Create and follow up on your TODOs, and show results in the chat.",
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 `![](path.png)` 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).',
45237
+ 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>] [--url <link>: tap opens it on the phone, e.g. a Google Maps navigation link]` 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 `![](path.png)` 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).',
45205
45238
  installCmd: "npm install -g @todoforai/cli",
45206
45239
  versionCmd: "tfa-cli --version 2>/dev/null | sed -n 1p",
45207
45240
  preinstallCloud: true,
@@ -45384,6 +45417,7 @@ var tool_catalog_default = {
45384
45417
  needsPython: true,
45385
45418
  installCmd: `case "$(uname -s)-$(uname -m)" in Darwin-arm64) p=darwin-arm;; Darwin-*) p=darwin-x86_64;; Linux-aarch64|Linux-arm64) p=linux-arm;; *) p=linux-x86_64;; esac && tmp="$HOME/.gcloud-new.$$" && rm -rf "$tmp" && mkdir -p "$tmp" && curl -fsSL "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-$p.tar.gz" | tar -xzC "$tmp" && rm -rf "$HOME/google-cloud-sdk" && mv "$tmp/google-cloud-sdk" "$HOME/google-cloud-sdk" && rm -rf "$tmp" && mkdir -p "$HOME/.todoforai/tools/bin" && rm -f "$HOME/.todoforai/tools/bin/gcloud" && printf '#!/bin/sh\\nCLOUDSDK_PYTHON="\${CLOUDSDK_PYTHON:-%s}" exec "$HOME/google-cloud-sdk/bin/gcloud" "$@"\\n' "$py" > "$HOME/.todoforai/tools/bin/gcloud" && chmod +x "$HOME/.todoforai/tools/bin/gcloud"`,
45386
45419
  statusCmd: `gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null | tr -d '\\r' | grep -m1 .`,
45420
+ logoutCmd: "gcloud auth revoke --all --quiet",
45387
45421
  loginCmd: "gcloud auth login --no-launch-browser",
45388
45422
  loginNeedsPty: true,
45389
45423
  stdinPaste: true,
@@ -45489,7 +45523,7 @@ var tool_catalog_default = {
45489
45523
  preinstallCloud: true,
45490
45524
  label: "Browser",
45491
45525
  summary: "Browse websites automatically: click, type, read and screenshot.",
45492
- 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`.",
45526
+ 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); connect prints `whoami` (browser=extension|embedded|local, headless, ua) — run `agent-browser whoami` again after any error or odd output, and stop on `ERROR`/`DISCONNECTED`/`headless=yes` where the user's browser was expected: re-run the connect command, never continue in another browser. If a page is 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 only marks the tab (yellow count badge on the extension icon, needs-you row with Show in the popup); it never pauses you or takes the tab away. Tell the user what to do and to reply here, then end your turn. When resumed, snapshot to verify, then clear the marker with `TodoforAI.clearHumanHelp` `{tabId: N}`. 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`.",
45493
45527
  versionCmd: "agent-browser --version 2>/dev/null | sed -n 1p",
45494
45528
  preinstallJarvis: true
45495
45529
  },
@@ -46090,7 +46124,7 @@ var tool_catalog_default = {
46090
46124
  summary: "Read and write your Outlook mail.",
46091
46125
  statusCmd: `outlook-api whoami 2>/dev/null | sed -n 's/.*"userPrincipalName"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p' | grep -m10 .`,
46092
46126
  logoutCmd: "outlook-api logout",
46093
- loginCmd: "outlook-api auth",
46127
+ loginCmd: "outlook-api auth --tenant common",
46094
46128
  credentialPaths: ["~/.config/outlook-api/config.json"],
46095
46129
  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.",
46096
46130
  versionCmd: "outlook-api --version 2>/dev/null | sed -n 1p",
@@ -46137,6 +46171,7 @@ var tool_catalog_default = {
46137
46171
  label: "Google Workspace",
46138
46172
  summary: "Work with Google Sheets, Docs, Slides, Drive, Forms, Tasks, Contacts and Chat.",
46139
46173
  statusCmd: `gws auth status 2>/dev/null | sed -n 's/.*"user"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p' | grep -m1 .`,
46174
+ logoutCmd: "gws auth logout",
46140
46175
  loginCmd: "gws auth login --scopes https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/documents,https://www.googleapis.com/auth/presentations,https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/forms.body,https://www.googleapis.com/auth/forms.responses.readonly,https://www.googleapis.com/auth/tasks,https://www.googleapis.com/auth/contacts,https://www.googleapis.com/auth/chat.spaces,https://www.googleapis.com/auth/chat.messages,https://www.googleapis.com/auth/chat.memberships,https://www.googleapis.com/auth/chat.delete",
46141
46176
  loginTerminalHint: "gws needs your own Google OAuth client first: in console.cloud.google.com create a Desktop-app OAuth client (consent screen External/testing, add yourself as test user), enable the APIs you need (Sheets/Docs/Slides/Drive/Forms/Tasks/People/Chat), save the JSON as ~/.config/gws/client_secret.json (or run `gws auth setup` if gcloud is logged in), then `gws auth login -s sheets,drive`.",
46142
46177
  oauthUrlPattern: "(https://accounts\\.google\\.com/o/oauth2/auth\\S+)",
@@ -46231,6 +46266,7 @@ var tool_catalog_default = {
46231
46266
  summary: "Read your Granola meeting notes and transcripts.",
46232
46267
  statusCmd: `granola auth status -o json 2>/dev/null | sed -n 's/.*"authenticated": *true.*/authenticated/p' | grep -m1 .`,
46233
46268
  loginCmd: "granola auth login",
46269
+ logoutCmd: "granola auth logout",
46234
46270
  loginTerminalHint: "Imports the Granola desktop app's session: install Granola on this host, sign in there, then run `granola auth login`.",
46235
46271
  credentialPaths: ["~/.config/keyring", "~/.config/granola"],
46236
46272
  versionCmd: "granola --version 2>/dev/null | sed -n 1p",
@@ -48798,6 +48834,7 @@ var tool_catalog_default = {
48798
48834
  label: "Heroku",
48799
48835
  summary: "Deploy and manage apps, add-ons and databases on Heroku.",
48800
48836
  statusCmd: "heroku auth:whoami 2>/dev/null | tr -d '\\r' | grep -m1 .",
48837
+ logoutCmd: "heroku logout",
48801
48838
  loginCmd: `k=<api_key>; f="$HOME/.netrc"; (umask 077; touch "$f"; chmod 600 "$f") && K="$k" python3 -c 'import os,re,sys; f=sys.argv[1]; k=os.environ["K"]; s=re.sub(r"machine\\s+(api|git)\\.heroku\\.com\\s+login\\s+\\S+\\s+password\\s+\\S+\\s*", "", open(f).read()); open(f, "w").write(s + "".join("machine %s.heroku.com\\n login token\\n password %s\\n" % (h, k) for h in ("api", "git")))' "$f" && heroku auth:whoami`,
48802
48839
  credentialPaths: ["~/.netrc"],
48803
48840
  versionCmd: "heroku --version 2>/dev/null | sed -n 1p",
@@ -49270,6 +49307,7 @@ var tool_catalog_default = {
49270
49307
  description: "Official Pinecone CLI (`pc`). Connect with a project API key (app.pinecone.io → API keys). `pc index list`, `pc index create --name <n> --dimension 1536 --metric cosine --cloud aws --region us-east-1`, `pc index describe --name <n>`.",
49271
49308
  loginCmd: "pc auth configure --api-key <api_key>",
49272
49309
  statusCmd: "pc index list --json >/dev/null 2>&1 && echo authenticated",
49310
+ logoutCmd: "pc auth logout",
49273
49311
  versionCmd: "pc version 2>/dev/null | sed -n 1p",
49274
49312
  credentialPaths: [
49275
49313
  "~/.config/pinecone/secrets.yaml",
@@ -49647,6 +49685,7 @@ var tool_catalog_default = {
49647
49685
  cloudinary: {
49648
49686
  category: "creative",
49649
49687
  pkg: "cloudinary-cli",
49688
+ pipSdist: ["cloudinary-cli"],
49650
49689
  installer: "pip",
49651
49690
  label: "Cloudinary",
49652
49691
  binName: "cld",
@@ -50099,6 +50138,24 @@ var tool_catalog_default = {
50099
50138
 
50100
50139
  // ../packages/shared-fbe/src/toolCatalog.ts
50101
50140
  var TOOL_CATALOG = tool_catalog_default;
50141
+ // ../packages/shared-fbe/src/emailDomains.ts
50142
+ var PUBLIC_EMAIL_DOMAINS = new Set([
50143
+ "gmail.com",
50144
+ "googlemail.com",
50145
+ "outlook.com",
50146
+ "hotmail.com",
50147
+ "live.com",
50148
+ "icloud.com",
50149
+ "me.com",
50150
+ "yahoo.com",
50151
+ "proton.me",
50152
+ "protonmail.com",
50153
+ "fastmail.com",
50154
+ "gmx.com",
50155
+ "gmx.net",
50156
+ "mail.com",
50157
+ "zoho.com"
50158
+ ]);
50102
50159
  // ../packages/shared-fbe/src/toolInstallCommand.ts
50103
50160
  var NPM_HOME = '"${TFA_NPM_HOME:-$HOME}"';
50104
50161
  var NPM_TOOLS = NPM_HOME.replace('}"', '}/.todoforai/tools"');
@@ -50338,13 +50395,14 @@ import { parseArgs } from "util";
50338
50395
  // package.json
50339
50396
  var package_default = {
50340
50397
  name: "@todoforai/cli",
50398
+ license: "MIT",
50341
50399
  description: "TODOforAI CLI — create and manage tasks with tfa-cli.",
50342
50400
  homepage: "https://todofor.ai",
50343
50401
  repository: {
50344
50402
  type: "git",
50345
50403
  url: "git+https://github.com/todoforai/cli.git"
50346
50404
  },
50347
- version: "0.1.69",
50405
+ version: "0.1.70",
50348
50406
  type: "module",
50349
50407
  bin: {
50350
50408
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -50412,10 +50470,11 @@ Examples:
50412
50470
  tfa-cli show <file|-> [todo-id] # Render a local file (image/pdf/html…) in the chat; - = stdin
50413
50471
  tfa-cli show list|rm … # List / take down shown files
50414
50472
  tfa-cli open <url> [todo-id] # Live url preview in the chat
50473
+ tfa-cli connect <key>… [--reason "why"] # Sign-in card for connectors on this machine (several keys = one choice)
50415
50474
  tfa-cli recommend --template <id> # Add a template as a recommendation card
50416
50475
  tfa-cli inbox [needs-you|messages|activity] [--unread] # Read the notification feed (the bell)
50417
50476
  tfa-cli inbox seen [tab] # Mark notifications read
50418
- tfa-cli notify [--to <email>] "Title" "message" [--href /t/<id>] # Note to your own user, or --to a project member (Messages tab + phone push)
50477
+ tfa-cli notify [--to <email>] "Title" "message" [--href /t/<id>] [--url <link>] # Note to your own user, or --to a project member (Messages tab + phone push)
50419
50478
  tfa-cli claim mint --seed <projectId> [--emails a@x,b@y] [--ttl <sec>] # Mint /claim/<token> links for a project you own
50420
50479
  tfa-cli next [--direction "<text>"] # Analyzer growth recommendation cards
50421
50480
  tfa-cli import [claude|codex|<file>...] [--dry-run] # Import Claude Code / Codex sessions as todos
@@ -50546,6 +50605,7 @@ function parseCliArgs() {
50546
50605
  link: { type: "boolean", default: false },
50547
50606
  force: { type: "boolean", default: false },
50548
50607
  direction: { type: "string" },
50608
+ reason: { type: "string" },
50549
50609
  url: { type: "string" },
50550
50610
  account: { type: "string" },
50551
50611
  "from-company": { type: "boolean", default: false },
@@ -53361,7 +53421,9 @@ Examples:
53361
53421
  tfa-cli agent update <agent> sysmsg="You are a terse video editor."
53362
53422
  tfa-cli agent create name=Reviewer model=claude sysmsg="Review diffs only."
53363
53423
 
53364
- From an agent shell (dst_ token) permissions, edgesMcpConfigs, devicesConfig and delete are refused by the backend.
53424
+ tfa-cli agent update <agent> judgeSysmsg="Ask before destructive actions. Everything else can run." (auto-approve policy)
53425
+
53426
+ From an agent shell (dst_ token) permissions, judgeSysmsg, edgesMcpConfigs, devicesConfig and delete are refused by the backend.
53365
53427
  `);
53366
53428
  }
53367
53429
  var FIELD_ALIASES = {
@@ -54953,15 +55015,17 @@ function printNotifyHelp() {
54953
55015
  tfa-cli notify — leave a note in someone's Messages inbox (+ phone push)
54954
55016
 
54955
55017
  Usage:
54956
- tfa-cli notify "Title" "message" [--href /t/<todo-id>] [--subject <key>] [--json]
55018
+ tfa-cli notify "Title" "message" [--href /t/<todo-id>] [--url <link>] [--subject <key>] [--json]
54957
55019
  tfa-cli notify --to <email> "Title" "message" [--project <id>]
54958
55020
  echo "message" | tfa-cli notify "Title"
54959
55021
 
54960
55022
  Recipient: your own user by default. --to <email> reaches a TEAMMATE: someone who is a member
54961
55023
  of the current project (--project, $TODOFORAI_PROJECT_ID or your default), shown as from you.
54962
55024
  Anyone else is refused. List them with: tfa-cli project members
54963
- --subject dedupes: the same subject is delivered once (default: hash of title+message,
55025
+ --subject dedupes: the same subject is delivered once (default: hash of title+message+url,
54964
55026
  so a retry never double-notifies). --href must be an in-app path.
55027
+ --url is what tapping the phone notification opens (https/tel/sms/mailto), e.g. navigation:
55028
+ --url "https://www.google.com/maps/dir/?api=1&destination=Deák+tér&dir_action=navigate"
54965
55029
  Limits: 10 notes/day to yourself, 20/day to teammates.
54966
55030
  `);
54967
55031
  }
@@ -55040,8 +55104,9 @@ async function notifyCommand(api, positionals, args, projectId) {
55040
55104
  process.exit(2);
55041
55105
  }
55042
55106
  const subject = args.subject || createHash("sha256").update(`${title}
55043
- ${message}`).digest("hex").slice(0, 16);
55044
- const msg = { subject, title, message, ...args.href ? { href: String(args.href) } : {} };
55107
+ ${message}${args.url ? `
55108
+ ${args.url}` : ""}`).digest("hex").slice(0, 16);
55109
+ const msg = { subject, title, message, ...args.href ? { href: String(args.href) } : {}, ...args.url ? { url: String(args.url) } : {} };
55045
55110
  const row = await (to ? api.sendMemberMessage({ ...msg, projectId, to }) : api.sendFeedMessage(msg));
55046
55111
  if (args.json) {
55047
55112
  console.log(JSON.stringify(row));
@@ -80252,6 +80317,21 @@ async function main() {
80252
80317
  console.log(res.ref);
80253
80318
  return;
80254
80319
  }
80320
+ if (positionals[0] === "connect") {
80321
+ const keys = positionals.slice(1);
80322
+ const todoId = aliasTodoId(args.alias) || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
80323
+ if (!keys.length || !todoId) {
80324
+ process.stderr.write(`${RED3}Usage: tfa-cli connect <key> [<key>\u2026] [--reason "why"]${RESET2}
80325
+ `);
80326
+ process.exit(2);
80327
+ }
80328
+ const res = await api2.showConnect(todoId, keys, { reason: args.reason, alias: args.alias });
80329
+ if (args.json)
80330
+ console.log(JSON.stringify(res, null, 2));
80331
+ else
80332
+ console.log(res.ref);
80333
+ return;
80334
+ }
80255
80335
  if (positionals[0] === "recommend") {
80256
80336
  const templateId = args.template || positionals[1];
80257
80337
  if (!templateId) {
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
+ "license": "MIT",
3
4
  "description": "TODOforAI CLI — create and manage tasks with tfa-cli.",
4
5
  "homepage": "https://todofor.ai",
5
6
  "repository": {
6
7
  "type": "git",
7
8
  "url": "git+https://github.com/todoforai/cli.git"
8
9
  },
9
- "version": "0.1.69",
10
+ "version": "0.1.70",
10
11
  "type": "module",
11
12
  "bin": {
12
13
  "todoforai-cli": "bin/todoforai-cli.js",