Skip to content

[Feature] implement server.base config for API base path #1632

@ulivz

Description

@ulivz

Summary

server.base configuration is missing, preventing API routes from using custom base paths. Currently only webui.base is implemented for UI routing.

Problem Analysis

Current Implementation Status

  • webui.base: Fully implemented for UI routing and static file serving
  • server.base: Missing configuration and implementation for API routes

Code Analysis

Missing Configuration Interface:

// multimodal/tarko/interface/src/server.ts
export interface AgentServerOptions {
  server?: {
    port?: number;
    storage?: AgentStorageImplementation;
    exclusive?: boolean;
    // ❌ Missing: base?: string;
  };
}

Hardcoded API Routes:

// multimodal/tarko/agent-server/src/api/routes/sessions.ts
export function registerSessionRoutes(app: express.Application): void {
  app.group('/api/v1/sessions', (router) => { // ❌ Hardcoded path
    router.get('/', sessionsController.getAllSessions);
  });
}

Expected Behavior

Configuration Examples

// Scenario 1: Unified base path
export default {
  server: { base: '/foo' },  // API: /foo/api/*
  webui: { base: '/foo' }    // UI: /foo/*
}

// Scenario 2: Different base paths  
export default {
  server: { base: '/api-gateway' }, // API: /api-gateway/api/*
  webui: { base: '/dashboard' }     // UI: /dashboard/*
}

// Scenario 3: Regex patterns (like webui.base)
export default {
  server: { base: '/tenant-.+' },   // API: /tenant-*/api/*
  webui: { base: '/tenant-.+' }     // UI: /tenant-*/*
}

URL Mapping

  • Without server.base: http://localhost:8888/api/v1/sessions
  • With server.base: '/foo': http://localhost:8888/foo/api/v1/sessions

Implementation Plan

1. Configuration Interface Extension

// multimodal/tarko/interface/src/server.ts
export interface AgentServerOptions {
  server?: {
    port?: number;
    base?: string; // 🆕 Add server base path configuration
    storage?: AgentStorageImplementation;
    exclusive?: boolean;
  };
}

2. API Route Registration Refactor

Option A: Router-based approach (Recommended)

// multimodal/tarko/agent-server/src/api/index.ts
export function setupAPI(
  app: express.Application,
  options?: {
    workspacePath?: string;
    isDebug?: boolean;
    serverBase?: string; // 🆕 Add server base parameter
  },
) {
  const { serverBase = '' } = options || {};
  
  // Apply middleware
  app.use(cors(getDefaultCorsOptions()));
  app.use(express.json({ limit: '20mb' }));
  
  // Register API routes with base path
  if (serverBase) {
    const apiRouter = express.Router();
    registerAllRoutes(apiRouter); // Register on sub-router
    app.use(serverBase, apiRouter); // Mount to base path
  } else {
    registerAllRoutes(app); // Register directly
  }
  
  // Setup workspace static server (unaffected by serverBase)
  if (options?.workspacePath) {
    setupWorkspaceStaticServer(app, options.workspacePath, options.isDebug);
  }
}

Option B: Path prefix approach

// multimodal/tarko/agent-server/src/api/routes/index.ts
export function registerAllRoutes(
  app: express.Application | express.Router,
  serverBase: string = ''
): void {
  const apiBase = serverBase ? `${serverBase}/api` : '/api';
  
  registerSessionRoutes(app, apiBase);
  registerQueryRoutes(app, apiBase);
  registerSystemRoutes(app, apiBase);
  // ...
}

// multimodal/tarko/agent-server/src/api/routes/sessions.ts
export function registerSessionRoutes(
  app: express.Application | express.Router, 
  apiBase: string = '/api'
): void {
  app.group(`${apiBase}/v1/sessions`, (router) => {
    router.get('/', sessionsController.getAllSessions);
    // ...
  });
}

3. Server Initialization Update

