Skip to content

[1630 by Claude Opus 4.8] Add propr tunnel on|off service toggle command - #1642

Merged
propr-dev[bot] merged 2 commits into
1624-epic-implement-ui-004from
1630/claude-opus-4-8-add-propr-tunnel-on-off-s-20260625-1421-pm3
Jun 25, 2026
Merged

[1630 by Claude Opus 4.8] Add propr tunnel on|off service toggle command#1642
propr-dev[bot] merged 2 commits into
1624-epic-implement-ui-004from
1630/claude-opus-4-8-add-propr-tunnel-on-off-s-20260625-1421-pm3

Conversation

@propr-dev

@propr-dev propr-dev Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

AI Implementation Summary

Closes #1630

Branch: 1630/claude-opus-4-8-add-propr-tunnel-on-off-s-20260625-1421-pm3
Commits: ✅ Changes committed (195b6b0)


AI Processing Completed

Execution Details:

Summary:
The lint error (react-hooks/exhaustive-deps rule not found) is in dist/ build output and is pre-existing/unrelated to my changes — none of my source files appear in the lint output.

Summary

I implemented propr tunnel on|off as a service toggle, mirroring the existing propr ui / propr docs pattern.

Changes

New commandpackages/cli/src/commands/tunnelCommand.ts

  • propr tunnel on: requires a configured token (cfg.uiTunnelToken). If missing, it fails clearly with a message pointing at PROPR_UI_TUNNEL_TOKEN. When present, it starts the cloudflared tunnel service and reports the public API URL.
  • propr tunnel off: stops and removes only the tunnel container; it explicitly never touches token/env values.
  • Persists the desired state via setTunnelEnabled() after the action succeeds.
  • Help text documents both PROPR_UI_TUNNEL_TOKEN and PROPR_UI_PUBLIC_API_URL.

Config persistence

  • config/types.ts: added tunnelEnabled to ConfigKey, CLIConfig, and DEFAULT_CONFIG.
  • config/ConfigManager.ts: added getTunnelEnabled() / setTunnelEnabled().

Honoring persisted state on propr start

  • docker/launcher/orchestrator.mjs: resolveConfig now lets an explicit uiTunnelEnabled override win over the env-derived default (overrides.uiTunnelEnabled ?? …), so an explicit off wins even when a token is present.
  • orchestrator/index.ts: getHostConfig forwards the persisted tunnelEnabled config as a uiTunnelEnabled cliOverride (same mechanism as docsEnabled).
  • tui/render.ts: propr start passes tunnel = cfg.uiTunnelEnabled to startStack.

Wiring & types

  • Registered the command and exports in index.ts / commands/index.ts, added tunnel to the Control Plane help line.
  • Updated the startStack/startStackAsync types to accept the tunnel option.

Tests — added two cases to test/orchestratorConfig.test.mjs verifying the override wins over the env default (both directions) and that an absent override falls back to env.

Verification

  • npm run typecheck (CLI): passes
  • npm run build (CLI): passes
  • node --test test/orchestratorConfig.test.mjs test/orchestratorTunnelStatus.test.mjs: all pass (29 + 34)

All four acceptance criteria are satisfied.

Detailed Logs:

  • Conversation: 216 messages
  • Session: 55834137-fe48-4a56-8d90-021305115f88

Log files stored at:

  • conversation: /tmp/claude-logs/issue-1630-2026-06-25T14-29-03-776Z-conversation.json
  • output: /tmp/claude-logs/issue-1630-2026-06-25T14-29-03-776Z-output.txt
Latest Conversation Messages
ASSISTANT: [content unavailable]

ASSISTANT: The lint error (`react-hooks/exhaustive-deps` rule not found) is in `dist/` build output and is pre-existing/unrelated to my changes — none of my source files appear in the lint output.

## Summary

I...


This PR was created automatically by ProPR after processing issue #1630.


💡 Need changes?

Comment on this PR to request refinements — the AI agent monitors comments and will update the implementation based on your feedback. Keep iterating until you're satisfied!

…dist/` build output and is pre-existing/unrelated to my changes — none of my source files appear in the lint output.

Summary

I implemented `propr tunnel on|off` as a service toggle, mirroring the existing `propr ui` / `propr docs` pattern.

Changes

New command — `packages/cli/src/commands/tunnelCommand.ts`
- `propr tunnel on`: requires a configured token (`cfg.uiTunnelToken`). If missing, it fails clearly with a message pointing at `PROPR_UI_TUNNEL_TOKEN`. When present, it starts the cloudflared `tunnel` service and reports the public API URL.
- `propr tunnel off`: stops and removes only the tunnel container; it explicitly never touches token/env values.
- Persists the desired state via `setTunnelEnabled()` after the action succeeds.
- Help text documents both `PROPR_UI_TUNNEL_TOKEN` and `PROPR_UI_PUBLIC_API_URL`.

Config persistence
- `config/types.ts`: added `tunnelEnabled` to `ConfigKey`, `CLIConfig`, and `DEFAULT_CONFIG`.
- `config/ConfigManager.ts`: added `getTunnelEnabled()` / `setTunnelEnabled()`.

Honoring persisted state on `propr start`
- `docker/launcher/orchestrator.mjs`: `resolveConfig` now lets an explicit `uiTunnelEnabled` override win over the env-derived default (`overrides.uiTunnelEnabled ?? …`), so an explicit `off` wins even when a token is present.
- `orchestrator/index.ts`: `getHostConfig` forwards the persisted `tunnelEnabled` config as a `uiTunnelEnabled` cliOverride (same mechanism as `docsEnabled`).
- `tui/render.ts`: `propr start` passes `tunnel = cfg.uiTunnelEnabled` to `startStack`.

Wiring & types
- Registered the command and exports in `index.ts` / `commands/index.ts`, added `tunnel` to the Control Plane help line.
- Updated the `startStack`/`startStackAsync` types to accept the `tunnel` option.

Tests — added two cases to `test/orchestratorConfig.test.mjs` verifying the override wins over the env default (both directions) and that an absent override falls back to env.

Verification
- `npm run typecheck` (CLI): passes
- `npm run build` (CLI): passes
- `node --test test/orchestratorConfig.test.mjs test/orchestratorTunnelStatus.test.mjs`: all pass (29 + 34)

All four acceptance criteria are satisfied.
@propr-dev

propr-dev Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

/ultrafix
Triggered automatically by Planner execution settings.

@propr-dev

propr-dev Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

🔄 Ultrafix loop started (goal: 8/10, max cycles: 10)

First action: /review

💡 Tip: Remove the ultrafix label from this PR to stop further ultrafix cycles.

@propr-dev

propr-dev Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

AI Code Review Complete requested by @propr-dev[bot]

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-5.5

Overall Evaluation

This PR adds propr tunnel on|off, wires it into propr start, and updates the launcher config so an explicit CLI tunnel toggle can override the token/env-derived default. The approach mostly matches the existing service-toggle pattern and includes useful orchestrator tests, but there is a persistence bug that prevents the new tunnelEnabled setting from surviving a fresh CLI process. Needs changes before merge.

Findings

🔴 Persisted tunnel setting is discarded on loadpackages/cli/src/config/ConfigManager.ts:135 sanitizes known config keys but does not include tunnelEnabled. As a result, setTunnelEnabled(false) writes the value, but the next CLI invocation drops it during load(). This breaks the core requirement that propr start honor a previous propr tunnel off, especially when PROPR_UI_TUNNEL_TOKEN is present and would re-enable the tunnel.

🟡 Getter default conflicts with documented fallback behaviorpackages/cli/src/config/ConfigManager.ts:329 makes getTunnelEnabled() default to false, while packages/cli/src/config/types.ts:61 says unset should fall back to env-derived defaults. Current getHostConfig() avoids this by using raw get("tunnelEnabled"), but the public helper is easy to misuse later and encodes the wrong semantic for an unset value.

🟢 Add config-level test coverage for persistence — The orchestrator tests cover uiTunnelEnabled override behavior, but they do not catch the ConfigManager.sanitizeConfig() omission. A focused test that writes tunnelEnabled: false, reloads ConfigManager, and verifies the value remains present would prevent this regression.

Launcher override behavior is correctly scopeddocker/launcher/orchestrator.mjs:230 uses nullish coalescing, so an explicit false override wins over a token-derived default while an absent override preserves existing behavior.

propr start integration is cleanpackages/cli/src/tui/render.ts:84 passes the resolved tunnel state through to startStack, keeping the service decision centralized in resolved config.

User-facing tunnel command is clearpackages/cli/src/commands/tunnelCommand.ts validates Docker availability, rejects missing tokens before starting, and avoids mutating token/env values when turning the tunnel off.

Score

The main implementation is coherent, but the missing sanitizeConfig() entry breaks persisted toggles across CLI invocations, which is central to this feature. Once that is fixed and covered with a config test, this should be close to mergeable.

Score: 6/10


🤖 Review Details

  • Model: gpt-5.5
  • Time: 31s
  • Tokens: 35,450 (34,407 in / 1,043 out)
  • Cost: $0.20

View Task


💡 Next step: Comment /fix on this PR to have the AI automatically implement the suggestions above.
The /fix command gathers all unprocessed AI review comments and applies fixes in a single pass.
You can edit or delete review comments before running /fix to control which suggestions are applied.
Add extra instructions if needed, e.g. /fix only address the critical findings.

@propr-dev

propr-dev Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Applied the requested follow-up changes in commit 393e534

Addressed 1 AI review comment (IDs: 4800427002)

Summary of Changes

I've addressed all the actionable findings from the review. Here's a summary:

Changes

1. 🔴 Persisted tunnel setting discarded on load (critical)
packages/cli/src/config/ConfigManager.ts — added the missing tunnelEnabled entry to sanitizeConfig(). Previously setTunnelEnabled(false) wrote the value, but the next CLI process dropped it during load(), so propr start would re-enable the tunnel from the env/token default despite a prior propr tunnel off. Now the boolean survives reload like uiEnabled/docsEnabled.

2. 🟡 Getter default conflicted with documented fallback semantics
Changed getTunnelEnabled() to return boolean | undefined instead of coercing unset to false. An unset value means "defer to the launcher's env-derived default" (per types.ts), so collapsing it to false encoded the wrong semantic and was easy to misuse. The orchestrator already reads the raw value via get("tunnelEnabled") and forwards it as an override only when set, so this aligns the public helper with that behavior. Updated the doc comment to explain why it differs from the UI/docs toggles.

3. 🟢 Config-level test coverage
test/cliConfigManager.test.ts — added two focused tests: one that writes tunnelEnabled: false, reloads a fresh ConfigManager, and asserts the value (and raw get) survive (regression guard for the sanitize omission, also covering the true round-trip), plus one asserting getTunnelEnabled() returns undefined when unset.

Verification

  • npm run build (CLI) — passes.
  • Direct functional verification of all four scenarios (false persists, raw get persists, true persists, unset → undefined) via tsx against the source — ALL PASS.
  • tsc --noEmit — no errors in the changed files.

Note: the full cliConfigManager.test.ts suite can't complete in this container because importing the @propr/cli barrel runs propr check, which aborts without Docker/a stack — this is a pre-existing environmental issue (the unmodified HEAD version of the file fails identically at the same subtest), not caused by my changes. I left the changes uncommitted as instructed.


🤖 Implementation Details

  • Model: Claude Opus 4.8
  • Requested By: @propr-ultrafix
  • Time: 4m 44s
  • Tokens: 1,322,953 (1,311,500 in / 11,453 out)
  • Cost: $1.19
  • Subscription usage: Session +3%

View Task Execution


💡 ProPR Slash Commands
Command Description Example
/merge Merge target branch into this PR and resolve conflicts /merge
/review Request an AI code review /review or /review claude-sonnet
/fix Implement fixes for issues found by /review /fix or /fix address the null check issue
/switch Change the AI model for this PR /switch claude-opus
/use Override the model for a single follow-up run /use claude-sonnet
/ultrafix Loop review→fix cycles until score goal is met /ultrafix or /ultrafix goal=8 max=10

… Here's a summary:

I've addressed all the actionable findings from the review. Here's a summary:

## Changes

**1. 🔴 Persisted tunnel setting discarded on load (critical)**
`packages/cli/src/config/ConfigManager.ts` — added the missing `tunnelEnabled` entry to `sanitizeConfig()`. Previously `setTunnelEnabled(false)` wrote the value, but the next CLI process dropped it during `load()`, so `propr start` would re-enable the tunnel from the env/token default despite a prior `propr tunnel off`. Now the boolean survives reload like `uiEnabled`/`docsEnabled`.

**2. 🟡 Getter default conflicted with documented fallback semantics**
Changed `getTunnelEnabled()` to return `boolean | undefined` instead of coercing unset to `false`. An unset value means "defer to the launcher's env-derived default" (per `types.ts`), so collapsing it to `false` encoded the wrong semantic and was easy to misuse. The orchestrator already reads the raw value via `get("tunnelEnabled")` and forwards it as an override only when set, so this aligns the public helper with that behavior. Updated the doc comment to explain why it differs from the UI/docs toggles.

**3. 🟢 Config-level test coverage**
`test/cliConfigManager.test.ts` — added two focused tests: one that writes `tunnelEnabled: false`, reloads a fresh `ConfigManager`, and asserts the value (and raw `get`) survive (regression guard for the sanitize omission, also covering the `true` round-trip), plus one asserting `getTunnelEnabled()` returns `undefined` when unset.

## Verification
- `npm run build` (CLI) — passes.
- Direct functional verification of all four scenarios (false persists, raw get persists, true persists, unset → undefined) via tsx against the source — **ALL PASS**.
- `tsc --noEmit` — no errors in the changed files.

Note: the full `cliConfigManager.test.ts` suite can't complete in this container because importing the `@propr/cli` barrel runs `propr check`, which aborts without Docker/a stack — this is a pre-existing environmental issue (the unmodified HEAD version of the file fails identically at the same subtest), not caused by my changes. I left the changes uncommitted as instructed.

PR: #1642
Comment by: @propr-ultrafix (ID: 0)
Model: claude-opus-4-8
@propr-dev

propr-dev Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

AI Code Review Complete requested by @propr-ultrafix

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-5.5

Overall Evaluation

This PR adds a persisted propr tunnel on|off toggle and wires it into stack startup through the shared orchestrator config. The approach mostly follows the existing UI/docs toggle pattern, preserves the unset state correctly, and adds useful regression coverage around config persistence and env-derived defaults. It needs one behavioral edge case checked before merge.

Findings

🟡 propr tunnel on may resolve config with a stale persisted false before starting — In packages/cli/src/commands/tunnelCommand.ts:24, getHostConfig({ configManager, root }) forwards any persisted tunnelEnabled value into cfg.uiTunnelEnabled. If the user previously ran propr tunnel off, then runs propr tunnel on, the command may call orch.startService(cfg, "tunnel", ...) with cfg.uiTunnelEnabled === false because persistence happens only after the start succeeds at packages/cli/src/commands/tunnelCommand.ts:57. If startService gates tunnel startup on resolved config, this can prevent the toggle from turning back on. Consider resolving config for the requested state, or persisting before starting with rollback/error handling.

🟢 Add command-level tests for the toggle flow — The config/orchestrator tests cover persistence and config resolution, but there is no direct coverage for propr tunnel on|off behavior in packages/cli/src/commands/tunnelCommand.ts. A mocked orchestrator test would catch the stale persisted-state case and verify token-required behavior.

🟢 Avoid direct process.exit inside toggleTunnel for testabilitypackages/cli/src/commands/tunnelCommand.ts:29 and :38 exit inside the helper instead of throwing typed errors and letting the commander action handle process termination. This matches some CLI patterns, but it makes isolated unit tests harder.

Good preservation of unset tunnel stateConfigManager.getTunnelEnabled() correctly returns boolean | undefined in packages/cli/src/config/ConfigManager.ts:315, which avoids collapsing “defer to env default” into false.

Config sanitization and regression coverage are solidpackages/cli/src/config/ConfigManager.ts:166 now preserves persisted tunnelEnabled, and test/cliConfigManager.test.ts adds coverage for both false and true round-trips.

Launcher override precedence is correctly modeleddocker/launcher/orchestrator.mjs now lets explicit CLI overrides win over token/env-derived defaults, and test/orchestratorConfig.test.mjs verifies both override and fallback behavior.

Score

Good implementation overall, but the possible stale-config behavior in the main toggle command should be resolved or explicitly disproven before merging.
Score: 8/10


🤖 Review Details

  • Model: gpt-5.5
  • Time: 26s
  • Tokens: 35,213 (34,106 in / 1,107 out)
  • Cost: $0.20

View Task


💡 Next step: Comment /fix on this PR to have the AI automatically implement the suggestions above.
The /fix command gathers all unprocessed AI review comments and applies fixes in a single pass.
You can edit or delete review comments before running /fix to control which suggestions are applied.
Add extra instructions if needed, e.g. /fix only address the critical findings.

@propr-dev propr-dev Bot removed the ultrafix label Jun 25, 2026
@propr-dev
propr-dev Bot merged commit 2244adf into 1624-epic-implement-ui-004 Jun 25, 2026
1 check passed
@propr-dev
propr-dev Bot deleted the 1630/claude-opus-4-8-add-propr-tunnel-on-off-s-20260625-1421-pm3 branch June 25, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants