export const PRODUCT_URL = 'https://github.com/claude-code-best/claude-code' // Claude Code Remote session URLs export const CLAUDE_AI_BASE_URL = 'https://claude.ai' export const CLAUDE_AI_STAGING_BASE_URL = 'https://claude-ai.staging.ant.dev' export const CLAUDE_AI_LOCAL_BASE_URL = 'http://localhost:4000' /** * Determine if we're in a staging environment for remote sessions. * Checks session ID format and ingress URL. */ export function isRemoteSessionStaging( sessionId?: string, ingressUrl?: string, ): boolean { return ( sessionId?.includes('_staging_') === true || ingressUrl?.includes('staging') === true ) } /** * Determine if we're in a local-dev environment for remote sessions. * Checks session ID format (e.g. `session_local_...`) and ingress URL. */ export function isRemoteSessionLocal( sessionId?: string, ingressUrl?: string, ): boolean { return ( sessionId?.includes('_local_') === true || ingressUrl?.includes('localhost') === true ) } /** * Get the base URL for Claude AI based on environment. * For localhost, derives the base URL from the ingress URL to preserve the * actual server port instead of using the hardcoded default (4000). */ export function getClaudeAiBaseUrl( sessionId?: string, ingressUrl?: string, ): string { if (isRemoteSessionLocal(sessionId, ingressUrl)) { // If an ingress URL is available, extract its origin to keep the correct port. // Self-hosted servers may run on any port (default 3000), not just 4000. if (ingressUrl) { try { const parsed = new URL(ingressUrl) return parsed.origin } catch { // Fall through to default } } return CLAUDE_AI_LOCAL_BASE_URL } if (isRemoteSessionStaging(sessionId, ingressUrl)) { return CLAUDE_AI_STAGING_BASE_URL } return CLAUDE_AI_BASE_URL } /** * Get the full session URL for a remote session. * * The cse_→session_ translation is a temporary shim gated by * tengu_bridge_repl_v2_cse_shim_enabled (see isCseShimEnabled). Worker * endpoints (/v1/code/sessions/{id}/worker/*) want `cse_*` but the claude.ai * frontend currently routes on `session_*` (compat/convert.go:27 validates * TagSession). Same UUID body, different tag prefix. Once the server tags by * environment_kind and the frontend accepts `cse_*` directly, flip the gate * off. No-op for IDs already in `session_*` form. See toCompatSessionId in * src/bridge/sessionIdCompat.ts for the canonical helper (lazy-required here * to keep constants/ leaf-of-DAG at module-load time). */ export function getRemoteSessionUrl( sessionId: string, ingressUrl?: string, ): string { /* eslint-disable @typescript-eslint/no-require-imports */ const { toCompatSessionId } = require('../bridge/sessionIdCompat.js') as typeof import('../bridge/sessionIdCompat.js') /* eslint-enable @typescript-eslint/no-require-imports */ const compatId = toCompatSessionId(sessionId) // Use CLAUDE_BRIDGE_BASE_URL from env if available, otherwise fall back to default logic const bridgeBaseUrl = process.env.CLAUDE_BRIDGE_BASE_URL if (bridgeBaseUrl) { const base = bridgeBaseUrl.replace(/\/+$/, '') return `${base}/code/${compatId}` } const baseUrl = getClaudeAiBaseUrl(compatId, ingressUrl) return `${baseUrl}/code/${compatId}` }