create-urateam 0.1.8 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,39 +1,95 @@
1
- # === Required ===
1
+ # =============================================================================
2
+ # urateam .env — copy to .env and fill in. NEVER commit .env to git.
3
+ # =============================================================================
4
+
5
+ # === Linear (REQUIRED) ===
2
6
  LINEAR_API_KEY=
3
7
  LINEAR_WEBHOOK_SECRET=
4
8
  LINEAR_TEAM_ID=
5
9
 
6
- # Repository REQUIRED to boot. `ura dev` and `ura start` exit with an
7
- # error if REPO_TEAM_ID or REPO_URL are missing or blank. Without these
8
- # the webhook server otherwise starts looking healthy but silently fails
9
- # every inbound Linear event with "no repo mapping". In the common
10
- # single-team setup, REPO_TEAM_ID is the same UUID as LINEAR_TEAM_ID.
10
+ # === Repository (REQUIRED) ===
11
+ # `ura dev` and `ura start` exit with an error if these are missing.
12
+ # In a single-team setup, REPO_TEAM_ID == LINEAR_TEAM_ID.
11
13
  REPO_URL=
12
14
  REPO_DEFAULT_BRANCH=main
13
15
  REPO_TEAM_ID=
14
16
 
15
- # Database
16
- DATABASE_URL=postgres://urateam:password@postgres:5432/urateam
17
+ # === Anthropic auth (REQUIRED for production / headless) ===
18
+ # Use ANTHROPIC_API_KEY for any deployment without an interactive shell
19
+ # (VPS, containerized, CI). The OSS-tier `claude` CLI session flow only
20
+ # works when you can `claude login` interactively on the host.
21
+ ANTHROPIC_API_KEY=
22
+
23
+ # === Pro license (REQUIRED for Pro features: Slack, multi-repo, deep-review, etc.) ===
24
+ URATEAM_LICENSE_KEY=
25
+
26
+ # === GitHub (REQUIRED for PR creation in production) ===
27
+ # Two paths: GitHub App credentials (production) OR `gh` CLI session
28
+ # inside the container (`docker compose exec agent gh auth login` after
29
+ # bringing the stack up — fine for solo / single-repo).
30
+ #
31
+ # Production path — GitHub App:
32
+ # 1. Create at github.com/settings/apps/new with Contents:Write,
33
+ # Pull requests:Write, Metadata:Read scopes
34
+ # 2. Install the app on the target repo
35
+ # 3. Download the private key .pem and bind-mount it into the container
36
+ # (e.g. add a volume map in docker-compose.yml: ./gh-app.pem:/run/gh-app.pem:ro)
37
+ # GITHUB_APP_ID=
38
+ # GITHUB_PRIVATE_KEY_PATH=/run/gh-app.pem
39
+ # GITHUB_INSTALLATION_ID=
40
+ # Optional: only needed if PR-comment-driven re-runs are enabled
41
+ # GITHUB_WEBHOOK_SECRET=
17
42
 
18
- # Dashboard auth (required)
43
+ # === Database ===
44
+ # Generate POSTGRES_PASSWORD with: openssl rand -base64 32
45
+ # DATABASE_URL is assembled in docker-compose.yml from this password —
46
+ # do NOT set it here (env_file does not interpolate variables).
47
+ POSTGRES_PASSWORD=
48
+
49
+ # === Domain (REQUIRED for production deploy with the Caddy reverse proxy) ===
50
+ # Public hostname Caddy will request a Let's Encrypt cert for. Must resolve
51
+ # to this host before bringing the stack up; ports 80 + 443 must be open.
52
+ DOMAIN=
53
+ # Email for ACME / Let's Encrypt expiry warnings. Strongly recommended.
54
+ CADDY_EMAIL=
55
+
56
+ # === Dashboard auth (REQUIRED) ===
57
+ # Generate a strong DASHBOARD_PASSWORD. Production refuses to start without one.
19
58
  DASHBOARD_USER=admin
20
59
  DASHBOARD_PASSWORD=
60
+ # Set DASHBOARD_BASE_PATH if mounting under a path prefix (no trailing slash).
61
+ # DASHBOARD_BASE_PATH=
62
+
63
+ # === Workspace (defaults inside the container) ===
64
+ # AGENT_RUN_DIR=/var/agent-runs
65
+ # REPO_CLONE_DIR=/var/agent-repos
66
+ # WORKTREE_TTL_HOURS=24
67
+
68
+ # === Concurrency ===
69
+ # MAX_CONCURRENT_RUNS=3
70
+
71
+ # =============================================================================
72
+ # Optional features below
73
+ # =============================================================================
21
74
 
22
- # === Optional ===
23
75
  # Override per-stage agent budget (maxTurns / maxInputTokens / model / tools).
24
- # Useful when the test stage has to bootstrap test infra from scratch and
25
- # burns through the default 25-turn cap. See urateam#38.
76
+ # Useful when the test stage has to bootstrap test infra and burns the
77
+ # default 25-turn cap. See urateam#38.
26
78
  # URATEAM_AGENT_PROFILES='{"test":{"maxTurns":50,"maxInputTokens":80000}}'
27
79
 
80
+ # Slack notifier — basic incoming-webhook (no Pro license needed)
28
81
  # SLACK_WEBHOOK_URL=
29
82
  # DISCORD_WEBHOOK_URL=
30
- # GITHUB_WEBHOOK_SECRET=
83
+
84
+ # PM Agent + Slack interface (Pro feature: slack-interface)
31
85
  # PM_AGENT_ENABLED=false
32
86
  # PM_AGENT_TEAM_IDS=
33
87
  # PM_AGENT_SLACK_CHANNEL_ID=
34
88
  # PM_AGENT_DAILY_TOKEN_BUDGET=5000000
35
89
  # PM_AGENT_MAX_IN_FLIGHT=3
90
+ # PM_AGENT_CRON_INTERVAL_MS=1800000
36
91
  # SLACK_BOT_TOKEN=
92
+ # SLACK_SIGNING_SECRET=
93
+
94
+ # Logging
37
95
  # LOG_LEVEL=info
