Commit Graph

4698 Commits

Author SHA1 Message Date
Alberto Schiabel 661d7b5f83 feat(cli): make automatic shell setup the installer default (#4042)
This PR:

- builds on top of https://github.com/ComposioHQ/composio/pull/4015
- makes `COMPOSIO_INSTALL_SHELL=auto` the default: the installer infers
the login shell from `$SHELL` (zsh, bash, fish) and always runs
idempotent PATH setup for it; `COMPOSIO_INSTALL_SHELL=none` is the
documented install-only opt-out for CI, Docker, and dotfile managers
- makes the ending truthful for every outcome: Case A/Case B are decided
against a pre-install `PATH` snapshot with physical-path identity,
startup-file failures never fail the install (warning + runnable
absolute-path command, exit 0), and the final action block is always the
last output
- seeds `~/.bash_profile` (sourcing `~/.profile`) on bash systems with
no login file, so login shells actually read the managed block; the
documented uninstall deletes that file again when only the block was in
it
- teaches `composio install` to reconcile a stale managed PATH block
(exactly one block per physical file, symlink-aware) and to suppress its
boxed restart hint when `COMPOSIO_CLI_INVOCATION_ORIGIN=installer`
- verifies delegated shell setup actually reconciled before trusting its
exit code, and writes rc files through symlinks with preserved mode and
per-process tmp names
- covers every output-matrix row with exact final-tail harness
assertions under `sh` and `dash`, adds default-flow/`SHELL`-unset/`none`
Docker e2e legs, and aligns CI workflows, docs, and the changelog
- includes one deliberate cross-cutting refactor (`8b7ebadef`): the
per-process atomic tmp-write helper used for rc files is shared with the
analytics and tool-permission state writers instead of duplicating it

## Review remediation (2026-08-04)

All P1/P2 findings from the stack review are addressed on this head:

- **Atomic replacement (P1):** #4016 (`fix/cli-upgrade-atomic-replace`)
is merged into this branch. `install.sh` publishes support files first,
`release-tag.txt` next, and the `composio` binary last, with
aside/rollback recovery and preserved staging on failed restores;
`composio upgrade` replaces the running binary via rename-based
`atomic-replace` helpers (no `fs.copy`), with running-version precedence
and Linux open-handle/changed-inode coverage.
- **Safe documented uninstall (P1):** the docs snippet stages each
startup-file rewrite in a 0600 `mktemp` file, promotes only on filter
success (preserving symlinks, inode, owner, mode), always cleans up, and
is extracted verbatim and exercised by
`test/install-sh-uninstall-snippet.test.sh` under `sh` and `dash`
(symlinks, modes, filter failure/partial output, `.bash_profile`
deletion).
- **Legacy block migration (P2):** both reconcilers migrate the old
three-line block (marker + `export COMPOSIO_INSTALL_DIR` + PATH export,
POSIX and fish) as a unit, with shared
`legacy-migrate-*`/`legacy-near-miss-*` fixtures run by the TS
conformance suite and the shell suite; the uninstall filter removes the
legacy lines too.
- **Checksum policy (P2):** for the default ComposioHQ GitHub source, a
missing `checksums.txt` or missing manifest entry is now a hard error
(mismatch/malformed stay fatal everywhere); overridden mirrors keep the
lenient path; a missing SHA-256 utility warns that verification was
skipped. The changelog claims match this exactly.
- **CI (P2):** the scheduled prod E2E stays on the Docker harness but
installs through `/install` with `COMPOSIO_INSTALL_SHELL` (the
`/install/<shell>` routes are still unprovisioned 404s; a workflow
comment records how to restore variant-route coverage), with a new fish
leg; the hourly canary installs with
`COMPOSIO_BIN_DIR=$HOME/.composio-canary/bin` so its `env -i` login
probe passes only when the managed block itself makes `composio`
resolvable.

## Context

The production installer already auto-configured the shell once, and a
first-time user still hit `composio: command not found` four times in a
row: the refresh hint printed mid-stream, plugin output buried it, and
the final block suggested an unrunnable command. Users execute the last
instruction on screen, so auto-setup is only safe together with a
truthful, copy-paste-safe ending — that pairing is the core of this PR.
The audience now includes non-technical users: one command, no shell
vocabulary, and the only follow-up ever needed is "open a new terminal,
then run `composio login`".

## Release sequencing and rollback

1. This PR merges into `feat/mise-style-installer-rollout` (#4015) after
review; checks and review rerun on the combined head.
2. #4011 merges to `next`; a stable `@composio/cli` containing `install
--shell` must be promoted and verified before #4015 moves.
3. #4015 retargets to `next` and merges; `https://composio.dev/install`
then serves the new default. Smoke-test the live `auto` and `none` flows
before deploying the linked dashboard PR ComposioHQ/dashboard#1204 and
announcing.

Rollback order is copy-first: restore the previous dashboard/docs copy,
then revert installer behavior if needed. The old copy stays compatible
with the new installer; the new promise is not compatible with the old
install-only default.

## Known Residuals

Accepted during review (recorded, not dropped):

- **Design observation (matches the plan's output matrix by design):**
agent-mode success with an unresolved inherited PATH prints only the
new-terminal notice, with no runnable command for automation; consider a
follow-up amendment.
- **Cross-repo risk:** `$SHELL`-inferred setup may target a different
shell than the interpreter driving an agent (e.g. the Claude Code plugin
runs bash while `$SHELL` is zsh); verify the plugin's install-then-login
sequence end to end before rollout.
- Minor: a stale block plus a completions append targeting the same file
performs two rewrites and prints `Updated` twice (requires
`--completions` and a changed bin dir).
- Minor: `test/install-sh-uninstall-snippet.test.sh` runs in the
release-validation workflow (like the other install-sh unit suites), not
on plain PRs, so docs-snippet drift is caught at release time.
- Minor: a legacy file already mangled by an unreleased build of this
branch (marker consumed, orphaned exports left) is not migrated —
without the marker anchor those lines are indistinguishable from user
content.

## Post-Deploy Monitoring & Validation

- **Live verification (owner: release driver, before dashboard
deploy):** in a fresh container/VM, run the plain `curl -fsSL
https://composio.dev/install | sh` with zsh/bash `$SHELL`; a new login
shell must resolve `composio`; repeat with `COMPOSIO_INSTALL_SHELL=none`
and assert no rc changes.
- **Canary:** `cli.install-health-check.yml` (hourly) now asserts the
auto contract in isolation — managed block present with the canary bin
dir, exact PATH line, `env -i bash -ilc 'command -v composio'` resolving
through the managed block only. Healthy = green hourly runs; failure
signal = red canary or a `warning: Automatic PATH setup` line in its
logs.
- **Telemetry:** PostHog `install` events with `invocation_origin:
installer` — watch install→login completion rate for regression in the
first 48h.
- **Rollback trigger:** sustained canary failures or a spike in `command
not found` support reports → roll back copy first, installer second
(order above).
- **Validation window:** 24h of hourly canary runs post-deploy.
@composio/cli@0.3.2 @composio/cli@0.3.2-beta.337
2026-08-05 19:40:36 +05:30
jkomyno 5941fbace1 docs(cli): announce the shell-setup default in its own dated entry
The 07-31 changelog entry is already published on next and describes the
install-only default that composio.dev/install serves today. Rewriting it
in place would replace a live, accurate description with its opposite
under a date on which that behavior never shipped, and readers who
already saw it would get no notice of the flip.

Restore that entry verbatim and add a dated 08-05 entry carrying the
behavior-change callout, the auto/none bullets, and the created
~/.bash_profile disclosure.

The download-verification section moves to the new entry rather than
staying on 07-31: the strict abort is new here. The live installer warns
and continues when checksums.txt is missing -- the official_release_source
gate that turns that into an abort does not exist there.

Also scope the HTTPS claim, which is_allowed_http_authority contradicts
for loopback and COMPOSIO_INSTALL_ALLOW_HTTP_HOST; document the second
shape of the installer-created ~/.bash_profile, which survives the
documented uninstall when ~/.profile existed; and record the checksum
abort in INSTALL.md, where someone hitting 'Refusing to install' will
look for it.
2026-08-05 19:33:16 +05:30
jkomyno a7c73b679a fix(ci): keep the canary teardown out of the pinned uninstall block
test/release-workflow.test.ts pins the `rm -rf` block in
cli.install-health-check.yml against INSTALL.md's documented uninstall,
and the health check listed $HOME/.composio-canary inside that block.
The canary bin dir is the workflow's own isolation directory, not
something a user uninstalls, so INSTALL.md must not grow an entry for it.

Tear it down in a separate statement instead. The parser only reads the
backslash-continued block, so a single-line `rm -rf` is invisible to it
while the cleanup still runs before the install.
2026-08-05 19:25:27 +05:30
jkomyno 5475c6a858 ci(cli): run install-script unit tests on installer-only pull requests
An installer-only change ran no lint or tests anywhere before landing:
Test Installation triggered on tag push, dispatch, and call only, and Build
CLI Binaries' push path filter did not cover install.sh or install/.

Add install.sh and install/** to that path filter, and trigger Test
Installation on pull requests touching the installer, its suites, or the
workflow itself. Only the ubuntu-only unit-test job (shellcheck plus the
install-sh-*.test.sh suites) runs there; the multi-OS matrix installs an
already-published release a pull request cannot produce, so it stays gated
off pull requests along with the summary that reports it.
2026-08-05 19:16:38 +05:30
jkomyno 0994c73b55 ci(cli): drop the orphaned built_from_this_checkout installation input
The input let legs that install an older published release accept
install.sh's legacy PATH block. That case no longer exists: install.sh
always comes from this checkout, and delegated_setup_verified only trusts a
delegated `composio install --shell` when every target startup file matches
its own rendering byte for byte, rewriting the block inline otherwise. The
unconditional managed-block assertion is therefore valid on every leg, and
no run body ever read the env var it was wired into.
2026-08-05 19:16:22 +05:30
jkomyno 9a6d59326f Merge branch 'next' into feat/installer-auto-shell-default
Keeps this branch's fixture-driven managed-block reconciler
(reconcileManagedPathBlock, pinned byte-for-byte against install.sh's awk
rewrite by test/managed-block-fixtures) and drops next's narrower
replaceManagedPathBlock/applyFileChanges pair, which it supersedes.

Adopts from next: the narrowed unsafe-path character set on both the TS and
sh sides, the removal of the $PATH-reachability write skip, the hedged
no-shell PATH message, and the COMPOSIO_BIN_DIR command documentation.
2026-08-05 19:12:05 +05:30
Alberto Schiabel 7106c665c3 fix(installer): satisfy shellcheck in install.sh and the shell variants (#4067)
## Why

`next` is red. The **Install Script Unit Tests** job runs:

```
shellcheck -s sh install.sh install/*.sh
```

shellcheck exits non-zero on *any* finding, including info-level ones,
and #4015 introduced four **SC2015** diagnostics ("Note that A && B || C
is not if-then-else"):

| file | line |
|---|---|
| `install.sh` | 535 |
| `install/bash.sh` | 11 |
| `install/zsh.sh` | 11 |
| `install/fish.sh` | 11 |

The knock-on matters more than the lint: `Test Install Script` **depends
on** that job, so it was **skipped** on the beta.336 build — meaning
that release's real installation legs never ran. See [run
31006150923](https://github.com/ComposioHQ/composio/actions/runs/31006150923).

This was not caught on #4015 because **Test Installation does not
trigger on pull requests** — it runs on tag push and from Build CLI
Binaries, so the job first executed after merge.

## What changed

Both patterns become explicit conditionals, with no behavior change:

```sh
# install/{bash,zsh,fish}.sh
-debug() { is_true "\${COMPOSIO_DEBUG:-}" && printf '+ %s\n' "\$*" >&2 || :; }
+debug() { if is_true "\${COMPOSIO_DEBUG:-}"; then printf '+ %s\n' "\$*" >&2; fi; }
```

`debug()` still prints only when `COMPOSIO_DEBUG` is truthy, and still
returns 0 otherwise (the `|| :` existed to keep it from tripping `set
-e`; `if` returns 0 on a false condition, so that property is
preserved).

```sh
# install.sh
-[ -n "\$version" ] && [ -n "\$archive_url" ] || error '...'
+if [ -z "\$version" ] || [ -z "\$archive_url" ]; then
+    error '...'
+fi
```

## Verification

- `shellcheck -s sh install.sh install/*.sh` — the exact CI command —
exits **0** locally
- All four files still pass `sh -n`

## Follow-up worth considering (not in this PR)

The gap that let this through is that the installer's own lint and unit
tests never run on the PR that changes the installer. Running the
`Install Script Unit Tests` job on PRs touching `install.sh` /
`install/**` would have caught this before merge.
2026-08-05 18:47:10 +05:30
jkomyno b11f31cec8 fix(installer): satisfy shellcheck in install.sh and the shell variants
The Install Script Unit Tests job runs `shellcheck -s sh install.sh
install/*.sh`, which exits non-zero on any finding including info-level
ones. Four SC2015 diagnostics ("A && B || C is not if-then-else") failed
the job on next, which also skipped the Test Install Script legs that
depend on it.

Rewrite both patterns as explicit conditionals. Behavior is unchanged:
debug() still prints only when COMPOSIO_DEBUG is truthy and still
returns 0 otherwise, and the release-metadata guard still aborts when
either field is empty.
2026-08-05 18:41:20 +05:30
Alberto Schiabel 8b3ccffb03 feat(cli): rewrite installer for POSIX shell (#4015)
This PR:

- builds on top of
[#4011](https://github.com/ComposioHQ/composio/pull/4011) ([Glen
review](https://app.tryglen.com/review/ComposioHQ/composio/4011)) and
implements Stage 2 of the mise-style installer rollout
- rewrites the base installer for POSIX `sh`, install-only defaults,
verified complete bundles, and a `~/.local/bin/composio` entry point
- adds explicit bash, zsh, and fish setup routes with compatibility
fallback for older CLI releases, enforced byte-identical except for the
shell each hardcodes in `requested_shell()` (drift guard in the
installer test harness)
- posts a sticky PR comment with copy-paste preview install commands
once the hermetic install E2E suite passes (`preview-comment` job). The
run-time release resolution means any branch's checked-in scripts are
complete working installers
- updates install workflows, release guidance, public docs, generated
LLM guidance, CODEOWNERS, and the installer changelog
- adds hermetic bash/zsh Docker coverage, corrupt-archive rejection,
upgrade symlink regression coverage, and PR/nightly install E2E CI
- simplifies internals after a review pass: dedupes the curl `--proto`
selection in `install.sh`, merges the duplicated install verification CI
steps, removes the `execPath` test bypass around the `NodeProcess`
service, and threads the Docker image platform through the E2E harness
instead of re-inspecting per test

## Verification

- `shellcheck -s sh install.sh install/*.sh`
- `bash test/install-sh-release-resolution.test.sh` (runs under `sh` and
`dash`, includes the shell-variant drift guard)
- `pnpm turbo typecheck --filter=@composio/cli --filter=@e2e-tests/utils
--filter=@e2e-tests/cli-install`
- `pnpm vitest run` from `ts/packages/cli` (full suite, 1094 tests)
- `pnpm run test:release-workflow` and `pnpm run validate:boundaries`
- Docker install E2E (bash + zsh hermetic legs) runs on this PR via
`cli.install-e2e.yml`
- Docs: typecheck, lint, link lint, static tests, and production build

## Stacked follow-up

- #4042 (`feat/installer-auto-shell-default`) targets this branch and
flips the default to automatic shell setup
(`COMPOSIO_INSTALL_SHELL=auto`, with `none` as the install-only
opt-out). It merges into this PR after its own review; checks and review
then rerun on the combined head before this PR moves to `next`.
- #4042's head now also carries the full stack-review remediation
(2026-08-04): the #4016 atomic-replacement port (support files first,
release metadata next, binary last; atomic `composio upgrade`), legacy
three-line managed-block migration in both reconcilers, a safe tested
uninstall snippet, mandatory checksum verification for official release
downloads, and the scheduled prod E2E moved to the base `/install` route
with `COMPOSIO_INSTALL_SHELL` (the variant routes are still
unprovisioned). See #4042's "Review remediation" section for details;
#4016 is superseded by that merge.
- Rollback order for the combined rollout is copy-first: restore the
previous dashboard/docs copy, then revert installer behavior if needed.
The old copy remains compatible with the new installer; the new promise
is not compatible with the old install-only default.

## Merge gates

- [ ] #4011 is merged and its stable CLI release handoff is verified.
- [ ] The live `next` ruleset requires code-owner review. It currently
requires one approval but reports `require_code_owner_review: false`.

Note: the Cloudflare routes `/install/bash`, `/install/zsh`, and
`/install/fish` remain unprovisioned (404) and are deliberately
undocumented until live checks return the expected scripts. They are no
longer a merge prerequisite; the release-asset/repo `install/*.sh`
variants cover explicit-shell installs.

## Next steps

- Render `install.sh` at release time with the version and per-target
checksums baked in (mise's `install.envsubst` + `render-install.sh`
model), sign the rendered script, and serve it from our own routes. This
removes run-time GitHub API release resolution (rate limits and
missing-asset races) from the user install path.
- If the shell variants ever grow real per-shell logic, generate them
from a single template at build time (mise's `shell.envsubst` model).
Until then, the drift guard enforces lockstep.

No changeset: the standalone CLI is excluded from Changesets, and the
remaining changes are installer, docs, tests, and CI.

## Review follow-up

- `cbb5f5a70` fixes unset `SHELL`, rejects `:` in compatibility fallback
paths, and makes signal traps terminate after cleanup.
`test/install-sh-release-resolution.test.sh` covers all three
regressions under `sh` and `dash`.
- `3d4c3b6cd` bounds the uninstall filter to the legacy install-dir
export and exactly one installer-owned `PATH` line. Legacy POSIX,
current POSIX, fish, unrelated `PATH`, and completions fixtures pass.
- Focused verification: `pnpm test:install-sh`, `shellcheck -s sh
install.sh install/*.sh`, `bash -n
test/install-sh-release-resolution.test.sh`, and `bun test
docs/tests/static/content.test.ts`.
@composio/cli@0.3.2-beta.336
2026-08-05 18:03:34 +05:30
jkomyno f1b25f551b Merge remote-tracking branch 'origin/next' into feat/mise-style-installer-rollout 2026-08-05 17:52:38 +05:30
Alberto Schiabel 5630041145 fix(ci): refresh mise.lock for the republished python 3.12.13 build (#4066)
## Why

`next` is currently red on **Audit Typescript SDK**, and so is every PR
based on it.

`python-build-standalone` republished cpython **3.12.13** under a new
date tag (`20260728` → `20260804`). The pinned version in `mise.toml`
did not change, but `mise lock` now resolves different URLs and
checksums for it. The Audit job runs:

```
mise lock --platform linux-x64,linux-arm64,macos-arm64,macos-x64
git diff --exit-code mise.lock
```

so the committed lockfile no longer matches what `mise lock` produces,
and that step fails on every branch.

Evidence: [Audit on `next` @
`973de6541`](https://github.com/ComposioHQ/composio/actions/runs/31001686227)
— the failing step's diff is exactly the four python entries updated
here.

## What changed

Only the four `[tools.python."platforms.*"]` URL/checksum pairs. No
version pins in `mise.toml` are touched, and no other tool entry moves.

The regenerated checksums match the ones CI computed in the run linked
above, byte for byte.

## Note

Running `mise lock` locally also wanted to append a `[[tools.node]]
version = "24.18.1"` block, picked up from a globally-installed node
rather than from this repo — `mise.toml` pins `node = "24.17.0"`, which
is already locked. That block is deliberately **not** included here.
@composio/cli@0.3.2-beta.335
2026-08-05 17:52:15 +05:30
jkomyno 2dae4c2541 fix(ci): refresh mise.lock for the republished python 3.12.13 build
python-build-standalone republished cpython 3.12.13 under the 20260804
tag, so `mise lock` now resolves different URLs and checksums for the
same pinned version. The Audit job regenerates the lockfile and runs
`git diff --exit-code`, so every branch fails that step until the
committed lockfile matches.

Only the four python platform entries change; the pinned versions in
mise.toml are untouched.
2026-08-05 17:47:54 +05:30
jkomyno aaa3830f35 Merge remote-tracking branch 'origin/next' into feat/mise-style-installer-rollout 2026-08-05 17:42:58 +05:30
Alberto Schiabel 973de6541d feat(cli): rework composio install PATH setup and add --shell (#4011)
# fix(cli): rework `composio install` PATH setup

## Why

`composio install` hardcoded `~/.composio` as the directory it added to
`PATH`, and it wrote the block to `~/.bashrc` only. Two consequences: an
installer that puts the entry point somewhere else (a `~/.local/bin`
shim, a version manager's `bin`) had no way to say so, and a login-only
bash shell — `ssh`, `Terminal.app` — never saw the PATH line at all
whenever a `.bash_profile` or `.bash_login` existed to shadow
`.profile`.

This PR makes the target directory a resolved value rather than a
constant, writes to every startup file bash actually reads, and turns
the command into something an installer can call and trust: it either
configures `PATH` or exits non-zero so the caller's own fallback runs.

## What changed

**Bin-dir resolution.** The `PATH` entry now resolves through
`COMPOSIO_BIN_DIR` → `~/.local/bin` when its `composio` entry point
resolves (through symlinks) to the running executable → the running
binary's own directory. A leftover `composio` from another installer no
longer wins the `~/.local/bin` step, so shells are never pointed at a
foreign program.

**`COMPOSIO_BIN_DIR` is public.** It is the user-facing override for the
`PATH` target and the CLI-side contract the mise-style installer will
use, so it is documented in the CLI README with its full resolution
order and named in the `install` command description — `composio install
--help` mentions it. Deliberately no `--bin-dir` flag: the env var is
the right shape for an installer handoff.

**`--shell <zsh|bash|fish>`.** Overrides `$SHELL` detection, for callers
that know which shell they are configuring.

**Bash login files.** The block also lands in `~/.bash_profile` or
`~/.bash_login` when either exists. `.profile` is deliberately not a
candidate: when neither override exists, bash reads `.profile` itself,
and common `.profile` files already source `.bashrc`. The restart hint
now prints `source ~/.bashrc` instead of the literal string `exec
$SHELL`, and mentions the login file when one was written.

**rc file handling.** Writes go through the file's real target, so a
symlinked `.zshrc` keeps its link; the atomic tmp copy is created with
the target's mode from the start, so a private rc's contents never sit
in a world-readable temp file. Two logical paths that alias one physical
file get the block once.

**Managed blocks are reconciled, not just detected.** The marker alone
no longer means "configured": a file is skipped only when it already
contains the exact rendered `PATH` line for the resolved bin dir.
Otherwise the existing managed block is spliced in place — which is what
migrates the legacy three-line block (`export COMPOSIO_INSTALL_DIR=...`
plus a `PATH` line derived from it), written by an older CLI or by
`install.sh`'s fallback, to the current single-line form. A completions
block elsewhere in the file is untouched, and re-running stays
idempotent.

**No reachability skip.** The command used to skip the write entirely
when the bin dir was already on the invoking process's `$PATH`. That is
not evidence about future shells — under an ad-hoc `export` or a
mise/direnv shim it exited 0 having persisted nothing. A
redundant-but-correct `PATH` line is harmless and the content check
already prevents duplicates. The separate early return for "no shell
detected *and* already on `$PATH`" stays (there is no rc file to write
in that case) with wording that says the conclusion came from the
current process.

**Abort contract.** `composio install` aborts, writing nothing and
exiting non-zero, on two conditions: the resolved bin dir is not
absolute, or it contains a character that cannot be embedded safely in a
quoted rc line. The non-zero exit is load-bearing — `install.sh` only
runs its inline PATH fallback when the CLI fails, so an aborting run
still leaves the user with a working `PATH` instead of a green install
and `command not found`.

**Narrow unsafe-character set.** The bin dir is only ever emitted inside
double quotes (`export PATH="<dir>:$PATH"`, `set --export PATH "<dir>"
$PATH`), where bash and zsh expand only `` ` ``, `$`, `\`, `"` and fish
a subset of those. `;`, `|`, `&`, `(`, `)`, `'` are literal there, so
they no longer abort — `/Users/o'brien/.composio` is a legitimate path.
Newline/carriage return and `:` remain rejected as structural: one
splits the block into extra lines, the other is the PATH separator.

**`install.sh` injection fix.** Because an abort now hands PATH setup to
`install.sh`'s inline fallback, that writer's escaping matters. It
interpolated `COMPOSIO_INSTALL_DIR` raw into the fish and zsh rc lines
and escaped only `"` in the bash arm — so a directory the CLI rejects
was written unescaped into `~/.zshrc` or `config.fish`, where `$(...)`
or a backtick becomes live command substitution at the next shell start.
All three arms now escape backslash, backtick, `$`, and `"` before
embedding, skipping the backtick for fish (which neither substitutes it
nor recognizes `` \` `` inside double quotes). The `$HOME` prefix is
re-applied after escaping, so the intended variable reference survives
while a user-supplied `$` does not.

**CI guard.** `cli.test-installation.yml`'s shell-config check accepted
the legacy `install.sh` block on every leg, justified by a comment
claiming no leg installs a binary built from this checkout. That was
false: `build-cli-binaries.yml` calls the workflow with the tag it just
released, so a completely broken `composio install` silently falling
through to `install.sh`'s writer still reported success. Callers now
declare `built_from_this_checkout`, and those legs must show the
CLI-managed line. `promote-stable` keeps the permissive check because it
rebuilds the promoted beta's own commit, which can predate the writer.

## Known behavior, documented rather than coded around

The rc block no longer records `export COMPOSIO_INSTALL_DIR=`. A user
who installed to a custom directory and later re-runs `curl ... | bash`
without re-specifying the variable gets a second install under
`~/.composio` while their rc `PATH` still resolves the old binary.
Reachability is narrow — existing users are unaffected and `composio
upgrade`, the documented path, replaces `process.execPath` in place and
never consults this. The obvious fix (`command -v composio` + `readlink
-f` inside `install.sh`) can misfire on a foreign `composio`, so this is
a CHANGELOG note telling custom-dir users to re-specify
`COMPOSIO_INSTALL_DIR` or use `composio upgrade`.

## Testing

- `ts/packages/cli/test/src/commands/install.cmd.test.ts` — 47 cases
covering per-shell rendering (full rendered lines, including fish), bash
login-file selection, symlink and mode preservation, legacy-block
migration, idempotency, bin-dir resolution order, and both abort
conditions.
- `test/install-sh-release-resolution.test.sh` — adds a case asserting
that an install directory containing `$(...)` lands in `.bashrc` as
literal text and does not execute when the file is sourced.

Verified locally: `vitest run` for the CLI package (113 files, 1103
passed / 1 skipped), `pnpm run typecheck`, `pnpm run
validate:boundaries`, `pnpm run lint`, `bash -n install.sh`, and `bash
test/install-sh-release-resolution.test.sh`.

## Notes for reviewers

- `@composio/cli` is in `.changeset/config.json`'s ignore list, so this
edits `ts/packages/cli/CHANGELOG.md` directly instead of adding a
changeset.
- No new lint-boundary disables; the two previously registered for this
file (`process.env.SHELL`, `process.env.COMPOSIO_INSTALL_DIR`) are gone,
since both reads now go through `effect/Config`.
- `install.sh` is rewritten wholesale by the follow-up installer PR, so
the change here is the minimum that closes the injection.
- Follow-up #4042 ships a fuller managed-block reconciler with fixtures;
the migration here is its core behavior landed early, without the
fixture harness.
@composio/cli@0.3.2-beta.334
2026-08-05 16:59:48 +05:30
jkomyno 2024fc4403 Merge remote-tracking branch 'origin/next' into feat/cli-install-shell-flag 2026-08-04 20:19:46 +05:30
Alberto Schiabel 85ce22401d fix(deps): let the hono security bump through the release-age floor (#4057)
## Why

`next` is red. Dependabot #4051 bumped the `hono` catalog entry to
`^4.12.34`, which pnpm resolved to `4.13.0`. Both that and `4.12.34`
were published inside the 4320-minute (72h) `minimumReleaseAge` floor,
so `pnpm install --frozen-lockfile` rejects the lockfile — on `next`
itself and on every PR branched from it.

```
[ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION] 1 lockfile entries failed verification:
  hono@4.13.0 was published at 2026-08-03T21:47:05.000Z, within the minimumReleaseAge cutoff
```

Waiting it out is not neutral, because of which versions sit on which
side of the floor:

| Version | Published | Clears the 72h floor | Has the CORS ReDoS fix |
|---|---|---|---|
| 4.12.33 | 2026-07-31 10:42 | yes | **no** |
| 4.12.34 | 2026-08-03 02:36 | not until Aug 6 02:36 | yes |
| 4.13.0 | 2026-08-03 21:54 | not until Aug 6 21:54 | yes |

The newest version old enough to pass the gate is the one the advisory
flags as vulnerable. So pinning back to a compliant version means
deliberately shipping the vulnerable release for two days.

## What changed

One entry added to `minimumReleaseAgeExclude`, with its removal
condition inline. No lockfile change — `pnpm install --frozen-lockfile`
goes from exit 1 to exit 0 with the lockfile untouched.

## Why this is a safe trade

The floor exists to catch compromised fresh releases. `hono` is only
pulled in by `ts/e2e-tests/runtimes/cloudflare/*` and
`ts/examples/cloudflare-wrangler` — test fixtures and examples. It ships
in no published package, so the blast radius of taking it early is
CI-only, while the advisory it fixes is real.

## Verification

```
pnpm install --frozen-lockfile   # exit 0, pnpm-lock.yaml unmodified
```

## Follow-up

Delete the `hono` entry once the resolved version is past the floor
(after 2026-08-06). The inline comment says so.
2026-08-04 20:19:08 +05:30
jkomyno fdb366d090 Merge branch 'feat/cli-install-shell-flag' into feat/mise-style-installer-rollout
# Conflicts:
#	install.sh
#	test/install-sh-release-resolution.test.sh
#	ts/packages/cli/test/__utils__/services/test-layer.ts
#	ts/packages/cli/test/src/commands/install.cmd.test.ts
2026-08-04 20:02:22 +05:30
dependabot[bot] 02c7af88ae chore(deps): bump hono from 4.12.31 to 4.12.34 in the npm-security group across 1 directory (#4051)
Bumps the npm-security group with 1 update in the / directory:
[hono](https://github.com/honojs/hono).

Updates `hono` from 4.12.31 to 4.12.34
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/honojs/hono/releases">hono's
releases</a>.</em></p>
<blockquote>
<h2>v4.12.34</h2>
<h2>Security fixes</h2>
<p>This release includes fixes for the following security issues:</p>
<h3><code>memo()</code> retains SSR output across requests, leading to
cross-user data disclosure</h3>
<p>Affects: <code>hono/jsx</code> (server-side rendering). Fixes
<code>memo()</code> reusing a retained render result across requests
when props compare equal, where a component reading request-scoped
values from ambient context — <code>useContext()</code>,
<code>useRequestContext()</code>, or <code>getContext()</code> — could
serve HTML rendered for another user's request, disclosing account data
or request-scoped secrets such as CSRF tokens. GHSA-f23p-vx2j-j53r</p>
<h3>ReDoS in CORS middleware via
<code>Access-Control-Request-Headers</code></h3>
<p>Affects: <code>hono/cors</code>. Fixes a whitespace-tolerant regular
expression with quadratic backtracking used to parse the
<code>Access-Control-Request-Headers</code> preflight header when
<code>allowHeaders</code> is not configured (the default), where a
single preflight request carrying a long whitespace run could consume
seconds of CPU and stall request processing. GHSA-8j4g-w8fx-2239</p>
<h3>Algorithmic complexity DoS in Language Middleware</h3>
<p>Affects: <code>hono/language</code>. Fixes quadratic string
processing in language-tag normalization, where a crafted language tag
with a large number of hyphen-separated subtags — supplied via a query
parameter, cookie, or <code>Accept-Language</code> header — could cause
excessive CPU consumption and block the event loop.
GHSA-54fx-42gc-7vw4</p>
<h3>Proxy Helper does not remove response headers listed in the
<code>Connection</code> header</h3>
<p>Affects: <code>hono/proxy</code>. Fixes <code>proxy()</code>
forwarding response headers that the origin's <code>Connection</code>
header designates as connection-scoped, where headers intended only for
the immediate peer — per RFC 9110 Section 7.6.1 — could be exposed to
clients, disclosing connection-scoped or internal metadata.
GHSA-79qm-7rj5-m7r9</p>
<hr />
<p>Users who use <code>hono/jsx</code> for server-side rendering,
<code>hono/cors</code>, <code>hono/language</code>, or
<code>hono/proxy</code> are strongly encouraged to upgrade to this
version.</p>
<h2>v4.12.33</h2>
<h2>What's Changed</h2>
<ul>
<li>fix(cookie): relax name validation when parsing Cookie header in <a
href="https://redirect.github.com/honojs/hono/pull/5164">honojs/hono#5164</a></li>
<li>chore: bump <code>@hono/node-server</code> in <a
href="https://redirect.github.com/honojs/hono/pull/5167">honojs/hono#5167</a></li>
<li>fix(jsx): handle useSyncExternalStore subscription and snapshot
changes in <a
href="https://redirect.github.com/honojs/hono/pull/5166">honojs/hono#5166</a></li>
<li>chore: remove undici in favor of global fetch in <a
href="https://redirect.github.com/honojs/hono/pull/5168">honojs/hono#5168</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/hono/compare/v4.12.32...v4.12.33">https://github.com/honojs/hono/compare/v4.12.32...v4.12.33</a></p>
<h2>v4.12.32</h2>
<h2>What's Changed</h2>
<ul>
<li>ci: enable reports for type &amp; bundle size check in <a
href="https://redirect.github.com/honojs/hono/pull/5148">honojs/hono#5148</a></li>
<li>fix(aws-lambda): add jwt and lambda authorizer types for API Gateway
v2 in <a
href="https://redirect.github.com/honojs/hono/pull/5142">honojs/hono#5142</a></li>
<li>fix(sse): emit empty id field to reset Last-Event-ID in <a
href="https://redirect.github.com/honojs/hono/pull/5138">honojs/hono#5138</a></li>
<li>test(cloudflare-workers): add coverage for onClose, onError, send,
and close in Cloudflare Workers websocket adapter in <a
href="https://redirect.github.com/honojs/hono/pull/5145">honojs/hono#5145</a></li>
<li>fix: use <code>Object.create(null)</code> when parsing query,
headers, and params in <a
href="https://redirect.github.com/honojs/hono/pull/5161">honojs/hono#5161</a></li>
<li>fix(secure-headers): keep CSP callbacks scoped to their header in <a
href="https://redirect.github.com/honojs/hono/pull/5147">honojs/hono#5147</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/honojs/hono/compare/v4.12.31...v4.12.32">https://github.com/honojs/hono/compare/v4.12.31...v4.12.32</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/honojs/hono/commit/734755ace341607628219ea1dd8ca17f01bf1a5c"><code>734755a</code></a>
4.12.34</li>
<li><a
href="https://github.com/honojs/hono/commit/9839ff32a349bf088b6ddfa604010879dd2d3a05"><code>9839ff3</code></a>
chore: update <code>bun.lock</code> (<a
href="https://redirect.github.com/honojs/hono/issues/5182">#5182</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/0c45036d6b0ddf42ab2fa44639dc8710825d5c0f"><code>0c45036</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/honojs/hono/commit/720b566290793d4358bf39843adcb7cf4da4548f"><code>720b566</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/honojs/hono/commit/f70e2c31684387b3231cc38512a31df6ca76a1c7"><code>f70e2c3</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/honojs/hono/commit/93fc250d8b4df58ea542cb945171de8013d5e6d5"><code>93fc250</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/honojs/hono/commit/51db3131d5e97076327edaa0afdb60ebb77c264f"><code>51db313</code></a>
4.12.33</li>
<li><a
href="https://github.com/honojs/hono/commit/aed146364d5c48a8336b8c82fedfabebf8063d07"><code>aed1463</code></a>
chore: remove undici in favor of global fetch (<a
href="https://redirect.github.com/honojs/hono/issues/5168">#5168</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/224d2f5cbf2b4bc2ebb7482d0592149a8d9f0574"><code>224d2f5</code></a>
fix(jsx): handle useSyncExternalStore subscription and snapshot changes
(<a
href="https://redirect.github.com/honojs/hono/issues/5166">#5166</a>)</li>
<li><a
href="https://github.com/honojs/hono/commit/df653cea0450657977887d56a5188368387456c8"><code>df653ce</code></a>
chore: bump <code>@hono/node-server</code> (<a
href="https://redirect.github.com/honojs/hono/issues/5167">#5167</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/honojs/hono/compare/v4.12.31...v4.12.34">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=hono&package-manager=npm_and_yarn&previous-version=4.12.31&new-version=4.12.34)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/ComposioHQ/composio/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 19:20:31 +05:30
dependabot[bot] f286ca98ac chore(deps): bump the uv group across 2 directories with 1 update (#4052)
Bumps the uv group with 1 update in the / directory:
[cryptography](https://github.com/pyca/cryptography).
Bumps the uv group with 1 update in the
/python/providers/claude_agent_sdk directory:
[cryptography](https://github.com/pyca/cryptography).

Updates `cryptography` from 49.0.0 to 50.0.0
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst">cryptography's
changelog</a>.</em></p>
<blockquote>
<p>50.0.0 - 2026-07-31</p>
<pre><code>
* **SECURITY ISSUE**:

:func:`~cryptography.hazmat.primitives.serialization.pkcs7.pkcs7_decrypt_der`
and its PEM and S/MIME variants no longer expose distinguishable errors
or
timing when unwrapping a ``RecipientInfo``'s ``encryptedKey``, which
could
act as a Bleichenbacher oracle for callers that decrypt untrusted
messages.
A random key is now substituted on failure, as described in :rfc:`3218`.
  Credit to **@X1AOxiang** for reporting the issue. **CVE-2026-69247**
* Deprecated Diffie-Hellman key exchange over finite fields (FFDH).
  Everything FFDH is deprecated, including the types in
``cryptography.hazmat.primitives.asymmetric.dh`` and loading FFDH keys
or
  parameters with the key loading APIs. Users should migrate to a more
  modern key exchange algorithm.
* Added ``xof()`` class methods to
  :class:`~cryptography.hazmat.primitives.hashes.SHAKE128` and
:class:`~cryptography.hazmat.primitives.hashes.SHAKE256` for
constructing
  algorithm instances configured for use with
  :class:`~cryptography.hazmat.primitives.hashes.XOFHash`.
* The :mod:`X.509 verification &lt;cryptography.x509.verification&gt;`
APIs are now
  considered stable and are subject to our API stability policy.
* Added the :doc:`/cobblestone` recipe, an implementation of the
  Cobblestone-128 and Cobblestone-256 instantiations of the `C2SP
  chunked-encryption specification
&lt;https://c2sp.org/chunked-encryption&gt;`_ for streaming
authenticated
  encryption of large messages.
* Parsing a Signed Certificate Timestamp list now rejects encodings that
carry trailing bytes after the list or after an individual SCT, instead
of
  silently ignoring them.
* Added support for using :class:`~cryptography.x509.Name` as a field
type in
  the :doc:`/hazmat/asn1/index` module.
* Loading a public key or an EC private key now rejects DER where the
``subjectPublicKey`` (or EC ``publicKey``) ``BIT STRING`` declares a
non-zero
  number of unused bits, instead of silently ignoring it.
* Parsing a CRL entry's ``InvalidityDate`` extension now rejects a
``GeneralizedTime`` that carries fractional seconds or another non-DER
form,
matching the strict encoding already required for every other X.509 time
  field.
* :func:`~cryptography.x509.ocsp.load_der_ocsp_request` and
:func:`~cryptography.x509.ocsp.load_der_ocsp_response` now reject a
request
or response whose ``version`` field is not ``v1``, the only version
defined
by RFC 6960, matching the version validation already performed when
loading
  certificates, CSRs and CRLs.
* :class:`~cryptography.hazmat.primitives.hashes.XOFHash` is now
supported
  when building against AWS-LC.
* HMAC (and therefore PBKDF2-HMAC) with SHA-3 hashes is now supported
when
  building against AWS-LC.
* Diffie-Hellman (:doc:`/hazmat/primitives/asymmetric/dh`) is now
supported
  when building against AWS-LC.
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pyca/cryptography/commit/dcb7050b807b00392fa9fe2eac7cb362fcf355cc"><code>dcb7050</code></a>
Prepare for 50.0.0 release (<a
href="https://redirect.github.com/pyca/cryptography/issues/15372">#15372</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f"><code>53fccd9</code></a>
Don't leak how PKCS#7 encryptedKey decryption failed (<a
href="https://redirect.github.com/pyca/cryptography/issues/15369">#15369</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/d472f978470fbefa521b86d98b2ecccbbb4d1dd8"><code>d472f97</code></a>
Add <code>from __future__ import annotations</code> to all src/ Python
files (<a
href="https://redirect.github.com/pyca/cryptography/issues/15371">#15371</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/908773d53829fb1466c6db364b31321c3cd8eb9a"><code>908773d</code></a>
Bump downstream dependencies in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15368">#15368</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/2cc07cc948948211899bcb0cddd1fddf86e95812"><code>2cc07cc</code></a>
Bump BoringSSL, OpenSSL, AWS-LC in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15367">#15367</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/c94ede9f040fa44942f7139772603419000acf66"><code>c94ede9</code></a>
chore(deps): bump ruff from 0.16.0 to 0.16.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15366">#15366</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/67a8308dc9ea4cce6056e0f1438f903c208c3f35"><code>67a8308</code></a>
chore(deps): bump virtualenv from 21.7.0 to 21.7.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15365">#15365</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/95018ffcdbbc510fd92fc872e3a3e80aa6e58596"><code>95018ff</code></a>
Release the GIL in one-shot AEAD encrypt/decrypt (<a
href="https://redirect.github.com/pyca/cryptography/issues/15361">#15361</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6954733eaf55a0074abf88f06f7242dfca3a5d02"><code>6954733</code></a>
Release the GIL during DH and DSA parameter generation (<a
href="https://redirect.github.com/pyca/cryptography/issues/15364">#15364</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6893b94c33e948f6240082461424cfb5da2dacc6"><code>6893b94</code></a>
Import _serialization instead of serialization in x509/extensions (<a
href="https://redirect.github.com/pyca/cryptography/issues/15363">#15363</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pyca/cryptography/compare/49.0.0...50.0.0">compare
view</a></li>
</ul>
</details>
<br />

Updates `cryptography` from 49.0.0 to 50.0.0
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst">cryptography's
changelog</a>.</em></p>
<blockquote>
<p>50.0.0 - 2026-07-31</p>
<pre><code>
* **SECURITY ISSUE**:

:func:`~cryptography.hazmat.primitives.serialization.pkcs7.pkcs7_decrypt_der`
and its PEM and S/MIME variants no longer expose distinguishable errors
or
timing when unwrapping a ``RecipientInfo``'s ``encryptedKey``, which
could
act as a Bleichenbacher oracle for callers that decrypt untrusted
messages.
A random key is now substituted on failure, as described in :rfc:`3218`.
  Credit to **@X1AOxiang** for reporting the issue. **CVE-2026-69247**
* Deprecated Diffie-Hellman key exchange over finite fields (FFDH).
  Everything FFDH is deprecated, including the types in
``cryptography.hazmat.primitives.asymmetric.dh`` and loading FFDH keys
or
  parameters with the key loading APIs. Users should migrate to a more
  modern key exchange algorithm.
* Added ``xof()`` class methods to
  :class:`~cryptography.hazmat.primitives.hashes.SHAKE128` and
:class:`~cryptography.hazmat.primitives.hashes.SHAKE256` for
constructing
  algorithm instances configured for use with
  :class:`~cryptography.hazmat.primitives.hashes.XOFHash`.
* The :mod:`X.509 verification &lt;cryptography.x509.verification&gt;`
APIs are now
  considered stable and are subject to our API stability policy.
* Added the :doc:`/cobblestone` recipe, an implementation of the
  Cobblestone-128 and Cobblestone-256 instantiations of the `C2SP
  chunked-encryption specification
&lt;https://c2sp.org/chunked-encryption&gt;`_ for streaming
authenticated
  encryption of large messages.
* Parsing a Signed Certificate Timestamp list now rejects encodings that
carry trailing bytes after the list or after an individual SCT, instead
of
  silently ignoring them.
* Added support for using :class:`~cryptography.x509.Name` as a field
type in
  the :doc:`/hazmat/asn1/index` module.
* Loading a public key or an EC private key now rejects DER where the
``subjectPublicKey`` (or EC ``publicKey``) ``BIT STRING`` declares a
non-zero
  number of unused bits, instead of silently ignoring it.
* Parsing a CRL entry's ``InvalidityDate`` extension now rejects a
``GeneralizedTime`` that carries fractional seconds or another non-DER
form,
matching the strict encoding already required for every other X.509 time
  field.
* :func:`~cryptography.x509.ocsp.load_der_ocsp_request` and
:func:`~cryptography.x509.ocsp.load_der_ocsp_response` now reject a
request
or response whose ``version`` field is not ``v1``, the only version
defined
by RFC 6960, matching the version validation already performed when
loading
  certificates, CSRs and CRLs.
* :class:`~cryptography.hazmat.primitives.hashes.XOFHash` is now
supported
  when building against AWS-LC.
* HMAC (and therefore PBKDF2-HMAC) with SHA-3 hashes is now supported
when
  building against AWS-LC.
* Diffie-Hellman (:doc:`/hazmat/primitives/asymmetric/dh`) is now
supported
  when building against AWS-LC.
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pyca/cryptography/commit/dcb7050b807b00392fa9fe2eac7cb362fcf355cc"><code>dcb7050</code></a>
Prepare for 50.0.0 release (<a
href="https://redirect.github.com/pyca/cryptography/issues/15372">#15372</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/53fccd93413a8d7f07d6d8999681f27b75cffa3f"><code>53fccd9</code></a>
Don't leak how PKCS#7 encryptedKey decryption failed (<a
href="https://redirect.github.com/pyca/cryptography/issues/15369">#15369</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/d472f978470fbefa521b86d98b2ecccbbb4d1dd8"><code>d472f97</code></a>
Add <code>from __future__ import annotations</code> to all src/ Python
files (<a
href="https://redirect.github.com/pyca/cryptography/issues/15371">#15371</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/908773d53829fb1466c6db364b31321c3cd8eb9a"><code>908773d</code></a>
Bump downstream dependencies in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15368">#15368</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/2cc07cc948948211899bcb0cddd1fddf86e95812"><code>2cc07cc</code></a>
Bump BoringSSL, OpenSSL, AWS-LC in CI (<a
href="https://redirect.github.com/pyca/cryptography/issues/15367">#15367</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/c94ede9f040fa44942f7139772603419000acf66"><code>c94ede9</code></a>
chore(deps): bump ruff from 0.16.0 to 0.16.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15366">#15366</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/67a8308dc9ea4cce6056e0f1438f903c208c3f35"><code>67a8308</code></a>
chore(deps): bump virtualenv from 21.7.0 to 21.7.1 (<a
href="https://redirect.github.com/pyca/cryptography/issues/15365">#15365</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/95018ffcdbbc510fd92fc872e3a3e80aa6e58596"><code>95018ff</code></a>
Release the GIL in one-shot AEAD encrypt/decrypt (<a
href="https://redirect.github.com/pyca/cryptography/issues/15361">#15361</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6954733eaf55a0074abf88f06f7242dfca3a5d02"><code>6954733</code></a>
Release the GIL during DH and DSA parameter generation (<a
href="https://redirect.github.com/pyca/cryptography/issues/15364">#15364</a>)</li>
<li><a
href="https://github.com/pyca/cryptography/commit/6893b94c33e948f6240082461424cfb5da2dacc6"><code>6893b94</code></a>
Import _serialization instead of serialization in x509/extensions (<a
href="https://redirect.github.com/pyca/cryptography/issues/15363">#15363</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pyca/cryptography/compare/49.0.0...50.0.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/ComposioHQ/composio/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 19:20:09 +05:30
jkomyno 16b167c94a docs(cli): document COMPOSIO_BIN_DIR and the install abort contract
COMPOSIO_BIN_DIR was discoverable only by reading the changelog: it is
read through a raw ConfigProvider and appeared in no help text. Add it to
the README env-var table with its resolution order, and name it in the
install command description so `composio install --help` mentions it.
No --bin-dir flag: the env var is the right shape for an installer
handoff.

The changelog entry also picks up the behavior changes it was missing —
the non-zero exit on abort and what it means for install.sh's fallback,
both abort conditions, the narrowed character set, legacy-block
migration, and a note for custom-install-dir users reinstalling without
re-specifying COMPOSIO_INSTALL_DIR.
2026-08-04 19:17:14 +05:30
jkomyno d17844c848 ci(cli): fail the installation guard when a freshly built release skips the PATH write
The shell-config check accepted the legacy install.sh block on every leg,
justified by a comment claiming no leg installs a binary built from this
checkout. Build CLI Binaries calls this workflow with the tag it just
released, so that leg does exercise the new binary — and a completely
broken `composio install` that fell through to install.sh's writer still
reported success.

Callers now declare built_from_this_checkout; those legs must show the
CLI-managed PATH line. promote-stable keeps the permissive check, since
it rebuilds the promoted beta's own commit, which can predate the writer.
2026-08-04 19:16:23 +05:30
jkomyno 745bd67cef fix(cli): make composio install reconcile stale managed blocks
Three changes to the PATH writer, all around the same failure mode: the
command reporting success while leaving a shell unable to find composio.

- The unsafe-path denylist rejected characters that are literal inside
  the double quotes the bin dir is always written in. Keep only what
  bash, zsh, and fish still expand there, plus newline and the PATH
  separator, which are structural. /Users/o'brien/.composio now works.
- A file carrying the marker was treated as configured forever, so the
  legacy three-line block written by install.sh's fallback or by an
  older CLI was never migrated. Skip only on the exact rendered PATH
  line for the resolved bin dir; otherwise splice the current block over
  the stale one in place.
- The reachability skip used the invoking process's $PATH as evidence
  about future shells, so an ad-hoc export or a mise/direnv shim made
  the command persist nothing and exit 0. A redundant PATH line is
  harmless, and the content check already prevents duplicates.

installShellIntegration no longer takes an execPath override; it always
reads NodeProcess, and tests thread their exec path through the test
layer instead.
2026-08-04 19:16:09 +05:30
jkomyno 9f10adcc8d fix(installer): escape the install dir before writing it into shell rc files
install.sh's inline PATH fallback interpolated COMPOSIO_INSTALL_DIR raw
into the fish and zsh rc lines, and escaped only " in the bash arm. Since
that fallback is exactly what runs when `composio install` rejects a path
and exits non-zero, a directory containing $(...) or a backtick was
written unescaped into ~/.zshrc or config.fish and became live command
substitution at the next shell start.

Escape backslash, backtick, $ and " for every arm before the value is
embedded, skipping the backtick for fish (which neither substitutes it
nor recognizes \` inside double quotes). The $HOME prefix is re-applied
after escaping so the intended variable reference survives while a
user-supplied $ does not.
2026-08-04 19:15:59 +05:30
Alberto Schiabel 331a1373de Merge branch 'next' into feat/cli-install-shell-flag 2026-08-04 18:25:57 +05:30
jkomyno 72c9fb1dc8 fix(docs): remove legacy fish path on uninstall 2026-08-04 18:21:05 +05:30
jkomyno 47ae5f3029 fix(installer): resolve bin dir before shell setup 2026-08-04 18:18:29 +05:30
jkomyno 4a179f8fdc ci(cli): avoid unavailable installer routes 2026-08-04 18:17:00 +05:30
Alberto Schiabel f7a4503bbe fix(deps): clear high-severity TypeScript audit findings (#4050)
This PR:
- follows up on the failed TypeScript audit in
https://github.com/ComposioHQ/composio/pull/4048
- adds patched transitive floors for `fast-uri`, `ip-address`, and
`brace-expansion`
- refreshes `pnpm-lock.yaml` to resolve 3.1.5, 10.4.0, and 5.0.9
respectively
- clears all high-severity findings from `pnpm audit --prod
--audit-level=high`; one low and one moderate advisory remain below the
gate
- verifies the affected CLI dependency graph with a frozen install,
package build, and 1,072 passing tests (1 skipped)
2026-08-04 17:23:05 +05:30
Alberto Schiabel 15c07ec6c7 docs(cli): restore 0.3.0 and 0.3.1 changelog entries (#4048)
This PR:
- restores missing `0.3.0` and `0.3.1` sections in
`ts/packages/cli/CHANGELOG.md`
- moves the plugin hint and telemetry changes out of `0.2.33`
- records post-`0.3.1` connected-account and login fixes under
`Unreleased`
- rewrites the restored notes as concise, user-facing bullets
- validates the ignored-package boundary with `pnpm validate:changesets`
@composio/cli@0.3.2-beta.333
2026-08-04 17:10:43 +05:30
Soumya Medapati 9a51faf574 docs: "latest" requires the skip-version-check flag for manual execution (#4046)
## What

Runtime testing during eval-rerun prep caught a docs/runtime mismatch:
the guides say `toolkit_versions={"x": "latest"}` works for direct
execution, but `tools.execute()` rejects it —

```
ToolVersionRequiredError: Toolkit version not specified. For manual execution of the
tool please pass a specific toolkit version.
  1. Pass the toolkit version as a parameter to the execute function
     ("latest" is not supported in manual execution)
  ...
  4. Set dangerously_skip_version_check to True
```

Notably, the [TypeScript SDK
reference](/reference/sdk-reference/typescript/tools) already documents
the real behavior — the guides contradicted it. An isolated eval agent
independently hit the same wall and reported it.

Per SDK guidance (@abir-taheer): pass
`dangerously_skip_version_check=True` / `dangerouslySkipVersionCheck:
true` when running `latest` manually.

## Changes

- **All direct-execution samples** (executing-tools,
sessions-vs-direct-execution, custom-auth-params,
before/after-execution-modifiers) keep `"latest"` and add the skip flag
to the execute call with a one-line comment
- **executing-tools warning callout** now presents both paths:
skip-check when an LLM consumes outputs; pinned version (via
`toolkits.get(slug).meta.version`) when code parses them
- **toolkit-versioning page**: the "latest is valid" claims are
corrected to name the manual-execution requirement
- **LLM guardrails** (direct-execution block): same correction — this
block is what AI agents copy verbatim
- **execute-version static test**: now also fails any page showing
`"latest"` in execute samples without the flag (with the runtime
evidence cited in the header comment)

## Verification

- Both patterns executed against the live API before shipping: `latest`
+ flag → success; pinned via `toolkits.get(slug).meta.version` (resolved
`20260615_00`) → success; `latest` alone → the error above
- `bun test tests/static/` — 145 pass; negative check confirmed the new
rule fails with an actionable message when the flag is removed
- `bun run lint:links` — clean

## Note for reviewer

The pinned-version path also returns the *documented* nested response
schema — the "flat response" drift agents reported was a base-version
(`00000000_00`) artifact. Fixing versioning quietly resolves that report
too.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-03 16:31:58 -07:00
Soumya Medapati 52e538286e docs: 'latest' requires dangerously_skip_version_check for manual execution
Runtime testing during eval-rerun prep caught that toolkit_versions
'latest' is rejected by tools.execute() (ToolVersionRequiredError:
'"latest" is not supported in manual execution') — the guides recommended
it anyway; only the TS SDK reference documented the real behavior.

Per SDK team (Abir): pass dangerously_skip_version_check=True /
dangerouslySkipVersionCheck: true when running 'latest' manually. All
direct-execution samples now include the flag with a one-line comment;
the version-required callout explains both paths (skip-check for
LLM-consumed outputs, pinned via toolkits.get(slug).meta.version for
parsed outputs); the toolkit-versioning page's 'latest' claims are
corrected; the execute-version static test now fails any page showing
'latest' without the flag. Both patterns runtime-verified against the
live API before shipping.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 16:23:22 -07:00
shams haroon fee2a73a7e docs: document Custom MCP toolkit logo upload (#4021)
## Problem

Custom MCP docs don't mention logos, so developers don't know they can
replace the default Composio logo their toolkit shows in the dashboard
and on end-user connect screens.

## What this adds

A new "Add a toolkit logo" section on the Custom MCP page, after
registration: the `toolkit_config.logo_file` field (base64 `content` +
`mime_type`), a cURL example with a placeholder base64 value, the
constraints (PNG/JPEG, square 256-1024px, max 3MB, single-line base64),
the default-logo behavior when omitted, and the insert-only implication
that changing a logo means delete + re-register. Follows the docs
convention of language/OS-neutral examples.

## Testing

`bun run lint` (0 errors) and `bun run lint:links` (0 errors) from
`docs/`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_016x2W47caMs6zW4V8uu1M9g
2026-08-03 16:00:25 -07:00
Soumya Medapati 8dffbcf888 docs: toolkit-discovery entry points + version-policy CI check (eval findings round 2) (#4045)
## What

Round 2 of fixes from the 97-agent docs eval (findings:
ctx.composio.io/soumya/docs-agent-eval/findings-97-run.md). Follow-up to
#3999.

## Commit 1 — toolkit-discovery entry points (eval finding 3)

51/97 agents struggled to identify the right toolkit; 15 abandoned the
docs for the raw REST API. The catalog and search already exist and
score well (46/97 praised them once found) — these edits wire them into
the paths readers actually take:

- Quickstart intro: "1000+ apps" now links to the /toolkits catalog
- executing-tools + fetching-tools: a "finding a toolkit and tool"
pointer (catalog, `composio search`, semantic search) before the
parameter-finding guidance
- direct-execution LLM guardrails: a discovery rule, so AI readers stop
inventing their own path via the REST API

## Commit 2 — version-policy static test (the durable half of eval
finding 1)

The content fix for the 72/97 `ToolVersionRequiredError` failure landed
in #3999; this keeps it fixed. New
`tests/static/execute-version.test.ts`: any authored page whose code
fences call `tools.execute()` must show version configuration in its
fences; the LLM guardrail blocks are held to the same rule. Changelog,
generated reference, and migration guides are exempt as
historical/point-in-time records.

Running the new rule against current content caught **five more pages**
shipping version-less execute samples that the eval never visited —
including sessions-vs-direct-execution. Fixed in the same commit.

## Verification

- `bun test tests/static/` — 145 pass (including the new test)
- Negative check verified: removing a version token from a page fails
the test with an actionable message naming the file
- `bun run lint:links` — clean

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-03 13:55:27 -07:00
Soumya Medapati 70f6772037 docs: enforce toolkit-version policy on execution samples via static test
Adds tests/static/execute-version.test.ts: any authored page whose code
fences call tools.execute() must show version configuration, and the LLM
guardrail blocks are held to the same rule. Fixes the five pages the
audit caught (sessions-vs-direct-execution, custom-auth-params,
before/after-execution-modifiers) which shipped version-less samples.
Changelog, generated reference, and migration guides are exempt as
historical/point-in-time records.

This is the durable half of eval finding 1: the content fix landed in
#3999; this test keeps future samples from regressing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 12:17:55 -07:00
Soumya Medapati 1857a8fa1c docs: add toolkit-discovery entry points from quickstart and execution pages
Eval finding 3: 51/97 agents struggled to identify the right toolkit;
15 abandoned the docs for the raw REST API. Links the toolkits catalog
from the quickstart intro, adds a find-your-toolkit pointer to the
executing-tools and fetching-tools pages, and adds a discovery rule to
the direct-execution LLM guardrails.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 11:57:27 -07:00
jkomyno 803f50f524 ci(cli): make the canary probe prove the managed PATH block
Install the no-arg canary with COMPOSIO_BIN_DIR=$HOME/.composio-canary/bin,
a directory Ubuntu's ~/.profile never contributes, so the env -i login
probe passes only when the managed block itself makes composio
resolvable, and assert nothing lands in ~/.local/bin.
2026-08-04 00:16:04 +05:30
jkomyno 558d1344dd ci(cli): keep scheduled prod install E2E off unprovisioned shell routes
The /install/<shell> variant routes still 404 in production, so the
harness's prod mode now selects the shell with COMPOSIO_INSTALL_SHELL on
the base /install route, and the scheduled matrix gains a fish leg. The
Docker harness invocation stays in place; a workflow comment records how
to restore variant-route coverage once the routes ship.
2026-08-04 00:16:03 +05:30
jkomyno 2940930381 docs(cli): make the documented uninstall safe and tested
The uninstall snippet now stages each startup-file rewrite in a 0600
mktemp file, promotes it only when the filter succeeds (preserving
symlinks, inode, owner, and mode), always cleans up, removes legacy
three-line blocks including the fish install-dir export, and deletes an
installer-created ~/.bash_profile left empty so bash regains its default
startup-file selection. A new suite extracts the snippet verbatim from
the docs and exercises it under sh and dash, wired into the install
script unit-test workflow.
2026-08-04 00:16:02 +05:30
jkomyno f00838c95f docs(changelog): disclose the auto shell setup default accurately
Call out the breaking default change (curl | sh now edits startup files
and can create ~/.bash_profile), document COMPOSIO_INSTALL_SHELL=none as
the install-only opt-out, and align the verification claims with the
implemented checksum policy.
2026-08-04 00:15:47 +05:30
jkomyno bd071f528c fix(cli): require checksum verification for official release downloads
For the default ComposioHQ GitHub source a missing checksums.txt or a
missing manifest entry is now a hard error instead of a warning;
malformed entries and mismatches stay fatal for every source. Overridden
mirrors keep the lenient path, and a missing SHA-256 utility still warns
that verification was skipped.
2026-08-04 00:15:47 +05:30
jkomyno 6e3f5af66d fix(cli): migrate legacy three-line managed blocks in both reconcilers
The previous installer wrote marker + export COMPOSIO_INSTALL_DIR + PATH
export. Both the shell and TypeScript reconcilers now consume that triple
as one managed block instead of orphaning the export lines, with shared
legacy-migrate and near-miss fixtures keeping the two implementations
byte-identical.
2026-08-04 00:15:38 +05:30
jkomyno f5be51665f Merge branch 'fix/cli-upgrade-atomic-replace' into feat/installer-auto-shell-default
Port the atomic replacement work from #4016: support files publish first,
release metadata next, the binary last, with aside/rollback recovery for
directory entries and preserved staging on failed restores.
2026-08-03 22:35:33 +05:30
Alberto Schiabel 1503786358 refactor(core): forbid explicit any and type JSON Schema recursively (#3968)
This PR:

- is refreshed directly onto the current next branch now that #3966 and
#3967 have merged; the PR contains only its intended type-safety work
- enforces no explicit any through the standard .oxlintrc.json, pnpm
lint, and plain oxlint --fix in lint-staged; docs remain covered by
their nested configuration
- replaces the loose JSONSchemaProperty in @composio/core with a
concrete recursive interface, removing explicit any reachable through
Tool.inputParameters and Tool.outputParameters
- includes a minor changeset documenting the type narrowing for
properties, default, and enum consumers
- adds a compile-only public-contract test for recursive schemas,
known-key validation, extension keywords, and Tool reachability
- removes explicit any from FileToolModifier, ts-builders, core and CLI
tests, provider tests, and json-schema-to-zod tests
- keeps TypeScript ESLint-parity rules enabled and closes the
tool-router example lint/typecheck gap
- makes no lockfile changes and no runtime behavior changes

## Context

Final PR from the original #3958 split. The migration foundations are
already on next via #3966 and #3967, so this branch no longer carries a
stacked base commit.
@composio/cli@0.3.2-beta.332
2026-08-03 22:24:57 +05:30
jkomyno be3942042a Merge branch 'feat/mise-style-installer-rollout' into feat/installer-auto-shell-default 2026-08-03 21:41:26 +05:30
jkomyno 6b964237b9 Merge branch 'feat/cli-install-shell-flag' into feat/mise-style-installer-rollout 2026-08-03 21:41:22 +05:30
jkomyno 7198dcce8d docs(cli): explain the bash login-override special case 2026-08-03 21:41:18 +05:30
jkomyno 2515db6ff7 test(cli): deduplicate the harness recovery tails and quiet/help no-op cases
The 'To get started(, now)?, run:' ending was retyped at eight call
sites; extract a recovery_tail() helper next to configured_line() so
the expected ending lives in one place. Fold the quiet_fail and
helpless_fail cases into the inline write-failure case with both
COMPOSIO_QUIET=1 and COMPOSIO_INSTALL_HELP=0 set: all three asserted
the same warning and tail, and both flags only gate the normal-success
final block, so one combined case proves they are no-ops on the
setup-failure path (six installer runs become two across the sh/dash
legs).
2026-08-03 21:13:57 +05:30
jkomyno 2655e30c70 docs(cli): state installer design constraints inline instead of KD tags
Ten comments across install.sh, install.cmd.ts, and the harness cited
KD2/KD3/KD4/KD7 decisions from a plan file that exists on no branch.
Rewrite each comment to carry its constraint inline — the truthful
final action block, delegation trust via physical path comparison,
non-fatal setup failures, and copy-paste-safe endings — and drop the
dangling references.
2026-08-03 21:12:07 +05:30
jkomyno 124e896ed7 fix(cli): align the TS and sh managed-block reconcilers on one contract
The CLI and install.sh implemented the managed-PATH-block contract
twice with three divergences: TS matched marker lines after trimming
while the sh awk program matches byte-exact (CRLF or padded markers
reconciled on one side and duplicated on the other), TS replaced stale
blocks in place while sh removes managed lines and appends the fresh
block, and renderWithHome rendered a bin dir equal to $HOME as the
$HOME literal while render_bin_dir left it as the raw path — making
delegated verification permanently stale for that layout.

Align both sides: TS goes byte-exact and remove-then-append (mirroring
the awk program including its newline normalization), and
render_bin_dir learns the $HOME-exact case. A shared fixture suite
under test/managed-block-fixtures now runs against both reconcilers —
vitest drives the exported TS functions, the harness sources
install.sh's definitions and runs write_path_block under sh and dash —
so future edits to one side fail loudly until the other matches byte
for byte.
2026-08-03 21:10:37 +05:30
jkomyno 8b7ebadef9 refactor(cli): share one atomic tmp-write helper with per-process tmp names
Three from-scratch tmp-write→rename implementations coexisted
(install.cmd.ts, tool-permissions writeCacheFile, analytics
writeAnalyticsStateFile), and the install ones raced concurrent installs
through a fixed .composio-tmp suffix. Extract src/utils/atomic-write.ts:
the tmp name embeds the pid, an optional preserveMode creates the tmp
with the target's mode and pins it via chmod, and the best-effort tmp
cleanup now also covers a failure of the initial write itself (ENOSPC
mid-write no longer leaves tmp litter). All three call sites migrate
with their semantics intact; tests scan for any .composio-tmp remnant
instead of probing the old fixed name. Extracting the block-append loop
also brings installShellIntegration back under the function-length
lint that the merge had pushed over.
2026-08-03 21:00:24 +05:30