Skip to content

feat(cp): copy multiple files/dirs in one call#131

Merged
madarco merged 1 commit into
nightlyfrom
feat/cp-multi-source
Jun 29, 2026
Merged

feat(cp): copy multiple files/dirs in one call#131
madarco merged 1 commit into
nightlyfrom
feat/cp-multi-source

Conversation

@madarco

@madarco madarco commented Jun 29, 2026

Copy link
Copy Markdown
Owner

What

agentbox cp and the in-box agentbox-ctl cp toHost|fromHost took a single source per call. Copying several files meant several invocations — and from inside a box, several host approval prompts. Both now accept multiple sources in one call: list files/dirs before the destination (which must then be a directory). Wildcards are handled by the invoking shell (host shell for host-side sources, box shell for in-box sources), so there's no glob expansion in the code and no box→host shell-injection surface.

agentbox cp a.txt b.txt src/ mybox:/workspace/dest/   # many sources into a dir
agentbox cp ./*.log mybox:/workspace/logs/            # shell-expanded wildcard
# in-box:
agentbox-ctl cp toHost /workspace/dist/*.js ./out/     # box shell expands the glob

How

  • Host CLI (apps/cli/src/commands/cp.ts): cp <paths...> (variadic, arity-split). One arg keeps download-to-cwd back-compat; a single source keeps full docker-cp rename semantics. >=2 sources require a directory dest. All sources must be on the side opposite the dest, and box sources must name one box. The size guard runs per source (not summed).
  • Providers: uploadPath/downloadPath take string[]. Docker groups sources by parent dir (one tar per group), hoists mkdir/parent-chain-chown out of the loop, and chowns each landed entry (never chown -R the whole dest). Cloud loops the single-source primitive serially (fixed remote staging path).
  • Wire: the cp.* RPC carries {sources[], dest} with backward-tolerant normalization of the legacy {boxPath, hostPath} shape sent by an older baked-in ctl — shared in packages/relay/src/cp-rpc.ts.
  • Relay convergence + latent bug fix: the cloud runCpRpc now re-shells agentbox cp like the docker path instead of calling the cloud primitives directly. So excludes, --no-default-excludes, and the size guard are honored identically on every provider — the cloud path previously dropped them while the consent prompt advertised them. The consent prompt now lists all sources.

Tests / verification

  • New unit tests: parseArgs arity/direction/errors (apps/cli/test/cp-parse.test.ts), the cp-rpc wire helpers incl. legacy normalization + that excludes reach the argv (packages/relay/test/cp-rpc.test.ts), cloud multi-source loop + dest-dir enforcement (packages/sandbox-cloud/test/cloud-cp.test.ts). Full repo typecheck + lint + tests green (the unrelated queue.test.ts maxWorking flake only appears under parallel turbo run test; passes deterministically in isolation).
  • Live (docker): multi-source + shell-wildcard upload, multi-source download, dest-not-a-dir / box-to-box errors, single-source rename + download-to-cwd back-compat, and the in-box ctl variadic parsing.

Docs (cli.mdx, sync-and-git.mdx, features.md), both agentbox-info skill copies, the setup skill, and all five custom-system-CLAUDE.md prompts updated.

https://claude.ai/code/session_01XvuW3YwvHzvCrmXMJyC33W


Note

Medium Risk
Touches host↔box file transfer and relay-gated cp on every provider; the cloud path behavior change (excludes/size guard) is a correctness fix but alters what actually runs after approval. Multi-source copies are serial and non-atomic.

Overview
agentbox cp and agentbox-ctl cp toHost|fromHost now take a variadic path list: several sources, then a destination (with ≥2 sources the dest must be a directory). Single-arg download-to-cwd and single-source rename semantics are unchanged. parseArgs enforces one box side, no mixed host/box sources, and one box for multi-downloads; upload size blocking runs per source, not on the total.

Providers change uploadPath / downloadPath to string[]. Docker groups sources by parent for tar streaming and handles multi-upload chown without re-owning the whole dest dir; cloud runs the single-source primitive serially per source.

Relay / in-box wire: cp.* RPC uses {sources, dest} with legacy {boxPath, hostPath} normalization in packages/relay/src/cp-rpc.ts. Docker and cloud relay handlers both build argv via shared helpers and re-exec agentbox cp — cloud no longer calls cloud cp primitives directly, so --exclude, default excludes, and the size guard match what the consent prompt shows. Call sites (paste-image, session teleport) pass one-element source arrays.

Docs, skills, and unit tests cover parsing, cp-rpc, and cloud multi-source behavior.

Reviewed by Cursor Bugbot for commit 675689f. Configure here.

`agentbox cp` and the in-box `agentbox-ctl cp toHost|fromHost` took a single
source per call; copying several files meant several invocations (and, from
inside a box, several host approval prompts). Both now accept multiple sources
in one call — list files/dirs before the destination, which must then be a
directory. Wildcards are handled by the invoking shell (host shell for host-side
sources, box shell for in-box sources), so no glob expansion lives in the code
and there's no box->host shell-injection surface.

- Host CLI: `cp <paths...>` (variadic, arity-split). One arg keeps the
  download-to-cwd back-compat; a single source keeps full docker-cp rename
  semantics. >=2 sources require a directory dest. All sources must be on the
  side opposite the dest, and box sources must name one box. Size guard runs
  per source.
- Providers: `uploadPath`/`downloadPath` take `string[]`. Docker groups sources
  by parent dir (one tar per group), hoists mkdir/parent-chain-chown, chowns
  each landed entry. Cloud loops the single-source primitive serially.
- Wire: `cp.*` RPC carries `{sources[], dest}` with backward-tolerant
  normalization of the legacy `{boxPath, hostPath}` shape; shared in cp-rpc.ts.
- Relay: the cloud `runCpRpc` now re-shells `agentbox cp` like the docker path
  instead of calling the cloud primitives directly — so excludes and the size
  guard are honored on every provider (the cloud path silently dropped them
  before, while the consent prompt advertised them). Consent prompt lists all
  sources.
- Docs/skills/system prompts updated; new unit tests for parseArgs, the cp-rpc
  wire helpers, and cloud multi-source orchestration.

Verified live (docker): multi-source + wildcard upload, multi-source download,
dest-not-a-dir / box-to-box errors, single-source rename + download-to-cwd
back-compat, and the in-box ctl variadic parsing.

Claude-Session: https://claude.ai/code/session_01XvuW3YwvHzvCrmXMJyC33W
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agentbox-web Ready Ready Preview, Comment Jun 29, 2026 11:52pm

Request Review

@madarco madarco merged commit 1d72ceb into nightly Jun 29, 2026
4 checks passed
@madarco madarco deleted the feat/cp-multi-source branch June 29, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant