propr-cli 0.8.3 → 0.8.5
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.
- package/README.md +4 -4
- package/dist/api/relay.js +10 -0
- package/dist/assets/env.example.txt +182 -59
- package/dist/auth/githubLogin.js +66 -0
- package/dist/commands/agentCommands.js +74 -0
- package/dist/commands/agentValidation.js +548 -0
- package/dist/commands/checkCommands.js +981 -76
- package/dist/commands/imageCommands.js +60 -0
- package/dist/commands/index.js +3 -0
- package/dist/commands/initStack.js +50 -1
- package/dist/commands/relayCommands.js +45 -12
- package/dist/commands/setup/agents.js +185 -0
- package/dist/commands/setup/engine.js +956 -0
- package/dist/commands/setup/github.js +181 -0
- package/dist/commands/setup/sequential.js +501 -0
- package/dist/commands/setup/state.js +242 -0
- package/dist/commands/setup/types.js +85 -0
- package/dist/commands/setupCommand.js +85 -0
- package/dist/commands/stackCommands.js +14 -2
- package/dist/commands/systemCommands.js +49 -2
- package/dist/commands/tunnelCommand.js +562 -0
- package/dist/config/ConfigManager.js +22 -0
- package/dist/config/types.js +1 -0
- package/dist/index.js +14 -45
- package/dist/orchestrator/format.js +46 -0
- package/dist/orchestrator/index.js +7 -2
- package/dist/orchestrator/manifest.json +12 -11
- package/dist/orchestrator/orchestrator.mjs +872 -73
- package/dist/tui/AgentTableApp.js +86 -0
- package/dist/tui/CheckApp.js +202 -0
- package/dist/tui/SetupApp.js +586 -0
- package/dist/tui/SetupApp.test.js +172 -0
- package/dist/tui/app.js +84 -0
- package/dist/tui/render.js +28 -2
- package/dist/utils/envFile.js +45 -0
- package/dist/vendor/shared/githubEventIntakeMode.js +41 -0
- package/dist/vendor/shared/index.js +17 -0
- package/dist/vendor/shared/intakeModePrerequisites.js +76 -0
- package/dist/vendor/shared/modelDefinitions.js +4 -4
- package/dist/vendor/shared/proprCompatibility.js +70 -0
- package/dist/vendor/shared/proprServiceUrls.js +124 -0
- package/dist/vendor/shared/statusKeys.js +14 -0
- package/dist/vendor/shared/validateRoutingUrl.js +46 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Command-line interface for interacting with the ProPR backend. ProPR enables AI-
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install -g
|
|
8
|
+
npm install -g propr-cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
The host CLI requires Node.js 22 or newer. The Docker launcher image is separate
|
|
@@ -223,12 +223,12 @@ For OpenCode agents, install and authenticate OpenCode on the host before adding
|
|
|
223
223
|
|
|
224
224
|
```bash
|
|
225
225
|
curl -fsSL https://opencode.ai/install | bash
|
|
226
|
-
mkdir -p ~/.config/opencode
|
|
226
|
+
mkdir -p ~/.config/opencode
|
|
227
227
|
opencode auth login
|
|
228
228
|
mkdir -p ~/.config/opencode/xdg-data/opencode && cp ~/.local/share/opencode/auth.json ~/.config/opencode/xdg-data/opencode/auth.json
|
|
229
229
|
```
|
|
230
230
|
|
|
231
|
-
OpenCode stores `auth.json` under `~/.local/share/opencode`, but ProPR mounts the configured OpenCode config directory into the agent container. When using copied file-based auth, set `XDG_DATA_HOME=/home/node/.config/opencode/xdg-data` on the OpenCode agent.
|
|
231
|
+
OpenCode stores `auth.json` under `~/.local/share/opencode`, but ProPR mounts the configured OpenCode config directory into the agent container. When using copied file-based auth, set `XDG_DATA_HOME=/home/node/.config/opencode/xdg-data` on the OpenCode agent. Use `~/.config/opencode` as the agent `configPath`.
|
|
232
232
|
|
|
233
233
|
The example model `opencode-minimax-m3-free` is a built-in free OpenCode model. OpenCode's model list changes with auth providers; run `opencode models` after logging in and register any desired provider/model IDs with ProPR's `opencode-` prefix, such as `opencode-openai/gpt-5.5`. ProPR converts these IDs back to OpenCode's native `provider/model` syntax at execution time and does not add authenticated provider models by default.
|
|
234
234
|
Dynamic OpenCode GitHub labels use the format `llm-<agent-alias>~<propr-opencode-model-id>`, for example `llm-opencode~opencode-openai/gpt-5.5`. The `~` separator is an intentional public contract — these labels are persisted on GitHub issues and resolved later for execution routing.
|
|
@@ -348,7 +348,7 @@ import {
|
|
|
348
348
|
createConfigManager,
|
|
349
349
|
createApiClient,
|
|
350
350
|
resolveProject,
|
|
351
|
-
} from '
|
|
351
|
+
} from 'propr-cli';
|
|
352
352
|
|
|
353
353
|
const config = await createConfigManager();
|
|
354
354
|
const client = await createApiClient();
|
package/dist/api/relay.js
CHANGED
|
@@ -58,6 +58,16 @@ async function relayRequest(options, path, method, body, init) {
|
|
|
58
58
|
throw new Error("The relay returned a malformed JSON response.");
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Identity probe: returns the GitHub user and the installations they may access
|
|
63
|
+
* (the same `/v1/auth/me` route the dashboard boots from). Used by `propr relay`
|
|
64
|
+
* to discover an installation id when none was passed explicitly. Note these are
|
|
65
|
+
* access-scoped (ownership or team membership); minting a relay token is stricter
|
|
66
|
+
* (owner-only), so a discovered installation may still 403 on enroll.
|
|
67
|
+
*/
|
|
68
|
+
export function fetchAuthenticatedUser(options) {
|
|
69
|
+
return relayRequest(options, "/auth/me", "GET");
|
|
70
|
+
}
|
|
61
71
|
export function enrollRelayToken(options, params) {
|
|
62
72
|
return relayRequest(options, "/relay-tokens", "POST", {
|
|
63
73
|
installation_id: params.installationId,
|
|
@@ -1,34 +1,128 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# GitHub event intake mode
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# How ProPR receives GitHub events. New installs use the hosted ProPR GitHub App
|
|
5
|
+
# with WebSocket routing — no public URL and no GitHub App of your own required.
|
|
6
|
+
# This is the default and the normal path; polling and direct webhook below are
|
|
7
|
+
# advanced opt-ins.
|
|
8
|
+
# routing_websocket — events stream over the ProPR routing WebSocket (default)
|
|
9
|
+
# polling — ProPR pulls events from the GitHub API (advanced opt-in)
|
|
10
|
+
# direct_webhook — GitHub delivers events to your own public /webhook URL
|
|
11
|
+
# (advanced; requires your own GitHub App)
|
|
12
|
+
#
|
|
13
|
+
# Migrating an existing install: the default is now routing_websocket. If you run
|
|
14
|
+
# your OWN GitHub App (the old polling/webhook setup), routing_websocket will not
|
|
15
|
+
# work for you — it needs the hosted ProPR App via the relay (relay auth mode).
|
|
16
|
+
# Keep your current behavior by setting this explicitly to `polling` or
|
|
17
|
+
# `direct_webhook`, or migrate to the hosted path with `propr relay enroll`.
|
|
18
|
+
GITHUB_EVENT_INTAKE_MODE=routing_websocket
|
|
5
19
|
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
20
|
+
# --- Routing WebSocket (default intake) --------------------------------------
|
|
21
|
+
# The hosted path: ProPR authenticates through the shared ProPR GitHub App via
|
|
22
|
+
# the token relay and streams events over the routing WebSocket. Run
|
|
23
|
+
# `propr relay enroll` to populate PROPR_GH_RELAY_URL / PROPR_GH_RELAY_TOKEN /
|
|
24
|
+
# GH_INSTALLATION_ID for you, or set them by hand below.
|
|
25
|
+
# PROPR_ROUTING_URL — routing WebSocket origin (wss://; ws:// only for localhost)
|
|
26
|
+
# PROPR_GH_RELAY_URL — token relay URL incl. version prefix
|
|
27
|
+
# (https://; http only for localhost)
|
|
28
|
+
# PROPR_GH_RELAY_TOKEN — durable relay credential issued for your install
|
|
29
|
+
# GH_INSTALLATION_ID — which GitHub App installation ProPR acts on
|
|
30
|
+
# Both URLs default to the hosted relay (webhook.propr.dev) when unset, so you
|
|
31
|
+
# only need them below to point at a self-hosted relay.
|
|
32
|
+
# PROPR_ROUTING_URL=wss://webhook.propr.dev
|
|
33
|
+
# PROPR_GH_RELAY_URL=https://webhook.propr.dev/v1
|
|
34
|
+
# Optional transport keepalive override. The default is 300000 ms (5 minutes);
|
|
35
|
+
# lower it only if a network path closes otherwise-healthy WebSockets too quickly.
|
|
36
|
+
# PROPR_ROUTING_WS_PING_INTERVAL_MS=300000
|
|
37
|
+
# Per-install secrets — left commented so an unedited copy is not mistaken for a
|
|
38
|
+
# configured relay. Run `propr relay enroll` to fill these in, or uncomment and set
|
|
39
|
+
# them by hand. (Active placeholder values here could make intake/auth mode look
|
|
40
|
+
# configured when it is not.)
|
|
22
41
|
# PROPR_GH_RELAY_TOKEN=your_relay_token
|
|
23
|
-
#
|
|
42
|
+
# GH_INSTALLATION_ID=your_installation_id
|
|
43
|
+
# Optional: force the auth mode explicitly instead of inferring (app | relay | demo).
|
|
44
|
+
# Relay mode is inferred automatically when PROPR_GH_RELAY_URL + PROPR_GH_RELAY_TOKEN
|
|
45
|
+
# are set, so you normally do not need this.
|
|
24
46
|
# GH_AUTH_MODE=relay
|
|
25
47
|
|
|
48
|
+
# --- Hosted UI tunnel (v1, optional) -----------------------------------------
|
|
49
|
+
# Expose this local stack's API to the hosted control plane at
|
|
50
|
+
# https://app.propr.dev through a Cloudflare Tunnel, so you can drive a
|
|
51
|
+
# locally-running stack from the hosted UI. The tunnel publishes the API
|
|
52
|
+
# container (only /api/* and /socket.io/* are routed; the root URL returns 404);
|
|
53
|
+
# the UI bundle is served by app.propr.dev, not through the tunnel. Cloudflare
|
|
54
|
+
# forwards the tunnel to the Docker-internal http://api:4000, NOT host port 4000,
|
|
55
|
+
# so the published host port is irrelevant to the tunnel and cannot conflict with
|
|
56
|
+
# it. Off by default; local development is unaffected (API_PUBLIC_URL /
|
|
57
|
+
# FRONTEND_URL keep their localhost defaults below).
|
|
58
|
+
#
|
|
59
|
+
# Normal setup is CLI-first: ProPR Connect shows a one-time command such as
|
|
60
|
+
# `propr tunnel setup --token ... --url https://<id>.proxy.propr.dev --start`.
|
|
61
|
+
# Run that from this stack directory so the CLI writes these values and restarts
|
|
62
|
+
# the stack for you. The variables below are documented as a fallback for older
|
|
63
|
+
# CLI versions, manual recovery, or auditing what the setup command persists.
|
|
64
|
+
#
|
|
65
|
+
# Enable the tunnel:
|
|
66
|
+
# PROPR_UI_TUNNEL_TOKEN — Cloudflare Tunnel token; required to start. Setting it
|
|
67
|
+
# enables the tunnel by default, so the next `propr start`
|
|
68
|
+
# brings up the sidecar (unless you ran `propr tunnel off`).
|
|
69
|
+
# WARNING: this is a LIVE Cloudflare credential — anyone
|
|
70
|
+
# with it can route traffic through your tunnel. Do not
|
|
71
|
+
# commit, log, or share it; keep it in this .env only.
|
|
72
|
+
# PROPR_UI_TUNNEL_ENABLED — explicitly enable the tunnel (`true`/`1`). A token is
|
|
73
|
+
# STILL required — `propr check` fails if this is set
|
|
74
|
+
# without PROPR_UI_TUNNEL_TOKEN. Redundant when a token
|
|
75
|
+
# is set, since a token alone already enables the tunnel
|
|
76
|
+
# PROPR_INSTANCE_ID — this stack's instance id; must be a valid DNS label
|
|
77
|
+
# (letters, digits, hyphens; 1-63 chars). Derives the
|
|
78
|
+
# public URL https://<id>.proxy.propr.dev when no
|
|
79
|
+
# explicit URL is set
|
|
80
|
+
# PROPR_UI_PUBLIC_API_URL — explicit public API URL the hosted UI talks to (overrides the derived one)
|
|
81
|
+
# PROPR_CLOUDFLARED_IMAGE — cloudflared image (default: cloudflare/cloudflared:2024.12.2,
|
|
82
|
+
# a pinned tag; override only if you need a different build)
|
|
83
|
+
# PROPR_UI_TUNNEL_TOKEN=your_cloudflare_tunnel_token
|
|
84
|
+
# PROPR_INSTANCE_ID=abc123
|
|
85
|
+
# PROPR_UI_PUBLIC_API_URL=https://abc123.proxy.propr.dev
|
|
86
|
+
# PROPR_CLOUDFLARED_IMAGE=cloudflare/cloudflared:2024.12.2
|
|
87
|
+
#
|
|
88
|
+
# Why `.proxy.propr.dev`: each enabled stack is published under a per-instance
|
|
89
|
+
# hostname `<PROPR_INSTANCE_ID>.proxy.propr.dev`. The hosted UI at
|
|
90
|
+
# https://app.propr.dev discovers and reaches your stack's API through that
|
|
91
|
+
# single shared suffix, without you owning or registering a domain.
|
|
92
|
+
#
|
|
93
|
+
# Browser origin vs API host — they are DIFFERENT and must be set accordingly.
|
|
94
|
+
# The browser loads the UI from app.propr.dev; the API, Socket.IO, OAuth
|
|
95
|
+
# callback, and session cookie all live on the proxy host. In tunnel mode
|
|
96
|
+
# FRONTEND_URL, API_PUBLIC_URL, and GH_OAUTH_CALLBACK_URL are derived
|
|
97
|
+
# automatically (FRONTEND_URL -> https://app.propr.dev, API_PUBLIC_URL -> the
|
|
98
|
+
# proxy host, GH_OAUTH_CALLBACK_URL -> <proxy host>/api/auth/github/callback);
|
|
99
|
+
# set them only to override. You must still register the callback URL — derived
|
|
100
|
+
# or explicit — in your GitHub OAuth App.
|
|
101
|
+
# FRONTEND_URL=https://app.propr.dev
|
|
102
|
+
# API_PUBLIC_URL=https://abc123.proxy.propr.dev
|
|
103
|
+
# GH_OAUTH_CALLBACK_URL=https://abc123.proxy.propr.dev/api/auth/github/callback
|
|
104
|
+
#
|
|
105
|
+
# COOKIE_DOMAIN: leave UNSET for v1. The session cookie is host-only on the
|
|
106
|
+
# single `<id>.proxy.propr.dev` host — correct because that host and
|
|
107
|
+
# app.propr.dev share the propr.dev registrable domain (same-site). Setting
|
|
108
|
+
# COOKIE_DOMAIN (e.g. `.proxy.propr.dev`) would scope the cookie across every
|
|
109
|
+
# instance's subdomain and is not supported for v1 proxy sessions.
|
|
110
|
+
# COOKIE_DOMAIN=
|
|
111
|
+
|
|
26
112
|
# Logging Configuration
|
|
27
113
|
LOG_LEVEL=info
|
|
28
114
|
NODE_ENV=development
|
|
29
115
|
|
|
30
116
|
# Daemon Configuration
|
|
31
117
|
GITHUB_REPOS_TO_MONITOR=owner/repo1,owner/repo2
|
|
118
|
+
|
|
119
|
+
# --- Polling intake (advanced opt-in) ----------------------------------------
|
|
120
|
+
# Polling is an explicit opt-in alternative to the default routing WebSocket.
|
|
121
|
+
# It is only used when GITHUB_EVENT_INTAKE_MODE=polling (set above); leave the
|
|
122
|
+
# intake mode at routing_websocket to keep the hosted path. In polling mode
|
|
123
|
+
# ProPR pulls events from the GitHub API on this interval using any usable
|
|
124
|
+
# GitHub auth (relay or your own App). POLLING_INTERVAL_MS is the poll period in
|
|
125
|
+
# milliseconds (default: 60000).
|
|
32
126
|
POLLING_INTERVAL_MS=60000
|
|
33
127
|
|
|
34
128
|
# Config Repository (for dynamic repository management)
|
|
@@ -55,10 +149,34 @@ COMMENT_BATCH_DELAY_MS=3000
|
|
|
55
149
|
# and fallback paths fail. Defaults to 3600000 (1 hour).
|
|
56
150
|
# SUMMARIZATION_QUOTA_COOLDOWN_MS=3600000
|
|
57
151
|
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
|
|
152
|
+
# --- Direct webhook intake (advanced) ----------------------------------------
|
|
153
|
+
# Direct webhook is an advanced alternative to the default routing WebSocket.
|
|
154
|
+
# It is only used when GITHUB_EVENT_INTAKE_MODE=direct_webhook (set near the top
|
|
155
|
+
# of this file). This path requires your OWN GitHub App (not the hosted ProPR
|
|
156
|
+
# App) and a publicly reachable POST /webhook URL that GitHub can deliver events
|
|
157
|
+
# to — the endpoint is served by the dashboard API service (port 4000). Because
|
|
158
|
+
# of those requirements, most installs should stay on routing_websocket.
|
|
159
|
+
#
|
|
160
|
+
# Own GitHub App credentials (app auth mode — required for direct webhook):
|
|
161
|
+
# GH_APP_ID — your GitHub App's numeric id
|
|
162
|
+
# GH_PRIVATE_KEY_PATH — path to your App's private key (.pem)
|
|
163
|
+
# GH_INSTALLATION_ID — which installation to act on (set in the routing
|
|
164
|
+
# section above; reused here)
|
|
165
|
+
# GH_APP_ID=your_app_id
|
|
166
|
+
# GH_PRIVATE_KEY_PATH=./path/to/your-app-private-key.pem
|
|
167
|
+
# Host path to the GitHub App private key (.pem). Recommended when running via
|
|
168
|
+
# the `propr` CLI or launcher: the key is bind-mounted (read-only) into the app
|
|
169
|
+
# containers and GH_PRIVATE_KEY_PATH above is overridden to point at it, so you
|
|
170
|
+
# can keep the key anywhere on the host instead of staging it under data/.
|
|
171
|
+
# Must be an absolute host path (no '~').
|
|
172
|
+
# HOST_GH_PRIVATE_KEY=/home/your-user/propr/app-private-key.pem
|
|
173
|
+
# Shared secret GitHub signs webhook deliveries with; required for this mode.
|
|
174
|
+
# GH_WEBHOOK_SECRET=your-webhook-secret
|
|
175
|
+
|
|
176
|
+
# Deprecated: ENABLE_GITHUB_WEBHOOKS no longer selects the intake mode. Use
|
|
177
|
+
# GITHUB_EVENT_INTAKE_MODE instead (routing_websocket | polling | direct_webhook).
|
|
178
|
+
# Documented here only so existing .env files are recognized; do not rely on it.
|
|
179
|
+
# ENABLE_GITHUB_WEBHOOKS=false
|
|
62
180
|
|
|
63
181
|
# System Task Authorization
|
|
64
182
|
# Secret used to sign system task requests (e.g., revert operations)
|
|
@@ -104,19 +222,14 @@ ANTIGRAVITY_TIMEOUT_MS=300000
|
|
|
104
222
|
OPENCODE_TIMEOUT_MS=3600000
|
|
105
223
|
# Launcher credential mount paths (required when using docker/launcher).
|
|
106
224
|
# HOST_OPENCODE_XDG_DIR points to the XDG config directory
|
|
107
|
-
# (/home/your-user/.config/opencode)
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
# HOST_OPENCODE_LEGACY_DIR points to the legacy /home/your-user/.opencode directory.
|
|
111
|
-
# The saved agent configPath still controls the runtime mount; the launcher
|
|
112
|
-
# sets OPENCODE_CONFIG_PATH only as the default path for worker/API processes.
|
|
225
|
+
# (/home/your-user/.config/opencode). The saved agent configPath still controls
|
|
226
|
+
# the runtime mount; the launcher sets OPENCODE_CONFIG_PATH only as the default
|
|
227
|
+
# path for worker/API processes.
|
|
113
228
|
# HOST_OPENCODE_DATA_DIR points to normal opencode auth data
|
|
114
229
|
# (/home/your-user/.local/share/opencode). Set it for launcher deployments
|
|
115
230
|
# so normal `opencode auth login` credentials are visible to spawned OpenCode
|
|
116
231
|
# agent containers and can refresh auth metadata when required.
|
|
117
232
|
# HOST_OPENCODE_XDG_DIR=/home/your-user/.config/opencode
|
|
118
|
-
# HOST_OPENCODE_DIR=/home/your-user/.config/opencode
|
|
119
|
-
# HOST_OPENCODE_LEGACY_DIR=/home/your-user/.opencode
|
|
120
233
|
# HOST_OPENCODE_DATA_DIR=/home/your-user/.local/share/opencode
|
|
121
234
|
|
|
122
235
|
# --- Mistral Vibe Configuration (only required when using a Vibe agent) ---
|
|
@@ -135,19 +248,31 @@ VIBE_TIMEOUT_MS=3600000
|
|
|
135
248
|
# directory name intentionally differs from the variable name.
|
|
136
249
|
# HOST_ANTIGRAVITY_DIR=/home/your-user/.gemini
|
|
137
250
|
# HOST_VIBE_DIR=/home/your-user/.vibe
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# Create the directory before starting: mkdir -p /tmp/propr-vibe-prompts
|
|
251
|
+
# Vibe Docker-outside-Docker writes prompt files to a host-visible directory so
|
|
252
|
+
# spawned agent containers can bind-mount them. When Vibe is enabled, the
|
|
253
|
+
# launcher defaults the host path to /tmp/propr-vibe-prompts-<uid>; the container
|
|
254
|
+
# path defaults to /tmp/propr-vibe-prompts. Set both only to override those
|
|
255
|
+
# locations.
|
|
144
256
|
# VIBE_PROMPT_CACHE_DIR=/tmp/propr-vibe-prompts
|
|
145
|
-
# HOST_VIBE_PROMPT_CACHE_DIR=/tmp/propr-vibe-prompts
|
|
257
|
+
# HOST_VIBE_PROMPT_CACHE_DIR=/tmp/propr-vibe-prompts-1000
|
|
146
258
|
|
|
147
259
|
# Dashboard API Configuration
|
|
148
260
|
DASHBOARD_API_PORT=4000
|
|
149
|
-
#
|
|
150
|
-
|
|
261
|
+
# Browser origin: required in normal and demo mode for CORS and auth redirects.
|
|
262
|
+
# Defaults to http://localhost:5173 when unset. In hosted UI tunnel mode this is
|
|
263
|
+
# derived automatically as the hosted UI origin (https://app.propr.dev) — NOT the
|
|
264
|
+
# proxy host — so leave it COMMENTED to let derivation win. An uncommented value
|
|
265
|
+
# here always overrides the derived origin (set one only to force a custom
|
|
266
|
+
# origin). See the "Hosted UI tunnel" section above.
|
|
267
|
+
# FRONTEND_URL=http://localhost:5173
|
|
268
|
+
# Public URL the API is reached at (auth redirects, attachment links, cookie
|
|
269
|
+
# security). Defaults to http://localhost:4000 when unset; set it to the
|
|
270
|
+
# https://<id>.proxy.propr.dev host when the hosted UI tunnel is enabled.
|
|
271
|
+
# API_PUBLIC_URL=http://localhost:4000
|
|
272
|
+
# Session cookie domain. Leave UNSET for v1 — including hosted UI tunnel proxy
|
|
273
|
+
# sessions, which run on a single <id>.proxy.propr.dev host (see the tunnel
|
|
274
|
+
# section above). Only set it for a custom multi-subdomain deployment.
|
|
275
|
+
# COOKIE_DOMAIN=
|
|
151
276
|
# Optional comma-separated redirect hosts for auth preview flows.
|
|
152
277
|
# Entries are exact host matches by default. Prefix with "." or "*." only for
|
|
153
278
|
# parent domains where every subdomain is trusted, for example .preview.example.com.
|
|
@@ -155,9 +280,16 @@ FRONTEND_URL=http://localhost:5173
|
|
|
155
280
|
# AUTH_REDIRECT_ALLOWED_HOSTS=preview.example.com
|
|
156
281
|
|
|
157
282
|
# GitHub OAuth Configuration
|
|
283
|
+
# The OAuth callback is served by the API and is derived automatically when left
|
|
284
|
+
# unset: <proxy host>/api/auth/github/callback in hosted UI tunnel mode, or
|
|
285
|
+
# http://localhost:4000/api/auth/github/callback for local development. Leave it
|
|
286
|
+
# commented so enabling the tunnel (token/instance id) cannot leave a stale
|
|
287
|
+
# localhost callback that breaks hosted OAuth — an active localhost value is used
|
|
288
|
+
# as-is even in tunnel mode (the launcher only warns). Uncomment to override; then
|
|
289
|
+
# register that exact URL in your GitHub OAuth App.
|
|
158
290
|
GH_OAUTH_CLIENT_ID=your_github_oauth_client_id
|
|
159
291
|
GH_OAUTH_CLIENT_SECRET=your_github_oauth_client_secret
|
|
160
|
-
GH_OAUTH_CALLBACK_URL=http://localhost:4000/api/auth/github/callback
|
|
292
|
+
# GH_OAUTH_CALLBACK_URL=http://localhost:4000/api/auth/github/callback
|
|
161
293
|
SESSION_SECRET=your-session-secret-here
|
|
162
294
|
|
|
163
295
|
# Bearer Token Authentication (CLI)
|
|
@@ -174,25 +306,16 @@ PROPR_DEMO_MODE=false
|
|
|
174
306
|
DB_FILENAME=./data/propr.sqlite
|
|
175
307
|
|
|
176
308
|
# --- PR Preview Environment ---
|
|
309
|
+
# docker-compose.yml and scripts/deploy-pr.sh consume these local names.
|
|
310
|
+
# The PR Preview workflow maps repository variable PR_PREVIEW_STAGING_ENV_FILE
|
|
311
|
+
# to STAGING_ENV_FILE for deploys.
|
|
177
312
|
# Path to staging .env file (provides base configuration for PR previews)
|
|
178
313
|
# PR-specific values (ports, API URLs) are passed as inline overrides
|
|
179
314
|
STAGING_ENV_FILE=/path/to/staging/.env
|
|
180
315
|
|
|
181
|
-
#
|
|
316
|
+
# Optional path to a staging database file for seeding PR preview environments.
|
|
317
|
+
# The workflow maps repository variable PR_PREVIEW_STAGING_DB_PATH to this env.
|
|
182
318
|
STAGING_DB_PATH=/path/to/staging/data/propr.sqlite
|
|
183
319
|
|
|
184
|
-
#
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
# The ProPR repository in 'owner/repo' format. Label events from this repo
|
|
188
|
-
# trigger processor assignment changes. Defaults to 'integry/propr'.
|
|
189
|
-
PROPR_REPO=integry/propr
|
|
190
|
-
|
|
191
|
-
# The label that designates a ProPR PR as the active processor for webhook routing.
|
|
192
|
-
# When this label is added to a ProPR repo PR, that PR's preview instance becomes
|
|
193
|
-
# the active processor. Defaults to 'preview-env'.
|
|
194
|
-
PROCESSOR_LABEL=preview-env
|
|
195
|
-
|
|
196
|
-
# Host address for forwarding webhooks to PR preview instances.
|
|
197
|
-
# Defaults to 'http://host.docker.internal' for Docker environments.
|
|
198
|
-
# HOST_GATEWAY_ADDRESS=http://host.docker.internal
|
|
320
|
+
# PR preview deploys are owned by the GitHub Actions workflow and run only after
|
|
321
|
+
# a maintainer adds the preview-env label to a pull request.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared GitHub authentication via the `gh` CLI.
|
|
3
|
+
*
|
|
4
|
+
* Both `propr login` (commands wired in index.ts) and `propr setup`'s relay
|
|
5
|
+
* enrollment need a stored GitHub token. This centralises the `gh`-CLI flow —
|
|
6
|
+
* reuse an existing `gh` session, or run the interactive `gh auth login` — so
|
|
7
|
+
* the two callers stay in sync. It returns a result object instead of writing to
|
|
8
|
+
* the console or calling process.exit, leaving presentation to the caller.
|
|
9
|
+
*/
|
|
10
|
+
/** Scopes requested when launching the interactive `gh auth login`. */
|
|
11
|
+
const GH_LOGIN_SCOPES = "repo,read:org";
|
|
12
|
+
/**
|
|
13
|
+
* Authenticate with GitHub through the `gh` CLI and persist the token.
|
|
14
|
+
*
|
|
15
|
+
* Order: confirm `gh` is installed → reuse an existing `gh auth token` →
|
|
16
|
+
* (interactive only) run `gh auth login` and read the token back.
|
|
17
|
+
*/
|
|
18
|
+
export async function loginWithGithubCli(configManager, options = {}) {
|
|
19
|
+
const { interactive = false, onLog } = options;
|
|
20
|
+
const { execSync, spawnSync } = await import("child_process");
|
|
21
|
+
// Require the gh CLI up front — every path below shells out to it.
|
|
22
|
+
try {
|
|
23
|
+
execSync("gh --version", { stdio: "ignore" });
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return {
|
|
27
|
+
ok: false,
|
|
28
|
+
message: "GitHub CLI (gh) is not installed. Install it from https://cli.github.com, or run `propr login <token>` with a personal access token.",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// Reuse an existing gh session when one is already authenticated.
|
|
32
|
+
const existing = readGhToken(execSync);
|
|
33
|
+
if (existing) {
|
|
34
|
+
await configManager.setGithubToken(existing);
|
|
35
|
+
return { ok: true, token: existing, message: "Authenticated using your existing gh CLI session." };
|
|
36
|
+
}
|
|
37
|
+
if (!interactive) {
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
message: "No gh CLI session found. Run `propr login` (or `gh auth login`) to authenticate first.",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Launch the interactive browser/device login. Inherits stdio so the user can
|
|
44
|
+
// complete the gh prompts directly.
|
|
45
|
+
onLog?.("No existing gh session found. Starting interactive login…");
|
|
46
|
+
const result = spawnSync("gh", ["auth", "login", "-s", GH_LOGIN_SCOPES], { stdio: "inherit" });
|
|
47
|
+
if (result.status !== 0) {
|
|
48
|
+
return { ok: false, message: "GitHub login failed or was cancelled." };
|
|
49
|
+
}
|
|
50
|
+
const token = readGhToken(execSync);
|
|
51
|
+
if (!token) {
|
|
52
|
+
return { ok: false, message: "Could not retrieve a token after login." };
|
|
53
|
+
}
|
|
54
|
+
await configManager.setGithubToken(token);
|
|
55
|
+
return { ok: true, token, message: "Authentication successful." };
|
|
56
|
+
}
|
|
57
|
+
/** Read the current `gh` token, or null when no session is authenticated. */
|
|
58
|
+
function readGhToken(execSync) {
|
|
59
|
+
try {
|
|
60
|
+
const token = execSync("gh auth token", { encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] }).trim();
|
|
61
|
+
return token || null;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
* Provides the `agent` command group with `list`, `add`, and `delete` subcommands.
|
|
6
6
|
*/
|
|
7
7
|
import { Command } from "commander";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
8
12
|
import { listAgents, addAgent, deleteAgent, setAgentEnabled, AGENT_TYPES, } from "../api/agents.js";
|
|
13
|
+
import { createConfigManager } from "../config/index.js";
|
|
14
|
+
import { getHostConfig } from "../orchestrator/index.js";
|
|
15
|
+
import { planAgentLogin, loginableAgents } from "./agentValidation.js";
|
|
9
16
|
import { ApiError, NetworkError, NotFoundError, UnauthorizedError } from "../api/errors.js";
|
|
10
17
|
import { printOutput, readJsonInput, validateJsonFields, JsonInputError, } from "../utils/index.js";
|
|
11
18
|
const AGENT_TYPE_LIST = AGENT_TYPES.join(", ");
|
|
@@ -401,5 +408,72 @@ Examples:
|
|
|
401
408
|
process.exit(1);
|
|
402
409
|
}
|
|
403
410
|
});
|
|
411
|
+
// agent login
|
|
412
|
+
agent
|
|
413
|
+
.command("login [type]")
|
|
414
|
+
.description("Authenticate an agent by logging in through its Docker image (writes host credentials)")
|
|
415
|
+
.option("--root <dir>", "Stack root directory (where .env/data/logs/repos live)")
|
|
416
|
+
.addHelpText("after", `
|
|
417
|
+
Logs in using the agent's own pinned CLI inside its image, with the credential
|
|
418
|
+
directory mounted — so the credentials match exactly what runs jobs (no host
|
|
419
|
+
install or host/image version drift). Useful after a failed image check.
|
|
420
|
+
|
|
421
|
+
Examples:
|
|
422
|
+
$ propr agent login antigravity
|
|
423
|
+
$ propr agent login opencode
|
|
424
|
+
`)
|
|
425
|
+
.action(async (type, options) => {
|
|
426
|
+
try {
|
|
427
|
+
const available = loginableAgents();
|
|
428
|
+
if (!type) {
|
|
429
|
+
console.log("Usage: propr agent login <type>");
|
|
430
|
+
console.log(`Agents with interactive login: ${available.join(", ")}`);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
434
|
+
console.error("Error: propr agent login requires an interactive terminal because Docker login runs with -it.");
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
const configManager = await createConfigManager();
|
|
438
|
+
const { orch, cfg } = await getHostConfig({ configManager, root: options.root });
|
|
439
|
+
const tmp = mkdtempSync(join(tmpdir(), "propr-login-"));
|
|
440
|
+
const workspaceDir = join(tmp, "workspace");
|
|
441
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
442
|
+
try {
|
|
443
|
+
const loginType = type.toLowerCase();
|
|
444
|
+
const { plan, error } = planAgentLogin(loginType, cfg, workspaceDir, orch.validateDockerBindPath);
|
|
445
|
+
if (error || !plan) {
|
|
446
|
+
console.error(`Error: ${error ?? "could not plan login"}`);
|
|
447
|
+
if (available.length > 0)
|
|
448
|
+
console.error(`Agents with interactive login: ${available.join(", ")}`);
|
|
449
|
+
process.exit(1);
|
|
450
|
+
}
|
|
451
|
+
if (orch.docker(["images", "-q", plan.image], { capture: true }).stdout.trim().length === 0) {
|
|
452
|
+
console.error(`Image ${plan.image} is not present locally. Pull it first: propr images pull`);
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
mkdirSync(plan.hostDir, { recursive: true, mode: 0o700 });
|
|
456
|
+
console.log(`Logging in to ${loginType} via ${plan.image}`);
|
|
457
|
+
console.log(`Credentials will be written to ${plan.hostDir}`);
|
|
458
|
+
console.log("");
|
|
459
|
+
const res = spawnSync("docker", plan.dockerArgs, { stdio: "inherit" });
|
|
460
|
+
if (res.status === 0) {
|
|
461
|
+
console.log("");
|
|
462
|
+
console.log(`${loginType} login finished. Verify with: propr check agents --agents ${loginType}`);
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
console.error(`\n${loginType} login exited with code ${res.status ?? "?"}.`);
|
|
466
|
+
process.exit(1);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
finally {
|
|
470
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
catch (error) {
|
|
474
|
+
console.error(`Error during agent login: ${error.message}`);
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
404
478
|
return agent;
|
|
405
479
|
}
|