mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-14 08:03:58 +00:00
Compare commits
5 Commits
dev
...
fix/may-8_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b3d78a252 | ||
|
|
da4be6c39d | ||
|
|
b4fff2716d | ||
|
|
b4aa55f56b | ||
|
|
9372d4eabc |
@@ -30,6 +30,7 @@ import type {
|
||||
AgentAdapter,
|
||||
AgentDefinition,
|
||||
} from '../../lib/agents/agent-types'
|
||||
import type { FilePreview } from '../../lib/agents/openclaw'
|
||||
import type { AgentHistoryPage, AgentStreamEvent } from '../../lib/agents/types'
|
||||
import {
|
||||
type AgentDefinitionWithActivity,
|
||||
@@ -46,7 +47,6 @@ import {
|
||||
TurnAlreadyActiveError,
|
||||
UnknownAgentError,
|
||||
} from '../services/agents/agent-harness-service'
|
||||
import type { FilePreview } from '../services/openclaw/file-preview'
|
||||
import type { Env } from '../types'
|
||||
import { resolveBrowserContextPageIds } from '../utils/resolve-browser-context-page-ids'
|
||||
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
|
||||
import { Hono } from 'hono'
|
||||
import { stream } from 'hono/streaming'
|
||||
import { logger } from '../../lib/logger'
|
||||
import {
|
||||
getOpenClawCliProvider,
|
||||
getOpenClawService,
|
||||
isUnsupportedOpenClawProviderError,
|
||||
OpenClawAgentAlreadyExistsError,
|
||||
OpenClawAgentNotFoundError,
|
||||
OpenClawInvalidAgentNameError,
|
||||
OpenClawProtectedAgentError,
|
||||
OpenClawSessionNotFoundError,
|
||||
} from '../services/openclaw/errors'
|
||||
import { getOpenClawCliProvider } from '../services/openclaw/openclaw-cli-providers/registry'
|
||||
import { isUnsupportedOpenClawProviderError } from '../services/openclaw/openclaw-provider-map'
|
||||
import { getOpenClawService } from '../services/openclaw/openclaw-service'
|
||||
} from '../../lib/agents/openclaw'
|
||||
import { logger } from '../../lib/logger'
|
||||
|
||||
function getCreateAgentValidationError(body: { name?: string }): string | null {
|
||||
if (!body.name?.trim()) {
|
||||
|
||||
@@ -17,6 +17,10 @@ import { cors } from 'hono/cors'
|
||||
import type { ContentfulStatusCode } from 'hono/utils/http-status'
|
||||
import { HttpAgentError } from '../agent/errors'
|
||||
import { INLINED_ENV } from '../env'
|
||||
import {
|
||||
convertOpenClawHistoryToAgentHistory,
|
||||
getOpenClawService,
|
||||
} from '../lib/agents/openclaw'
|
||||
import { KlavisClient } from '../lib/clients/klavis/klavis-client'
|
||||
import { initializeOAuth, shutdownOAuth } from '../lib/clients/oauth'
|
||||
import { getDb } from '../lib/db'
|
||||
@@ -46,8 +50,6 @@ import {
|
||||
connectKlavisInBackground,
|
||||
type KlavisProxyRef,
|
||||
} from './services/klavis/strata-proxy'
|
||||
import { convertOpenClawHistoryToAgentHistory } from './services/openclaw/history-mapper'
|
||||
import { getOpenClawService } from './services/openclaw/openclaw-service'
|
||||
import type { Env, HttpServerConfig } from './types'
|
||||
import { defaultCorsConfig } from './utils/cors'
|
||||
import { requireTrustedAppOrigin } from './utils/request-auth'
|
||||
|
||||
@@ -19,13 +19,15 @@ import type {
|
||||
} from '../../../lib/agents/agent-store'
|
||||
import type { AgentDefinition } from '../../../lib/agents/agent-types'
|
||||
import { DbAgentStore } from '../../../lib/agents/db-agent-store'
|
||||
import {
|
||||
getHermesProviderMapping,
|
||||
writeHermesPerAgentProvider,
|
||||
} from '../../../lib/agents/hermes'
|
||||
import {
|
||||
FileMessageQueue,
|
||||
type QueuedMessage,
|
||||
type QueuedMessageAttachment,
|
||||
} from '../../../lib/agents/message-queue'
|
||||
import { writeHermesPerAgentProvider } from '../hermes/hermes-paths'
|
||||
import { getHermesProviderMapping } from '../hermes/hermes-provider-map'
|
||||
|
||||
export {
|
||||
MessageQueueFullError,
|
||||
@@ -34,6 +36,15 @@ export {
|
||||
} from '../../../lib/agents/message-queue'
|
||||
|
||||
import { basename } from 'node:path'
|
||||
import {
|
||||
buildFilePreview,
|
||||
detectMimeType,
|
||||
type FilePreview,
|
||||
type FileSnapshot,
|
||||
getHostWorkspaceDir,
|
||||
type ProducedFileRow,
|
||||
ProducedFilesStore,
|
||||
} from '../../../lib/agents/openclaw'
|
||||
import type {
|
||||
AgentHistoryPage,
|
||||
AgentRowSnapshot,
|
||||
@@ -42,17 +53,6 @@ import type {
|
||||
} from '../../../lib/agents/types'
|
||||
import { getOpenClawDir } from '../../../lib/browseros-dir'
|
||||
import { logger } from '../../../lib/logger'
|
||||
import {
|
||||
buildFilePreview,
|
||||
detectMimeType,
|
||||
type FilePreview,
|
||||
} from '../openclaw/file-preview'
|
||||
import { getHostWorkspaceDir } from '../openclaw/openclaw-env'
|
||||
import {
|
||||
type FileSnapshot,
|
||||
type ProducedFileRow,
|
||||
ProducedFilesStore,
|
||||
} from '../openclaw/produced-files-store'
|
||||
|
||||
export type AgentLiveness = 'working' | 'idle' | 'asleep' | 'error'
|
||||
|
||||
|
||||
@@ -5,12 +5,10 @@
|
||||
*/
|
||||
|
||||
import type { AgentDefinition } from './agent-types'
|
||||
import { prepareOpenClawContext } from './openclaw/prepare'
|
||||
import {
|
||||
prepareClaudeCodeContext,
|
||||
prepareCodexContext,
|
||||
prepareHermesContext,
|
||||
} from './runtime'
|
||||
import { prepareClaudeCodeContext } from './claude'
|
||||
import { prepareCodexContext } from './codex'
|
||||
import { prepareHermesContext } from './hermes'
|
||||
import { prepareOpenClawContext } from './openclaw'
|
||||
|
||||
export interface PreparedAcpxAgentContext {
|
||||
cwd: string
|
||||
@@ -21,8 +19,7 @@ export interface PreparedAcpxAgentContext {
|
||||
useBrowserosMcp: boolean
|
||||
/**
|
||||
* Hostname the agent should use to reach the BrowserOS HTTP MCP server.
|
||||
* Default `127.0.0.1` is correct for host-process adapters (claude, codex,
|
||||
* Phase A host-mode hermes). Container-spawned adapters override this to
|
||||
* Default `127.0.0.1` is correct for host-process adapters. Container-spawned adapters override this to
|
||||
* `host.containers.internal` so the URL injected into ACP newSession's
|
||||
* mcpServers resolves from inside the container.
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,7 @@ import type {
|
||||
AgentHistoryEntry,
|
||||
AgentHistoryToolCall,
|
||||
} from './agent-types'
|
||||
import { getHermesRuntime } from './runtime'
|
||||
import { getHermesRuntime } from './hermes'
|
||||
import type {
|
||||
AgentHistoryPage,
|
||||
AgentPromptInput,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 BrowserOS
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export {
|
||||
ClaudeRuntime,
|
||||
type ClaudeRuntimeConfig,
|
||||
type ConfigureClaudeRuntimeOptions,
|
||||
configureClaudeRuntime,
|
||||
getClaudeRuntime,
|
||||
prepareClaudeCodeContext,
|
||||
} from './runtime'
|
||||
@@ -15,9 +15,9 @@ import {
|
||||
prepareBrowserosManagedContext,
|
||||
} from '../acpx-agent-common'
|
||||
import { resolveAgentRuntimePaths } from '../acpx-runtime-context'
|
||||
import { HostProcessAgentRuntime } from './host-process-agent-runtime'
|
||||
import { getAgentRuntimeRegistry } from './registry'
|
||||
import type { RuntimeDescriptor } from './types'
|
||||
import { HostProcessAgentRuntime } from '../runtime/host-process-agent-runtime'
|
||||
import { getAgentRuntimeRegistry } from '../runtime/registry'
|
||||
import type { RuntimeDescriptor } from '../runtime/types'
|
||||
|
||||
const CLAUDE_BINARY = 'claude'
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 BrowserOS
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export {
|
||||
CodexRuntime,
|
||||
type CodexRuntimeConfig,
|
||||
type ConfigureCodexRuntimeOptions,
|
||||
configureCodexRuntime,
|
||||
getCodexRuntime,
|
||||
prepareCodexContext,
|
||||
} from './runtime'
|
||||
@@ -18,9 +18,9 @@ import {
|
||||
materializeCodexHome,
|
||||
resolveAgentRuntimePaths,
|
||||
} from '../acpx-runtime-context'
|
||||
import { HostProcessAgentRuntime } from './host-process-agent-runtime'
|
||||
import { getAgentRuntimeRegistry } from './registry'
|
||||
import type { RuntimeDescriptor } from './types'
|
||||
import { HostProcessAgentRuntime } from '../runtime/host-process-agent-runtime'
|
||||
import { getAgentRuntimeRegistry } from '../runtime/registry'
|
||||
import type { RuntimeDescriptor } from '../runtime/types'
|
||||
|
||||
const CODEX_BINARY = 'codex'
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 BrowserOS
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export {
|
||||
getHermesAgentHomeHostDir,
|
||||
getHermesHarnessHostDir,
|
||||
getHermesHostStateDir,
|
||||
writeHermesPerAgentProvider,
|
||||
} from './paths'
|
||||
export {
|
||||
getHermesProviderMapping,
|
||||
type HermesProviderMapping,
|
||||
isHermesSupportedProviderType,
|
||||
} from './provider-map'
|
||||
export {
|
||||
type ConfigureHermesRuntimeOptions,
|
||||
configureHermesRuntime,
|
||||
getHermesRuntime,
|
||||
HermesContainerRuntime,
|
||||
type HermesContainerRuntimeConfig,
|
||||
prepareHermesContext,
|
||||
} from './runtime'
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import { mkdir, writeFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
import { getVmStateDir } from '../../../lib/browseros-dir'
|
||||
import { getVmStateDir } from '../../browseros-dir'
|
||||
|
||||
/** Top-level Hermes state directory: `<browserosDir>/vm/hermes`. */
|
||||
export function getHermesHostStateDir(browserosDir?: string): string {
|
||||
@@ -15,11 +15,6 @@ import {
|
||||
HERMES_CONTAINER_NAME,
|
||||
HERMES_IMAGE,
|
||||
} from '@browseros/shared/constants/hermes'
|
||||
import {
|
||||
getHermesAgentHomeHostDir,
|
||||
getHermesHarnessHostDir,
|
||||
getHermesHostStateDir,
|
||||
} from '../../../api/services/hermes/hermes-paths'
|
||||
import { getBrowserosDir } from '../../browseros-dir'
|
||||
import { ContainerCli } from '../../container/container-cli'
|
||||
import { ImageLoader } from '../../container/image-loader'
|
||||
@@ -46,9 +41,14 @@ import {
|
||||
finishBrowserosManagedContext,
|
||||
prepareBrowserosManagedContext,
|
||||
} from '../acpx-agent-common'
|
||||
import { ContainerAgentRuntime } from './container-agent-runtime'
|
||||
import { getAgentRuntimeRegistry } from './registry'
|
||||
import type { ExecSpec } from './types'
|
||||
import { ContainerAgentRuntime } from '../runtime/container-agent-runtime'
|
||||
import { getAgentRuntimeRegistry } from '../runtime/registry'
|
||||
import type { ExecSpec } from '../runtime/types'
|
||||
import {
|
||||
getHermesAgentHomeHostDir,
|
||||
getHermesHarnessHostDir,
|
||||
getHermesHostStateDir,
|
||||
} from './paths'
|
||||
|
||||
const HERMES_BINARY = '/opt/hermes/.venv/bin/hermes'
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
|
||||
import { cpSync, existsSync, mkdirSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { getBrowserosDir } from '../../../lib/browseros-dir'
|
||||
import { ContainerCli, ImageLoader } from '../../../lib/container'
|
||||
import { logger } from '../../../lib/logger'
|
||||
import { getBrowserosDir } from '../../browseros-dir'
|
||||
import { ContainerCli, ImageLoader } from '../../container'
|
||||
import { logger } from '../../logger'
|
||||
import {
|
||||
getLimaHomeDir,
|
||||
resolveBundledLimactl,
|
||||
resolveBundledLimaTemplate,
|
||||
VM_NAME,
|
||||
VmRuntime,
|
||||
} from '../../../lib/vm'
|
||||
import { VM_TELEMETRY_EVENTS } from '../../../lib/vm/telemetry'
|
||||
} from '../../vm'
|
||||
import { VM_TELEMETRY_EVENTS } from '../../vm/telemetry'
|
||||
import { ContainerRuntime } from './container-runtime'
|
||||
|
||||
const UNSUPPORTED_PLATFORM_MESSAGE =
|
||||
@@ -16,15 +16,11 @@ import type {
|
||||
ContainerSpec,
|
||||
LogFn,
|
||||
WaitForContainerNameReleaseOptions,
|
||||
} from '../../../lib/container'
|
||||
import { isContainerNameInUse } from '../../../lib/container'
|
||||
import { logger } from '../../../lib/logger'
|
||||
import {
|
||||
GUEST_VM_STATE,
|
||||
hostPathToGuest,
|
||||
type VmRuntime,
|
||||
} from '../../../lib/vm'
|
||||
import { ContainerNameInUseError } from '../../../lib/vm/errors'
|
||||
} from '../../container'
|
||||
import { isContainerNameInUse } from '../../container'
|
||||
import { logger } from '../../logger'
|
||||
import { GUEST_VM_STATE, hostPathToGuest, type VmRuntime } from '../../vm'
|
||||
import { ContainerNameInUseError } from '../../vm/errors'
|
||||
|
||||
const GATEWAY_CONTAINER_HOME = '/home/node'
|
||||
const GATEWAY_STATE_DIR = `${GATEWAY_CONTAINER_HOME}/.openclaw`
|
||||
@@ -21,12 +21,12 @@
|
||||
* resulting AgentHistoryToolCall has both input and output.
|
||||
*/
|
||||
|
||||
import { unwrapBrowserosAcpUserMessage } from '../../../lib/agents/acpx-runtime'
|
||||
import { unwrapBrowserosAcpUserMessage } from '../../agents/acpx-runtime'
|
||||
import type {
|
||||
AgentHistoryEntry,
|
||||
AgentHistoryToolCall,
|
||||
} from '../../../lib/agents/agent-types'
|
||||
import type { AgentHistoryPage } from '../../../lib/agents/types'
|
||||
} from '../../agents/agent-types'
|
||||
import type { AgentHistoryPage } from '../../agents/types'
|
||||
import type {
|
||||
OpenClawSessionHistory,
|
||||
OpenClawSessionHistoryMessage,
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 BrowserOS
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export {
|
||||
type AgentLiveStatus,
|
||||
type AgentSessionState,
|
||||
ClawSession,
|
||||
} from './claw-session'
|
||||
export {
|
||||
ContainerRuntime,
|
||||
type ContainerRuntimeConfig,
|
||||
type GatewayContainerSpec,
|
||||
} from './container-runtime'
|
||||
export {
|
||||
buildContainerRuntime,
|
||||
type ContainerRuntimeFactoryInput,
|
||||
migrateLegacyOpenClawDir,
|
||||
} from './container-runtime-factory'
|
||||
export {
|
||||
OpenClawAgentAlreadyExistsError,
|
||||
OpenClawAgentNotFoundError,
|
||||
OpenClawInvalidAgentNameError,
|
||||
OpenClawProtectedAgentError,
|
||||
OpenClawSessionNotFoundError,
|
||||
} from './errors'
|
||||
export {
|
||||
buildFilePreview,
|
||||
detectMimeType,
|
||||
type FilePreview,
|
||||
} from './file-preview'
|
||||
export { convertOpenClawHistoryToAgentHistory } from './history-mapper'
|
||||
export {
|
||||
type OpenClawAgentRecord,
|
||||
OpenClawCliClient,
|
||||
type OpenClawConfigBatchEntry,
|
||||
type OpenClawSessionEntry,
|
||||
} from './openclaw-cli-client'
|
||||
export {
|
||||
buildOpenClawCliProviderModelRef,
|
||||
getOpenClawCliProvider,
|
||||
OPENCLAW_CLI_PROVIDERS,
|
||||
} from './openclaw-cli-providers/registry'
|
||||
export type {
|
||||
OpenClawCliProvider,
|
||||
OpenClawCliProviderAuthStatus,
|
||||
} from './openclaw-cli-providers/types'
|
||||
export {
|
||||
getHostWorkspaceDir,
|
||||
getOpenClawStateConfigPath,
|
||||
getOpenClawStateDir,
|
||||
getOpenClawStateEnvPath,
|
||||
isAgentWorkspaceNameSafe,
|
||||
mergeEnvContent,
|
||||
} from './openclaw-env'
|
||||
export {
|
||||
OpenClawHttpClient,
|
||||
type OpenClawSessionHistory,
|
||||
type OpenClawSessionHistoryEvent,
|
||||
type OpenClawSessionHistoryMessage,
|
||||
} from './openclaw-http-client'
|
||||
export {
|
||||
isUnsupportedOpenClawProviderError,
|
||||
type ResolvedOpenClawProviderConfig,
|
||||
resolveSupportedOpenClawProvider,
|
||||
UnsupportedOpenClawProviderError,
|
||||
} from './openclaw-provider-map'
|
||||
export {
|
||||
type BrowserOSChatHistoryAttachment,
|
||||
type BrowserOSChatHistoryItem,
|
||||
type BrowserOSChatHistoryReasoning,
|
||||
type BrowserOSChatHistoryToolCall,
|
||||
type BrowserOSOpenClawAgentSessionResponse,
|
||||
type BrowserOSOpenClawSession,
|
||||
configureOpenClawService,
|
||||
configureVmRuntime,
|
||||
getOpenClawService,
|
||||
normalizeBrowserOSChatSessionKey,
|
||||
type OpenClawAgentEntry,
|
||||
type OpenClawControlPlaneStatus,
|
||||
type OpenClawGatewayRecoveryReason,
|
||||
OpenClawService,
|
||||
type OpenClawServiceConfig,
|
||||
type OpenClawSessionSource,
|
||||
type OpenClawStatus,
|
||||
type OpenClawStatusResponse,
|
||||
type SetupInput,
|
||||
} from './openclaw-service'
|
||||
export type { OpenClawStreamEvent } from './openclaw-types'
|
||||
export { prepareOpenClawContext } from './prepare'
|
||||
export {
|
||||
type FileSnapshot,
|
||||
type FileSnapshotEntry,
|
||||
type FinalizeTurnInput,
|
||||
type ProducedFileRow,
|
||||
ProducedFilesStore,
|
||||
type ResolvedFile,
|
||||
resolveSafeWorkspacePath,
|
||||
} from './produced-files-store'
|
||||
export {
|
||||
type WorkspaceFileMetadata,
|
||||
type WorkspaceFileVisitor,
|
||||
walkWorkspace,
|
||||
} from './produced-files-walker'
|
||||
export {
|
||||
type AllocateGatewayPortOptions,
|
||||
allocateGatewayPort,
|
||||
readPersistedGatewayPort,
|
||||
} from './runtime-state'
|
||||
@@ -17,10 +17,10 @@ import {
|
||||
OPENCLAW_IMAGE,
|
||||
} from '@browseros/shared/constants/openclaw'
|
||||
import { DEFAULT_PORTS } from '@browseros/shared/constants/ports'
|
||||
import type { AgentStreamEvent } from '../../../lib/agents/types'
|
||||
import { getOpenClawDir } from '../../../lib/browseros-dir'
|
||||
import { logger } from '../../../lib/logger'
|
||||
import { withProcessLock } from '../../../lib/process-lock'
|
||||
import type { AgentStreamEvent } from '../../agents/types'
|
||||
import { getOpenClawDir } from '../../browseros-dir'
|
||||
import { logger } from '../../logger'
|
||||
import { withProcessLock } from '../../process-lock'
|
||||
import {
|
||||
type AgentLiveStatus,
|
||||
type AgentSessionState,
|
||||
@@ -18,13 +18,13 @@ import { randomUUID } from 'node:crypto'
|
||||
import { realpath, stat } from 'node:fs/promises'
|
||||
import { relative, resolve, sep } from 'node:path'
|
||||
import { and, desc, eq } from 'drizzle-orm'
|
||||
import { type BrowserOsDatabase, getDb } from '../../../lib/db'
|
||||
import { type BrowserOsDatabase, getDb } from '../../db'
|
||||
import {
|
||||
agentDefinitions,
|
||||
type NewProducedFileRow,
|
||||
type ProducedFileRow,
|
||||
producedFiles,
|
||||
} from '../../../lib/db/schema'
|
||||
} from '../../db/schema'
|
||||
import { walkWorkspace } from './produced-files-walker'
|
||||
|
||||
const TURN_PROMPT_MAX_CHARS = 280
|
||||
@@ -5,32 +5,8 @@
|
||||
*/
|
||||
|
||||
export type { AgentRuntime } from './agent-runtime'
|
||||
export {
|
||||
ClaudeRuntime,
|
||||
type ClaudeRuntimeConfig,
|
||||
type ConfigureClaudeRuntimeOptions,
|
||||
configureClaudeRuntime,
|
||||
getClaudeRuntime,
|
||||
prepareClaudeCodeContext,
|
||||
} from './claude-host-process-runtime'
|
||||
export {
|
||||
CodexRuntime,
|
||||
type CodexRuntimeConfig,
|
||||
type ConfigureCodexRuntimeOptions,
|
||||
configureCodexRuntime,
|
||||
getCodexRuntime,
|
||||
prepareCodexContext,
|
||||
} from './codex-host-process-runtime'
|
||||
export { ContainerAgentRuntime } from './container-agent-runtime'
|
||||
export { ActionNotSupportedError, RuntimeNotReadyError } from './errors'
|
||||
export {
|
||||
type ConfigureHermesRuntimeOptions,
|
||||
configureHermesRuntime,
|
||||
getHermesRuntime,
|
||||
HermesContainerRuntime,
|
||||
type HermesContainerRuntimeConfig,
|
||||
prepareHermesContext,
|
||||
} from './hermes-container-runtime'
|
||||
export {
|
||||
HostProcessAgentRuntime,
|
||||
type HostProcessAgentRuntimeDeps,
|
||||
|
||||
@@ -12,21 +12,18 @@ import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { EXIT_CODES } from '@browseros/shared/constants/exit-codes'
|
||||
import { createHttpServer } from './api/server'
|
||||
import {
|
||||
configureOpenClawService,
|
||||
configureVmRuntime,
|
||||
getOpenClawService,
|
||||
} from './api/services/openclaw/openclaw-service'
|
||||
import { CdpBackend } from './browser/backends/cdp'
|
||||
import { Browser } from './browser/browser'
|
||||
import type { ServerConfig } from './config'
|
||||
import { INLINED_ENV } from './env'
|
||||
import { configureClaudeRuntime } from './lib/agents/claude'
|
||||
import { configureCodexRuntime } from './lib/agents/codex'
|
||||
import { configureHermesRuntime, getHermesRuntime } from './lib/agents/hermes'
|
||||
import {
|
||||
configureClaudeRuntime,
|
||||
configureCodexRuntime,
|
||||
configureHermesRuntime,
|
||||
getHermesRuntime,
|
||||
} from './lib/agents/runtime'
|
||||
configureOpenClawService,
|
||||
configureVmRuntime,
|
||||
getOpenClawService,
|
||||
} from './lib/agents/openclaw'
|
||||
import {
|
||||
cleanOldSessions,
|
||||
ensureBrowserosDir,
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
import { afterEach, describe, expect, it, mock } from 'bun:test'
|
||||
import { OpenClawSessionNotFoundError } from '../../../src/api/services/openclaw/errors'
|
||||
import { UnsupportedOpenClawProviderError } from '../../../src/api/services/openclaw/openclaw-provider-map'
|
||||
import { OpenClawSessionNotFoundError } from '../../../src/lib/agents/openclaw/errors'
|
||||
import { UnsupportedOpenClawProviderError } from '../../../src/lib/agents/openclaw/openclaw-provider-map'
|
||||
|
||||
describe('createOpenClawRoutes', () => {
|
||||
afterEach(() => {
|
||||
@@ -14,13 +14,13 @@ describe('createOpenClawRoutes', () => {
|
||||
|
||||
it('returns 400 for unsupported provider payloads', async () => {
|
||||
const actualOpenClawService = await import(
|
||||
'../../../src/api/services/openclaw/openclaw-service'
|
||||
'../../../src/lib/agents/openclaw'
|
||||
)
|
||||
const updateProviderKeys = mock(async () => {
|
||||
throw new UnsupportedOpenClawProviderError('google')
|
||||
})
|
||||
|
||||
mock.module('../../../src/api/services/openclaw/openclaw-service', () => ({
|
||||
mock.module('../../../src/lib/agents/openclaw', () => ({
|
||||
...actualOpenClawService,
|
||||
getOpenClawService: () =>
|
||||
({
|
||||
@@ -54,14 +54,14 @@ describe('createOpenClawRoutes', () => {
|
||||
|
||||
it('returns a non-restarting response when only the default model changes', async () => {
|
||||
const actualOpenClawService = await import(
|
||||
'../../../src/api/services/openclaw/openclaw-service'
|
||||
'../../../src/lib/agents/openclaw'
|
||||
)
|
||||
const updateProviderKeys = mock(async () => ({
|
||||
restarted: false,
|
||||
modelUpdated: true,
|
||||
}))
|
||||
|
||||
mock.module('../../../src/api/services/openclaw/openclaw-service', () => ({
|
||||
mock.module('../../../src/lib/agents/openclaw', () => ({
|
||||
...actualOpenClawService,
|
||||
getOpenClawService: () =>
|
||||
({
|
||||
@@ -109,7 +109,7 @@ describe('createOpenClawRoutes', () => {
|
||||
|
||||
it('ignores role fields when creating agents', async () => {
|
||||
const actualOpenClawService = await import(
|
||||
'../../../src/api/services/openclaw/openclaw-service'
|
||||
'../../../src/lib/agents/openclaw'
|
||||
)
|
||||
const createAgent = mock(async () => ({
|
||||
agentId: 'research',
|
||||
@@ -117,7 +117,7 @@ describe('createOpenClawRoutes', () => {
|
||||
workspace: '/home/node/.openclaw/workspace-research',
|
||||
}))
|
||||
|
||||
mock.module('../../../src/api/services/openclaw/openclaw-service', () => ({
|
||||
mock.module('../../../src/lib/agents/openclaw', () => ({
|
||||
...actualOpenClawService,
|
||||
getOpenClawService: () =>
|
||||
({
|
||||
@@ -162,7 +162,7 @@ describe('createOpenClawRoutes', () => {
|
||||
|
||||
it('returns JSON history from the session history route and forwards query params', async () => {
|
||||
const actualOpenClawService = await import(
|
||||
'../../../src/api/services/openclaw/openclaw-service'
|
||||
'../../../src/lib/agents/openclaw'
|
||||
)
|
||||
const getSessionHistory = mock(async () => ({
|
||||
sessionKey: 'agent:main:main',
|
||||
@@ -171,7 +171,7 @@ describe('createOpenClawRoutes', () => {
|
||||
hasMore: false,
|
||||
}))
|
||||
|
||||
mock.module('../../../src/api/services/openclaw/openclaw-service', () => ({
|
||||
mock.module('../../../src/lib/agents/openclaw', () => ({
|
||||
...actualOpenClawService,
|
||||
getOpenClawService: () => ({ getSessionHistory }) as never,
|
||||
}))
|
||||
@@ -201,13 +201,13 @@ describe('createOpenClawRoutes', () => {
|
||||
|
||||
it('returns 404 when the service reports a missing session', async () => {
|
||||
const actualOpenClawService = await import(
|
||||
'../../../src/api/services/openclaw/openclaw-service'
|
||||
'../../../src/lib/agents/openclaw'
|
||||
)
|
||||
const getSessionHistory = mock(async () => {
|
||||
throw new OpenClawSessionNotFoundError('missing')
|
||||
})
|
||||
|
||||
mock.module('../../../src/api/services/openclaw/openclaw-service', () => ({
|
||||
mock.module('../../../src/lib/agents/openclaw', () => ({
|
||||
...actualOpenClawService,
|
||||
getOpenClawService: () => ({ getSessionHistory }) as never,
|
||||
}))
|
||||
@@ -227,7 +227,7 @@ describe('createOpenClawRoutes', () => {
|
||||
|
||||
it('streams named SSE frames when Accept: text/event-stream', async () => {
|
||||
const actualOpenClawService = await import(
|
||||
'../../../src/api/services/openclaw/openclaw-service'
|
||||
'../../../src/lib/agents/openclaw'
|
||||
)
|
||||
const streamSessionHistory = mock(
|
||||
async () =>
|
||||
@@ -255,7 +255,7 @@ describe('createOpenClawRoutes', () => {
|
||||
}),
|
||||
)
|
||||
|
||||
mock.module('../../../src/api/services/openclaw/openclaw-service', () => ({
|
||||
mock.module('../../../src/lib/agents/openclaw', () => ({
|
||||
...actualOpenClawService,
|
||||
getOpenClawService: () => ({ streamSessionHistory }) as never,
|
||||
}))
|
||||
|
||||
@@ -21,9 +21,9 @@ import {
|
||||
unwrapBrowserosAcpUserMessage,
|
||||
} from '../../../src/lib/agents/acpx-runtime'
|
||||
import type { AgentDefinition } from '../../../src/lib/agents/agent-types'
|
||||
import { HermesContainerRuntime } from '../../../src/lib/agents/hermes'
|
||||
import {
|
||||
getAgentRuntimeRegistry,
|
||||
HermesContainerRuntime,
|
||||
resetAgentRuntimeRegistry,
|
||||
} from '../../../src/lib/agents/runtime'
|
||||
import type { AgentStreamEvent } from '../../../src/lib/agents/types'
|
||||
|
||||
@@ -10,9 +10,11 @@ import { join } from 'node:path'
|
||||
import {
|
||||
ClaudeRuntime,
|
||||
configureClaudeRuntime,
|
||||
getAgentRuntimeRegistry,
|
||||
getClaudeRuntime,
|
||||
prepareClaudeCodeContext,
|
||||
} from '../../../../src/lib/agents/claude'
|
||||
import {
|
||||
getAgentRuntimeRegistry,
|
||||
resetAgentRuntimeRegistry,
|
||||
} from '../../../../src/lib/agents/runtime'
|
||||
|
||||
@@ -10,9 +10,11 @@ import { join } from 'node:path'
|
||||
import {
|
||||
CodexRuntime,
|
||||
configureCodexRuntime,
|
||||
getAgentRuntimeRegistry,
|
||||
getCodexRuntime,
|
||||
prepareCodexContext,
|
||||
} from '../../../../src/lib/agents/codex'
|
||||
import {
|
||||
getAgentRuntimeRegistry,
|
||||
resetAgentRuntimeRegistry,
|
||||
} from '../../../../src/lib/agents/runtime'
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 BrowserOS
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'bun:test'
|
||||
import { mkdtemp, readFile, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import {
|
||||
getHermesAgentHomeHostDir,
|
||||
getHermesHarnessHostDir,
|
||||
getHermesHostStateDir,
|
||||
getHermesProviderMapping,
|
||||
writeHermesPerAgentProvider,
|
||||
} from '../../../../src/lib/agents/hermes'
|
||||
|
||||
describe('Hermes adapter helpers', () => {
|
||||
it('resolves Hermes state, harness, and per-agent home paths under vm/hermes', () => {
|
||||
const root = '/tmp/browseros'
|
||||
expect(getHermesHostStateDir(root)).toBe('/tmp/browseros/vm/hermes')
|
||||
expect(getHermesHarnessHostDir(root)).toBe(
|
||||
'/tmp/browseros/vm/hermes/harness',
|
||||
)
|
||||
expect(
|
||||
getHermesAgentHomeHostDir({ browserosDir: root, agentId: 'agent-1' }),
|
||||
).toBe('/tmp/browseros/vm/hermes/harness/agent-1/home')
|
||||
})
|
||||
|
||||
it('writes provider config and requires a base URL for custom providers', async () => {
|
||||
const browserosDir = await mkdtemp(join(tmpdir(), 'browseros-hermes-'))
|
||||
try {
|
||||
await writeHermesPerAgentProvider({
|
||||
browserosDir,
|
||||
agentId: 'agent-1',
|
||||
providerId: 'custom',
|
||||
envVarName: 'OPENAI_API_KEY',
|
||||
apiKey: 'sk-test',
|
||||
modelId: 'gpt-5.5',
|
||||
baseUrl: 'https://api.openai.com/v1',
|
||||
})
|
||||
const home = getHermesAgentHomeHostDir({
|
||||
browserosDir,
|
||||
agentId: 'agent-1',
|
||||
})
|
||||
expect(await readFile(join(home, 'config.yaml'), 'utf8')).toContain(
|
||||
'base_url: "https://api.openai.com/v1"',
|
||||
)
|
||||
expect(await readFile(join(home, '.env'), 'utf8')).toBe(
|
||||
'OPENAI_API_KEY=sk-test\n',
|
||||
)
|
||||
await expect(
|
||||
writeHermesPerAgentProvider({
|
||||
browserosDir,
|
||||
agentId: 'agent-2',
|
||||
providerId: 'custom',
|
||||
envVarName: 'OPENAI_API_KEY',
|
||||
apiKey: 'sk-test',
|
||||
modelId: 'gpt-5.5',
|
||||
}),
|
||||
).rejects.toThrow(/requires base_url/)
|
||||
} finally {
|
||||
await rm(browserosDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('maps BrowserOS provider types to Hermes provider config', () => {
|
||||
expect(getHermesProviderMapping('anthropic')).toEqual({
|
||||
hermesProvider: 'anthropic',
|
||||
envVarName: 'ANTHROPIC_API_KEY',
|
||||
requiresBaseUrl: false,
|
||||
})
|
||||
expect(getHermesProviderMapping('openai')?.defaultBaseUrl).toBe(
|
||||
'https://api.openai.com/v1',
|
||||
)
|
||||
expect(getHermesProviderMapping('unknown')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -15,9 +15,11 @@ import {
|
||||
} from '../../../../../../packages/shared/src/constants/hermes'
|
||||
import {
|
||||
configureHermesRuntime,
|
||||
getAgentRuntimeRegistry,
|
||||
getHermesRuntime,
|
||||
HermesContainerRuntime,
|
||||
} from '../../../../src/lib/agents/hermes'
|
||||
import {
|
||||
getAgentRuntimeRegistry,
|
||||
resetAgentRuntimeRegistry,
|
||||
} from '../../../../src/lib/agents/runtime'
|
||||
import type {
|
||||
@@ -9,7 +9,7 @@ import { dirname, join } from 'node:path'
|
||||
import {
|
||||
buildContainerRuntime,
|
||||
migrateLegacyOpenClawDir,
|
||||
} from '../../../../src/api/services/openclaw/container-runtime-factory'
|
||||
} from '../../../../src/lib/agents/openclaw/container-runtime-factory'
|
||||
import { logger } from '../../../../src/lib/logger'
|
||||
|
||||
describe('container-runtime factory', () => {
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
OPENCLAW_GATEWAY_CONTAINER_NAME,
|
||||
OPENCLAW_IMAGE,
|
||||
} from '@browseros/shared/constants/openclaw'
|
||||
import { ContainerRuntime } from '../../../../src/api/services/openclaw/container-runtime'
|
||||
import { ContainerRuntime } from '../../../../src/lib/agents/openclaw/container-runtime'
|
||||
import { ContainerNameInUseError } from '../../../../src/lib/vm/errors'
|
||||
|
||||
const PROJECT_DIR = '/tmp/openclaw'
|
||||
@@ -7,8 +7,8 @@ import { describe, expect, it } from 'bun:test'
|
||||
import {
|
||||
cleanHistoryUserText,
|
||||
convertOpenClawHistoryToAgentHistory,
|
||||
} from '../../../../src/api/services/openclaw/history-mapper'
|
||||
import type { OpenClawSessionHistory } from '../../../../src/api/services/openclaw/openclaw-http-client'
|
||||
} from '../../../../src/lib/agents/openclaw/history-mapper'
|
||||
import type { OpenClawSessionHistory } from '../../../../src/lib/agents/openclaw/openclaw-http-client'
|
||||
|
||||
describe('cleanHistoryUserText', () => {
|
||||
it('extracts the cron payload and drops the trailer', () => {
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { describe, expect, it, mock } from 'bun:test'
|
||||
import { OPENCLAW_CONTAINER_HOME } from '@browseros/shared/constants/openclaw'
|
||||
import { OpenClawCliClient } from '../../../../src/api/services/openclaw/openclaw-cli-client'
|
||||
import { OpenClawCliClient } from '../../../../src/lib/agents/openclaw/openclaw-cli-client'
|
||||
|
||||
describe('OpenClawCliClient', () => {
|
||||
it('passes real non-interactive onboarding flags through to the upstream cli', async () => {
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
getHostWorkspaceDir,
|
||||
isAgentWorkspaceNameSafe,
|
||||
mergeEnvContent,
|
||||
} from '../../../../src/api/services/openclaw/openclaw-env'
|
||||
} from '../../../../src/lib/agents/openclaw/openclaw-env'
|
||||
|
||||
describe('isAgentWorkspaceNameSafe', () => {
|
||||
it('accepts plain slugs', () => {
|
||||
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
import { afterEach, describe, expect, it, mock } from 'bun:test'
|
||||
import { OpenClawSessionNotFoundError } from '../../../../src/api/services/openclaw/errors'
|
||||
import { OpenClawHttpClient } from '../../../../src/api/services/openclaw/openclaw-http-client'
|
||||
import { OpenClawSessionNotFoundError } from '../../../../src/lib/agents/openclaw/errors'
|
||||
import { OpenClawHttpClient } from '../../../../src/lib/agents/openclaw/openclaw-http-client'
|
||||
|
||||
describe('OpenClawHttpClient', () => {
|
||||
const originalFetch = globalThis.fetch
|
||||
@@ -15,11 +15,11 @@ import {
|
||||
import {
|
||||
resolveSupportedOpenClawProvider,
|
||||
UnsupportedOpenClawProviderError,
|
||||
} from '../../../../src/api/services/openclaw/openclaw-provider-map'
|
||||
} from '../../../../src/lib/agents/openclaw/openclaw-provider-map'
|
||||
import {
|
||||
normalizeBrowserOSChatSessionKey,
|
||||
OpenClawService,
|
||||
} from '../../../../src/api/services/openclaw/openclaw-service'
|
||||
} from '../../../../src/lib/agents/openclaw/openclaw-service'
|
||||
|
||||
type MutableOpenClawService = OpenClawService & {
|
||||
openclawDir: string
|
||||
@@ -8,7 +8,7 @@ import { mkdtempSync } from 'node:fs'
|
||||
import { mkdir, rm, symlink, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { resolveSafeWorkspacePath } from '../../../../src/api/services/openclaw/produced-files-store'
|
||||
import { resolveSafeWorkspacePath } from '../../../../src/lib/agents/openclaw/produced-files-store'
|
||||
|
||||
describe('resolveSafeWorkspacePath', () => {
|
||||
const tempDirs: string[] = []
|
||||
@@ -118,10 +118,10 @@ async function setupApplicationTest() {
|
||||
const apiServer = await import('../src/api/server')
|
||||
const browserModule = await import('../src/browser/browser')
|
||||
const cdpModule = await import('../src/browser/backends/cdp')
|
||||
const openclawService = await import(
|
||||
'../src/api/services/openclaw/openclaw-service'
|
||||
)
|
||||
const runtimeModule = await import('../src/lib/agents/runtime')
|
||||
const openclawService = await import('../src/lib/agents/openclaw')
|
||||
const claudeModule = await import('../src/lib/agents/claude')
|
||||
const codexModule = await import('../src/lib/agents/codex')
|
||||
const hermesModule = await import('../src/lib/agents/hermes')
|
||||
const browserosDir = await import('../src/lib/browseros-dir')
|
||||
const dbModule = await import('../src/lib/db')
|
||||
const identityModule = await import('../src/lib/identity')
|
||||
@@ -209,16 +209,16 @@ async function setupApplicationTest() {
|
||||
|
||||
const hermesExecuteAction = mock(async () => {})
|
||||
const fakeHermesRuntime = { executeAction: hermesExecuteAction } as never
|
||||
spyOn(runtimeModule, 'configureHermesRuntime').mockImplementation(
|
||||
spyOn(hermesModule, 'configureHermesRuntime').mockImplementation(
|
||||
() => fakeHermesRuntime,
|
||||
)
|
||||
spyOn(runtimeModule, 'getHermesRuntime').mockImplementation(
|
||||
spyOn(hermesModule, 'getHermesRuntime').mockImplementation(
|
||||
() => fakeHermesRuntime,
|
||||
)
|
||||
spyOn(runtimeModule, 'configureClaudeRuntime').mockImplementation(
|
||||
spyOn(claudeModule, 'configureClaudeRuntime').mockImplementation(
|
||||
() => ({}) as never,
|
||||
)
|
||||
spyOn(runtimeModule, 'configureCodexRuntime').mockImplementation(
|
||||
spyOn(codexModule, 'configureCodexRuntime').mockImplementation(
|
||||
() => ({}) as never,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user