refactor(channels): make ChannelDef.envKey optional and add static-token guards#3443
Conversation
…ken guards ChannelDef previously required envKey, but a QR-paired channel cannot satisfy that — its credential lives inside the sandbox, not in a host env var. Mark envKey as optional, add channelUsesQrPairing and channelHasStaticToken type guards, and have getChannelTokenKeys return an empty list when envKey is absent. No current channel becomes tokenless, so behaviour is unchanged for telegram, discord, and slack. Replace inline 'channel.appTokenEnvKey ? [...] : [...]' patterns in onboard.ts with getChannelTokenKeys, guard direct ch.envKey accesses, and skip the token prompt loop for tokenless channels. Extract the local getMessagingToken closure into a shared src/lib/onboard/messaging-token.ts module — both copies in onboard.ts were identical, and the shared helper now accepts an optional envKey so callers can pass channel.envKey directly without an extra guard. Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR centralizes messaging token lookup into a new shared module, makes ChangesMessaging Token Centralization
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
E2E Advisor RecommendationRequired E2E: Dispatch hint: Auto-dispatched E2E: Full advisor summaryE2E Recommendation AdvisorBase: Required E2E
Optional E2E
New E2E recommendations
Dispatch hint
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/lib/onboard.ts (1)
344-345: Please run the messaging-focused E2Es on this onboarding path.This touches core onboarding behavior for channel selection/token handling, so
messaging-compatible-endpoint-e2e,hermes-discord-e2e, andhermes-slack-e2eare the highest-signal regressions to exercise before merge.As per coding guidelines, "src/lib/onboard.ts: This file contains core onboarding logic. Changes here affect the full sandbox creation and configuration flow." and the listed E2E recommendations.
Also applies to: 5089-5090, 5138-5138, 5150-5150, 5687-5687, 8263-8263
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard.ts` around lines 344 - 345, Run the messaging-focused end-to-end suites to validate changes to channel selection and token handling in src/lib/onboard.ts: execute messaging-compatible-endpoint-e2e, hermes-discord-e2e, and hermes-slack-e2e (and re-run any failing runs listed near the referenced changes) to exercise logic in channelHasStaticToken, getChannelTokenKeys, listChannels and getMessagingToken; if tests fail, trace the onboarding flow in onboard.ts, reproduce the failing scenario locally, and fix the code paths that mishandle channel selection or token retrieval so the E2Es pass before merging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/onboard.ts`:
- Line 8263: The loop currently uses "if (!channelHasStaticToken(ch)) continue;"
which skips all subsequent per-channel prompts; remove that continue and instead
only gate the static-token/app-token prompt with "if (channelHasStaticToken(ch))
{ ... }" so that other per-channel config prompts (e.g., serverIdEnvKey,
requireMentionEnvKey, userIdEnvKey) still run for tokenless channels; update the
code around the token collection logic (where channelHasStaticToken(ch) is
checked) to conditionally prompt for and persist tokens but leave the rest of
the per-channel flow (the prompts that set serverIdEnvKey, requireMentionEnvKey,
userIdEnvKey) outside that conditional so they always execute for each ch in the
loop.
---
Nitpick comments:
In `@src/lib/onboard.ts`:
- Around line 344-345: Run the messaging-focused end-to-end suites to validate
changes to channel selection and token handling in src/lib/onboard.ts: execute
messaging-compatible-endpoint-e2e, hermes-discord-e2e, and hermes-slack-e2e (and
re-run any failing runs listed near the referenced changes) to exercise logic in
channelHasStaticToken, getChannelTokenKeys, listChannels and getMessagingToken;
if tests fail, trace the onboarding flow in onboard.ts, reproduce the failing
scenario locally, and fix the code paths that mishandle channel selection or
token retrieval so the E2Es pass before merging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: a771d6aa-326a-41b2-876c-0810cc363faa
📒 Files selected for processing (1)
src/lib/onboard.ts
Selective E2E Results — ✅ All requested jobs passedRun: 25876096387
|
Selective E2E Results — ✅ All requested jobs passedRun: 25877273658
|
Selective E2E Results — ✅ All requested jobs passedRun: 25880983733
|
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/lib/onboard.ts (1)
8260-8319:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon't short-circuit tokenless channels out of the rest of setup.
This
continuestill skips every later prompt in the loop, so tokenless channels never collect server/mention/allowlist config. Only gate the static-token/app-token prompts behindchannelHasStaticToken(ch)and let the rest of the per-channel setup keep running.Suggested fix
- if (!channelHasStaticToken(ch)) continue; - if (getMessagingToken(ch.envKey)) { - console.log(` ✓ ${ch.name} — already configured`); - } else { - console.log(""); - console.log(` ${ch.help}`); - const token = normalizeCredentialValue(await prompt(` ${ch.label}: `, { secret: true })); - if (token && ch.tokenFormat && !ch.tokenFormat.test(token)) { - console.log( - ` ✗ Invalid format. ${ch.tokenFormatHint || "Check the token and try again."}`, - ); - console.log(` Skipped ${ch.name} (invalid token format)`); - enabled.delete(ch.name); - continue; - } - if (token) { - saveCredential(ch.envKey, token); - process.env[ch.envKey] = token; - console.log(` ✓ ${ch.name} token saved`); - } else { - console.log(` Skipped ${ch.name} (no token entered)`); - enabled.delete(ch.name); - continue; - } - } - if (ch.appTokenEnvKey) { - const existingAppToken = getMessagingToken(ch.appTokenEnvKey); - if (existingAppToken) { - console.log(` ✓ ${ch.name} app token — already configured`); - } else { - console.log(""); - console.log(` ${ch.appTokenHelp}`); - const appToken = normalizeCredentialValue( - await prompt(` ${ch.appTokenLabel}: `, { secret: true }), - ); - if (appToken && ch.appTokenFormat && !ch.appTokenFormat.test(appToken)) { - console.log( - ` ✗ Invalid format. ${ch.appTokenFormatHint || "Check the token and try again."}`, - ); - console.log(` Skipped ${ch.name} app token (invalid token format)`); - enabled.delete(ch.name); - continue; - } - if (appToken) { - saveCredential(ch.appTokenEnvKey, appToken); - process.env[ch.appTokenEnvKey] = appToken; - console.log(` ✓ ${ch.name} app token saved`); - } else { - console.log(` Skipped ${ch.name} app token (Socket Mode requires both tokens)`); - enabled.delete(ch.name); - continue; - } - } - } + if (channelHasStaticToken(ch)) { + if (getMessagingToken(ch.envKey)) { + console.log(` ✓ ${ch.name} — already configured`); + } else { + console.log(""); + console.log(` ${ch.help}`); + const token = normalizeCredentialValue(await prompt(` ${ch.label}: `, { secret: true })); + if (token && ch.tokenFormat && !ch.tokenFormat.test(token)) { + console.log( + ` ✗ Invalid format. ${ch.tokenFormatHint || "Check the token and try again."}`, + ); + console.log(` Skipped ${ch.name} (invalid token format)`); + enabled.delete(ch.name); + continue; + } + if (token) { + saveCredential(ch.envKey, token); + process.env[ch.envKey] = token; + console.log(` ✓ ${ch.name} token saved`); + } else { + console.log(` Skipped ${ch.name} (no token entered)`); + enabled.delete(ch.name); + continue; + } + } + if (ch.appTokenEnvKey) { + const existingAppToken = getMessagingToken(ch.appTokenEnvKey); + if (existingAppToken) { + console.log(` ✓ ${ch.name} app token — already configured`); + } else { + console.log(""); + console.log(` ${ch.appTokenHelp}`); + const appToken = normalizeCredentialValue( + await prompt(` ${ch.appTokenLabel}: `, { secret: true }), + ); + if (appToken && ch.appTokenFormat && !ch.appTokenFormat.test(appToken)) { + console.log( + ` ✗ Invalid format. ${ch.appTokenFormatHint || "Check the token and try again."}`, + ); + console.log(` Skipped ${ch.name} app token (invalid token format)`); + enabled.delete(ch.name); + continue; + } + if (appToken) { + saveCredential(ch.appTokenEnvKey, appToken); + process.env[ch.appTokenEnvKey] = appToken; + console.log(` ✓ ${ch.name} app token saved`); + } else { + console.log(` Skipped ${ch.name} app token (Socket Mode requires both tokens)`); + enabled.delete(ch.name); + continue; + } + } + } + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/onboard.ts` around lines 8260 - 8319, The loop over selected channels currently uses "if (!channelHasStaticToken(ch)) continue;" which short-circuits the rest of per-channel setup (server/mention/allowlist config) for tokenless channels; instead, only skip the static-token/app-token prompt blocks when channelHasStaticToken(ch) is false. Move the static token prompt logic (the branch that checks getMessagingToken(ch.envKey), reads prompt, validates format, calls saveCredential and sets process.env) and the app token block (the ch.appTokenEnvKey branch) behind a conditional like "if (channelHasStaticToken(ch)) { ... }" so that channels without static tokens still execute the subsequent per-channel configuration and you remove the early "continue" that currently skips the rest of the loop.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@src/lib/onboard.ts`:
- Around line 8260-8319: The loop over selected channels currently uses "if
(!channelHasStaticToken(ch)) continue;" which short-circuits the rest of
per-channel setup (server/mention/allowlist config) for tokenless channels;
instead, only skip the static-token/app-token prompt blocks when
channelHasStaticToken(ch) is false. Move the static token prompt logic (the
branch that checks getMessagingToken(ch.envKey), reads prompt, validates format,
calls saveCredential and sets process.env) and the app token block (the
ch.appTokenEnvKey branch) behind a conditional like "if
(channelHasStaticToken(ch)) { ... }" so that channels without static tokens
still execute the subsequent per-channel configuration and you remove the early
"continue" that currently skips the rest of the loop.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 0cb5b571-b1a6-40b0-8386-a3e1f7fd4a01
📒 Files selected for processing (1)
src/lib/onboard.ts
Selective E2E Results — ✅ All requested jobs passedRun: 25895812383
|
Summary
ChannelDef.envKeyis currently mandatory, but a channel whose credential is established inside the sandbox has no host-side env var to assign. MakeenvKeyoptional, add type guards so consumers can distinguish static-token channels from tokenless ones, and route inline ternary patterns throughgetChannelTokenKeys. Behaviour is unchanged for telegram, discord, and slack — all three still declareenvKey.Related Issue
Prerequisite for #3392 (WhatsApp Web QR-paired channel), which uses these helpers to enable a tokenless channel and addresses #361 and #513.
Changes
src/lib/sandbox/channels.ts:ChannelDef.envKeyis nowenvKey?: string.getChannelTokenKeys(channel)returns[]whenenvKeyis absent.channelUsesQrPairing(channel)boolean helper.channelHasStaticToken(channel)type guard that narrows toChannelDef & { envKey: string }.src/lib/onboard.ts:channel.appTokenEnvKey ? [...] : [channel.envKey]patterns withgetChannelTokenKeys.def.envKey/ch.envKeynarrowing guards before the picker loop accesseschannel.envKey.channelHasStaticToken.getMessagingTokenclosures in favour of a shared module so the file stays net-negative.src/lib/onboard/messaging-token.ts(new): single source of truth for the messaging-token resolver. Acceptsstring | undefinedand returnsnullfor absent keys.src/lib/sandbox/channels.test.ts: cover the new helpers and the tokenless branch ofgetChannelTokenKeys.src/lib/onboard.tsbudget: net −1.Type of Change
Verification
Signed-off-by: Tinson Lai tinsonl@nvidia.com
Summary by CodeRabbit
New Features
Bug Fixes
Tests