38
- # MAX_CONCURRENT_RUNS=3
39
- # URATEAM_LICENSE_KEY=
@@ -0,0 +1,36 @@
1
+ # urateam reverse proxy + auto-HTTPS via Let's Encrypt.
2
+ #
3
+ # Caddy routes:
4
+ # - /webhooks/* → agent:3000 (Linear, GitHub webhook receivers)
5
+ # - /slack/* → agent:3000 (Slack events + slash commands, optional PM agent)
6
+ # - /health → agent:3000 (liveness probe)
7
+ # - everything else → agent:3001 (the ops dashboard)
8
+ #
9
+ # DOMAIN is read from .env; required for cert issuance.
10
+ # Ports 80 and 443 must be reachable from the public internet for ACME.
11
+
12
+ {
13
+ email {$CADDY_EMAIL}
14
+ }
15
+
16
+ {$DOMAIN} {
17
+ encode zstd gzip
18
+
19
+ handle /webhooks/* {
20
+ reverse_proxy agent:3000
21
+ }
22
+ handle /slack/* {
23
+ reverse_proxy agent:3000
24
+ }
25
+ handle /health {
26
+ reverse_proxy agent:3000
27
+ }
28
+ handle {
29
+ reverse_proxy agent:3001
30
+ }
31
+
32
+ log {
33
+ output stdout
34
+ format console
35
+ }
36
+ }
@@ -1,7 +1,16 @@
1
1
  FROM node:22-slim
2
2
 
3
- RUN apt-get update && apt-get install -y git gh && rm -rf /var/lib/apt/lists/*
3
+ # git + gh: needed by the agent for cloning, branch operations, PR creation.
4
+ # Claude Code CLI: required for OSS-tier subscription auth and as a fallback
5
+ # for Pro-tier deployments that don't set ANTHROPIC_API_KEY. Persisted auth
6
+ # lives at /root/.config/claude — bind-mounted via a docker volume below
7
+ # so `claude login` only has to run once.
8
+ RUN apt-get update \
9
+ && apt-get install -y --no-install-recommends git gh ca-certificates \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
4
12
  RUN corepack enable
13
+ RUN npm install -g @anthropic-ai/claude-code
5
14
 
6
15
  WORKDIR /app
7
16
  COPY package.json pnpm-lock.yaml* ./
@@ -7,10 +7,18 @@ to implement features, fix bugs, and create PRs automatically.
7
7
 
8
8
  ## Setup
9
9
 
10
- 1. Fill in `.env` with your Linear API key, webhook secret, team ID, and repo URL
10
+ Two paths: pick one.
11
+
12
+ ### Local dev (your laptop)
13
+
14
+ 1. Copy `.env.example` to `.env` and fill in. NEVER commit `.env`.
11
15
  2. Install dependencies: `pnpm install`
12
- 3. **Authenticate Claude** (OSS path only — see [Claude auth lifecycle](#claude-auth-lifecycle-oss-tier) below): `claude login`
13
- 4. Start the agent: `pnpm dev` (SQLite, local dev) or `pnpm start` (production)
16
+ 3. **Authenticate Claude** (OSS path only — see [Claude auth lifecycle](#claude-auth-lifecycle-oss-tier) below): `claude login`. Skip if you set `ANTHROPIC_API_KEY`.
17
+ 4. Start the agent: `pnpm dev`
18
+
19
+ ### Production VPS
20
+
21
+ Skip the `pnpm install` / `pnpm dev` steps and jump straight to [Production deploy](#production-deploy-via-docker-compose) — Docker Compose handles everything.
14
22
 
15
23
  ## Claude auth lifecycle (OSS tier)
16
24
 
@@ -44,23 +52,98 @@ Use that variant when re-authing inside a running container.
44
52
  have session-lifetime semantics, so this whole concern goes away. See the
45
53
  [urateam docs](https://github.com/JonB32/urateam) for upgrade paths.
46
54
 
47
- ## Expose the webhook
55
+ ## Expose the webhook (local dev only)
48
56
 
49
- The agent listens on `http://localhost:3000/webhooks/linear`. To receive
50
- webhooks from Linear, expose this port via ngrok or a reverse proxy:
57
+ The agent listens on `http://localhost:3000/webhooks/linear`. For local
58
+ development, expose via ngrok:
51
59
 
52
60
  ```bash
53
61
  ngrok http 3000
54
62
  ```
55
63
 
56
64
  Configure the ngrok URL as a webhook in Linear settings with the
57
- `LINEAR_WEBHOOK_SECRET` from your `.env`.
65
+ `LINEAR_WEBHOOK_SECRET` from your `.env`. For production, see
66
+ [Production deploy](#production-deploy-via-docker-compose) — Caddy
67
+ handles HTTPS termination directly and you wire Linear to your real domain.
58
68
 
59
69
  ## Dashboard
60
70
 
61
71
  The ops dashboard runs on `http://localhost:3001`. Credentials from
62
72
  `DASHBOARD_USER` / `DASHBOARD_PASSWORD` in `.env`.
63
73
 
74
+ ## Production deploy via docker compose
75
+
76
+ Compose template ships a hardened three-service stack:
77
+
78
+ - **caddy** — reverse proxy on :80/:443 with automatic Let's Encrypt certs.
79
+ Routes `/webhooks/*` and `/slack/*` to the agent (:3000), everything else
80
+ to the dashboard (:3001).
81
+ - **agent** — `ura start`. No public ports; reachable only via Caddy.
82
+ - **postgres** — internal-only network, no published ports. Password from
83
+ `POSTGRES_PASSWORD` (compose refuses to start without it).
84
+
85
+ ### Pre-flight
86
+
87
+ 1. **Provision a VPS** (Hetzner, DigitalOcean, Linode, Fly, etc.). 4 GB RAM
88
+ minimum for `MAX_CONCURRENT_RUNS=3`.
89
+ 2. **Point a domain** (e.g. `urateam.your-domain.com`) at the VPS IP. Caddy needs
90
+ ports 80 + 443 open for ACME challenges.
91
+ 3. **Install Docker** and the Compose plugin on the box.
92
+
93
+ ### Deploy
94
+
95
+ ```bash
96
+ # 1. On the VPS, clone or scp this project
97
+ cd /opt/<project>/.urateam
98
+
99
+ # 2. Copy and fill in env
100
+ cp .env.example .env
101
+ # At minimum set: DOMAIN, CADDY_EMAIL, POSTGRES_PASSWORD (openssl rand -base64 32),
102
+ # ANTHROPIC_API_KEY, URATEAM_LICENSE_KEY (for Pro), LINEAR_*, REPO_*,
103
+ # DASHBOARD_PASSWORD.
104
+
105
+ # 3. Bring up the stack
106
+ docker compose up -d --build
107
+
108
+ # 4. Authenticate Claude Code CLI inside the container (skip if you set
109
+ # ANTHROPIC_API_KEY in .env). Login is persisted in a docker volume,
110
+ # so this only has to run once per deployment.
111
+ docker compose exec agent claude login
112
+
113
+ # 5. Authenticate gh CLI inside the container (required for PR creation
114
+ # unless you wired GITHUB_APP_ID / GITHUB_PRIVATE_KEY_PATH /
115
+ # GITHUB_INSTALLATION_ID in .env). Also persisted across rebuilds.
116
+ docker compose exec agent gh auth login
117
+
118
+ # 4. Tail logs to verify license, webhooks, dashboard
119
+ docker compose logs -f agent
120
+ ```
121
+
122
+ After the first run, Caddy will request and store a Let's Encrypt cert for
123
+ `$DOMAIN`. The dashboard is reachable at `https://$DOMAIN`, webhooks at
124
+ `https://$DOMAIN/webhooks/linear`.
125
+
126
+ ### Wiring Linear
127
+
128
+ In Linear → Workspace settings → API → Webhooks → Create:
129
+
130
+ - URL: `https://$DOMAIN/webhooks/linear`
131
+ - Secret: paste `LINEAR_WEBHOOK_SECRET` from `.env`
132
+ - Subscribe to: Issue state changes (and any others your pipelines key off of).
133
+
134
+ ### Re-deploy
135
+
136
+ ```bash
137
+ git pull && docker compose up -d --build
138
+ ```
139
+
140
+ ### Backups
141
+
142
+ `pgdata` and `agent-runs` are named docker volumes. For backups, snapshot the
143
+ host volume directory or use `docker run --rm -v pgdata:/data … pg_dump` style
144
+ sidecars. Workspace dirs (`/var/agent-runs`, `/var/agent-repos`) auto-clean
145
+ older than `WORKTREE_TTL_HOURS` (default 24h).
146
+
64
147
  ## How it works
65
148
 
66
149
  1. Move a Linear issue to the `Todo` state with an appropriate pipeline label
@@ -1,28 +1,67 @@
1
1
  services:
2
- postgres:
3
- image: postgres:16
2
+ caddy:
3
+ image: caddy:2
4
+ restart: unless-stopped
5
+ ports:
6
+ - "80:80"
7
+ - "443:443"
4
8
  environment:
5
- POSTGRES_USER: urateam
6
- POSTGRES_PASSWORD: password
7
- POSTGRES_DB: urateam
9
+ DOMAIN: ${DOMAIN}
8
10
  volumes:
9
- - pgdata:/var/lib/postgresql/data
10
- ports:
11
- - "5432:5432"
11
+ - ./Caddyfile:/etc/caddy/Caddyfile:ro
12
+ - caddy-data:/data
13
+ - caddy-config:/config
14
+ depends_on:
15
+ - agent
16
+ networks:
17
+ - frontend
12
18
 
13
19
  agent:
14
20
  build: .
21
+ restart: unless-stopped
15
22
  env_file: .env
23
+ environment:
24
+ # Compose interpolates ${POSTGRES_PASSWORD} from .env here. It does NOT
25
+ # interpolate inside .env itself, so DATABASE_URL must be assembled
26
+ # at the compose layer rather than the env-file layer.
27
+ DATABASE_URL: "postgres://urateam:${POSTGRES_PASSWORD}@postgres:5432/urateam"
16
28
  depends_on:
17
29
  - postgres
18
- ports:
19
- - "3000:3000"
20
- - "3001:3001"
30
+ expose:
31
+ - "3000"
32
+ - "3001"
21
33
  volumes:
22
34
  - agent-runs:/var/agent-runs
23
35
  - agent-repos:/var/agent-repos
36
+ # Persist the `claude` and `gh` CLI login state across rebuilds so
37
+ # operators don't have to re-auth every `docker compose up --build`.
38
+ - claude-config:/root/.config/claude
39
+ - gh-config:/root/.config/gh
40
+ networks:
41
+ - frontend
42
+ - backend
43
+
44
+ postgres:
45
+ image: postgres:16
46
+ restart: unless-stopped
47
+ environment:
48
+ POSTGRES_USER: urateam
49
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set in .env}
50
+ POSTGRES_DB: urateam
51
+ volumes:
52
+ - pgdata:/var/lib/postgresql/data
53
+ networks:
54
+ - backend
55
+
56
+ networks:
57
+ frontend:
58
+ backend:
24
59
 
25
60
  volumes:
26
61
  pgdata:
27
62
  agent-runs:
28
63
  agent-repos:
64
+ caddy-data:
65
+ caddy-config:
66
+ claude-config:
67
+ gh-config: