pnpm install
pnpm build
pnpm testUse this when you already have Paperclip on your machine and want to watch the imported company appear in the local UI.
pnpm install
pnpm build
# If Paperclip is not already running, start it in another terminal.
./node_modules/.bin/paperclipai run
# Import the fixture through the current local Paperclip instance.
node dist/index.js add ./fixtures/minimal-company \
--connection custom-url \
--api-base http://127.0.0.1:3100 \
--target newThen open http://127.0.0.1:3100 in the browser and confirm that Minimal Company appears in the company list.
If you want the wrapper to manage local bootstrap for you instead, run:
node dist/index.js add ./fixtures/minimal-company --target newThat path uses the bundled stable paperclipai release, runs paperclipai onboard --yes when needed, and starts the local server automatically before importing.
Use this when you want the automated clean-room Docker smoke test. It packages the current CLI, starts a fresh Linux container with Node 20, installs only production dependencies inside that container, then verifies the wrapper bootstraps Paperclip, imports the fixture, and serves the UI on the container loopback interface without any standalone paperclipai binary on PATH.
pnpm test:dockerIf you specifically want the vanilla npx install path in Docker, run:
pnpm test:docker:npxThat smoke test starts from a clean Node 20 container, installs only the Debian prerequisites, switches to the non-root node user, and runs the current local tarball through npx. It verifies the full success condition end to end: no standalone paperclipai on PATH, npx companies.sh add ... bootstraps local Paperclip, imports paperclipai/companies/gstack, lists the imported company, and serves the Paperclip UI over container-local HTTP.
If you want a manual shell inside the same clean-room setup, run:
tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/companies-docker-handtest.XXXXXX")"
pnpm pack --pack-destination "$tmpdir"
tar -xzf "$tmpdir"/companies.sh-*.tgz -C "$tmpdir"
cp -R fixtures "$tmpdir/package/fixtures"
chmod -R a+rwx "$tmpdir"
docker run --rm -it \
-e COMPANIES_PAPERCLIP_START_TIMEOUT_MS=180000 \
-e HOST=127.0.0.1 \
-e PORT=3210 \
-e SERVE_UI=true \
-e PAPERCLIP_OPEN_ON_LISTEN=false \
-v "$tmpdir/package:/app" \
-w /app \
node:20-bookworm-slim \
bashInside that shell:
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends ca-certificates locales
sed -i '/en_US.UTF-8/s/^# //' /etc/locale.gen
locale-gen
su node -s /bin/bash
export HOME=/app/.home
export npm_config_cache=/app/.npm-cache
export TMPDIR=/tmp
mkdir -p "$HOME" "$npm_config_cache"
npm install --omit=dev --no-audit --no-fund
command -v paperclipai && exit 1
printf '%s\n' '#!/usr/bin/env bash' 'exec node /app/dist/index.js "$@"' >/app/companies.sh
chmod +x /app/companies.sh
ln -s /app/companies.sh /app/companies
export PATH="/app:$PATH"
DATA_DIR=$(mktemp -d "$TMPDIR/companies-docker-handtest.XXXXXX")
companies.sh add ./fixtures/minimal-company --yes --data-dir "$DATA_DIR" --target new
companies.sh list --yes --data-dir "$DATA_DIR"
node --input-type=module -e 'const response = await fetch("http://127.0.0.1:3210/"); console.log(response.status);'That loopback binding is intentional: Paperclip quickstart uses local_trusted, which requires 127.0.0.1 inside the container. The automated smoke test verifies the UI over container-local HTTP rather than Docker port publishing.
The default manual shell setup binds to 127.0.0.1 inside the container, which means the host cannot reach it. To open the Paperclip UI in your host browser you need two changes:
- Bind to all interfaces — set
HOST=0.0.0.0so Paperclip listens on the container's external interface, not just loopback. - Publish the port — add
-p 3210:3210to thedocker runcommand so Docker forwards host traffic into the container.
docker run --rm -it \
-e COMPANIES_PAPERCLIP_START_TIMEOUT_MS=180000 \
-e HOST=0.0.0.0 \
-e PORT=3210 \
-e SERVE_UI=true \
-e PAPERCLIP_OPEN_ON_LISTEN=false \
-p 3210:3210 \
-v "$tmpdir/package:/app" \
-w /app \
node:20-bookworm-slim \
bashAfter running the setup steps inside the container, open http://localhost:3210 on your host machine. The companies.sh CLI resolves HOST=0.0.0.0 to 127.0.0.1 internally for its own API calls, so the import flow still works as expected.
Use this when you want to manually exercise the exact published-canary style flow from a plain node:20-bookworm-slim shell.
docker run --rm -it \
-e HOST=0.0.0.0 \
-e PORT=3210 \
-p 3210:3210 \
node:20-bookworm-slim bashIf you don't need host-browser access, a plain docker run --rm -it node:20-bookworm-slim bash works — the UI will only be reachable inside the container.
Inside that shell:
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends ca-certificates locales
sed -i '/en_US.UTF-8/s/^# //' /etc/locale.gen
locale-gen
su node -s /bin/bash
npx companies.sh@canary add paperclipai/companies/gstack
npx companies.sh@canary listRun that npx command as node, not root. Local Paperclip bootstrap uses embedded services that can stall under Linux root sessions, so companies.sh now fails fast there and tells you to switch users instead of waiting forever.
Expected interactive flow:
- pick
paperclip - pick
auto - pick the target company mode you want, usually
new - wait for the local Paperclip bootstrap note to finish on first run
- confirm the imported company appears in
npx companies.sh@canary list
The important behavior is that the CLI should keep advancing after auto instead of appearing to stall there. On a fresh container the first bootstrap can still take a while, but the prompt flow now reaches the target selection before local Paperclip startup begins.
Use this to verify the wrapper can reach a local Paperclip instance without applying writes:
export PAPERCLIPAI_CMD="pnpm --dir /path/to/paperclip run paperclipai"
npx companies.sh add paperclipai/companies/gstack \
--target new \
--dry-run \
--yes \
--api-base http://127.0.0.1:3103 \
--api-key "$PAPERCLIP_API_KEY"Expected result: the command reaches the Paperclip import preview path and does not create a company because --dry-run routes to preview only. Use a board-scoped API key or board-authenticated context for this check because preview endpoints may reject agent-scoped credentials.
companies.shpublishes to npm with calendar versions: stableYYYY.MDD.P, canaryYYYY.MDD.P-canary.N.- Canary publishes are intended to run automatically from GitHub
master; stable publishes are manual promotions through GitHub Actions. - Release docs live in doc/RELEASING.md, doc/PUBLISHING.md, and doc/RELEASE-AUTOMATION-SETUP.md.