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
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default URLs for the vendor-run propr-routing service.
|
|
3
|
+
*
|
|
4
|
+
* The routing relay and the GitHub token relay are not separate deployments —
|
|
5
|
+
* they are the same Cloudflare Worker (propr-routing), served from a single
|
|
6
|
+
* custom domain and exposing every endpoint under `/v1`:
|
|
7
|
+
* - `wss://webhook.propr.dev/v1/connect` (routing WebSocket intake)
|
|
8
|
+
* - `https://webhook.propr.dev/v1/relay-tokens`, `/v1/installation-token` …
|
|
9
|
+
*
|
|
10
|
+
* These constants are the single source of truth for that host so the CLI
|
|
11
|
+
* (`propr relay enroll`), the daemon dialer, and the boot/`propr check`
|
|
12
|
+
* prerequisite validators all agree on the hosted default without anyone having
|
|
13
|
+
* to set PROPR_ROUTING_URL / PROPR_GH_RELAY_URL by hand.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Default routing WebSocket origin (PROPR_ROUTING_URL). A bare origin without a
|
|
17
|
+
* path — the routing service owns the `/v1/...` paths it appends (connect +
|
|
18
|
+
* payload pull), so a path here would corrupt the derived URLs.
|
|
19
|
+
*/
|
|
20
|
+
export const DEFAULT_PROPR_ROUTING_URL = 'wss://webhook.propr.dev';
|
|
21
|
+
/**
|
|
22
|
+
* Default GitHub token relay base URL (PROPR_GH_RELAY_URL). Includes the `/v1`
|
|
23
|
+
* version prefix because the relay client appends paths like `/relay-tokens`
|
|
24
|
+
* directly to this value.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_PROPR_GH_RELAY_URL = 'https://webhook.propr.dev/v1';
|
|
27
|
+
/**
|
|
28
|
+
* Origin of the hosted Propr UI (https://app.propr.dev). This is where the
|
|
29
|
+
* managed control plane is served from; a local stack exposes its own UI on a
|
|
30
|
+
* tunnel under {@link PROPR_UI_PROXY_SUFFIX} so the hosted UI can reach it.
|
|
31
|
+
*/
|
|
32
|
+
export const DEFAULT_PROPR_UI_ORIGIN = 'https://app.propr.dev';
|
|
33
|
+
/**
|
|
34
|
+
* DNS suffix for per-instance UI/API tunnel hostnames. Each local stack with an
|
|
35
|
+
* instance id is reachable at `https://<instanceId>.proxy.propr.dev`, so the
|
|
36
|
+
* hosted UI at {@link DEFAULT_PROPR_UI_ORIGIN} can discover and address it.
|
|
37
|
+
*/
|
|
38
|
+
export const PROPR_UI_PROXY_SUFFIX = 'proxy.propr.dev';
|
|
39
|
+
/**
|
|
40
|
+
* Default Cloudflare Tunnel image used to expose the local stack's UI/API to
|
|
41
|
+
* the hosted control plane when a UI tunnel is enabled. This is only a fallback:
|
|
42
|
+
* the launcher prefers the `cloudflared` entry pinned in the stack manifest
|
|
43
|
+
* (docker/launcher/manifest.json). Keep this tag in sync with that manifest pin
|
|
44
|
+
* so the effective default is the same regardless of which source supplies it.
|
|
45
|
+
*/
|
|
46
|
+
export const DEFAULT_CLOUDFLARED_IMAGE = 'cloudflare/cloudflared:2024.12.2';
|
|
47
|
+
/**
|
|
48
|
+
* Whether an instance id is usable as a single DNS label in the per-instance
|
|
49
|
+
* proxy hostname (`<id>.proxy.propr.dev`). Enforces the standard label rules:
|
|
50
|
+
* 1–63 characters, ASCII letters/digits/hyphens only, and no leading or
|
|
51
|
+
* trailing hyphen. This rejects spaces, slashes, dots, underscores, and other
|
|
52
|
+
* characters that would produce an invalid or ambiguous hostname.
|
|
53
|
+
*/
|
|
54
|
+
export function isValidProprInstanceId(instanceId) {
|
|
55
|
+
const id = (instanceId ?? '').trim();
|
|
56
|
+
return /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/i.test(id);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Derive the public API/UI URL for a local stack from its instance id, using
|
|
60
|
+
* the shared {@link PROPR_UI_PROXY_SUFFIX}. Returns `https://abc123.proxy.propr.dev`
|
|
61
|
+
* for instance id `abc123`. Returns `undefined` for a missing/blank id — or an
|
|
62
|
+
* id that is not a valid DNS label (see {@link isValidProprInstanceId}) — so
|
|
63
|
+
* callers can fall back to an explicit URL or a local-development default
|
|
64
|
+
* rather than emitting a malformed hostname. The id is lowercased so a
|
|
65
|
+
* mixed-case instance id yields a canonical hostname (DNS is case-insensitive).
|
|
66
|
+
*/
|
|
67
|
+
export function proprInstanceProxyUrl(instanceId) {
|
|
68
|
+
const id = (instanceId ?? '').trim();
|
|
69
|
+
if (!isValidProprInstanceId(id))
|
|
70
|
+
return undefined;
|
|
71
|
+
return `https://${id.toLowerCase()}.${PROPR_UI_PROXY_SUFFIX}`;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Whether a URL is a hosted per-instance proxy URL (`https://<id>.proxy.propr.dev`).
|
|
75
|
+
* propr-routing only forwards `/api/*` and `/socket.io/*` on these hosts, so the
|
|
76
|
+
* tunnel base URL must be one of them. Requires https and *exactly one* valid
|
|
77
|
+
* instance-id label in front of the shared {@link PROPR_UI_PROXY_SUFFIX} — a
|
|
78
|
+
* multi-label host like `foo.bar.proxy.propr.dev` is rejected because routing
|
|
79
|
+
* only addresses a single instance label. It must also be a bare origin: a
|
|
80
|
+
* non-root path, query, or fragment (e.g. `https://abc.proxy.propr.dev/api`) is
|
|
81
|
+
* rejected because {@link proprTunnelEndpoints} appends `/api/...` itself and a
|
|
82
|
+
* base path would double it up (`.../api/api/status`). Returns false for a
|
|
83
|
+
* malformed URL.
|
|
84
|
+
*/
|
|
85
|
+
export function isProprProxyUrl(url) {
|
|
86
|
+
if (!url)
|
|
87
|
+
return false;
|
|
88
|
+
try {
|
|
89
|
+
const { protocol, hostname, pathname, search, hash } = new URL(url);
|
|
90
|
+
if (protocol !== 'https:')
|
|
91
|
+
return false;
|
|
92
|
+
// Must be a bare origin — the tunnel endpoint helpers own the path suffix.
|
|
93
|
+
// Trailing slashes (`/`, `//`) are tolerated (callers trim them); any real
|
|
94
|
+
// path segment, query, or fragment is rejected so a base path can't double
|
|
95
|
+
// up the appended `/api/...`.
|
|
96
|
+
if (/[^/]/.test(pathname) || search || hash)
|
|
97
|
+
return false;
|
|
98
|
+
const suffix = `.${PROPR_UI_PROXY_SUFFIX}`;
|
|
99
|
+
if (!hostname.endsWith(suffix))
|
|
100
|
+
return false;
|
|
101
|
+
// The portion before the suffix must be a single valid DNS label (no dots),
|
|
102
|
+
// so isValidProprInstanceId rejects nested hosts like `a.b.proxy.propr.dev`.
|
|
103
|
+
return isValidProprInstanceId(hostname.slice(0, -suffix.length));
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The concrete endpoints the hosted UI reaches through the tunnel base URL.
|
|
111
|
+
* propr-routing only allows `/api/*` and `/socket.io/*`, so the base (root) URL
|
|
112
|
+
* itself intentionally returns 404 — it is NOT a health target. Use `apiStatus`
|
|
113
|
+
* to probe liveness. The base is normalized (trailing slashes trimmed) so the
|
|
114
|
+
* derived paths never double up a slash.
|
|
115
|
+
*/
|
|
116
|
+
export function proprTunnelEndpoints(baseUrl) {
|
|
117
|
+
const base = baseUrl.replace(/\/+$/, '');
|
|
118
|
+
return {
|
|
119
|
+
apiStatus: `${base}/api/status`,
|
|
120
|
+
socketIo: `${base}/socket.io/`,
|
|
121
|
+
root: `${base}/`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=proprServiceUrls.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redis keys carrying cross-process runtime status.
|
|
3
|
+
*
|
|
4
|
+
* Centralized here so the daemon (publisher), the API status route, the CLI, and
|
|
5
|
+
* their tests all reference one string instead of duplicating literals that could
|
|
6
|
+
* silently drift out of sync.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Routing WebSocket intake runtime state, published by the daemon and read by the
|
|
10
|
+
* API status route / `propr check` to report routing connectivity, last delivery
|
|
11
|
+
* id, and last ACK for the default routing_websocket intake path.
|
|
12
|
+
*/
|
|
13
|
+
export const ROUTING_STATUS_REDIS_KEY = 'system:status:routing';
|
|
14
|
+
//# sourceMappingURL=statusKeys.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// URL.hostname returns brackets for IPv6, e.g. '[::1]'.
|
|
2
|
+
const LOCALHOST_HOSTS = ['localhost', '127.0.0.1', '[::1]'];
|
|
3
|
+
/**
|
|
4
|
+
* Validate a PROPR_ROUTING_URL origin. This is the single source of truth for
|
|
5
|
+
* the routing-URL policy, shared by the boot/CLI prerequisite checks
|
|
6
|
+
* (intakeModePrerequisites) and the daemon service that actually dials it
|
|
7
|
+
* (RoutingWebSocketIntakeService), so the two can never disagree.
|
|
8
|
+
*
|
|
9
|
+
* Policy:
|
|
10
|
+
* - must be a parseable URL
|
|
11
|
+
* - must use a secure scheme: `wss://` or `https://`
|
|
12
|
+
* (`ws://`/`http://` is allowed ONLY for localhost development)
|
|
13
|
+
* - must be a bare ORIGIN — no path/query/fragment — because the service owns
|
|
14
|
+
* the `/v1/...` paths it appends (connect + payload pull); a configured path
|
|
15
|
+
* like `wss://routing/v1` would corrupt the derived URLs (`/v1/v1/connect`).
|
|
16
|
+
*
|
|
17
|
+
* Note the `wss://`/`ws://` (and `https://`/`http://`) schemes are all accepted
|
|
18
|
+
* because the service derives both the WebSocket connect URL and the HTTP
|
|
19
|
+
* payload-pull URL from this single value. The default `wss://webhook.propr.dev`
|
|
20
|
+
* is valid under this policy.
|
|
21
|
+
*
|
|
22
|
+
* Returns an error message string, or `null` when valid.
|
|
23
|
+
*/
|
|
24
|
+
export function validateRoutingUrl(url) {
|
|
25
|
+
let parsed;
|
|
26
|
+
try {
|
|
27
|
+
parsed = new URL(url);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return `Routing URL ("${url}") is not a valid URL. Set PROPR_ROUTING_URL to a wss:// origin, e.g. wss://webhook.propr.dev.`;
|
|
31
|
+
}
|
|
32
|
+
const scheme = parsed.protocol;
|
|
33
|
+
if (scheme !== 'ws:' && scheme !== 'wss:' && scheme !== 'http:' && scheme !== 'https:') {
|
|
34
|
+
return `Routing URL must use wss:// or https:// (ws://, http:// only for localhost); got "${scheme}//".`;
|
|
35
|
+
}
|
|
36
|
+
const isSecure = scheme === 'wss:' || scheme === 'https:';
|
|
37
|
+
const isLocalhost = LOCALHOST_HOSTS.includes(parsed.hostname);
|
|
38
|
+
if (!isSecure && !isLocalhost) {
|
|
39
|
+
return 'Routing URL must use wss:// or https:// (ws://, http:// is only allowed for localhost).';
|
|
40
|
+
}
|
|
41
|
+
if (parsed.pathname.replace(/\/+$/, '') !== '' || parsed.search || parsed.hash) {
|
|
42
|
+
return `Routing URL must be an origin without a path (e.g. wss://webhook.propr.dev), got "${url}".`;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=validateRoutingUrl.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "propr-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "CLI for interacting with the ProPR backend",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"commander": "^13.1.0",
|
|
18
18
|
"dotenv": "^16.5.0",
|
|
19
19
|
"ink": "^7.0.5",
|
|
20
|
-
"react": "
|
|
20
|
+
"react": "19.2.7"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"propr",
|
|
@@ -27,5 +27,5 @@
|
|
|
27
27
|
"code-review",
|
|
28
28
|
"automation"
|
|
29
29
|
],
|
|
30
|
-
"license": "
|
|
30
|
+
"license": "Apache-2.0"
|
|
31
31
|
}
|