Skip to content

fix(cli): split outboundUrlGuard's DB helpers so setup-opencode packages cleanly (#7682) - #7760

Merged
diegosouzapw merged 1 commit into
release/v3.8.49from
fix/7682-opencode-shared-alias
Jul 19, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.49from
fix/7682-opencode-shared-alias

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Closes #7682

Root cause

omniroute setup-opencode crashed with Cannot find package '@/shared' imported from src/shared/network/outboundUrlGuard.ts on any global npm install. A prior fix (#6162/#6163) converted the direct @/shared/... imports in the 4 src/lib/cli-helper/*.ts files to relative paths, but missed that src/shared/network/outboundUrlGuard.ts — transitively loaded by config-generator/opencode.ts — still had a top-level import { resolveFeatureFlag } from "@/shared/utils/featureFlags". ES module static imports resolve eagerly at load time even for bindings the CLI path never calls, and the published npm package doesn't ship tsconfig.json, so tsx's alias resolution (which is cwd-based, not importing-file-based) has nowhere to resolve @/* in a real global install.

A naive "relative-ize the next import" fix doesn't work cleanly: it just shifts the failure one hop deeper (@/shared/utils/featureFlags → @/lib/db/featureFlags → @/types in src/lib/db/migrationRunner.ts), cascading through much of src/lib/db/.

Fix

Split src/shared/network/outboundUrlGuard.ts into two modules:

  • src/shared/network/outboundUrlGuard.ts — the "pure" module (isPrivateHost, isCloudMetadataHost, OutboundUrlGuardError, parseOutboundUrl, parseAndValidatePublicUrl, parseAndValidateNonMetadataUrl). Zero @/-aliased imports — only node:net. This is what the CLI transitively loads.
  • src/shared/network/outboundUrlGuardPolicy.ts (new) — the DB/feature-flag-backed helpers (arePrivateProviderUrlsAllowed, areLocalProviderUrlsAllowed, getProviderOutboundGuard, getProviderValidationGuard, parseAndValidateWebhookUrl), which keep the @/shared/utils/featureFlags import. This file is only ever loaded by Next.js/webpack-bundled server code, never the CLI, so @/ always resolves fine there.

Updated the ~16 call sites that imported the moved functions to import from outboundUrlGuardPolicy.ts instead (mechanical import-path rename, zero logic change — same functions, same bodies, just re-homed).

Regression test (Hard Rule #18 — TDD)

tests/unit/cli-setup-opencode-nested-alias-7682.test.ts — stages a tsconfig-less copy of bin/, src/lib/, src/shared/ + package.json (mirroring a real global npm install), symlinks node_modules, and spawns a fresh Node process that imports config-generator/opencode.ts via tsx/esm. Confirmed RED on unfixed code with the byte-identical reporter error:

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@/shared' imported from
.../src/shared/network/outboundUrlGuard.ts

GREEN after the split.

Gates run (all green)

  • node --import tsx/esm --test tests/unit/cli-setup-opencode-nested-alias-7682.test.ts — RED → GREEN
  • Sibling suites for the touched area (60 + 333 + 82 tests, all passing): outbound-url-guard-feature-flag, provider-models-route-lan-guard, proxy-fallback-ssrf, webhook-metadata-guard-3269, webhook-private-optin-3269, webhook-ssrf-guard, cli-helper-tool-detector-paths-6162, provider-validation-ssrf-guard, plus the broader provider-validation/webhook/remote-image-fetch suites
  • npm run typecheck:core — exit 0
  • npx eslint --suppressions-location config/quality/eslint-suppressions.json <changed files> — exit 0
  • node scripts/check/check-file-size.mjs — OK
  • node scripts/check/check-complexity.mjs — OK (2058 violations vs baseline 2059)
  • node scripts/check/check-cognitive-complexity.mjs — OK (890 vs baseline 890)
  • node scripts/check/check-changelog-integrity.mjs — OK
  • npm run check:cycles — OK, no cycles

Scope note

Only the confirmed root cause (setup-opencode CLI crash) is fixed here. The plan-file's secondary reported symptom (OpenCode Free missing from Combo Builder) is a separate, unconfirmed issue per the triage analysis and is not bundled into this fix.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request splits the database and feature-flag-backed helpers of the outbound URL guard from outboundUrlGuard.ts into a new module outboundUrlGuardPolicy.ts. This resolves an issue where the CLI command omniroute setup-opencode crashes on global npm installs due to unresolved @/ path aliases. Feedback on the changes highlights a logic bug in arePrivateProviderUrlsAllowed where a DB override of "false" is ignored if the environment variable is set to "true", and a potential test failure on Windows due to symlinkSync requiring admin privileges for 'dir' symlinks.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +30 to +41
export function arePrivateProviderUrlsAllowed() {
// 1) DB override takes precedence — it represents an explicit user toggle in
// the dashboard ("Allow Private Provider URLs"). This is critical for the
// Electron build (#2575) where the server is spawned with the env value
// captured at boot, so subsequent UI toggles only land in the DB and the
// env-first ordering would otherwise mask them.
try {
const dbValue = resolveFeatureFlag(PRIVATE_PROVIDER_URLS_ENV);
if (isTrueValue(dbValue)) return true;
} catch {
// DB not initialized yet — fall through to env-only check.
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a logic bug where an explicit DB override of "false" (e.g., when a user disables private provider URLs in the dashboard) will be ignored if the environment variable OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS is set to "true". This is because isTrueValue(dbValue) evaluates to false, causing the function to fall through to the environment variable check. To ensure the DB override takes precedence as intended, check if dbValue is defined and not empty, and return its evaluated truthiness directly.

Suggested change
export function arePrivateProviderUrlsAllowed() {
// 1) DB override takes precedence — it represents an explicit user toggle in
// the dashboard ("Allow Private Provider URLs"). This is critical for the
// Electron build (#2575) where the server is spawned with the env value
// captured at boot, so subsequent UI toggles only land in the DB and the
// env-first ordering would otherwise mask them.
try {
const dbValue = resolveFeatureFlag(PRIVATE_PROVIDER_URLS_ENV);
if (isTrueValue(dbValue)) return true;
} catch {
// DB not initialized yet — fall through to env-only check.
}
export function arePrivateProviderUrlsAllowed(): boolean {
// 1) DB override takes precedence — it represents an explicit user toggle in
// the dashboard ("Allow Private Provider URLs"). This is critical for the
// Electron build (#2575) where the server is spawned with the env value
// captured at boot, so subsequent UI toggles only land in the DB and the
// env-first ordering would otherwise mask them.
try {
const dbValue = resolveFeatureFlag(PRIVATE_PROVIDER_URLS_ENV);
if (dbValue !== undefined && dbValue !== "") {
return isTrueValue(dbValue);
}
} catch {
// DB not initialized yet — fall through to env-only check.
}

cpSync(join(REPO_ROOT, rel), join(stage, rel), { recursive: true });
}
cpSync(join(REPO_ROOT, "package.json"), join(stage, "package.json"));
symlinkSync(join(REPO_ROOT, "node_modules"), join(stage, "node_modules"), "dir");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

On Windows, creating a directory symlink using symlinkSync with the 'dir' type requires administrator privileges or developer mode enabled, which can cause test failures in local or CI environments. Using a 'junction' link on Windows avoids this requirement and works out of the box.

    const isWindows = process.platform === "win32";
    symlinkSync(
      join(REPO_ROOT, "node_modules"),
      join(stage, "node_modules"),
      isWindows ? "junction" : "dir"
    );

@diegosouzapw
diegosouzapw merged commit f7e88f4 into release/v3.8.49 Jul 19, 2026
9 of 10 checks passed
@diegosouzapw
diegosouzapw deleted the fix/7682-opencode-shared-alias branch July 19, 2026 12:55
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(providers): OpenCode Free models do not appear in Combo Builder on Windows

1 participant