initial commit
This commit is contained in:
75
api-client/src/utils/config.ts
Normal file
75
api-client/src/utils/config.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @fileoverview Configuration for the OpenClaw API client.
|
||||
*
|
||||
* Centralizes gateway URL, auth token, WebSocket options, and polling intervals.
|
||||
* Designed for Angular environment injection — values can be overridden at build time
|
||||
* or via Angular's environment.ts files.
|
||||
*/
|
||||
|
||||
export interface OpenClawClientConfig {
|
||||
/** Gateway HTTP base URL, e.g. "http://10.60.1.145:18789" */
|
||||
gatewayUrl: string;
|
||||
|
||||
/** Gateway WebSocket URL, e.g. "ws://10.60.1.145:18789" */
|
||||
gatewayWsUrl: string;
|
||||
|
||||
/** Auth token (from OPENCLAW_GATEWAY_TOKEN or configured secret) */
|
||||
authToken: string;
|
||||
|
||||
/** Auth mode — must match gateway.auth.mode in openclaw.json */
|
||||
authMode: 'token' | 'password' | 'none' | 'trusted-proxy';
|
||||
|
||||
/** How often (ms) to poll for session status when WS is not connected */
|
||||
pollIntervalMs: number;
|
||||
|
||||
/** How often (ms) to poll for agent list refresh */
|
||||
agentListPollMs: number;
|
||||
|
||||
/** WebSocket reconnect delay (ms), with exponential backoff */
|
||||
wsReconnectDelayMs: number;
|
||||
|
||||
/** Maximum WebSocket reconnect attempts before giving up */
|
||||
wsMaxReconnectAttempts: number;
|
||||
|
||||
/** Request timeout for HTTP calls (ms) */
|
||||
httpTimeoutMs: number;
|
||||
|
||||
/** Protocol version for WS handshake */
|
||||
protocolVersion: number;
|
||||
|
||||
/** Client identity for WS handshake */
|
||||
clientId: string;
|
||||
|
||||
/** Client version for WS handshake */
|
||||
clientVersion: string;
|
||||
|
||||
/** Client platform for WS handshake */
|
||||
clientPlatform: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default configuration — targets the LAN-bound gateway on port 18789.
|
||||
* Override specific fields via Angular environment injection.
|
||||
*/
|
||||
export const DEFAULT_CONFIG: OpenClawClientConfig = {
|
||||
gatewayUrl: 'http://10.60.1.145:18789',
|
||||
gatewayWsUrl: 'ws://10.60.1.145:18789',
|
||||
authToken: '',
|
||||
authMode: 'token',
|
||||
pollIntervalMs: 5_000,
|
||||
agentListPollMs: 30_000,
|
||||
wsReconnectDelayMs: 1_000,
|
||||
wsMaxReconnectAttempts: 50,
|
||||
httpTimeoutMs: 15_000,
|
||||
protocolVersion: 3,
|
||||
clientId: 'control-center',
|
||||
clientVersion: '0.1.0',
|
||||
clientPlatform: 'web',
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a resolved config by merging partial overrides onto defaults.
|
||||
*/
|
||||
export function createConfig(overrides: Partial<OpenClawClientConfig> = {}): OpenClawClientConfig {
|
||||
return { ...DEFAULT_CONFIG, ...overrides };
|
||||
}
|
||||
6
api-client/src/utils/index.ts
Normal file
6
api-client/src/utils/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* @fileoverview Re-export barrel for utilities.
|
||||
*/
|
||||
|
||||
export * from './config';
|
||||
export * from './status-mapper';
|
||||
178
api-client/src/utils/status-mapper.ts
Normal file
178
api-client/src/utils/status-mapper.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* @fileoverview Status mapping utilities.
|
||||
*
|
||||
* Translates OpenClaw Gateway session statuses and timestamps
|
||||
* into the AgentStatus enum and elapsed-time strings used by AgentCardData.
|
||||
*/
|
||||
|
||||
import { AgentStatus, AgentLifecycleStatus, OpenClawSession, AGENT_ROLES } from '../models/types';
|
||||
|
||||
/**
|
||||
* Map an OpenClaw session status to an AgentStatus for the UI.
|
||||
*
|
||||
* Mapping logic:
|
||||
* - "running" or "streaming" with no tokens yet → ACTIVE (agent processing turn)
|
||||
* - "streaming" with output tokens → THINKING (LLM call in flight)
|
||||
* - "done" → IDLE (agent finished, waiting for next input)
|
||||
* - "error" → ERROR
|
||||
* - "aborted" → IDLE (abort = intentional stop, not an error)
|
||||
* - null/undefined → IDLE (no active session = idle)
|
||||
*/
|
||||
export function mapSessionStatusToAgentStatus(sessionStatus?: string | null): AgentStatus {
|
||||
switch (sessionStatus) {
|
||||
case 'running':
|
||||
return 'active';
|
||||
case 'streaming':
|
||||
return 'thinking';
|
||||
case 'done':
|
||||
return 'idle';
|
||||
case 'error':
|
||||
return 'error';
|
||||
case 'aborted':
|
||||
return 'idle';
|
||||
default:
|
||||
return 'idle';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine lifecycle status for an agent that may not have an active session.
|
||||
* If no session exists, the agent is considered "offline".
|
||||
*/
|
||||
export function determineLifecycleStatus(
|
||||
session?: OpenClawSession | null
|
||||
): AgentLifecycleStatus {
|
||||
if (!session) return 'offline';
|
||||
return mapSessionStatusToAgentStatus(session.status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an OpenClaw session to the lifecycle status used by the UI.
|
||||
* Offline agents are presented as "idle" to the UI.
|
||||
*/
|
||||
export function lifecycleToUiStatus(lifecycle: AgentLifecycleStatus): AgentStatus {
|
||||
return lifecycle === 'offline' ? 'idle' : lifecycle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format elapsed time from a session's startedAt timestamp.
|
||||
* Returns a human-readable string like "04m 12s" or "1h 23m".
|
||||
*
|
||||
* If the session hasn't started or is done, returns undefined.
|
||||
*/
|
||||
export function formatElapsedTime(
|
||||
session?: OpenClawSession | null,
|
||||
nowMs?: number
|
||||
): string | undefined {
|
||||
if (!session?.startedAt) return undefined;
|
||||
const endTime = session.endedAt || nowMs || Date.now();
|
||||
const elapsedMs = endTime - session.startedAt;
|
||||
if (elapsedMs < 0) return undefined;
|
||||
|
||||
const totalSeconds = Math.floor(elapsedMs / 1000);
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}h ${String(minutes).padStart(2, '0')}m`;
|
||||
}
|
||||
return `${String(minutes).padStart(2, '0')}m ${String(seconds).padStart(2, '0')}s`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the channel name from a session key.
|
||||
* Session key format: "agent:<agentId>:<channel>:<kind>:<peerId>"
|
||||
* Or from the session's channel/origin fields.
|
||||
*/
|
||||
export function extractChannelFromSession(session: OpenClawSession): string {
|
||||
// Prefer explicit channel field
|
||||
if (session.channel && session.channel !== 'heartbeat') {
|
||||
return session.channel;
|
||||
}
|
||||
// Fall back to origin provider
|
||||
if (session.origin?.provider) {
|
||||
return session.origin.provider;
|
||||
}
|
||||
// Parse session key: "agent:otto:telegram:direct:8787451565"
|
||||
const parts = session.key.split(':');
|
||||
if (parts.length >= 3 && parts[0] === 'agent') {
|
||||
return parts[2];
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the agent ID from a session key.
|
||||
* Session key format: "agent:<agentId>:..."
|
||||
*/
|
||||
export function extractAgentIdFromSessionKey(sessionKey: string): string {
|
||||
const parts = sessionKey.split(':');
|
||||
if (parts[0] === 'agent' && parts.length >= 2) {
|
||||
return parts[1];
|
||||
}
|
||||
return sessionKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the role description for an agent ID.
|
||||
* Falls back to AGENT_ROLES map, then to a generic string.
|
||||
*/
|
||||
export function getAgentRole(agentId: string): string {
|
||||
return AGENT_ROLES[agentId] || `${agentId} Agent`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate task progress based on session state.
|
||||
*
|
||||
* OpenClaw doesn't natively expose a 0–100 progress percentage.
|
||||
* We derive an approximation:
|
||||
* - "running" session → 10–50% (early in the task, we don't know)
|
||||
* - "streaming" → 60–90% (LLM is responding)
|
||||
* - "done" → 100%
|
||||
* - "error" → whatever progress was made before the error
|
||||
*
|
||||
* This is intentionally coarse. A future enhancement could parse
|
||||
* sub-agent progress or tool call completion ratios.
|
||||
*/
|
||||
export function estimateTaskProgress(session?: OpenClawSession | null): number | undefined {
|
||||
if (!session) return undefined;
|
||||
|
||||
switch (session.status) {
|
||||
case 'running':
|
||||
return 25; // processing, early stage
|
||||
case 'streaming':
|
||||
return 75; // LLM responding
|
||||
case 'done':
|
||||
return 100;
|
||||
case 'error':
|
||||
return undefined; // unknown how far it got
|
||||
case 'aborted':
|
||||
return undefined;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a current-task description from session metadata.
|
||||
* Uses the session label (for cron sessions) or constructs a generic description.
|
||||
*/
|
||||
export function deriveCurrentTask(session?: OpenClawSession | null): string | undefined {
|
||||
if (!session) return undefined;
|
||||
|
||||
// Cron sessions have a label
|
||||
if (session.label) return session.label;
|
||||
|
||||
// Sessions with displayName that isn't generic
|
||||
if (session.displayName && session.displayName !== 'direct' && session.displayName !== 'heartbeat') {
|
||||
return session.displayName;
|
||||
}
|
||||
|
||||
// Active sessions get a generic description
|
||||
if (session.status === 'running' || session.status === 'streaming') {
|
||||
return 'Processing task...';
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
Reference in New Issue
Block a user