@todoforai/cli 0.1.60 → 0.1.62

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.
Files changed (2) hide show
  1. package/dist/todoforai-cli.js +230 -76
  2. package/package.json +1 -1
@@ -43055,6 +43055,19 @@ class ApiClient {
43055
43055
  throw new Error(`API GET /resources/show failed: ${res.status} ${await res.text()}`);
43056
43056
  return res.json();
43057
43057
  }
43058
+ async shareShow(todoId, key) {
43059
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/show/${encodeURIComponent(todoId)}/${encodeURIComponent(key)}/share`;
43060
+ const res = await fetch(url, { method: "POST", headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
43061
+ if (!res.ok)
43062
+ throw new Error(`API POST /resources/show/share failed: ${res.status} ${await res.text()}`);
43063
+ return res.json();
43064
+ }
43065
+ async unshareShow(todoId, key) {
43066
+ const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/show/${encodeURIComponent(todoId)}/${encodeURIComponent(key)}/share`;
43067
+ const res = await fetch(url, { method: "DELETE", headers: { "x-api-key": this.apiKey }, signal: AbortSignal.timeout(30000) });
43068
+ if (!res.ok)
43069
+ throw new Error(`API DELETE /resources/show/share failed: ${res.status} ${await res.text()}`);
43070
+ }
43058
43071
  async removeShow(todoId, key) {
43059
43072
  const url = `${this.apiUrl}${restBasePath(this.apiKey)}/resources/show/${encodeURIComponent(todoId)}/${encodeURIComponent(key)}`;
43060
43073
  const res = await fetch(url, {
@@ -43597,6 +43610,11 @@ function qualifiedModelIds(listing, filter) {
43597
43610
  return q ? ids.filter((id) => id.toLowerCase().includes(q)) : ids;
43598
43611
  }
43599
43612
  // ../packages/shared-fbe/src/billing.ts
43613
+ var HOBBY_ALLOWED = new Set([
43614
+ "anthropic/claude-sonnet-5.5",
43615
+ "anthropic/claude-sonnet-5",
43616
+ "anthropic/claude-haiku-4.5"
43617
+ ]);
43600
43618
  var USAGE_WINDOWS = {
43601
43619
  SESSION_MS: 5 * 60 * 60 * 1000,
43602
43620
  WEEK_MS: 7 * 24 * 60 * 60 * 1000,
@@ -43901,6 +43919,15 @@ function manualAnswersOf(sources) {
43901
43919
  }
43902
43920
  var manualSamples = (answers) => Object.entries(answers).filter(([, a]) => a.trim()).map(([prompt, text]) => ({ prompt, text: text.trim() }));
43903
43921
  var senderLabel = (s) => s.kind === "team" ? `${s.name} · TODO for AI team` : s.kind === "member" ? `${s.name} · teammate` : `${s.name} · your agent`;
43922
+ // ../packages/shared-fbe/src/jarvisEvents.ts
43923
+ var STATE = {
43924
+ ["READY" /* READY */]: "agent ended its run and handed back; not opened yet",
43925
+ ["READY_CHECKED" /* READY_CHECKED */]: "agent ended its run and handed back; its window was opened",
43926
+ ["DONE" /* DONE */]: "done and closed",
43927
+ ["ERROR" /* ERROR */]: "failed; not opened yet",
43928
+ ["ERROR_CHECKED" /* ERROR_CHECKED */]: "failed; its window was opened",
43929
+ ["WAITING_FOR_USER" /* WAITING_FOR_USER */]: "blocked, waiting on the user's answer or approval"
43930
+ };
43904
43931
  // ../packages/shared-fbe/src/templateBody.ts
43905
43932
  var USER_HEADINGS = new Set([
43906
43933
  "your task",
@@ -44478,6 +44505,7 @@ var tool_catalog_default = {
44478
44505
  versionCmd: "linkedin-api --version 2>/dev/null | sed -n 1p"
44479
44506
  },
44480
44507
  "figma-api": {
44508
+ preinstallCloud: true,
44481
44509
  category: "design",
44482
44510
  pkg: "@todoforai/figma-api",
44483
44511
  installer: "npm",
@@ -44612,6 +44640,20 @@ var tool_catalog_default = {
44612
44640
  ],
44613
44641
  description: "AI music generation, custom lyrics, multiple genres."
44614
44642
  },
44643
+ "tfa-suno": {
44644
+ preinstallCloud: true,
44645
+ category: "media",
44646
+ pkg: "@todoforai/suno-api",
44647
+ installer: "npm",
44648
+ label: "Suno (TODOforAI)",
44649
+ summary: "Generate music and songs from a prompt, billed to your TODOforAI credit.",
44650
+ platformAuth: true,
44651
+ versionCmd: "tfa-suno --version 2>/dev/null | sed -n 1p",
44652
+ credentialPaths: [
44653
+ "~/.config/todoforai/credentials.json"
44654
+ ],
44655
+ description: "Suno music generation via TODOforAI — no Suno key needed, billed to the user's TODOforAI credit (generate $0.12 for 2 songs, refunded if the task fails). Same commands as suno-api: `generate [prompt] [--customMode --lyrics <file> --style --title] [--instrumental] [--model V6]` polls until done and prints audio URLs; `--no-poll` + `details <taskId> [--wait]`; `stems`, `midi`, `lyrics-ts`, `replace-section`. Audio URLs expire after 14 days — download the mp3s you keep. Prefer this over suno-api unless the user has their own sunoapi.org key."
44656
+ },
44615
44657
  ntn: {
44616
44658
  preinstallCloud: true,
44617
44659
  category: "utility",
@@ -44672,6 +44714,9 @@ var tool_catalog_default = {
44672
44714
  preinstallCloud: true
44673
44715
  },
44674
44716
  glab: {
44717
+ preinstallCloud: true,
44718
+ cloudInstallCmd: "curl -fsSL https://gitlab.com/gitlab-org/cli/-/releases/v1.119.0/downloads/glab_1.119.0_linux_amd64.tar.gz | tar -xz -C /tmp bin/glab && install -m0755 /tmp/bin/glab /usr/local/bin/glab",
44719
+ cloudVerifyCmd: "glab --version",
44675
44720
  category: "development",
44676
44721
  pkg: "glab",
44677
44722
  installer: "binary",
@@ -44703,6 +44748,7 @@ var tool_catalog_default = {
44703
44748
  canStreamLogin: true
44704
44749
  },
44705
44750
  eas: {
44751
+ preinstallCloud: true,
44706
44752
  category: "deployment",
44707
44753
  pkg: "eas-cli",
44708
44754
  installer: "npm",
@@ -44718,6 +44764,7 @@ var tool_catalog_default = {
44718
44764
  versionCmd: "eas --version 2>/dev/null | sed -n 1p"
44719
44765
  },
44720
44766
  netlify: {
44767
+ preinstallCloud: true,
44721
44768
  category: "deployment",
44722
44769
  pkg: "netlify-cli",
44723
44770
  installer: "npm",
@@ -44783,6 +44830,9 @@ var tool_catalog_default = {
44783
44830
  canStreamLogin: true
44784
44831
  },
44785
44832
  flyctl: {
44833
+ preinstallCloud: true,
44834
+ cloudInstallCmd: "curl -fsSL https://github.com/superfly/flyctl/releases/download/v0.4.108/flyctl_0.4.108_Linux_x86_64.tar.gz | tar -xz -C /usr/local/bin flyctl",
44835
+ cloudVerifyCmd: "flyctl version",
44786
44836
  category: "cloud",
44787
44837
  pkg: "flyctl",
44788
44838
  installer: "binary",
@@ -44810,6 +44860,7 @@ var tool_catalog_default = {
44810
44860
  versionCmd: "supabase --version 2>/dev/null | sed -n 1p"
44811
44861
  },
44812
44862
  railway: {
44863
+ preinstallCloud: true,
44813
44864
  category: "cloud",
44814
44865
  pkg: "@railway/cli",
44815
44866
  installer: "npm",
@@ -44851,6 +44902,7 @@ var tool_catalog_default = {
44851
44902
  versionCmd: "shop-app --version 2>/dev/null | sed -n 1p"
44852
44903
  },
44853
44904
  "datadog-ci": {
44905
+ preinstallCloud: true,
44854
44906
  category: "monitoring",
44855
44907
  pkg: "@datadog/datadog-ci",
44856
44908
  installer: "npm",
@@ -44879,7 +44931,7 @@ var tool_catalog_default = {
44879
44931
  installer: "npm",
44880
44932
  label: "TODOforAI",
44881
44933
  summary: "Create and follow up on your TODOs, and show results in the chat.",
44882
- 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; `--card <name>` via project renderer; `--alias <name>` updates in place), `open <url>` embeds a live preview. 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.',
44934
+ 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 `![](path.png)` with a local/relative path does NOT resolve to workspace files or attachments and 404s in the browser. `--template` for registry workflows.',
44883
44935
  installCmd: "npm install -g @todoforai/cli",
44884
44936
  versionCmd: "tfa-cli --version 2>/dev/null | sed -n 1p",
44885
44937
  preinstallCloud: true,
@@ -45006,7 +45058,7 @@ var tool_catalog_default = {
45006
45058
  label: "Google Cloud",
45007
45059
  summary: "Manage your Google Cloud projects and services.",
45008
45060
  needsPython: true,
45009
- installCmd: `curl -fsSL https://dl.google.com/dl/cloudsdk/channels/rapid/google-cloud-sdk.tar.gz | tar -xzC "$HOME" && CLOUDSDK_PYTHON="$py" "$HOME/google-cloud-sdk/install.sh" --quiet --install-python false --usage-reporting false --path-update false --command-completion false && 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"`,
45061
+ 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"`,
45010
45062
  statusCmd: `gcloud auth list --filter=status:ACTIVE --format="value(account)" 2>/dev/null | tr -d '\\r' | grep -m1 .`,
45011
45063
  loginCmd: "gcloud auth login --no-launch-browser",
45012
45064
  loginNeedsPty: true,
@@ -45015,8 +45067,7 @@ var tool_catalog_default = {
45015
45067
  credentialPaths: ["~/.config/gcloud"],
45016
45068
  description: "Official Google Cloud CLI. `gcloud auth login --no-launch-browser` prints the OAuth URL and waits for the pasted verification code.",
45017
45069
  versionCmd: "gcloud --version 2>/dev/null | sed -n 1p",
45018
- uninstallCmd: 'rm -rf "$HOME/google-cloud-sdk" "$HOME/.todoforai/tools/bin/gcloud"; hash -r 2>/dev/null; ! command -v gcloud >/dev/null 2>&1 || { echo "gcloud is still on PATH: $(command -v gcloud)"; exit 1; }',
45019
- updateCmd: "gcloud components update --quiet"
45070
+ uninstallCmd: 'rm -rf "$HOME/google-cloud-sdk" "$HOME/.todoforai/tools/bin/gcloud"; hash -r 2>/dev/null; ! command -v gcloud >/dev/null 2>&1 || { echo "gcloud is still on PATH: $(command -v gcloud)"; exit 1; }'
45020
45071
  },
45021
45072
  az: {
45022
45073
  category: "cloud",
@@ -45150,19 +45201,6 @@ var tool_catalog_default = {
45150
45201
  },
45151
45202
  preinstallJarvis: true
45152
45203
  },
45153
- "todoforai-browser": {
45154
- category: "development",
45155
- pkg: "@todoforai/browser",
45156
- aliases: ["tfa-browser"],
45157
- installer: "npm",
45158
- preinstallCloud: true,
45159
- label: "Browser (Extension)",
45160
- summary: "Drive your own browser through the TODOforAI extension.",
45161
- versionCmd: `node -p "require(require('fs').realpathSync(require('child_process').execSync('command -v todoforai-browser',{encoding:'utf8'}).trim()).replace(/\\/dist\\/index\\.js$/, '/package.json')).version" 2>/dev/null`,
45162
- internal: true,
45163
- preinstallJarvis: true,
45164
- description: "Browser automation via extension (non-headless), web scraping, accessibility tree snapshots, screenshots, PDF generation"
45165
- },
45166
45204
  "tfa-explore": {
45167
45205
  preinstallCloud: true,
45168
45206
  category: "development",
@@ -45633,6 +45671,20 @@ var tool_catalog_default = {
45633
45671
  description: "WordPress REST API CLI. `wordpress-api auth --site https://example.com --user <user> --password '<application password>'` (application password, not the login password). Reading published content needs no auth. `posts list|get|create|update|delete`, `pages`, `media`, `categories`. `posts create` saves a draft unless --status publish. On Elementor/JetEngine sites content.rendered comes back empty — fetch the post's public URL for the body.",
45634
45672
  versionCmd: "wordpress-api --version 2>/dev/null | sed -n 1p"
45635
45673
  },
45674
+ "mailchimp-api": {
45675
+ category: "marketing",
45676
+ pkg: "@todoforai/mailchimp-api",
45677
+ installer: "npm",
45678
+ label: "Mailchimp",
45679
+ summary: "Manage Mailchimp audiences, contacts, email campaigns and reports.",
45680
+ loginCmd: "mailchimp-api auth --api-key <api_key>",
45681
+ statusCmd: `mailchimp-api whoami 2>/dev/null | sed -n 's/.*"account_name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p' | grep -m1 .`,
45682
+ credentialPaths: [
45683
+ "~/.config/mailchimp-api/credentials.json"
45684
+ ],
45685
+ versionCmd: "mailchimp-api --version 2>/dev/null | sed -n 1p",
45686
+ description: "Independent CLI over the official Mailchimp Marketing API: 155 endpoint commands for lists (email audiences), members (contacts; email or MD5), tags, segments, merge fields, interests, notes/events, campaigns/content/tests/scheduling, templates, reports, assets, landing pages, classic automations, journey triggers, webhooks and batches. JSON output; --data JSON|@file|-; --schema; --all pagination. Every write requires --yes; use --dry-run first. Raw request METHOD /path covers other Marketing endpoints. API-key auth, not OAuth onboarding. Plan/API restrictions apply; no Transactional/Mandrill or Open Commerce. Run --help for limits and consent cautions."
45687
+ },
45636
45688
  "facebook-pages-api": {
45637
45689
  preinstallCloud: true,
45638
45690
  category: "social",
@@ -45647,8 +45699,10 @@ var tool_catalog_default = {
45647
45699
  versionCmd: "facebook-pages-api --version 2>/dev/null | sed -n 1p"
45648
45700
  },
45649
45701
  "ews-api": {
45702
+ preinstallCloud: true,
45650
45703
  category: "email",
45651
45704
  pkg: "ews-api",
45705
+ packages: ["ews-api==0.2.0"],
45652
45706
  installer: "pip",
45653
45707
  label: "Exchange (EWS)",
45654
45708
  summary: "Read and write mail on a company Exchange server.",
@@ -45676,6 +45730,7 @@ var tool_catalog_default = {
45676
45730
  canStreamLogin: true
45677
45731
  },
45678
45732
  neonctl: {
45733
+ preinstallCloud: true,
45679
45734
  category: "database",
45680
45735
  pkg: "neonctl",
45681
45736
  installer: "npm",
@@ -45698,7 +45753,7 @@ var tool_catalog_default = {
45698
45753
  label: "Microsoft 365",
45699
45754
  summary: "Work with Microsoft 365 mail, Teams, SharePoint and users.",
45700
45755
  statusCmd: `m365 status --output text 2>/dev/null | tr -d '"' | grep -vi 'logged out' | grep -m1 .`,
45701
- loginCmd: "m365 login --authType deviceCode --appId 04b07795-8ddb-461a-bbee-02f9e1bf7b46",
45756
+ loginCmd: "m365 login --authType deviceCode --appId 14d82eec-204b-4c2f-b7e8-296a70dab67e",
45702
45757
  deviceCodePattern: "enter the code ([A-Z0-9]{8,}) to authenticate",
45703
45758
  oauthUrlPattern: "open the page (https://\\S+) and enter",
45704
45759
  credentialPaths: ["~/.cli-m365-msal.json", "~/.cli-m365-connection.json"],
@@ -45737,6 +45792,7 @@ var tool_catalog_default = {
45737
45792
  versionCmd: "posthog-cli --version 2>/dev/null | sed -n 1p"
45738
45793
  },
45739
45794
  canva: {
45795
+ preinstallCloud: true,
45740
45796
  category: "design",
45741
45797
  pkg: "@canva/cli",
45742
45798
  installer: "npm",
@@ -45752,6 +45808,7 @@ var tool_catalog_default = {
45752
45808
  oauthUrlPattern: "OPEN_URL: (https://www\\.canva\\.com/api/oauth/authorize\\S+)"
45753
45809
  },
45754
45810
  webflow: {
45811
+ preinstallCloud: true,
45755
45812
  category: "development",
45756
45813
  pkg: "@webflow/webflow-cli",
45757
45814
  installer: "npm",
@@ -45766,6 +45823,7 @@ var tool_catalog_default = {
45766
45823
  versionCmd: "webflow --version 2>/dev/null | sed -n 1p"
45767
45824
  },
45768
45825
  readwise: {
45826
+ preinstallCloud: true,
45769
45827
  category: "productivity",
45770
45828
  pkg: "@readwise/cli",
45771
45829
  installer: "npm",
@@ -45836,6 +45894,7 @@ var tool_catalog_default = {
45836
45894
  versionCmd: "exa --version 2>/dev/null | sed -n 1p"
45837
45895
  },
45838
45896
  mobbin: {
45897
+ preinstallCloud: true,
45839
45898
  category: "design",
45840
45899
  pkg: "mobbin",
45841
45900
  installer: "npm",
@@ -45866,6 +45925,7 @@ var tool_catalog_default = {
45866
45925
  versionCmd: "{ python -c 'import sys; from importlib.metadata import version; print(version(sys.argv[1]))' blender-mcp || python3 -c 'import sys; from importlib.metadata import version; print(version(sys.argv[1]))' blender-mcp; } 2>/dev/null"
45867
45926
  },
45868
45927
  "google-ads": {
45928
+ preinstallCloud: true,
45869
45929
  category: "marketing",
45870
45930
  pkg: "google-ads",
45871
45931
  packages: [
@@ -45935,6 +45995,7 @@ var model_promos_default = {
45935
45995
  { family: "gpt-5-6-sol", rate: 0.85, starts: "2026-08-26", ends: "2026-09-13" },
45936
45996
  { family: "gpt-5-6-sol", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21" },
45937
45997
  { family: "gpt-6-sol", rate: 0.8, starts: "2026-09-22", ends: "2026-11-21" },
45998
+ { family: "gpt-6-1-sol", rate: 0.8, starts: "2026-09-29", ends: "2026-11-21" },
45938
45999
  { family: "gpt-6-luna", rate: 0.8, starts: "2026-09-22", ends: "2026-11-21" },
45939
46000
  { family: "gpt-6-astra", rate: 0.85, starts: "2026-09-05", ends: "2026-09-13", boostable: false },
45940
46001
  { family: "gpt-6-astra", rate: 0.8, starts: "2026-09-14", ends: "2026-11-21", boostable: false },
@@ -46135,7 +46196,7 @@ import { parseArgs } from "util";
46135
46196
  // package.json
46136
46197
  var package_default = {
46137
46198
  name: "@todoforai/cli",
46138
- version: "0.1.60",
46199
+ version: "0.1.62",
46139
46200
  type: "module",
46140
46201
  bin: {
46141
46202
  "todoforai-cli": "bin/todoforai-cli.js",
@@ -46247,15 +46308,27 @@ Usage:
46247
46308
  tfa-cli show <file|-> [todo-id] Render a file by mimetype (- reads stdin)
46248
46309
  [--title T] [--alias A] [--mime M] [--card <name>] [--link] [--json]
46249
46310
  --link: compact download chip instead of inline render
46250
- Prints "<todoId>:<alias|id> <public url>". Re-showing with the same
46251
- --alias updates the block in place; old versions stay at /<id>/<version>
46252
- tfa-cli show rm <ref|alias> Take it down (block, url, every version)
46311
+ Prints "<todoId>:<alias|id>". Private: visible to whoever can read the todo.
46312
+ Re-showing with the same --alias updates the block in place (new version)
46313
+ --alias <todoId>:<alias> updates that todo's block instead,
46314
+ so a later chat can revise an artifact made elsewhere
46315
+ tfa-cli show share <ref|alias> Make it public; prints its link (serves the latest version)
46316
+ tfa-cli show unshare <ref|alias> Make it private again (the link stops working)
46317
+ tfa-cli show rm <ref|alias> Take it down (block, link, every version)
46253
46318
  tfa-cli show list [todo-id] List show blocks (ref, title, mime/url, card)
46254
46319
  [--project <id>] [--card <name>] [--json]
46255
46320
  no todo-id + --project (or $TODOFORAI_PROJECT_ID) = every todo
46256
46321
  tfa-cli open <url> [todo-id] Live http(s) url as a preview [--title T] [--alias A] [--json]
46257
46322
 
46258
46323
  todo-id defaults to $TODOFORAI_TODO_ID (agent shell) or the last todo this CLI touched.
46324
+
46325
+ When to use what:
46326
+ read once, move on → plain chat reply
46327
+ interactive, this chat only → html_snippet (can exec/connect/chat back)
46328
+ keep / revise later → show <file> --alias <name> (versioned, private)
46329
+ someone outside needs it → show share <alias> (public link, revocable)
46330
+ a live web page → open <url>
46331
+ a format the user asked for → show the file (pptx, docx, xlsx, pdf…)
46259
46332
  `);
46260
46333
  }
46261
46334
  var STATUS_HELP = {
@@ -46314,7 +46387,6 @@ function parseCliArgs() {
46314
46387
  link: { type: "boolean", default: false },
46315
46388
  direction: { type: "string" },
46316
46389
  url: { type: "string" },
46317
- "pasted-file": { type: "string" },
46318
46390
  account: { type: "string" },
46319
46391
  "from-company": { type: "boolean", default: false },
46320
46392
  "dry-run": { type: "boolean", default: false },
@@ -48309,7 +48381,7 @@ var createBuilder = (self, _styler, _isEmpty) => {
48309
48381
  return applyStyle(builder, "" + arguments_[0]);
48310
48382
  }
48311
48383
  if (arguments_.length === 2) {
48312
- return applyStyle(builder, arguments_[0] + " " + arguments_[1]);
48384
+ return applyStyle(builder, `${arguments_[0] ?? ""} ${arguments_[1] ?? ""}`);
48313
48385
  }
48314
48386
  return applyStyle(builder, arguments_.join(" "));
48315
48387
  };
@@ -49499,7 +49571,7 @@ var ADAPTERS = { Gmail: gmail, Outlook: outlook, Chat: chat };
49499
49571
  var DEVICE_CHANNELS = Object.keys(ADAPTERS);
49500
49572
  function checkChannel(channel) {
49501
49573
  const a = ADAPTERS[channel];
49502
- return a ? a.check() : `${channel} is not read on the device (server-side: pass --url, or paste)`;
49574
+ return a ? a.check() : `${channel} is not read on the device (server-side: pass --url)`;
49503
49575
  }
49504
49576
  function collectChannel(channel, max = 40) {
49505
49577
  const a = ADAPTERS[channel];
@@ -49779,12 +49851,12 @@ Usage:
49779
49851
  tfa-cli brand rename <name>
49780
49852
  tfa-cli brand voice Learned profile + sources
49781
49853
  tfa-cli brand voice answers [<q>=<a>…] Show / set the Manual source: answers to the style questions
49782
- tfa-cli brand voice collect <channel> [--url U | --pasted-file <F|-> | --max N]
49854
+ tfa-cli brand voice collect <channel> [--url U | --max N]
49783
49855
  Add a writing sample source.
49784
49856
  On this device (signed-in CLI, reply pairs):
49785
49857
  Gmail (zele) Outlook (outlook-api) Chat (tfa-memory)
49786
- Server-side (--url or paste):
49787
- X LinkedIn Facebook Instagram Slack Teams
49858
+ Server-side (--url):
49859
+ X LinkedIn Facebook Instagram
49788
49860
  --dry-run print the samples as JSONL, store nothing
49789
49861
  tfa-cli brand voice check [<channel>|--all] Can this device read the channel? tool, login, sample count
49790
49862
  tfa-cli brand voice remove <channel>
@@ -49792,7 +49864,6 @@ Usage:
49792
49864
  tfa-cli brand voice correct "<what is off>" Tell the learner what it got wrong; profile is updated
49793
49865
  in one pass and the correction is kept for every re-learn
49794
49866
 
49795
- --pasted-file - reads stdin. Delete is UI-only.
49796
49867
 
49797
49868
  Questions the UI asks (keys for 'answers'):
49798
49869
  "What makes you different from competitors?"
@@ -49892,7 +49963,7 @@ async function voiceDeviceCommand(rest, args) {
49892
49963
  async function voiceCommand(api, projectId, rest, args) {
49893
49964
  const [verb, ...vargs] = rest;
49894
49965
  if (verb === "collect" && !vargs[0])
49895
- fail("Usage: tfa-cli brand voice collect <channel> [--url U | --pasted-file F|- | --account <page-id> | --max N] [--dry-run]");
49966
+ fail("Usage: tfa-cli brand voice collect <channel> [--url U | --account <page-id> | --max N] [--dry-run]");
49896
49967
  if (verb === "answers") {
49897
49968
  const answers = manualAnswersOf((await api.listVoiceSources(projectId)).sources);
49898
49969
  if (vargs.length) {
@@ -49938,12 +50009,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
49938
50009
  }
49939
50010
  if (verb === "collect") {
49940
50011
  const channel = vargs[0];
49941
- let pasted;
49942
- if (args["pasted-file"]) {
49943
- const { readFileSync: readFileSync3 } = await import("node:fs");
49944
- pasted = readFileSync3(args["pasted-file"] === "-" ? 0 : args["pasted-file"], "utf8");
49945
- }
49946
- const onDevice = !pasted && !args.url && ADAPTERS[channel];
50012
+ const onDevice = !args.url && ADAPTERS[channel];
49947
50013
  if (onDevice) {
49948
50014
  const reason = checkChannel(channel);
49949
50015
  if (reason)
@@ -49958,7 +50024,7 @@ ${DIM}match ${profile.match}/100 · source ${profile.source}${RESET}
49958
50024
  `);
49959
50025
  return;
49960
50026
  }
49961
- const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, pasted, account: args.account });
50027
+ const { sources } = await api.collectVoiceSource(projectId, channel, { url: args.url, account: args.account });
49962
50028
  process.stderr.write(`${GREEN}✅ ${channel} collected (${sources.length} source(s))${RESET}
49963
50029
  `);
49964
50030
  return;
@@ -56031,7 +56097,7 @@ class $ZodCyclicError extends Error {
56031
56097
  this.name = "ZodCyclicError";
56032
56098
  }
56033
56099
  }
56034
- var STATE = "~memo";
56100
+ var STATE2 = "~memo";
56035
56101
  var NO_ISSUES = [];
56036
56102
  function isRef(value) {
56037
56103
  return value !== null && typeof value === "object";
@@ -56241,10 +56307,10 @@ var memo = {
56241
56307
  const input = payload.value;
56242
56308
  if (!isRef(input))
56243
56309
  return base(payload, ctx);
56244
- let state = ctx[STATE];
56310
+ let state = ctx[STATE2];
56245
56311
  if (!state) {
56246
56312
  state = { buckets: new WeakMap, backEdges: undefined };
56247
- ctx[STATE] = state;
56313
+ ctx[STATE2] = state;
56248
56314
  }
56249
56315
  let bucket;
56250
56316
  if (lastCtx === ctx) {
@@ -56293,7 +56359,7 @@ function memoizer() {
56293
56359
  return memo;
56294
56360
  }
56295
56361
  function isBackEdge(ctx, value) {
56296
- const backEdges = ctx[STATE]?.backEdges;
56362
+ const backEdges = ctx[STATE2]?.backEdges;
56297
56363
  return backEdges !== undefined && isRef(value) && backEdges.has(value);
56298
56364
  }
56299
56365
  // node_modules/zod/v4/locales/index.js
@@ -73532,53 +73598,124 @@ function protocolErrorResponse(error62) {
73532
73598
  };
73533
73599
  }
73534
73600
 
73601
+ // node_modules/@agentclientprotocol/sdk/dist/stream-limits.js
73602
+ var DEFAULT_MAX_MESSAGE_BYTES = 32 * 1024 * 1024;
73603
+
73604
+ class MessageTooLargeError extends Error {
73605
+ maxMessageBytes;
73606
+ constructor(maxMessageBytes) {
73607
+ super(`Incoming ACP data exceeds the configured ${maxMessageBytes} byte limit`);
73608
+ this.maxMessageBytes = maxMessageBytes;
73609
+ this.name = "MessageTooLargeError";
73610
+ }
73611
+ }
73612
+ function resolveMaxMessageBytes(value) {
73613
+ const maxMessageBytes = value ?? DEFAULT_MAX_MESSAGE_BYTES;
73614
+ if (!Number.isSafeInteger(maxMessageBytes) || maxMessageBytes <= 0) {
73615
+ throw new RangeError("maxMessageBytes must be a positive safe integer");
73616
+ }
73617
+ return maxMessageBytes;
73618
+ }
73619
+
73620
+ class MessageBuffer {
73621
+ #buffer = new Uint8Array(0);
73622
+ #length = 0;
73623
+ #maxMessageBytes;
73624
+ constructor(maxMessageBytes) {
73625
+ this.#maxMessageBytes = resolveMaxMessageBytes(maxMessageBytes);
73626
+ }
73627
+ get byteLength() {
73628
+ return this.#length;
73629
+ }
73630
+ get lastByte() {
73631
+ return this.#buffer[this.#length - 1];
73632
+ }
73633
+ #checkAppend(byteLength) {
73634
+ if (byteLength > this.#maxMessageBytes - this.#length) {
73635
+ this.clear();
73636
+ throw new MessageTooLargeError(this.#maxMessageBytes);
73637
+ }
73638
+ }
73639
+ append(bytes) {
73640
+ this.#checkAppend(bytes.byteLength);
73641
+ const length = this.#length + bytes.byteLength;
73642
+ if (length > this.#buffer.byteLength) {
73643
+ const capacity = Math.min(this.#maxMessageBytes, Math.max(length, this.#buffer.byteLength * 2, 1024));
73644
+ const buffer = new Uint8Array(capacity);
73645
+ buffer.set(this.#buffer.subarray(0, this.#length));
73646
+ this.#buffer = buffer;
73647
+ }
73648
+ this.#buffer.set(bytes, this.#length);
73649
+ this.#length = length;
73650
+ }
73651
+ take() {
73652
+ const bytes = this.#buffer.subarray(0, this.#length);
73653
+ this.clear();
73654
+ return bytes;
73655
+ }
73656
+ clear() {
73657
+ this.#buffer = new Uint8Array(0);
73658
+ this.#length = 0;
73659
+ }
73660
+ }
73661
+
73535
73662
  // node_modules/@agentclientprotocol/sdk/dist/line-buffer.js
73536
73663
  var newline = 10;
73537
73664
 
73538
73665
  class LineBuffer {
73539
- #pending = [];
73540
- push(chunk) {
73541
- const lines = [];
73666
+ #pending;
73667
+ #maxMessageBytes;
73668
+ constructor(maxMessageBytes) {
73669
+ this.#maxMessageBytes = resolveMaxMessageBytes(maxMessageBytes);
73670
+ this.#pending = new MessageBuffer(Math.min(Number.MAX_SAFE_INTEGER, this.#maxMessageBytes + 1));
73671
+ }
73672
+ *push(chunk) {
73542
73673
  let start = 0;
73543
73674
  let newlineIndex = chunk.indexOf(newline, start);
73544
73675
  while (newlineIndex !== -1) {
73545
- lines.push(this.#takeLine(chunk.subarray(start, newlineIndex)));
73676
+ yield this.#takeLine(chunk.subarray(start, newlineIndex));
73546
73677
  start = newlineIndex + 1;
73547
73678
  newlineIndex = chunk.indexOf(newline, start);
73548
73679
  }
73549
73680
  if (start < chunk.byteLength) {
73550
- this.#pending.push(start === 0 ? chunk : new Uint8Array(chunk.subarray(start)));
73681
+ const tail = chunk.subarray(start);
73682
+ this.#checkLine(tail);
73683
+ this.#pending.append(tail);
73551
73684
  }
73552
- return lines;
73553
73685
  }
73554
73686
  flush() {
73555
- if (this.#pending.length === 0) {
73687
+ if (this.#pending.byteLength === 0) {
73556
73688
  return;
73557
73689
  }
73558
- return this.#takeLine(new Uint8Array(0));
73690
+ return stripCarriageReturn(this.#pending.take());
73691
+ }
73692
+ clear() {
73693
+ this.#pending.clear();
73559
73694
  }
73560
73695
  #takeLine(tail) {
73561
- if (this.#pending.length === 0) {
73562
- return tail;
73563
- }
73564
- let total = tail.byteLength;
73565
- for (const part of this.#pending) {
73566
- total += part.byteLength;
73696
+ this.#checkLine(tail);
73697
+ if (this.#pending.byteLength === 0) {
73698
+ return stripCarriageReturn(tail);
73567
73699
  }
73568
- const line = new Uint8Array(total);
73569
- let offset = 0;
73570
- for (const part of this.#pending) {
73571
- line.set(part, offset);
73572
- offset += part.byteLength;
73700
+ this.#pending.append(tail);
73701
+ return stripCarriageReturn(this.#pending.take());
73702
+ }
73703
+ #checkLine(tail) {
73704
+ const lastByte = tail.byteLength > 0 ? tail[tail.byteLength - 1] : this.#pending.lastByte;
73705
+ const byteLength = this.#pending.byteLength + tail.byteLength - (lastByte === 13 ? 1 : 0);
73706
+ if (byteLength > this.#maxMessageBytes) {
73707
+ this.clear();
73708
+ throw new MessageTooLargeError(this.#maxMessageBytes);
73573
73709
  }
73574
- line.set(tail, offset);
73575
- this.#pending = [];
73576
- return line;
73577
73710
  }
73578
73711
  }
73712
+ function stripCarriageReturn(line) {
73713
+ return line[line.byteLength - 1] === 13 ? line.subarray(0, -1) : line;
73714
+ }
73579
73715
 
73580
73716
  // node_modules/@agentclientprotocol/sdk/dist/stream.js
73581
- function ndJsonStream(output2, input2) {
73717
+ function ndJsonStream(output2, input2, options = {}) {
73718
+ const maxMessageBytes = resolveMaxMessageBytes(options.maxMessageBytes);
73582
73719
  const textEncoder = new TextEncoder;
73583
73720
  const textDecoder = new TextDecoder;
73584
73721
  let cancelled = false;
@@ -73600,7 +73737,7 @@ function ndJsonStream(output2, input2) {
73600
73737
  };
73601
73738
  const readable = new ReadableStream({
73602
73739
  async start(controller) {
73603
- const lines = new LineBuffer;
73740
+ const lines = new LineBuffer(maxMessageBytes);
73604
73741
  const enqueueLine = async (lineBytes) => {
73605
73742
  const trimmedLine = textDecoder.decode(lineBytes).trim();
73606
73743
  if (!trimmedLine) {
@@ -73652,8 +73789,10 @@ function ndJsonStream(output2, input2) {
73652
73789
  return;
73653
73790
  }
73654
73791
  controller.error(err);
73792
+ reader.cancel(err).catch(() => {});
73655
73793
  return;
73656
73794
  } finally {
73795
+ lines.clear();
73657
73796
  if (inputReader === reader) {
73658
73797
  inputReader = undefined;
73659
73798
  }
@@ -73695,8 +73834,8 @@ var zGuardCreateElicitationResponseCancel = object({
73695
73834
  action: literal("cancel")
73696
73835
  });
73697
73836
  // node_modules/@agentclientprotocol/sdk/dist/acp.js
73698
- function ndJsonStream2(output2, input2) {
73699
- return ndJsonStream(output2, input2);
73837
+ function ndJsonStream2(output2, input2, options) {
73838
+ return ndJsonStream(output2, input2, options);
73700
73839
  }
73701
73840
  function emptyObjectResponse(response) {
73702
73841
  return response ?? {};
@@ -75135,6 +75274,7 @@ try {
75135
75274
  const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
75136
75275
  checkForUpdates(JSON.parse(readFileSync3(pkgPath, "utf-8")));
75137
75276
  } catch {}
75277
+ var aliasTodoId = (alias) => typeof alias === "string" && alias.includes(":") ? alias.slice(0, alias.indexOf(":")) : undefined;
75138
75278
  function formatPathWithTilde(path5) {
75139
75279
  const home = homedir3();
75140
75280
  return path5.startsWith(home) ? path5.replace(home, "~") : path5;
@@ -75479,14 +75619,16 @@ async function main() {
75479
75619
  const kind = it.url ? `url ${it.url}` : it.mime || "";
75480
75620
  const card = it.cardRef ? ` card=${it.cardRef}` : "";
75481
75621
  const link = it.display === "link" ? " link" : "";
75482
- console.log(`${it.ref} ${it.title || it.filename || ""} ${kind}${card}${link}`);
75622
+ const shared = it.shareUrl ? ` ${it.shareUrl}` : "";
75623
+ console.log(`${it.ref} ${it.title || it.filename || ""} ${kind}${card}${link}${shared}`);
75483
75624
  }
75484
75625
  return;
75485
75626
  }
75486
- if (positionals[0] === "show" && positionals[1] === "rm") {
75627
+ const showSub = positionals[1];
75628
+ if (positionals[0] === "show" && (showSub === "rm" || showSub === "share" || showSub === "unshare")) {
75487
75629
  const arg = positionals[2];
75488
75630
  if (!arg) {
75489
- process.stderr.write(`${RED3}Usage: tfa-cli show rm <ref|alias>${RESET2}
75631
+ process.stderr.write(`${RED3}Usage: tfa-cli show ${showSub} <ref|alias>${RESET2}
75490
75632
  `);
75491
75633
  process.exit(2);
75492
75634
  }
@@ -75494,18 +75636,30 @@ async function main() {
75494
75636
  const todoId = sep >= 0 ? arg.slice(0, sep) : getEnv("TODO_ID") || cfgScope.data.last_todo_id;
75495
75637
  const key = sep >= 0 ? arg.slice(sep + 1) : arg;
75496
75638
  if (!todoId || !key) {
75497
- process.stderr.write(`${RED3}show rm: need <todoId>:<alias> (no current todo)${RESET2}
75639
+ process.stderr.write(`${RED3}show ${showSub}: need <todoId>:<alias> (no current todo)${RESET2}
75498
75640
  `);
75499
75641
  process.exit(2);
75500
75642
  }
75501
- await api2.removeShow(todoId, key);
75502
- process.stderr.write(`${GREEN2}\u2705 removed ${todoId}:${key}${RESET2}
75643
+ if (showSub === "rm") {
75644
+ await api2.removeShow(todoId, key);
75645
+ process.stderr.write(`${GREEN2}\u2705 removed ${todoId}:${key}${RESET2}
75503
75646
  `);
75647
+ } else if (showSub === "share") {
75648
+ const res = await api2.shareShow(todoId, key);
75649
+ if (args.json)
75650
+ console.log(JSON.stringify(res, null, 2));
75651
+ else
75652
+ console.log(res.url);
75653
+ } else {
75654
+ await api2.unshareShow(todoId, key);
75655
+ process.stderr.write(`${GREEN2}\u2705 ${todoId}:${key} is private again${RESET2}
75656
+ `);
75657
+ }
75504
75658
  return;
75505
75659
  }
75506
75660
  if (positionals[0] === "show") {
75507
75661
  const [, filePath, todoArg] = positionals;
75508
- const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
75662
+ const todoId = todoArg || aliasTodoId(args.alias) || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
75509
75663
  if (!filePath || !todoId) {
75510
75664
  process.stderr.write(`${RED3}Usage: tfa-cli show <file|-> [todo-id] [--title T] [--alias A] [--mime M] [--card <name>] [--link]${RESET2}
75511
75665
  `);
@@ -75543,12 +75697,12 @@ async function main() {
75543
75697
  if (args.json)
75544
75698
  console.log(JSON.stringify(res, null, 2));
75545
75699
  else
75546
- console.log(res.url ? `${res.ref} ${res.url}` : res.ref);
75700
+ console.log(res.shareUrl ? `${res.ref} ${res.shareUrl}` : res.ref);
75547
75701
  return;
75548
75702
  }
75549
75703
  if (positionals[0] === "open") {
75550
75704
  const [, url2, todoArg] = positionals;
75551
- const todoId = todoArg || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
75705
+ const todoId = todoArg || aliasTodoId(args.alias) || getEnv("TODO_ID") || cfgScope.data.last_todo_id;
75552
75706
  if (!url2 || !todoId) {
75553
75707
  process.stderr.write(`${RED3}Usage: tfa-cli open <url> [todo-id]${RESET2}
75554
75708
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.60",
3
+ "version": "0.1.62",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "bin/todoforai-cli.js",