// multimodal/tarko/agent-server/src/server.ts
export class AgentServer<T extends AgentAppConfig = AgentAppConfig> {
  constructor(instantiationOptions: AgentServerInitOptions<T>) {
    // ... existing code
    
    // Extract server.base configuration
    const serverBase = appConfig.server?.base;
    
    // Setup API routes with base path
    setupAPI(this.app, {
      workspacePath: this.getCurrentWorkspace(),
      isDebug: this.isDebug,
      serverBase, // 🆕 Pass server.base configuration
    });
  }
}

4. Frontend API URL Construction

// multimodal/tarko/agent-ui/src/config/web-ui-config.ts
export function getAPIBaseUrl() {
  const configuredBaseUrl = ENV_CONFIG.AGENT_BASE_URL ?? window.AGENT_BASE_URL;
  
  if (configuredBaseUrl === '') {
    const webUIBase = getWebUIRouteBase();
    const serverBase = getServerBase(); // 🆕 Get server.base from config
    
    // Use server.base for API URLs, fallback to webui.base
    const apiBase = serverBase || webUIBase;
    return configuredBaseUrl + apiBase;
  }
  
  return configuredBaseUrl;
}

function getServerBase(): string {
  // 🆕 Extract server.base from injected configuration
  const config = getWebUIConfig();
  return (config as any).serverBase || '';
}

5. Configuration Injection

// multimodal/tarko/agent-cli/src/core/commands/start.ts
function setupUI(...) {
  const injectBaseURL = (req, res, next) => {
    // ...
    const scriptTag = `<script>
      window.AGENT_BASE_URL = "";
      window.AGENT_WEB_UI_CONFIG = ${JSON.stringify({
        ...mergedWebUIConfig,
        serverBase: appConfig.server?.base || '' // 🆕 Inject server.base
      })};
    </script>`;
    // ...
  };
}

Technical Considerations

1. Regex Pattern Support

Like webui.base, server.base should support regex patterns:

// Support patterns like '/tenant-.+' for multi-tenant scenarios
function createServerBaseRouter(serverBase: string) {
  if (isRegexPattern(serverBase)) {
    // Dynamic routing for regex patterns
    return createDynamicRouter(serverBase);
  } else {
    // Static routing for simple paths
    return express.Router();
  }
}

2. Middleware Order

Ensure proper middleware order:

  1. server.base API routing (if configured)
  2. webui.base static file serving (if configured)
  3. Default routing

3. Backward Compatibility

  • If only webui.base is configured, API URLs should work without server.base
  • If both are configured, they work independently
  • Default behavior (no base paths) remains unchanged

Use Cases

Multi-tenant Deployments

// Configuration
export default {
  server: { base: '/tenant-.+' },
  webui: { base: '/tenant-.+' }
}

// URLs
// Tenant A: /tenant-a/api/v1/sessions, /tenant-a/
// Tenant B: /tenant-b/api/v1/sessions, /tenant-b/

Reverse Proxy Scenarios

// Nginx routes /api-gateway/* to Agent Server
export default {
  server: { base: '/api-gateway' },
  webui: { base: '/dashboard' }
}

// URLs  
// API: /api-gateway/api/v1/sessions
// UI: /dashboard/

Microservice Architecture

// Service mesh with path-based routing
export default {
  server: { base: '/ui-tars-api' },
  webui: { base: '/ui-tars-ui' }
}

Files to Modify

  1. Configuration: multimodal/tarko/interface/src/server.ts
  2. API Setup: multimodal/tarko/agent-server/src/api/index.ts
  3. Route Registration: multimodal/tarko/agent-server/src/api/routes/*.ts
  4. Server Init: multimodal/tarko/agent-server/src/server.ts
  5. Frontend Config: multimodal/tarko/agent-ui/src/config/web-ui-config.ts
  6. Config Injection: multimodal/tarko/agent-cli/src/core/commands/start.ts

Testing Requirements

  • API routes work with static base paths
  • API routes work with regex base paths
  • Frontend constructs correct API URLs
  • Independent configuration of server.base and webui.base
  • Backward compatibility with existing configurations
  • Multi-tenant deployment scenarios

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions