Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
# Manual npm publish for the alpha SDK + CLI.
#
# Triggered on-demand via the GitHub Actions UI. We deliberately do NOT
# publish on tag push during alpha — the surface is still moving, and we
# want a human-in-the-loop to confirm a green main and the right version
# bumps before each release. Once the SDK reaches 0.2.x we'll consider
# wiring this to release-tag events.
#
# Auth: NPM_TOKEN repo secret (an npm "automation" token scoped to
# `@titular/*`).
#
# Provenance: enabled via `--provenance`. Requires `id-token: write` so the
# runner can mint an OIDC token for the npm registry. Provenance attests
# the package was built from this exact commit on a GitHub-hosted runner;
# consumers can verify with `npm audit signatures`.
name: publish-npm

on:
workflow_dispatch:
inputs:
tag:
description: "npm dist-tag to publish under"
required: false
default: "alpha"
type: choice
options:
- alpha
- beta
- latest
dry-run:
description: "Run `pnpm publish --dry-run` only (no upload)"
required: false
default: false
type: boolean

concurrency:
group: publish-npm
cancel-in-progress: false

jobs:
publish:
name: build + publish
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
# Required for `--provenance`: lets the runner mint an OIDC token
# for npm to attest the build.
id-token: write
# `read` is the default for hosted runners but we pin it explicitly
# to make the security posture obvious — this workflow does NOT
# write to the repo.
contents: read
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: "10.6.2"

- uses: actions/setup-node@v4
with:
node-version: "22"
cache: pnpm
# `setup-node` writes the auth token into the runner-scoped .npmrc
# so subsequent `pnpm publish` calls authenticate without us
# echoing the token anywhere.
registry-url: "https://registry.npmjs.org"

# Provenance attestation requires npm CLI >= 9.5. The npm bundled with
# node 22 satisfies this today, but pinning to `npm@latest` keeps the
# workflow resilient to base-image drift on the hosted runner.
- name: pin npm cli for provenance
run: npm i -g npm@latest

- name: install
run: pnpm install --frozen-lockfile

- name: build sdk
run: pnpm -F @titular/acp-sdk build

- name: build cli
run: pnpm -F @titular/acp-cli build

# `pnpm publish` honours `--provenance` directly (pnpm >= 8.6); it does
# NOT read the `NPM_CONFIG_PROVENANCE` env var that `npm publish` does,
# so we deliberately do not set it here.
- name: publish @titular/acp-sdk
if: ${{ inputs.dry-run == false }}
run: |
pnpm -F @titular/acp-sdk publish \
--tag "${{ inputs.tag }}" \
--no-git-checks \
--access public \
--provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: publish @titular/acp-cli
if: ${{ inputs.dry-run == false }}
run: |
pnpm -F @titular/acp-cli publish \
--tag "${{ inputs.tag }}" \
--no-git-checks \
--access public \
--provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: dry-run @titular/acp-sdk
if: ${{ inputs.dry-run == true }}
run: |
pnpm -F @titular/acp-sdk publish \
--tag "${{ inputs.tag }}" \
--no-git-checks \
--access public \
--dry-run

- name: dry-run @titular/acp-cli
if: ${{ inputs.dry-run == true }}
run: |
pnpm -F @titular/acp-cli publish \
--tag "${{ inputs.tag }}" \
--no-git-checks \
--access public \
--dry-run
15 changes: 15 additions & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,18 @@ CVE-2025-52881 # runtime-only, test-only dep
CVE-2025-52565 # runtime-only, test-only dep
CVE-2025-31133 # runtime-only, test-only dep
CVE-2024-21626 # leaked fd, runtime-only; test-only dep

# --- packages/acp-sdk-e2e test deps (testcontainers -> undici 5.x) ---------
#
# testcontainers@10 pins undici to 5.29.0 internally for its docker-engine
# HTTP client. The package is in devDependencies of @titular/acp-sdk-e2e and
# is only loaded by integration test suites that spin up containers — it is
# never imported by the published acp-sdk / acp-cli runtime, the gateway, or
# the indexer. We previously tried a workspace-wide `pnpm.overrides` to force
# undici@^6, but that broke vitest in apps/web: jsdom@29 imports the internal
# path `undici/lib/handler/wrap-handler.js` which only exists in undici 7.x.
# Both CVEs are DoS vectors against the HTTP client; testcontainers only ever
# speaks to a local docker socket inside CI, so the threat model doesn't apply.
# Re-audit when testcontainers ships a release that targets undici >=6.
CVE-2026-1526 # undici DoS via unbounded memory; test-only (testcontainers)
CVE-2026-2229 # undici DoS via invalid WebSocket; test-only (testcontainers)
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
"@commitlint/config-conventional": "19.6.0",
"husky": "9.1.7",
"lint-staged": "15.2.11",
"turbo": "2.3.3"
"turbo": "2.3.3",
"typedoc": "0.27.6",
"typedoc-plugin-markdown": "4.4.1"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,json,css}": ["biome check --write --no-errors-on-unmatched"],
Expand Down
4 changes: 4 additions & 0 deletions packages/acp-cli/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Generated TypeDoc output. Regenerate locally with `pnpm -F @titular/acp-cli
# docs`. Not committed: docs would drift between releases and force noisy
# diffs on every public-API change.
docs/api/
81 changes: 81 additions & 0 deletions packages/acp-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# @titular/acp-cli

Terminal CLI wrapping [`@titular/acp-sdk`](../acp-sdk-ts). Configure a
gateway, register and browse agents, create and list jobs, and tail the
gateway event stream from your shell.

> **Status:** `0.1.0-alpha` — alpha-quality, expect breaking changes
> between minor versions. Pin exact versions until `0.2.x`.

## Install

```sh
# globally — installs an `acp` binary on PATH
npm install -g @titular/acp-cli

# or as a dev dep, invoked via npx / pnpm exec
pnpm add -D @titular/acp-cli
pnpm exec acp --help
```

## Configure

The first thing to do is point the CLI at a gateway and a chain:

```sh
acp configure \
--gateway-url https://api.titular.dev \
--chain base-sepolia \
--alchemy-api-key "$ALCHEMY_API_KEY"
```

Config lives at `$XDG_CONFIG_HOME/titular/cli.json` (or the platform
equivalent). Re-run `configure` to update; sensitive fields are redacted
when printed.

## Commands

| Command | What it does |
| -------------------------- | ----------------------------------------------------------- |
| `acp configure` | Set or update gateway URL, chain, RPC, signer. |
| `acp agent create` | Register an agent on chain. |
| `acp agent list` | Browse the gateway-indexed registry. |
| `acp agent status <id>` | Inspect a single agent's on-chain + indexed status. |
| `acp browse` | Filter agents by kind / capability / pagination. |
| `acp job create` | Create an escrowed job against a counterparty. |
| `acp job list` | List jobs you've created or received. |
| `acp events listen` | Tail the gateway SSE event stream (jobs + agents). |

Run `acp <command> --help` for full flag list and examples.

## Programmatic use

The CLI binary and its commands are also exposed as a library, so dashboards
and integration tests can call the same code paths without shelling out:

```ts
import { runAgentList, buildGatewayClient } from "@titular/acp-cli";

const gateway = buildGatewayClient({ gatewayUrl: "https://api.titular.dev" });
const page = await runAgentList(
{ kind: "service", limit: 20 },
{ gateway, log: console },
);
```

See [`src/index.ts`](./src/index.ts) for the full re-export surface.

## API reference

Full TypeDoc-generated reference (markdown) lives under `docs/api/` after
running:

```sh
pnpm -F @titular/acp-cli docs
```

The output is gitignored — regenerate per release.

## License

[MIT](../../LICENSE)
25 changes: 24 additions & 1 deletion packages/acp-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,28 @@
"name": "@titular/acp-cli",
"version": "0.1.0-alpha.0",
"description": "Terminal CLI wrapping `@titular/acp-sdk`. Configure a gateway, register/browse agents, create/list jobs, and tail the gateway event stream from the shell.",
"keywords": [
"titular",
"acp",
"agent-commerce-protocol",
"agents",
"ai-agents",
"ethereum",
"evm",
"base",
"cli",
"sdk"
],
"homepage": "https://github.com/0xDevNinja/titular/tree/main/packages/acp-cli#readme",
"bugs": {
"url": "https://github.com/0xDevNinja/titular/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/0xDevNinja/titular.git",
"directory": "packages/acp-cli"
},
"author": "Titular Contributors",
"license": "MIT",
"type": "module",
"bin": {
Expand All @@ -12,7 +34,8 @@
"build": "tsc -p tsconfig.build.json && node ./scripts/postbuild.mjs",
"lint": "biome check src",
"typecheck": "tsc --noEmit",
"test": "vitest run --coverage"
"test": "vitest run --coverage",
"docs": "typedoc --options typedoc.json"
},
"dependencies": {
"@titular/acp-sdk": "workspace:*",
Expand Down
20 changes: 20 additions & 0 deletions packages/acp-cli/typedoc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://typedoc.org/schema.json",
"name": "@titular/acp-cli",
"entryPoints": ["src/index.ts"],
"entryPointStrategy": "expand",
"out": "docs/api",
"tsconfig": "tsconfig.build.json",
"plugin": ["typedoc-plugin-markdown"],
"readme": "README.md",
"includeVersion": true,
"excludePrivate": true,
"excludeProtected": true,
"excludeInternal": true,
"excludeExternals": true,
"categorizeByGroup": true,
"hideGenerator": true,
"githubPages": false,
"gitRevision": "main",
"sourceLinkTemplate": "https://github.com/0xDevNinja/titular/blob/{gitRevision}/{path}#L{line}"
}
4 changes: 4 additions & 0 deletions packages/acp-sdk-ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Generated TypeDoc output. Regenerate locally with `pnpm -F @titular/acp-sdk
# docs`. Not committed: docs would drift between releases and force noisy
# diffs on every public-API change.
docs/api/
Loading
Loading