Skip to content

Releases: QMahyar/tele-cli

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 09 Sep 00:03

0.12.0 - 2026-09-08

Full audit ship: six new capabilities, a release supply chain (SBOM + build provenance), and roughly forty fixes from the five-domain adversarial audit (kernel, commands, streams, security, release engineering). Every done capability row is contract-tested against the real CLI surface.

Added

  • msg export - per-chat history export: tele msg export --account A --chat X [--format txt|jsonl] [--out FILE] [--limit N] [--offset-id M] [--since T] [--until T]. One message JSON object per line (default) or a human transcript; --out files are created with private permissions and sensitive basenames are refused; channel rows carry an additive link t.me permalink (https://t.me/<user>/<id> or https://t.me/c/<internal>/<id>); rows carry count/scanned/truncated when writing to a file. Also routed as a serve/MCP msg export op (Read lane, unbounded like download).
  • msg get --replied - with a single --id, fetches the message the target replies to via grammers' get_reply_to_message (resolves cross-chat discussion parents for channel-post comments) and embeds it as an additive replied_to object. Rejected for --ids batches; absent when there is no parent. Serve/MCP: GetParams.replied.
  • profile photos - profile photo history via iter_profile_photos: tele profile photos [--user USER] [--limit N]. Rows carry {id, date, size, sizes[], current} (photos.getUserPhotos for users, chat-photos sweep for channels/supergroups). Serve/MCP: profile photos op.
  • chat permissions - participant rights read-back: tele chat permissions --chat X --user U. Full admin-rights flag map (12 write-side flags), full banned-rights map (22 flags + until_date as RFC 3339), creator rights, plus rank/can_edit/kicked_by/promoted_by where provided; basic groups degrade to the participant role with an explanatory note. Serve/MCP: chat permissions op.
  • msg send --file - - streamed stdin uploads via upload_stream: --file-size <BYTES> (exact stdin byte count) and --file-name <NAME> required; same 2 GiB cap; cannot be combined with other --file paths or albums; --file-size/--file-name are rejected when no stdin upload is present.
  • MCP resources - tele mcp now advertises the MCP resources capability with three read-only context resources: tele://skill (the embedded SKILL.md, same bytes as tele skill print), tele://profile (bound account profile as JSON), and tele://dialogs (first 100 dialogs as JSON). Available in full and --read-only modes; unknown URIs fail with a clean -32602 listing the valid set.
  • Release supply chain - every release target ships an SPDX SBOM (anchore/sbom-action) and a build-provenance attestation (actions/attest-build-provenance); both actions are SHA-pinned and the token scope is limited to the build job. docs/release.md documents gh attestation verify and corrects the manual npm-publish fallback to reproduce the CI staging (publishing from the bare npm/ directory would ship a launcher that can never find a binary).
  • Skill versioning - the installed SKILL.md compatibility line is stamped from the binary version and tele skill install warns when an existing install predates the running binary; a contract test pins the stamp to Cargo.toml so it cannot drift again.

Fixed

Kernel and executor

  • FTS5 message cache: INSERT OR REPLACE no longer silently bypasses the sync triggers - PRAGMA recursive_triggers = ON at open, with an fts5vocab/integrity-check regression test that fails without it.
  • Token bucket: idle time no longer banks credit - the refill anchor advances while the bucket is full and refills are bounded by the remaining room, so a long idle gap cannot mint an instant full refill or keep over-minting afterwards.
  • Replay dedupe (CappedMap): re-delivered updates refresh their eviction position, so a redelivered update is no longer re-emitted as new when its original entry rotates out.
  • Peer eviction set is bounded (1024, clear-on-full) instead of permanent and unbounded.
  • +phone targets now require at least 5 digits (including country code): +1 can no longer trigger a real contacts.ImportContacts side effect; parenthesized/dotted phone spellings classify consistently between parse_target and classify_target.
  • Per-account fan-out: msg download, msg export, story send, and takeout export run without the 300s per-account budget (matching the documented serve lane table - the old cap killed legitimate mid-transfer work and reported exit 3), and timed-out account tasks are aborted instead of left running.
  • Proxy URLs: IPv6 hosts are bracket-wrapped (socks5://[::1]:9050) instead of producing a malformed authority.
  • --tag warns on tagged accounts that have no session file yet instead of skipping them silently.
  • config.toml writes fsync the temp file before rename (no zero-length config on crash); the missing-HOME panic prints an actionable, scrubbed message on stderr.
  • Session import: stale -wal/-shm sidecars of the previous database are deleted before the rename (a hot WAL from a crashed run would be replayed onto the imported session), and the account lock now covers only the copy+probe+install window instead of the whole source read.
  • Cache open: directory creation and permission hardening run via spawn_blocking instead of blocking the async runtime.

Messaging

  • msg delete reports the server's real affected count (partial when some ids were not deleted) instead of fabricating "deleted": N.
  • msg download --all resume no longer permanently skips the checkpoint boundary: two-bound checkpoints (last_message_id + max_seen_id), local-timezone --since/--until date handling, and an additive truncated flag.
  • msg get --ids batch fetch (single-RPC chunks of 100, additive missing_ids).
  • msg search --from me uses the server-side own-messages filter.
  • msg send --split N chunks oversized text into sequential UTF-16-aware messages (paragraph-preferred cuts).
  • listen --count N and --until <ts> finite-stream exits (combined across accounts, stderr notice, exit 0).
  • listen: the connect semaphore no longer starves accounts at --parallel 1; gap detection uses the incoming pts_count (grammers continuity rule) and gap/peer state now survives reconnects; stream task panics surface their payload; filter applicability is consistent and documented (direction/pattern filters suppress action-family rows instead of silently bypassing them).
  • chat settings --noforwards actually works: messages.toggleNoForwards exists at layer 227 and is now wired (the old rejection claimed the layer lacked it).
  • admin-log --until rejects post-2038 timestamps instead of silently wrapping via as i32 and disabling pagination early-stop; --since is now counted against the limit inside collection so it can no longer hide matching events.

Chats and contacts

  • chat create no longer reports failure after server-side success on Updates::Combined responses (the retry path duplicated chats).
  • contact add no longer reports first-time adds as failures (min-user contact:false response is a warning).
  • --signatures no longer clears author profiles (writes signature_profiles too).
  • chat requests: --user with --link is rejected instead of silently ignoring the link scope.
  • chat kick: --ban no longer overrides an explicit --rights view_messages value; --demote rejects --preset/--rights (they were silently discarded); empty --rights is rejected instead of promoting nobody.
  • Bare invite hashes are canonicalized to full https://t.me/+... URLs where the API expects a link (--edit/--importers), while --check still extracts the hash.
  • dialog --folder pagination anchors per-page and album bundling no longer drops entries.
  • dialog folder-create verifies the written filter after UpdateDialogFilter (a racing creator overwriting the id now surfaces an honest error) and counts Chatlist folders when allocating ids.
  • dialog drafts docs pinned to the exact channel-id form (the -100 Bot-API convention, matching numeric --chat).

Accounts, privacy, and security

  • Login: inverted session_existed_before fixed (failed logins deleted good sessions) across the code and staged paths.
  • Staged login: --stage resend/--stage cancel-code, 303 DC migration on sign-in/resend/cancel and change-phone flows (auto home-DC switch + one retry), 2FA accepts piped stdin like the code step, takeout start clears stale export artifacts.
  • account delete purges session, pending secrets, and config entry after server-side delete; --dry-run is reachable without --yes.
  • privacy set --replace (revocation) added; merge mode rejects a user resolved on both the allow and deny sides (alias-proof: username vs numeric id of the same person).
  • phone --confirm-code dry-run redacts the OTP code and phone_code_hash; one-time secrets on argv (--confirm-code/--phone-hash) warn about process listings like --phone does; --show-token bypasses the log scrubber so the promised login URI actually prints in quiet mode.
  • account sessions --terminate dry-run applies the same current-session guard as the real run instead of promising a refused action.
  • Upload exfiltration guard blocks .session.export/.session.tmp; export-session refuses a hard link to the live session (file-identity comparison).
  • Callback button data_str decodes lossy UTF-8 (invalid bytes become U+FFFD) instead of an empty string, consistent with listen rows and the click selector.
  • rand_seed mixes a process-wide call counter: same-tick topic creations no longer collide on random_id.

Streams and serving

  • stream.resync now arms on every StreamError (auto-catch-up after a broken stream) and completion guards use bounded retries, so a saturated driver cannot silently drop final envelopes (10s drain budget doc...
Read more

v0.11.3

Choose a tag to compare

@github-actions github-actions released this 04 Sep 14:31

0.11.3 - 2026-09-04

Fixed

  • dialog folder-create: the 12-char title cap and peer-requirement validation now also apply on the CLI path (previously only serve/MCP enforced them).

v0.11.2

Choose a tag to compare

@github-actions github-actions released this 04 Sep 14:09

0.11.2 - 2026-09-04

Fixed

  • dialog folder-create: titles over 12 characters are rejected offline naming the Telegram cap, instead of the server's misleading MESSAGE_TOO_LONG.
  • Pinned the embedded skill.md asset to LF in .gitattributes so include_str! output matches the contract tests on Windows CRLF checkouts (pre-existing CI failure on main).

Note

  • npm 0.11.1 was published from an intermediate commit and lacks only the folder-title offline validation; 0.11.2 supersedes it.

v0.11.1

Choose a tag to compare

@github-actions github-actions released this 04 Sep 13:11

0.11.1 - 2026-09-04

Fixed

  • cache search: hyphenated queries no longer crash with a raw SQLite error; user queries are escaped as FTS5 phrases (LIVE-TEST no longer parses as column syntax).
  • cache sync: media_kind now stores clean labels (document, photo, …) matching msg get, instead of the Rust Debug format.
  • msg send --schedule: relative durations (90s, 30m, 24h, 7d, 2w, and +-prefixed variants) are now accepted, matching chat invite --expire; negative durations are rejected.
  • dialog folder-create: new --include-chat / --pin-chat / --exclude-chat flags; rule-only folders with no peers are rejected offline with a clear error instead of the server's obscure MESSAGE_TOO_LONG.
  • RPC errors PREMIUM_ACCOUNT_REQUIRED and MESSAGE_TOO_LONG now carry a short plain-language hint appended to the message; code and name are unchanged.

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 05:26

0.11.0 - 2026-09-04

Added

  • tele dialog folders / folder-create / folder-delete / folder-reorder: list, create, delete, and reorder chat folders (dialog filters) via messages.{getDialogFilters,updateDialogFilter,updateDialogFiltersOrder}.
  • tele msg scheduled / scheduled-delete / scheduled-send: list scheduled messages (messages.getScheduledHistory), delete them, or send them immediately.
  • tele cache sync / search / stats / clear: per-account local SQLite message cache ({app}/cache/{name}.cache.db) with FTS5 full-text search for offline queries.
  • Serve/MCP surface grows 67 → 78 routed ops (cache group + 4 dialog folder ops + 3 msg scheduled ops).

Fixed

  • docs/getting-started.md: npm install command now matches the published @qmahyar/telecli package name.
  • docs/CONTRIBUTING.md: removed the stale hardcoded test count.
  • docs/examples.md: added MCP/Cursor setup, tele serve embedding recipe, agent skill, local cache, folders, and scheduled-message recipes.
  • README.md: added the Changelog to the documentation table.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 02 Sep 06:01

0.10.0 - 2026-09-02

Changed

  • Renamed the command from telecli to tele end to end: cargo builds both tele and a telecli alias; the npm package installs tele as the primary bin with telecli kept as a deprecated alias for one transition cycle; release archives, bundled binaries, and the npm launcher now use tele names; help text, completions, README, and docs all say tele.
  • App data directory renamed from ~/.config/telecli / %APPDATA%\telecli to ~/.config/tele / %APPDATA%\tele. tele migrates an existing legacy telecli data directory automatically on first run (one-time rename; a TELE_APP_DIR override skips migration).

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 02 Sep 03:02

0.9.0 - 2026-09-02

Added

  • tele skill prints an embedded SKILL.md (Agent Skills spec: frontmatter, normative usage rules, 16-group command map, JSON envelope, recipes) to stdout — an agent loads it into context in one command.
  • tele skill install [--dir PATH] [--force] writes the skill to tele/SKILL.md under detected agent skill directories (.claude/skills, .config/opencode/skills, .cursor/skills) or a custom dir; overwrites are refused without --force; nothing detected and no --dir is a usage error.
  • README now leads with install and quick start; new "For agents" section covering tele skill and MCP.

Changed

  • README rewritten as a concise front door (482 → 180 lines): command tables replaced by tele --help pointers, every count verified (16 groups, 67 MCP tools, 25 raw methods, 13 build targets); stale session-report artifacts (SHIPPED.md, implementation-summary.md) removed.
  • docs/getting-started.md: removed the false cargo install tele-cli instruction (the crate is not on crates.io).

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 02 Sep 01:49

0.8.0 - 2026-09-02

Fixed

  • tele serve: omitting "account" in params while serving multiple accounts now returns a ServeError naming the served accounts instead of silently targeting the alphabetically-first one (including mutating ops). Single-account serve keeps the implicit default. stream.resync with multiple accounts and no "account" now resyncs every account, matching the documented contract.
  • Error taxonomy survives the ClientGuard::connect boundary: a bad config.toml now exits 1 with JSON kind ConfigError (was exit 3 / generic Error); auth failures keep exit 4.
  • TeleError::Timeout (e.g. msg get --watch) now exits 3 (runtime outcome) instead of 1 (usage); runtime request-state failures — message not found, no poll/media/reply markup, poll closed — now exit 3 as Invocation errors instead of exit-1 usage errors across msg get/vote/click/send copy-from/download.
  • tele listen now honors --parallel (and parallel_max): concurrent account connections are capped by a semaphore held for each task's lifetime; previously every selected account connected concurrently.
  • Album event ids no longer truncate i64 message ids past i32::MAX (wrapping cast → try_from).
  • Runtime-thread panics are no longer swallowed silently: the panic message (scrubbed) is logged to stderr and the process exits 3; a clap derive-conversion failure now degrades to the standard usage-error path instead of panicking.
  • Session filesystem paths are no longer embedded in user-facing account export-session / Telethon-import errors (full path only at --verbose debug level).
  • account import-session no longer buffers the entire source file into memory; it validates the 16-byte SQLite header, then streams through a private-mode (0600/user-DACL) temp file.
  • Session files (including the main SQLite auth-key file) are created with private permissions from first open; every startup sweep-tightens permissions on all files under sessions/, covering restores from backup with wide permissions.
  • A missing .env is created 0600/user-DACL on first credentials() call, closing the default-permissions creation race.
  • Sensitive-file upload blocklist hardened: credentials.bak, vault.kdbx.bak, my.env, my.credentials.json, embedded id_rsa names are now rejected; lookalikes such as env.example and my_env remain allowed.
  • Stale .part-* download temps owned by the current process are no longer swept mid-download.
  • Executor outcome errors with unprintable messages log <unprintable error> instead of an empty reason.
  • tele topic close/reopen/delete/pin account-selection errors now name the actual subcommand (e.g. topic close requires --account …).
  • Unknown --events errors list valid events plainly instead of printing a Rust Debug slice.
  • Internal milestone codename serve-A removed from help text and errors; root help drops the internal client-library name.

Changed

  • Removed the unicode-segmentation dependency (emoji validation uses the existing 4-byte single-codepoint rule).
  • Net −41 lines: one generic CappedDedupe<K> replaces the byte-identical serve/listen dedupe structs; shared truncate_text helper replaces copies in msg/stories; deleted dead wrappers (base64_encode, print_envelope, print_json_result delegation); tmp-dir hash no longer folds in the pid already present in the name.
  • --preview on msg send is hidden from help (it was a no-op flag; preview is on by default, --no-preview disables).
  • tele completions --help now describes each shell variant.

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 31 Aug 05:49

0.7.0 - 2026-08-31

Fixed

  • Removed the unsafe struct-layout hack in peer-cache eviction (entities.rs): purge_peer no longer reinterpret-casts SqliteSession as a fake layout-matched struct to run SQL. Stale peer cache entries are now evicted in-memory via a process-global eviction set consulted by the cache lookups.
  • Replaced the hand-rolled FIPS SHA-256 in session.rs with the sha2 crate (Sha256::digest), removing ~70 lines of hand-written compression code while keeping the same checksum output.
  • Destructive commands now require an explicit account selection: msg delete, chat kick, chat leave, and dialog delete refuse to run against all sessions implicitly and error with requires --account <name> or --tag <tag> unless --account/--tag is given.
  • Collapsed the four duplicated usage-error JSON-envelope blocks in main.rs into one emit_usage_error(machine, dry_run, command, message) helper.
  • Replaced 14 duplicated match emit_row(...) broken-pipe-handling blocks in listen.rs with an emit_row_or_stop helper plus a pure emit_stops_stream decision function.

v0.6.8

Choose a tag to compare

@github-actions github-actions released this 28 Aug 03:27

0.6.8 - 2026-08-28

Fixed

  • Flaky session tests on CI: replaced tokio::sync::Mutex with std::sync::Mutex for TEST_ENV_LOCK to eliminate race conditions between parallel test threads mutating TELE_APP_DIR env var. The sync mutex properly blocks the calling OS thread. Poisoned-mutex recovery ensures cascading panics don't break subsequent tests.