@todoforai/cli 0.1.61 → 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 +223 -62
  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.61",
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 = {
@@ -48308,7 +48381,7 @@ var createBuilder = (self, _styler, _isEmpty) => {
48308
48381
  return applyStyle(builder, "" + arguments_[0]);
48309
48382
  }
48310
48383
  if (arguments_.length === 2) {
48311
- return applyStyle(builder, arguments_[0] + " " + arguments_[1]);
48384
+ return applyStyle(builder, `${arguments_[0] ?? ""} ${arguments_[1] ?? ""}`);
48312
48385
  }
48313
48386
  return applyStyle(builder, arguments_.join(" "));
48314
48387
  };
@@ -56024,7 +56097,7 @@ class $ZodCyclicError extends Error {
56024
56097
  this.name = "ZodCyclicError";
56025
56098
  }
56026
56099
  }
56027
- var STATE = "~memo";
56100
+ var STATE2 = "~memo";
56028
56101
  var NO_ISSUES = [];
56029
56102
  function isRef(value) {
56030
56103
  return value !== null && typeof value === "object";
@@ -56234,10 +56307,10 @@ var memo = {
56234
56307
  const input = payload.value;
56235
56308
  if (!isRef(input))
56236
56309
  return base(payload, ctx);
56237
- let state = ctx[STATE];
56310
+ let state = ctx[STATE2];
56238
56311
  if (!state) {
56239
56312
  state = { buckets: new WeakMap, backEdges: undefined };
56240
- ctx[STATE] = state;
56313
+ ctx[STATE2] = state;
56241
56314
  }
56242
56315
  let bucket;
56243
56316
  if (lastCtx === ctx) {
@@ -56286,7 +56359,7 @@ function memoizer() {
56286
56359
  return memo;
56287
56360
  }
56288
56361
  function isBackEdge(ctx, value) {
56289
- const backEdges = ctx[STATE]?.backEdges;
56362
+ const backEdges = ctx[STATE2]?.backEdges;
56290
56363
  return backEdges !== undefined && isRef(value) && backEdges.has(value);
56291
56364
  }
56292
56365
  // node_modules/zod/v4/locales/index.js
@@ -73525,53 +73598,124 @@ function protocolErrorResponse(error62) {
73525
73598
  };
73526
73599
  }
73527
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
+
73528
73662
  // node_modules/@agentclientprotocol/sdk/dist/line-buffer.js
73529
73663
  var newline = 10;
73530
73664
 
73531
73665
  class LineBuffer {
73532
- #pending = [];
73533
- push(chunk) {
73534
- 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) {
73535
73673
  let start = 0;
73536
73674
  let newlineIndex = chunk.indexOf(newline, start);
73537
73675
  while (newlineIndex !== -1) {
73538
- lines.push(this.#takeLine(chunk.subarray(start, newlineIndex)));
73676
+ yield this.#takeLine(chunk.subarray(start, newlineIndex));
73539
73677
  start = newlineIndex + 1;
73540
73678
  newlineIndex = chunk.indexOf(newline, start);
73541
73679
  }
73542
73680
  if (start < chunk.byteLength) {
73543
- 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);
73544
73684
  }
73545
- return lines;
73546
73685
  }
73547
73686
  flush() {
73548
- if (this.#pending.length === 0) {
73687
+ if (this.#pending.byteLength === 0) {
73549
73688
  return;
73550
73689
  }
73551
- return this.#takeLine(new Uint8Array(0));
73690
+ return stripCarriageReturn(this.#pending.take());
73691
+ }
73692
+ clear() {
73693
+ this.#pending.clear();
73552
73694
  }
73553
73695
  #takeLine(tail) {
73554
- if (this.#pending.length === 0) {
73555
- return tail;
73696
+ this.#checkLine(tail);
73697
+ if (this.#pending.byteLength === 0) {
73698
+ return stripCarriageReturn(tail);
73556
73699
  }
73557
- let total = tail.byteLength;
73558
- for (const part of this.#pending) {
73559
- total += part.byteLength;
73560
- }
73561
- const line = new Uint8Array(total);
73562
- let offset = 0;
73563
- for (const part of this.#pending) {
73564
- line.set(part, offset);
73565
- 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);
73566
73709
  }
73567
- line.set(tail, offset);
73568
- this.#pending = [];
73569
- return line;
73570
73710
  }
73571
73711
  }
73712
+ function stripCarriageReturn(line) {
73713
+ return line[line.byteLength - 1] === 13 ? line.subarray(0, -1) : line;
73714
+ }
73572
73715
 
73573
73716
  // node_modules/@agentclientprotocol/sdk/dist/stream.js
73574
- function ndJsonStream(output2, input2) {
73717
+ function ndJsonStream(output2, input2, options = {}) {
73718
+ const maxMessageBytes = resolveMaxMessageBytes(options.maxMessageBytes);
73575
73719
  const textEncoder = new TextEncoder;
73576
73720
  const textDecoder = new TextDecoder;
73577
73721
  let cancelled = false;
@@ -73593,7 +73737,7 @@ function ndJsonStream(output2, input2) {
73593
73737
  };
73594
73738
  const readable = new ReadableStream({
73595
73739
  async start(controller) {
73596
- const lines = new LineBuffer;
73740
+ const lines = new LineBuffer(maxMessageBytes);
73597
73741
  const enqueueLine = async (lineBytes) => {
73598
73742
  const trimmedLine = textDecoder.decode(lineBytes).trim();
73599
73743
  if (!trimmedLine) {
@@ -73645,8 +73789,10 @@ function ndJsonStream(output2, input2) {
73645
73789
  return;
73646
73790
  }
73647
73791
  controller.error(err);
73792
+ reader.cancel(err).catch(() => {});
73648
73793
  return;
73649
73794
  } finally {
73795
+ lines.clear();
73650
73796
  if (inputReader === reader) {
73651
73797
  inputReader = undefined;
73652
73798
  }
@@ -73688,8 +73834,8 @@ var zGuardCreateElicitationResponseCancel = object({
73688
73834
  action: literal("cancel")
73689
73835
  });
73690
73836
  // node_modules/@agentclientprotocol/sdk/dist/acp.js
73691
- function ndJsonStream2(output2, input2) {
73692
- return ndJsonStream(output2, input2);
73837
+ function ndJsonStream2(output2, input2, options) {
73838
+ return ndJsonStream(output2, input2, options);
73693
73839
  }
73694
73840
  function emptyObjectResponse(response) {
73695
73841
  return response ?? {};
@@ -75128,6 +75274,7 @@ try {
75128
75274
  const pkgPath = path4.resolve(fileURLToPath(import.meta.url), "../../package.json");
75129
75275
  checkForUpdates(JSON.parse(readFileSync3(pkgPath, "utf-8")));
75130
75276
  } catch {}
75277
+ var aliasTodoId = (alias) => typeof alias === "string" && alias.includes(":") ? alias.slice(0, alias.indexOf(":")) : undefined;
75131
75278
  function formatPathWithTilde(path5) {
75132
75279
  const home = homedir3();
75133
75280
  return path5.startsWith(home) ? path5.replace(home, "~") : path5;
@@ -75472,14 +75619,16 @@ async function main() {
75472
75619
  const kind = it.url ? `url ${it.url}` : it.mime || "";
75473
75620
  const card = it.cardRef ? ` card=${it.cardRef}` : "";
75474
75621
  const link = it.display === "link" ? " link" : "";
75475
- 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}`);
75476
75624
  }
75477
75625
  return;
75478
75626
  }
75479
- if (positionals[0] === "show" && positionals[1] === "rm") {
75627
+ const showSub = positionals[1];
75628
+ if (positionals[0] === "show" && (showSub === "rm" || showSub === "share" || showSub === "unshare")) {
75480
75629
  const arg = positionals[2];
75481
75630
  if (!arg) {
75482
- 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}
75483
75632
  `);
75484
75633
  process.exit(2);
75485
75634
  }
@@ -75487,18 +75636,30 @@ async function main() {
75487
75636
  const todoId = sep >= 0 ? arg.slice(0, sep) : getEnv("TODO_ID") || cfgScope.data.last_todo_id;
75488
75637
  const key = sep >= 0 ? arg.slice(sep + 1) : arg;
75489
75638
  if (!todoId || !key) {
75490
- 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}
75491
75640
  `);
75492
75641
  process.exit(2);
75493
75642
  }
75494
- await api2.removeShow(todoId, key);
75495
- 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}
75496
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
+ }
75497
75658
  return;
75498
75659
  }
75499
75660
  if (positionals[0] === "show") {
75500
75661
  const [, filePath, todoArg] = positionals;
75501
- 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;
75502
75663
  if (!filePath || !todoId) {
75503
75664
  process.stderr.write(`${RED3}Usage: tfa-cli show <file|-> [todo-id] [--title T] [--alias A] [--mime M] [--card <name>] [--link]${RESET2}
75504
75665
  `);
@@ -75536,12 +75697,12 @@ async function main() {
75536
75697
  if (args.json)
75537
75698
  console.log(JSON.stringify(res, null, 2));
75538
75699
  else
75539
- console.log(res.url ? `${res.ref} ${res.url}` : res.ref);
75700
+ console.log(res.shareUrl ? `${res.ref} ${res.shareUrl}` : res.ref);
75540
75701
  return;
75541
75702
  }
75542
75703
  if (positionals[0] === "open") {
75543
75704
  const [, url2, todoArg] = positionals;
75544
- 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;
75545
75706
  if (!url2 || !todoId) {
75546
75707
  process.stderr.write(`${RED3}Usage: tfa-cli open <url> [todo-id]${RESET2}
75547
75708
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@todoforai/cli",
3
- "version": "0.1.61",
3
+ "version": "0.1.62",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "todoforai-cli": "bin/todoforai-cli.js",