@revealui/harnesses 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/{chunk-PG4RAOWS.js → chunk-6WO7SSCX.js} +198 -66
- package/dist/chunk-6WO7SSCX.js.map +1 -0
- package/dist/{chunk-BDA7D725.js → chunk-MO2EXBUG.js} +363 -83
- package/dist/chunk-MO2EXBUG.js.map +1 -0
- package/dist/cli.js +48 -23
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +63 -136
- package/dist/index.js +28 -24
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +151 -0
- package/dist/types/index.js +1 -0
- package/dist/workboard/index.d.ts +44 -3
- package/dist/workboard/index.js +15 -4
- package/package.json +14 -4
- package/dist/chunk-BDA7D725.js.map +0 -1
- package/dist/chunk-JUNNIQS3.js +0 -1
- package/dist/chunk-PG4RAOWS.js.map +0 -1
- /package/dist/{chunk-JUNNIQS3.js.map → types/index.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapters/claude-code-adapter.ts","../src/adapters/copilot-adapter.ts","../src/adapters/cursor-adapter.ts","../src/config/config-sync.ts","../src/config/harness-config-paths.ts","../src/coordinator.ts","../src/detection/auto-detector.ts","../src/registry/harness-registry.ts","../src/server/rpc-server.ts","../src/detection/process-detector.ts"],"sourcesContent":["/**\n * @revealui/harnesses — AI Harness Integration System (Server-side)\n *\n * Adapters, registry, workboard coordination, and JSON-RPC server for\n * integrating AI coding tools (Claude Code, Cursor, Copilot) into the\n * RevealUI development workflow.\n *\n * Pro tier feature: gated behind isFeatureEnabled(\"ai\").\n *\n * @packageDocumentation\n */\n\nimport { isFeatureEnabled } from '@revealui/core/features'\nimport { initializeLicense } from '@revealui/core/license'\nimport { logger } from '@revealui/core/observability/logger'\n\n/** Check whether the harnesses feature is licensed for this installation. */\nexport async function checkHarnessesLicense(): Promise<boolean> {\n await initializeLicense()\n if (!isFeatureEnabled('ai')) {\n logger.warn(\n '[@revealui/harnesses] AI harness integration requires a Pro or Enterprise license. ' +\n 'Visit https://revealui.com/pricing for details.',\n { feature: 'ai' },\n )\n return false\n }\n return true\n}\n\n// Adapters\nexport { ClaudeCodeAdapter } from './adapters/claude-code-adapter.js'\nexport { CopilotAdapter } from './adapters/copilot-adapter.js'\nexport { CursorAdapter } from './adapters/cursor-adapter.js'\n\n// Config\nexport {\n diffAllConfigs,\n diffConfig,\n syncAllConfigs,\n syncConfig,\n validateConfigJson,\n} from './config/config-sync.js'\nexport {\n getConfigurableHarnesses,\n getLocalConfigPath,\n getRootConfigPath,\n} from './config/harness-config-paths.js'\nexport type { CoordinatorOptions } from './coordinator.js'\n// Coordinator\nexport { HarnessCoordinator } from './coordinator.js'\n\n// Detection\nexport { autoDetectHarnesses } from './detection/auto-detector.js'\nexport {\n findAllHarnessProcesses,\n findClaudeCodeSockets,\n findHarnessProcesses,\n findProcesses,\n} from './detection/process-detector.js'\n\n// Registry\nexport { HarnessRegistry } from './registry/harness-registry.js'\n\n// Server\nexport { RpcServer } from './server/rpc-server.js'\nexport type { HarnessAdapter } from './types/adapter.js'\n\n// Types — harness core\nexport type {\n ConfigDiffEntry,\n ConfigSyncDirection,\n ConfigSyncResult,\n HarnessCapabilities,\n HarnessCommand,\n HarnessCommandResult,\n HarnessEvent,\n HarnessInfo,\n HarnessProcessInfo,\n HealthCheckResult,\n} from './types/core.js'\n// Workboard\nexport {\n acquireLock,\n atomicWriteSync,\n deriveSessionId,\n detectSessionType,\n lockPathFor,\n releaseLock,\n withLock,\n withLockAsync,\n WorkboardManager,\n} from './workboard/index.js'\n// Types — session identity\nexport type { SessionType } from './workboard/session-identity.js'\n// Types — workboard protocol\nexport type {\n ConflictResult,\n WorkboardEntry,\n WorkboardSession,\n WorkboardState,\n} from './workboard/workboard-protocol.js'\n","import { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport type { HarnessAdapter } from '../types/adapter.js'\nimport type {\n HarnessCapabilities,\n HarnessCommand,\n HarnessCommandResult,\n HarnessEvent,\n HarnessInfo,\n} from '../types/core.js'\nimport { WorkboardManager } from '../workboard/workboard-manager.js'\n\nconst execFileAsync = promisify(execFile)\n\n/**\n * Adapter for Anthropic Claude Code (CLI: `claude`).\n *\n * Claude Code communicates via its CLI. Config lives at\n * ~/.claude/settings.json and project-level .claude/settings.json.\n * MCP integration is handled separately by @revealui/mcp.\n *\n * Workboard read/write requires REVEALUI_WORKBOARD_PATH to be set to the\n * absolute path of the workboard.md file.\n */\nexport class ClaudeCodeAdapter implements HarnessAdapter {\n readonly id = 'claude-code'\n readonly name = 'Claude Code'\n\n private readonly eventHandlers = new Set<(event: HarnessEvent) => void>()\n private readonly workboardPath: string | undefined\n\n constructor(workboardPath?: string) {\n this.workboardPath = workboardPath ?? process.env.REVEALUI_WORKBOARD_PATH\n }\n\n getCapabilities(): HarnessCapabilities {\n return {\n generateCode: false, // interactive only — no headless CLI interface\n analyzeCode: false, // interactive only — no headless CLI interface\n applyEdit: false, // interactive only — edits are applied inside Claude Code sessions\n applyConfig: false, // config managed interactively via ~/.claude/settings.json\n readWorkboard: this.workboardPath !== undefined,\n writeWorkboard: this.workboardPath !== undefined,\n }\n }\n\n async getInfo(): Promise<HarnessInfo> {\n let version: string | undefined\n try {\n const { stdout } = await execFileAsync('claude', ['--version'], {\n timeout: 5000,\n })\n version = stdout.trim().split('\\n')[0]\n } catch {\n // Not installed or version flag unsupported.\n }\n return { id: this.id, name: this.name, version, capabilities: this.getCapabilities() }\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n await execFileAsync('claude', ['--version'], { timeout: 3000 })\n return true\n } catch {\n return false\n }\n }\n\n notifyRegistered(): void {\n this.emit({ type: 'harness-connected', harnessId: this.id })\n }\n\n notifyUnregistering(): void {\n this.emit({ type: 'harness-disconnected', harnessId: this.id })\n }\n\n async execute(command: HarnessCommand): Promise<HarnessCommandResult> {\n try {\n return await this.executeInner(command)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.emit({ type: 'error', harnessId: this.id, message })\n return { success: false, command: command.type, message }\n }\n }\n\n private async executeInner(command: HarnessCommand): Promise<HarnessCommandResult> {\n switch (command.type) {\n case 'get-status': {\n const available = await this.isAvailable()\n return { success: true, command: command.type, data: { available } }\n }\n case 'get-running-instances': {\n // Claude Code process enumeration is not supported.\n return { success: true, command: command.type, data: [] }\n }\n case 'generate-code':\n case 'analyze-code': {\n return {\n success: false,\n command: command.type,\n message: `${command.type} is not supported — Claude Code operates interactively`,\n }\n }\n case 'apply-config': {\n return {\n success: false,\n command: command.type,\n message: 'Config is managed interactively via ~/.claude/settings.json',\n }\n }\n case 'read-workboard': {\n if (!this.workboardPath) {\n return {\n success: false,\n command: command.type,\n message: 'REVEALUI_WORKBOARD_PATH is not set',\n }\n }\n const manager = new WorkboardManager(this.workboardPath)\n const state = await manager.readAsync()\n return { success: true, command: command.type, data: state }\n }\n case 'update-workboard': {\n if (!this.workboardPath) {\n return {\n success: false,\n command: command.type,\n message: 'REVEALUI_WORKBOARD_PATH is not set',\n }\n }\n const manager = new WorkboardManager(this.workboardPath)\n manager.updateSession(command.sessionId, {\n ...(command.task !== undefined && { task: command.task }),\n ...(command.files !== undefined && { files: command.files.join(', ') }),\n updated: `${new Date().toISOString().slice(0, 16)}Z`,\n })\n return { success: true, command: command.type }\n }\n default: {\n return {\n success: false,\n command: (command as HarnessCommand).type,\n message: `Command not supported by ${this.name}`,\n }\n }\n }\n }\n\n onEvent(handler: (event: HarnessEvent) => void): () => void {\n this.eventHandlers.add(handler)\n return () => this.eventHandlers.delete(handler)\n }\n\n async dispose(): Promise<void> {\n this.eventHandlers.clear()\n }\n\n private emit(event: HarnessEvent): void {\n for (const handler of this.eventHandlers) {\n try {\n handler(event)\n } catch {\n // Swallow subscriber errors — never let a listener crash the adapter\n }\n }\n }\n}\n","import type { HarnessAdapter } from '../types/adapter.js'\nimport type {\n HarnessCapabilities,\n HarnessCommand,\n HarnessCommandResult,\n HarnessEvent,\n HarnessInfo,\n} from '../types/core.js'\n\n/**\n * Stub adapter for GitHub Copilot.\n *\n * Copilot has no standalone CLI — it runs as a VS Code extension.\n * This adapter is a stub for future integration once Copilot exposes\n * a programmatic interface.\n */\nexport class CopilotAdapter implements HarnessAdapter {\n readonly id = 'copilot'\n readonly name = 'GitHub Copilot'\n\n private readonly eventHandlers = new Set<(event: HarnessEvent) => void>()\n\n getCapabilities(): HarnessCapabilities {\n return {\n generateCode: false, // stub — no CLI available\n analyzeCode: false,\n applyEdit: false,\n applyConfig: false,\n readWorkboard: false,\n writeWorkboard: false,\n }\n }\n\n async getInfo(): Promise<HarnessInfo> {\n return { id: this.id, name: this.name, capabilities: this.getCapabilities() }\n }\n\n async isAvailable(): Promise<boolean> {\n // Copilot is a VS Code extension — treat it as unavailable for standalone harness use.\n return false\n }\n\n notifyRegistered(): void {\n this.emit({ type: 'harness-connected', harnessId: this.id })\n }\n\n notifyUnregistering(): void {\n this.emit({ type: 'harness-disconnected', harnessId: this.id })\n }\n\n async execute(command: HarnessCommand): Promise<HarnessCommandResult> {\n return {\n success: false,\n command: command.type,\n message: 'Copilot adapter is a stub — no standalone CLI available',\n }\n }\n\n onEvent(handler: (event: HarnessEvent) => void): () => void {\n this.eventHandlers.add(handler)\n return () => this.eventHandlers.delete(handler)\n }\n\n async dispose(): Promise<void> {\n this.eventHandlers.clear()\n }\n\n private emit(event: HarnessEvent): void {\n for (const handler of this.eventHandlers) {\n try {\n handler(event)\n } catch {\n // Swallow subscriber errors\n }\n }\n }\n}\n","import { execFile } from 'node:child_process'\nimport { promisify } from 'node:util'\nimport type { HarnessAdapter } from '../types/adapter.js'\nimport type {\n HarnessCapabilities,\n HarnessCommand,\n HarnessCommandResult,\n HarnessEvent,\n HarnessInfo,\n} from '../types/core.js'\nimport { WorkboardManager } from '../workboard/workboard-manager.js'\n\nconst execFileAsync = promisify(execFile)\n\n/**\n * Adapter for Cursor IDE.\n *\n * Cursor is a VS Code fork with built-in AI capabilities.\n * This adapter detects Cursor via `cursor --version` and provides\n * workboard read/write when REVEALUI_WORKBOARD_PATH is set.\n */\nexport class CursorAdapter implements HarnessAdapter {\n readonly id = 'cursor'\n readonly name = 'Cursor'\n\n private readonly eventHandlers = new Set<(event: HarnessEvent) => void>()\n private readonly workboardPath: string | undefined\n\n constructor(workboardPath?: string) {\n this.workboardPath = workboardPath ?? process.env.REVEALUI_WORKBOARD_PATH\n }\n\n getCapabilities(): HarnessCapabilities {\n return {\n generateCode: false,\n analyzeCode: false,\n applyEdit: false,\n applyConfig: false,\n readWorkboard: this.workboardPath !== undefined,\n writeWorkboard: this.workboardPath !== undefined,\n }\n }\n\n async getInfo(): Promise<HarnessInfo> {\n let version: string | undefined\n try {\n const { stdout } = await execFileAsync('cursor', ['--version'], {\n timeout: 5000,\n })\n version = stdout.trim().split('\\n')[0]\n } catch {\n // Not installed or version flag unsupported.\n }\n return { id: this.id, name: this.name, version, capabilities: this.getCapabilities() }\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n await execFileAsync('cursor', ['--version'], { timeout: 3000 })\n return true\n } catch {\n return false\n }\n }\n\n notifyRegistered(): void {\n this.emit({ type: 'harness-connected', harnessId: this.id })\n }\n\n notifyUnregistering(): void {\n this.emit({ type: 'harness-disconnected', harnessId: this.id })\n }\n\n async execute(command: HarnessCommand): Promise<HarnessCommandResult> {\n try {\n return await this.executeInner(command)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n this.emit({ type: 'error', harnessId: this.id, message })\n return { success: false, command: command.type, message }\n }\n }\n\n private async executeInner(command: HarnessCommand): Promise<HarnessCommandResult> {\n switch (command.type) {\n case 'get-status': {\n const available = await this.isAvailable()\n return { success: true, command: command.type, data: { available } }\n }\n case 'read-workboard': {\n if (!this.workboardPath) {\n return {\n success: false,\n command: command.type,\n message: 'REVEALUI_WORKBOARD_PATH is not set',\n }\n }\n const manager = new WorkboardManager(this.workboardPath)\n const state = await manager.readAsync()\n return { success: true, command: command.type, data: state }\n }\n case 'update-workboard': {\n if (!this.workboardPath) {\n return {\n success: false,\n command: command.type,\n message: 'REVEALUI_WORKBOARD_PATH is not set',\n }\n }\n const manager = new WorkboardManager(this.workboardPath)\n manager.updateSession(command.sessionId, {\n ...(command.task !== undefined && { task: command.task }),\n ...(command.files !== undefined && { files: command.files.join(', ') }),\n updated: `${new Date().toISOString().slice(0, 16)}Z`,\n })\n return { success: true, command: command.type }\n }\n default: {\n return {\n success: false,\n command: (command as HarnessCommand).type,\n message: `Command not supported by ${this.name}`,\n }\n }\n }\n }\n\n onEvent(handler: (event: HarnessEvent) => void): () => void {\n this.eventHandlers.add(handler)\n return () => this.eventHandlers.delete(handler)\n }\n\n async dispose(): Promise<void> {\n this.eventHandlers.clear()\n }\n\n private emit(event: HarnessEvent): void {\n for (const handler of this.eventHandlers) {\n try {\n handler(event)\n } catch {\n // Swallow subscriber errors — never let a listener crash the adapter\n }\n }\n }\n}\n","import { copyFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport type { ConfigDiffEntry, ConfigSyncDirection, ConfigSyncResult } from '../types/core.js'\nimport {\n getConfigurableHarnesses,\n getLocalConfigPath,\n getRootConfigPath,\n} from './harness-config-paths.js'\n\n/**\n * Syncs harness config between local filesystem and root backup.\n * Mirrors config-sync.ts from packages/editors.\n */\nexport function syncConfig(\n harnessId: string,\n direction: ConfigSyncDirection,\n root?: string,\n): ConfigSyncResult {\n const localPath = getLocalConfigPath(harnessId)\n const rootPath = getRootConfigPath(harnessId, root)\n\n if (!(localPath && rootPath)) {\n return {\n success: false,\n harnessId,\n direction,\n message: `No config path known for harness: ${harnessId}`,\n }\n }\n\n try {\n if (direction === 'pull') {\n // Pull: root → local\n if (!existsSync(rootPath)) {\n return { success: false, harnessId, direction, message: `Root config not found: ${rootPath}` }\n }\n mkdirSync(dirname(localPath), { recursive: true })\n backupIfExists(localPath)\n copyFileSync(rootPath, localPath)\n return { success: true, harnessId, direction, message: `Pulled ${rootPath} → ${localPath}` }\n } else {\n // Push: local → root\n if (!existsSync(localPath)) {\n return {\n success: false,\n harnessId,\n direction,\n message: `Local config not found: ${localPath}`,\n }\n }\n mkdirSync(dirname(rootPath), { recursive: true })\n backupIfExists(rootPath)\n copyFileSync(localPath, rootPath)\n return { success: true, harnessId, direction, message: `Pushed ${localPath} → ${rootPath}` }\n }\n } catch (err) {\n return {\n success: false,\n harnessId,\n direction,\n message: err instanceof Error ? err.message : String(err),\n }\n }\n}\n\n/** Compares local vs root config for a harness. */\nexport function diffConfig(harnessId: string, root?: string): ConfigDiffEntry {\n const localPath = getLocalConfigPath(harnessId)\n const rootPath = getRootConfigPath(harnessId, root)\n\n const localExists = !!localPath && existsSync(localPath)\n const ssdExists = !!rootPath && existsSync(rootPath)\n\n if (!(localExists && ssdExists)) {\n return { harnessId, localExists, ssdExists, identical: false }\n }\n\n try {\n const localContent = readFileSync(localPath, 'utf8')\n const ssdContent = readFileSync(rootPath, 'utf8')\n return { harnessId, localExists, ssdExists, identical: localContent === ssdContent }\n } catch {\n return { harnessId, localExists, ssdExists, identical: false }\n }\n}\n\n/**\n * Sync all known harness configs in a given direction.\n * Returns results for each harness.\n */\nexport function syncAllConfigs(\n direction: ConfigSyncDirection,\n root?: string,\n): ConfigSyncResult[] {\n return getConfigurableHarnesses().map((id) => syncConfig(id, direction, root))\n}\n\n/**\n * Diff all known harness configs.\n * Returns diff entries for each harness.\n */\nexport function diffAllConfigs(root?: string): ConfigDiffEntry[] {\n return getConfigurableHarnesses().map((id) => diffConfig(id, root))\n}\n\n/**\n * Validate that a config file contains parseable JSON.\n * Returns null if valid, or an error message if invalid.\n */\nexport function validateConfigJson(harnessId: string): string | null {\n const localPath = getLocalConfigPath(harnessId)\n if (!localPath) return `No config path known for harness: ${harnessId}`\n if (!existsSync(localPath)) return `Config file not found: ${localPath}`\n\n try {\n const content = readFileSync(localPath, 'utf8')\n JSON.parse(content)\n return null\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n}\n\n/** Create a .bak copy of a file if it exists (non-fatal if backup fails). */\nfunction backupIfExists(filePath: string): void {\n try {\n if (existsSync(filePath)) {\n copyFileSync(filePath, `${filePath}.bak`)\n }\n } catch {\n // Backup is best-effort — don't block the sync\n }\n}\n","import { homedir } from 'node:os'\nimport { join } from 'node:path'\n\n/**\n * Config path mappings for AI harnesses.\n * Mirrors editor-config-paths.ts from packages/editors.\n *\n * Local paths: harness config on this machine.\n * Root paths: backup/sync target on the DevPod (ext4 USB) or LTS (NTFS).\n */\n\nconst HOME = homedir()\nconst REVEALUI_ROOT = process.env.REVEALUI_ROOT ?? join(HOME, '.revealui')\n\nconst LOCAL_CONFIG_PATHS: Record<string, string> = {\n 'claude-code': join(HOME, '.claude', 'settings.json'),\n cursor: join(HOME, '.cursor', 'settings.json'),\n copilot: join(HOME, '.config', 'github-copilot', 'hosts.json'),\n}\n\nconst ROOT_CONFIG_FILES: Record<string, string> = {\n 'claude-code': 'settings.json',\n cursor: 'settings.json',\n copilot: 'hosts.json',\n}\n\n/** Returns the local config file path for a given harness id, or undefined if unknown. */\nexport function getLocalConfigPath(harnessId: string): string | undefined {\n return LOCAL_CONFIG_PATHS[harnessId]\n}\n\n/** Returns the root config file path for a given harness id, or undefined if unknown. */\nexport function getRootConfigPath(harnessId: string, root = REVEALUI_ROOT): string | undefined {\n const file = ROOT_CONFIG_FILES[harnessId]\n if (!file) return undefined\n return join(root, 'harness-configs', harnessId, file)\n}\n\n/** Returns ids of all harnesses with known config paths. */\nexport function getConfigurableHarnesses(): string[] {\n return Object.keys(LOCAL_CONFIG_PATHS)\n}\n","import { join } from 'node:path'\nimport { autoDetectHarnesses } from './detection/auto-detector.js'\nimport { HarnessRegistry } from './registry/harness-registry.js'\nimport { RpcServer } from './server/rpc-server.js'\nimport type { HarnessAdapter } from './types/adapter.js'\nimport type { HealthCheckResult } from './types/core.js'\nimport { deriveSessionId, detectSessionType } from './workboard/session-identity.js'\nimport { WorkboardManager } from './workboard/workboard-manager.js'\n\nexport interface CoordinatorOptions {\n /** Absolute path to the project root (where .claude/workboard.md lives) */\n projectRoot: string\n /** Unix socket path for the RPC server */\n socketPath?: string\n /** Session task description shown in the workboard */\n task?: string\n}\n\n/**\n * HarnessCoordinator — single entry point for harness-to-harness coordination.\n *\n * On start:\n * 1. Auto-detects installed AI harnesses and registers them\n * 2. Registers this session in the workboard\n * 3. Starts the RPC server\n *\n * On stop:\n * 1. Unregisters this session from the workboard\n * 2. Stops the RPC server\n * 3. Disposes all adapters\n */\nexport class HarnessCoordinator {\n private readonly registry = new HarnessRegistry()\n private rpcServer: RpcServer | null = null\n private sessionId: string | null = null\n private readonly workboard: WorkboardManager\n\n constructor(private readonly options: CoordinatorOptions) {\n const workboardPath = join(options.projectRoot, '.claude', 'workboard.md')\n this.workboard = new WorkboardManager(workboardPath)\n }\n\n async start(): Promise<void> {\n // 1. Auto-detect harnesses\n await autoDetectHarnesses(this.registry)\n\n // 2. Register in workboard\n const type = detectSessionType()\n const state = this.workboard.read()\n const existingIds = state.sessions.map((s) => s.id)\n this.sessionId = deriveSessionId(type, existingIds)\n\n const envLabels: Record<string, string> = {\n zed: 'Zed/ACP',\n cursor: 'Cursor',\n terminal: 'WSL/bash',\n }\n\n this.workboard.registerSession({\n id: this.sessionId,\n env: envLabels[type] ?? type,\n started: `${new Date().toISOString().slice(0, 16)}Z`,\n task: this.options.task ?? 'Harness coordination active',\n files: '',\n updated: `${new Date().toISOString().slice(0, 16)}Z`,\n })\n\n // 3. Start RPC server\n const socketPath =\n this.options.socketPath ??\n join(process.env.HOME ?? '/tmp', '.local', 'share', 'revealui', 'harness.sock')\n\n this.rpcServer = new RpcServer(this.registry, socketPath)\n this.rpcServer.setHealthCheck(() => this.healthCheck())\n await this.rpcServer.start()\n }\n\n async stop(): Promise<void> {\n // Unregister from workboard\n if (this.sessionId) {\n this.workboard.unregisterSession(this.sessionId)\n this.workboard.addRecentEntry({\n timestamp: new Date().toISOString().slice(0, 16).replace('T', ' '),\n sessionId: this.sessionId,\n description: `Session ended — ${this.options.task ?? 'harness coordination'}`,\n })\n }\n\n // Stop RPC server\n if (this.rpcServer) {\n await this.rpcServer.stop()\n this.rpcServer = null\n }\n\n // Dispose all adapters\n await this.registry.disposeAll()\n }\n\n /** The registry of detected harnesses. Available after start(). */\n getRegistry(): HarnessRegistry {\n return this.registry\n }\n\n /** The workboard manager. */\n getWorkboard(): WorkboardManager {\n return this.workboard\n }\n\n /** Register a custom adapter (must be called before start()). */\n registerAdapter(adapter: HarnessAdapter): void {\n this.registry.register(adapter)\n }\n\n /** Run a health check across all registered harnesses and the workboard. */\n async healthCheck(): Promise<HealthCheckResult> {\n const diagnostics: string[] = []\n const STALE_MS = 4 * 60 * 60 * 1000\n\n // Check registered harnesses\n const allIds = this.registry.listAll()\n const registeredHarnesses = await Promise.all(\n allIds.map(async (harnessId) => {\n const adapter = this.registry.get(harnessId)\n let available = false\n try {\n available = adapter ? await adapter.isAvailable() : false\n } catch (err) {\n diagnostics.push(\n `${harnessId}: availability check failed — ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n if (!available) diagnostics.push(`${harnessId}: not available`)\n return { harnessId, available }\n }),\n )\n\n // Check workboard\n let readable = false\n let sessionCount = 0\n const staleSessionIds: string[] = []\n try {\n const state = this.workboard.read()\n readable = true\n sessionCount = state.sessions.length\n const now = Date.now()\n for (const s of state.sessions) {\n const ts = Date.parse(s.updated)\n if (!Number.isNaN(ts) && now - ts > STALE_MS) {\n staleSessionIds.push(s.id)\n }\n }\n if (staleSessionIds.length > 0) {\n diagnostics.push(`Stale sessions: ${staleSessionIds.join(', ')}`)\n }\n } catch (err) {\n diagnostics.push(\n `Workboard unreadable: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n\n const healthy =\n registeredHarnesses.some((h) => h.available) && readable && staleSessionIds.length === 0\n\n return {\n healthy,\n timestamp: new Date().toISOString(),\n registeredHarnesses,\n workboard: { readable, sessionCount, staleSessionIds },\n diagnostics,\n }\n }\n}\n","import { ClaudeCodeAdapter } from '../adapters/claude-code-adapter.js'\nimport { CopilotAdapter } from '../adapters/copilot-adapter.js'\nimport { CursorAdapter } from '../adapters/cursor-adapter.js'\nimport type { HarnessRegistry } from '../registry/harness-registry.js'\n\n/**\n * Detects available AI harnesses and registers them in the registry.\n * Mirrors autoDetectEditors from packages/editors.\n *\n * Creates an adapter for each known harness, checks isAvailable(),\n * and registers those that respond. Unavailable adapters are disposed.\n */\nexport async function autoDetectHarnesses(registry: HarnessRegistry): Promise<string[]> {\n const candidates = [new ClaudeCodeAdapter(), new CursorAdapter(), new CopilotAdapter()]\n\n const registered: string[] = []\n\n await Promise.all(\n candidates.map(async (adapter) => {\n try {\n if (await adapter.isAvailable()) {\n registry.register(adapter)\n registered.push(adapter.id)\n } else {\n await adapter.dispose()\n }\n } catch {\n await adapter.dispose()\n }\n }),\n )\n\n return registered\n}\n","import type { HarnessAdapter } from '../types/adapter.js'\n\n/**\n * Manages the lifecycle of HarnessAdapter instances.\n * Mirrors EditorRegistry from packages/editors.\n */\nexport class HarnessRegistry {\n private readonly adapters = new Map<string, HarnessAdapter>()\n\n /** Register an adapter. Throws if an adapter with the same id already exists. */\n register(adapter: HarnessAdapter): void {\n if (this.adapters.has(adapter.id)) {\n throw new Error(`Harness adapter already registered: ${adapter.id}`)\n }\n this.adapters.set(adapter.id, adapter)\n adapter.notifyRegistered?.()\n }\n\n /** Unregister an adapter, disposing it in the process. */\n async unregister(id: string): Promise<void> {\n const adapter = this.adapters.get(id)\n if (adapter) {\n adapter.notifyUnregistering?.()\n await adapter.dispose()\n this.adapters.delete(id)\n }\n }\n\n /** Retrieve an adapter by id. */\n get(id: string): HarnessAdapter | undefined {\n return this.adapters.get(id)\n }\n\n /** List all registered adapter ids. */\n listAll(): string[] {\n return Array.from(this.adapters.keys())\n }\n\n /** List ids of adapters that report isAvailable() === true. */\n async listAvailable(): Promise<string[]> {\n const results = await Promise.all(\n Array.from(this.adapters.entries()).map(async ([id, adapter]) => ({\n id,\n available: await adapter.isAvailable(),\n })),\n )\n return results.filter((r) => r.available).map((r) => r.id)\n }\n\n /** Dispose all adapters and clear the registry. */\n async disposeAll(): Promise<void> {\n await Promise.all(Array.from(this.adapters.values()).map((a) => a.dispose()))\n this.adapters.clear()\n }\n}\n","import { createServer } from 'node:net'\nimport { diffConfig, syncConfig } from '../config/config-sync.js'\nimport { findHarnessProcesses } from '../detection/process-detector.js'\nimport type { HarnessRegistry } from '../registry/harness-registry.js'\nimport type { ConfigSyncDirection } from '../types/core.js'\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0'\n id: number | string | null\n method: string\n params?: unknown\n}\n\ninterface JsonRpcResponse {\n jsonrpc: '2.0'\n id: number | string | null\n result?: unknown\n error?: { code: number; message: string; data?: unknown }\n}\n\nconst ERR_PARSE = -32700\nconst ERR_INVALID_PARAMS = -32602\nconst ERR_METHOD_NOT_FOUND = -32601\nconst ERR_INTERNAL = -32603\n\n/**\n * JSON-RPC 2.0 server over a Unix domain socket.\n * Mirrors RpcServer from packages/editors.\n *\n * Methods:\n * harness.list → HarnessInfo[]\n * harness.execute → HarnessCommandResult\n * harness.info → HarnessInfo\n * harness.listRunning → HarnessProcessInfo[]\n * harness.syncConfig → ConfigSyncResult\n * harness.diffConfig → ConfigDiffEntry\n * harness.health → HealthCheckResult\n */\nexport class RpcServer {\n private server = createServer()\n private healthCheckFn: (() => Promise<unknown>) | null = null\n\n constructor(\n private readonly registry: HarnessRegistry,\n private readonly socketPath: string,\n ) {\n this.server.on('connection', (socket) => {\n let buffer = ''\n socket.on('data', (chunk) => {\n buffer += chunk.toString()\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n this.handleLine(line.trim(), (response) => {\n socket.write(`${JSON.stringify(response)}\\n`)\n })\n }\n })\n })\n }\n\n private handleLine(line: string, reply: (r: JsonRpcResponse) => void): void {\n let req: JsonRpcRequest\n try {\n req = JSON.parse(line) as JsonRpcRequest\n } catch {\n reply({ jsonrpc: '2.0', id: null, error: { code: ERR_PARSE, message: 'Parse error' } })\n return\n }\n\n this.dispatch(req)\n .then(reply)\n .catch((err) => {\n reply({\n jsonrpc: '2.0',\n id: req.id,\n error: { code: ERR_INTERNAL, message: err instanceof Error ? err.message : String(err) },\n })\n })\n }\n\n private async dispatch(req: JsonRpcRequest): Promise<JsonRpcResponse> {\n const { id, method, params } = req\n const p = (params ?? {}) as Record<string, unknown>\n\n switch (method) {\n case 'harness.list': {\n const ids = await this.registry.listAvailable()\n const infos = await Promise.all(ids.map((id) => this.registry.get(id)?.getInfo()))\n return { jsonrpc: '2.0', id, result: infos }\n }\n\n case 'harness.info': {\n const harnessId = p.harnessId as string | undefined\n if (!harnessId) {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: ERR_INVALID_PARAMS, message: 'harnessId required' },\n }\n }\n const adapter = this.registry.get(harnessId)\n if (!adapter) {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: ERR_INVALID_PARAMS, message: `Harness not found: ${harnessId}` },\n }\n }\n return { jsonrpc: '2.0', id, result: await adapter.getInfo() }\n }\n\n case 'harness.execute': {\n const harnessId = p.harnessId as string | undefined\n const command = p.command as Record<string, unknown> | undefined\n if (!(harnessId && command)) {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: ERR_INVALID_PARAMS, message: 'harnessId and command required' },\n }\n }\n const adapter = this.registry.get(harnessId)\n if (!adapter) {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: ERR_INVALID_PARAMS, message: `Harness not found: ${harnessId}` },\n }\n }\n const result = await adapter.execute(command as Parameters<typeof adapter.execute>[0])\n return { jsonrpc: '2.0', id, result }\n }\n\n case 'harness.syncConfig': {\n const harnessId = p.harnessId as string | undefined\n const direction = p.direction as ConfigSyncDirection | undefined\n if (!(harnessId && direction)) {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: ERR_INVALID_PARAMS, message: 'harnessId and direction required' },\n }\n }\n return { jsonrpc: '2.0', id, result: syncConfig(harnessId, direction) }\n }\n\n case 'harness.diffConfig': {\n const harnessId = p.harnessId as string | undefined\n if (!harnessId) {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: ERR_INVALID_PARAMS, message: 'harnessId required' },\n }\n }\n return { jsonrpc: '2.0', id, result: diffConfig(harnessId) }\n }\n\n case 'harness.listRunning': {\n const harnessId = p.harnessId as string | undefined\n if (!harnessId) {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: ERR_INVALID_PARAMS, message: 'harnessId required' },\n }\n }\n const processes = await findHarnessProcesses(harnessId)\n return { jsonrpc: '2.0', id, result: processes }\n }\n\n case 'harness.health': {\n if (!this.healthCheckFn) {\n return {\n jsonrpc: '2.0',\n id,\n error: { code: ERR_INTERNAL, message: 'Health check not configured' },\n }\n }\n const health = await this.healthCheckFn()\n return { jsonrpc: '2.0', id, result: health }\n }\n\n default:\n return {\n jsonrpc: '2.0',\n id,\n error: { code: ERR_METHOD_NOT_FOUND, message: `Method not found: ${method}` },\n }\n }\n }\n\n /** Set the health check function (called by coordinator after construction). */\n setHealthCheck(fn: () => Promise<unknown>): void {\n this.healthCheckFn = fn\n }\n\n start(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server.listen(this.socketPath, () => resolve())\n this.server.once('error', reject)\n })\n }\n\n stop(): Promise<void> {\n return new Promise((resolve) => this.server.close(() => resolve()))\n }\n}\n","import { execFile } from 'node:child_process'\nimport { readdir } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport { promisify } from 'node:util'\nimport type { HarnessProcessInfo } from '../types/core.js'\n\nconst execFileAsync = promisify(execFile)\n\n/** Finds running processes matching a pattern using pgrep. */\nexport async function findProcesses(pattern: string): Promise<{ pid: number; command: string }[]> {\n try {\n const { stdout } = await execFileAsync('pgrep', ['-a', pattern], { timeout: 3000 })\n return stdout\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map((line) => {\n const spaceIdx = line.indexOf(' ')\n const pid = parseInt(line.slice(0, spaceIdx), 10)\n const command = line.slice(spaceIdx + 1)\n return { pid, command }\n })\n } catch {\n return []\n }\n}\n\nconst HARNESS_PROCESS_PATTERNS: Record<string, string[]> = {\n 'claude-code': ['claude'],\n cursor: ['cursor', 'Cursor'],\n copilot: ['copilot'],\n}\n\n/** Finds running process instances for a specific harness. */\nexport async function findHarnessProcesses(harnessId: string): Promise<HarnessProcessInfo[]> {\n const patterns = HARNESS_PROCESS_PATTERNS[harnessId]\n if (!patterns) return []\n\n const results = await Promise.all(patterns.map((p) => findProcesses(p)))\n return results.flat().map((p) => ({ ...p, harnessId }))\n}\n\n/** Finds running processes for all known harnesses. */\nexport async function findAllHarnessProcesses(): Promise<HarnessProcessInfo[]> {\n const results = await Promise.all(\n Object.keys(HARNESS_PROCESS_PATTERNS).map((id) => findHarnessProcesses(id)),\n )\n return results.flat()\n}\n\n/** Finds Claude Code Unix socket files (used for IPC). */\nexport async function findClaudeCodeSockets(): Promise<string[]> {\n const dirs = [\n '/tmp',\n process.env.XDG_RUNTIME_DIR,\n join(process.env.HOME ?? '/tmp', '.claude'),\n ].filter(Boolean) as string[]\n\n const sockets: string[] = []\n for (const dir of dirs) {\n try {\n const entries = await readdir(dir)\n for (const entry of entries) {\n if (/^claude.*\\.sock$/.test(entry)) {\n sockets.push(join(dir, entry))\n }\n }\n } catch {\n // Directory not accessible.\n }\n }\n return sockets\n}\n"],"mappings":"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","names":["execFile","promisify","execFileAsync","promisify","execFile","join","execFile","join","promisify","execFileAsync","id","join"]}
|
package/dist/cli.js
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
HarnessCoordinator
|
|
4
|
-
|
|
3
|
+
HarnessCoordinator,
|
|
4
|
+
checkHarnessesLicense
|
|
5
|
+
} from "./chunk-MO2EXBUG.js";
|
|
5
6
|
import {
|
|
6
7
|
WorkboardManager
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6WO7SSCX.js";
|
|
8
9
|
|
|
9
10
|
// src/cli.ts
|
|
10
11
|
import { createConnection } from "net";
|
|
11
12
|
import { homedir } from "os";
|
|
12
13
|
import { join } from "path";
|
|
13
|
-
import { isFeatureEnabled } from "@revealui/core/features";
|
|
14
|
-
import { initializeLicense } from "@revealui/core/license";
|
|
15
|
-
async function checkLicense() {
|
|
16
|
-
await initializeLicense();
|
|
17
|
-
return isFeatureEnabled("harnesses");
|
|
18
|
-
}
|
|
19
14
|
var DEFAULT_SOCKET = join(homedir(), ".local", "share", "revealui", "harness.sock");
|
|
20
15
|
var DEFAULT_PROJECT = process.cwd();
|
|
21
16
|
var [, , command, ...args] = process.argv;
|
|
@@ -52,15 +47,14 @@ async function rpcCall(method, params = {}) {
|
|
|
52
47
|
});
|
|
53
48
|
}
|
|
54
49
|
async function main() {
|
|
50
|
+
if (!await checkHarnessesLicense()) {
|
|
51
|
+
process.stderr.write(
|
|
52
|
+
"\u26A0 @revealui/harnesses requires a Pro license. Visit https://revealui.com/pricing\n"
|
|
53
|
+
);
|
|
54
|
+
process.exit(2);
|
|
55
|
+
}
|
|
55
56
|
switch (command) {
|
|
56
57
|
case "start": {
|
|
57
|
-
const licensed = await checkLicense();
|
|
58
|
-
if (!licensed) {
|
|
59
|
-
process.stderr.write(
|
|
60
|
-
"\u26A0 @revealui/harnesses requires a Pro license. Visit https://revealui.com/pricing\n"
|
|
61
|
-
);
|
|
62
|
-
process.exit(2);
|
|
63
|
-
}
|
|
64
58
|
const projectIdx = args.indexOf("--project");
|
|
65
59
|
const projectRoot = projectIdx >= 0 ? args[projectIdx + 1] ?? DEFAULT_PROJECT : DEFAULT_PROJECT;
|
|
66
60
|
const coordinator = new HarnessCoordinator({
|
|
@@ -138,13 +132,6 @@ async function main() {
|
|
|
138
132
|
}
|
|
139
133
|
case "coordinate": {
|
|
140
134
|
if (args.includes("--init")) {
|
|
141
|
-
const licensed = await checkLicense();
|
|
142
|
-
if (!licensed) {
|
|
143
|
-
process.stderr.write(
|
|
144
|
-
"\u26A0 @revealui/harnesses requires a Pro license. Visit https://revealui.com/pricing\n"
|
|
145
|
-
);
|
|
146
|
-
process.exit(2);
|
|
147
|
-
}
|
|
148
135
|
const pathIdx = args.indexOf("--init");
|
|
149
136
|
const projectRoot = args[pathIdx + 1] ?? DEFAULT_PROJECT;
|
|
150
137
|
const coordinator = new HarnessCoordinator({ projectRoot, task: "Coordinate harnesses" });
|
|
@@ -174,6 +161,43 @@ async function main() {
|
|
|
174
161
|
}
|
|
175
162
|
break;
|
|
176
163
|
}
|
|
164
|
+
case "health": {
|
|
165
|
+
try {
|
|
166
|
+
const result = await rpcCall("harness.health");
|
|
167
|
+
process.stdout.write(`Health: ${result.healthy ? "HEALTHY" : "UNHEALTHY"}
|
|
168
|
+
`);
|
|
169
|
+
process.stdout.write(`Harnesses:
|
|
170
|
+
`);
|
|
171
|
+
for (const h of result.registeredHarnesses) {
|
|
172
|
+
process.stdout.write(` ${h.harnessId}: ${h.available ? "available" : "unavailable"}
|
|
173
|
+
`);
|
|
174
|
+
}
|
|
175
|
+
process.stdout.write(
|
|
176
|
+
`Workboard: ${result.workboard.readable ? "readable" : "unreadable"}, ${result.workboard.sessionCount} session(s)
|
|
177
|
+
`
|
|
178
|
+
);
|
|
179
|
+
if (result.workboard.staleSessionIds.length > 0) {
|
|
180
|
+
process.stdout.write(
|
|
181
|
+
` Stale: ${result.workboard.staleSessionIds.join(", ")}
|
|
182
|
+
`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (result.diagnostics.length > 0) {
|
|
186
|
+
process.stdout.write(`Diagnostics:
|
|
187
|
+
`);
|
|
188
|
+
for (const d of result.diagnostics) {
|
|
189
|
+
process.stdout.write(` ${d}
|
|
190
|
+
`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (!result.healthy) process.exit(1);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
process.stderr.write(`RPC error: ${err instanceof Error ? err.message : String(err)}
|
|
196
|
+
`);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
177
201
|
default:
|
|
178
202
|
process.stdout.write(`revealui-harnesses \u2014 AI harness coordination for RevealUI
|
|
179
203
|
|
|
@@ -182,6 +206,7 @@ Commands:
|
|
|
182
206
|
status List available harnesses (requires daemon)
|
|
183
207
|
list List harnesses in TSV format (requires daemon)
|
|
184
208
|
sync <id> <push|pull> Sync harness config to/from SSD (requires daemon)
|
|
209
|
+
health Run health check (requires daemon)
|
|
185
210
|
coordinate [--project <path>] Print workboard state
|
|
186
211
|
coordinate --init [<path>] Register + start daemon
|
|
187
212
|
`);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * revealui-harnesses — CLI daemon and RPC client for AI harness coordination.\n *\n * Commands:\n * start [--project <path>] Detect harnesses, register in workboard, start RPC server\n * status List available harnesses via RPC\n * list List harnesses in TSV format\n * sync <harnessId> <push|pull> Sync harness config to/from SSD\n * coordinate [--print] Print current workboard state\n * coordinate --init <path> Register this session in the workboard and start daemon\n *\n * License: Pro tier required (isFeatureEnabled(\"harnesses\"))\n */\n\nimport { createConnection } from 'node:net'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { isFeatureEnabled } from '@revealui/core/features'\nimport { initializeLicense } from '@revealui/core/license'\nimport { HarnessCoordinator } from './coordinator.js'\nimport { WorkboardManager } from './workboard/workboard-manager.js'\n\nasync function checkLicense(): Promise<boolean> {\n await initializeLicense()\n return isFeatureEnabled('harnesses')\n}\n\nconst DEFAULT_SOCKET = join(homedir(), '.local', 'share', 'revealui', 'harness.sock')\nconst DEFAULT_PROJECT = process.cwd()\n\nconst [, , command, ...args] = process.argv\n\nasync function rpcCall(method: string, params: unknown = {}): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const socket = createConnection(DEFAULT_SOCKET)\n let buffer = ''\n socket.on('connect', () => {\n const req = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })\n socket.write(`${req}\\n`)\n })\n socket.on('data', (chunk) => {\n buffer += chunk.toString()\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n if (!line.trim()) continue\n try {\n const resp = JSON.parse(line) as { result?: unknown; error?: { message: string } }\n socket.destroy()\n if (resp.error) reject(new Error(resp.error.message))\n else resolve(resp.result)\n } catch {\n reject(new Error(`Invalid JSON: ${line}`))\n }\n }\n })\n socket.on('error', reject)\n setTimeout(() => {\n socket.destroy()\n reject(new Error('RPC timeout'))\n }, 5000)\n })\n}\n\nasync function main() {\n switch (command) {\n case 'start': {\n const licensed = await checkLicense()\n if (!licensed) {\n process.stderr.write(\n '⚠ @revealui/harnesses requires a Pro license. Visit https://revealui.com/pricing\\n',\n )\n process.exit(2)\n }\n\n const projectIdx = args.indexOf('--project')\n const projectRoot =\n projectIdx >= 0 ? (args[projectIdx + 1] ?? DEFAULT_PROJECT) : DEFAULT_PROJECT\n\n const coordinator = new HarnessCoordinator({\n projectRoot,\n task: 'Harness coordination active',\n })\n\n await coordinator.start()\n const ids = await coordinator.getRegistry().listAvailable()\n process.stdout.write(`✓ Detected harnesses: ${ids.length > 0 ? ids.join(', ') : 'none'}\\n`)\n process.stdout.write(`✓ RPC server listening on ${DEFAULT_SOCKET}\\n`)\n process.stdout.write(`✓ Session registered in workboard\\n`)\n\n const shutdown = async () => {\n await coordinator.stop()\n process.exit(0)\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n break\n }\n\n case 'status': {\n try {\n const infos = (await rpcCall('harness.list')) as Array<{\n id: string\n name: string\n version?: string\n }>\n if (infos.length === 0) {\n process.stdout.write('No harnesses available\\n')\n } else {\n for (const info of infos) {\n process.stdout.write(\n `${info.id}\\t${info.name}${info.version ? `\\t${info.version}` : ''}\\n`,\n )\n }\n }\n } catch (err) {\n process.stderr.write(`RPC error: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n }\n break\n }\n\n case 'list': {\n try {\n const infos = (await rpcCall('harness.list')) as Array<{ id: string; name: string }>\n for (const info of infos) {\n process.stdout.write(`${info.id}\\t${info.name}\\n`)\n }\n } catch (err) {\n process.stderr.write(`RPC error: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n }\n break\n }\n\n case 'sync': {\n const [harnessId, direction] = args\n if (!(harnessId && direction && ['push', 'pull'].includes(direction))) {\n process.stderr.write('Usage: revealui-harnesses sync <harnessId> <push|pull>\\n')\n process.exit(1)\n }\n try {\n const result = (await rpcCall('harness.syncConfig', { harnessId, direction })) as {\n success: boolean\n message?: string\n }\n process.stdout.write(result.success ? `✓ ${result.message}\\n` : `✗ ${result.message}\\n`)\n if (!result.success) process.exit(1)\n } catch (err) {\n process.stderr.write(`RPC error: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n }\n break\n }\n\n case 'coordinate': {\n if (args.includes('--init')) {\n const licensed = await checkLicense()\n if (!licensed) {\n process.stderr.write(\n '⚠ @revealui/harnesses requires a Pro license. Visit https://revealui.com/pricing\\n',\n )\n process.exit(2)\n }\n\n const pathIdx = args.indexOf('--init')\n const projectRoot = args[pathIdx + 1] ?? DEFAULT_PROJECT\n const coordinator = new HarnessCoordinator({ projectRoot, task: 'Coordinate harnesses' })\n await coordinator.start()\n const workboard = coordinator.getWorkboard()\n const conflicts = workboard.checkConflicts('', [])\n process.stdout.write(\n `✓ Session registered. Conflicts: ${conflicts.clean ? 'none' : conflicts.conflicts.length}\\n`,\n )\n await coordinator.stop()\n } else {\n // --print: dump current workboard to stdout\n const projectRoot = args[args.indexOf('--project') + 1] ?? DEFAULT_PROJECT\n const workboardPath = join(projectRoot, '.claude', 'workboard.md')\n const manager = new WorkboardManager(workboardPath)\n const state = manager.read()\n process.stdout.write(`Sessions (${state.sessions.length}):\\n`)\n for (const s of state.sessions) {\n const stale = Date.now() - new Date(s.updated).getTime() > 4 * 60 * 60 * 1000\n process.stdout.write(` ${s.id} [${s.env}] — ${s.task}${stale ? ' (STALE)' : ''}\\n`)\n if (s.files) process.stdout.write(` files: ${s.files}\\n`)\n }\n if (state.sessions.length === 0) process.stdout.write(' (no active sessions)\\n')\n }\n break\n }\n\n default:\n process.stdout.write(`revealui-harnesses — AI harness coordination for RevealUI\n\nCommands:\n start [--project <path>] Start daemon (detects harnesses, registers session)\n status List available harnesses (requires daemon)\n list List harnesses in TSV format (requires daemon)\n sync <id> <push|pull> Sync harness config to/from SSD (requires daemon)\n coordinate [--project <path>] Print workboard state\n coordinate --init [<path>] Register + start daemon\n`)\n break\n }\n}\n\nmain().catch((err) => {\n process.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n})\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * revealui-harnesses — CLI daemon and RPC client for AI harness coordination.\n *\n * Commands:\n * start [--project <path>] Detect harnesses, register in workboard, start RPC server\n * status List available harnesses via RPC\n * list List harnesses in TSV format\n * sync <harnessId> <push|pull> Sync harness config to/from SSD\n * coordinate [--print] Print current workboard state\n * coordinate --init <path> Register this session in the workboard and start daemon\n *\n * License: Pro tier required (isFeatureEnabled(\"harnesses\"))\n */\n\nimport { createConnection } from 'node:net'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { HarnessCoordinator } from './coordinator.js'\nimport { checkHarnessesLicense } from './index.js'\nimport { WorkboardManager } from './workboard/workboard-manager.js'\n\nconst DEFAULT_SOCKET = join(homedir(), '.local', 'share', 'revealui', 'harness.sock')\nconst DEFAULT_PROJECT = process.cwd()\n\nconst [, , command, ...args] = process.argv\n\nasync function rpcCall(method: string, params: unknown = {}): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const socket = createConnection(DEFAULT_SOCKET)\n let buffer = ''\n socket.on('connect', () => {\n const req = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })\n socket.write(`${req}\\n`)\n })\n socket.on('data', (chunk) => {\n buffer += chunk.toString()\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) {\n if (!line.trim()) continue\n try {\n const resp = JSON.parse(line) as { result?: unknown; error?: { message: string } }\n socket.destroy()\n if (resp.error) reject(new Error(resp.error.message))\n else resolve(resp.result)\n } catch {\n reject(new Error(`Invalid JSON: ${line}`))\n }\n }\n })\n socket.on('error', reject)\n setTimeout(() => {\n socket.destroy()\n reject(new Error('RPC timeout'))\n }, 5000)\n })\n}\n\nasync function main() {\n if (!(await checkHarnessesLicense())) {\n process.stderr.write(\n '⚠ @revealui/harnesses requires a Pro license. Visit https://revealui.com/pricing\\n',\n )\n process.exit(2)\n }\n\n switch (command) {\n case 'start': {\n const projectIdx = args.indexOf('--project')\n const projectRoot =\n projectIdx >= 0 ? (args[projectIdx + 1] ?? DEFAULT_PROJECT) : DEFAULT_PROJECT\n\n const coordinator = new HarnessCoordinator({\n projectRoot,\n task: 'Harness coordination active',\n })\n\n await coordinator.start()\n const ids = await coordinator.getRegistry().listAvailable()\n process.stdout.write(`✓ Detected harnesses: ${ids.length > 0 ? ids.join(', ') : 'none'}\\n`)\n process.stdout.write(`✓ RPC server listening on ${DEFAULT_SOCKET}\\n`)\n process.stdout.write(`✓ Session registered in workboard\\n`)\n\n const shutdown = async () => {\n await coordinator.stop()\n process.exit(0)\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n break\n }\n\n case 'status': {\n try {\n const infos = (await rpcCall('harness.list')) as Array<{\n id: string\n name: string\n version?: string\n }>\n if (infos.length === 0) {\n process.stdout.write('No harnesses available\\n')\n } else {\n for (const info of infos) {\n process.stdout.write(\n `${info.id}\\t${info.name}${info.version ? `\\t${info.version}` : ''}\\n`,\n )\n }\n }\n } catch (err) {\n process.stderr.write(`RPC error: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n }\n break\n }\n\n case 'list': {\n try {\n const infos = (await rpcCall('harness.list')) as Array<{ id: string; name: string }>\n for (const info of infos) {\n process.stdout.write(`${info.id}\\t${info.name}\\n`)\n }\n } catch (err) {\n process.stderr.write(`RPC error: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n }\n break\n }\n\n case 'sync': {\n const [harnessId, direction] = args\n if (!(harnessId && direction && ['push', 'pull'].includes(direction))) {\n process.stderr.write('Usage: revealui-harnesses sync <harnessId> <push|pull>\\n')\n process.exit(1)\n }\n try {\n const result = (await rpcCall('harness.syncConfig', { harnessId, direction })) as {\n success: boolean\n message?: string\n }\n process.stdout.write(result.success ? `✓ ${result.message}\\n` : `✗ ${result.message}\\n`)\n if (!result.success) process.exit(1)\n } catch (err) {\n process.stderr.write(`RPC error: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n }\n break\n }\n\n case 'coordinate': {\n if (args.includes('--init')) {\n const pathIdx = args.indexOf('--init')\n const projectRoot = args[pathIdx + 1] ?? DEFAULT_PROJECT\n const coordinator = new HarnessCoordinator({ projectRoot, task: 'Coordinate harnesses' })\n await coordinator.start()\n const workboard = coordinator.getWorkboard()\n const conflicts = workboard.checkConflicts('', [])\n process.stdout.write(\n `✓ Session registered. Conflicts: ${conflicts.clean ? 'none' : conflicts.conflicts.length}\\n`,\n )\n await coordinator.stop()\n } else {\n // --print: dump current workboard to stdout\n const projectRoot = args[args.indexOf('--project') + 1] ?? DEFAULT_PROJECT\n const workboardPath = join(projectRoot, '.claude', 'workboard.md')\n const manager = new WorkboardManager(workboardPath)\n const state = manager.read()\n process.stdout.write(`Sessions (${state.sessions.length}):\\n`)\n for (const s of state.sessions) {\n const stale = Date.now() - new Date(s.updated).getTime() > 4 * 60 * 60 * 1000\n process.stdout.write(` ${s.id} [${s.env}] — ${s.task}${stale ? ' (STALE)' : ''}\\n`)\n if (s.files) process.stdout.write(` files: ${s.files}\\n`)\n }\n if (state.sessions.length === 0) process.stdout.write(' (no active sessions)\\n')\n }\n break\n }\n\n case 'health': {\n try {\n const result = (await rpcCall('harness.health')) as {\n healthy: boolean\n registeredHarnesses: Array<{ harnessId: string; available: boolean }>\n workboard: { readable: boolean; sessionCount: number; staleSessionIds: string[] }\n diagnostics: string[]\n }\n process.stdout.write(`Health: ${result.healthy ? 'HEALTHY' : 'UNHEALTHY'}\\n`)\n process.stdout.write(`Harnesses:\\n`)\n for (const h of result.registeredHarnesses) {\n process.stdout.write(` ${h.harnessId}: ${h.available ? 'available' : 'unavailable'}\\n`)\n }\n process.stdout.write(\n `Workboard: ${result.workboard.readable ? 'readable' : 'unreadable'}, ${result.workboard.sessionCount} session(s)\\n`,\n )\n if (result.workboard.staleSessionIds.length > 0) {\n process.stdout.write(\n ` Stale: ${result.workboard.staleSessionIds.join(', ')}\\n`,\n )\n }\n if (result.diagnostics.length > 0) {\n process.stdout.write(`Diagnostics:\\n`)\n for (const d of result.diagnostics) {\n process.stdout.write(` ${d}\\n`)\n }\n }\n if (!result.healthy) process.exit(1)\n } catch (err) {\n process.stderr.write(`RPC error: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n }\n break\n }\n\n default:\n process.stdout.write(`revealui-harnesses — AI harness coordination for RevealUI\n\nCommands:\n start [--project <path>] Start daemon (detects harnesses, registers session)\n status List available harnesses (requires daemon)\n list List harnesses in TSV format (requires daemon)\n sync <id> <push|pull> Sync harness config to/from SSD (requires daemon)\n health Run health check (requires daemon)\n coordinate [--project <path>] Print workboard state\n coordinate --init [<path>] Register + start daemon\n`)\n break\n }\n}\n\nmain().catch((err) => {\n process.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n})\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,132 +1,6 @@
|
|
|
1
|
+
import { HarnessAdapter, HarnessCapabilities, HarnessInfo, HarnessCommand, HarnessCommandResult, HarnessEvent, ConfigDiffEntry, ConfigSyncDirection, ConfigSyncResult, HealthCheckResult, HarnessProcessInfo } from './types/index.js';
|
|
1
2
|
import { WorkboardManager } from './workboard/index.js';
|
|
2
|
-
export { ConflictResult, SessionType, WorkboardEntry, WorkboardSession, WorkboardState, deriveSessionId, detectSessionType } from './workboard/index.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Core types for the AI harness integration system.
|
|
6
|
-
* Mirrors packages/editors/src/types/core.ts for the harness domain.
|
|
7
|
-
*/
|
|
8
|
-
interface HarnessCapabilities {
|
|
9
|
-
/** Can generate code from a prompt */
|
|
10
|
-
generateCode: boolean;
|
|
11
|
-
/** Can analyze/explain existing code */
|
|
12
|
-
analyzeCode: boolean;
|
|
13
|
-
/** Can apply inline code edits */
|
|
14
|
-
applyEdit: boolean;
|
|
15
|
-
/** Can sync configuration files */
|
|
16
|
-
applyConfig: boolean;
|
|
17
|
-
/** Can query workboard state */
|
|
18
|
-
readWorkboard: boolean;
|
|
19
|
-
/** Can write workboard state */
|
|
20
|
-
writeWorkboard: boolean;
|
|
21
|
-
}
|
|
22
|
-
type ConfigSyncDirection = 'push' | 'pull';
|
|
23
|
-
type HarnessCommand = {
|
|
24
|
-
type: 'generate-code';
|
|
25
|
-
prompt: string;
|
|
26
|
-
context?: string;
|
|
27
|
-
language?: string;
|
|
28
|
-
} | {
|
|
29
|
-
type: 'analyze-code';
|
|
30
|
-
filePath: string;
|
|
31
|
-
question?: string;
|
|
32
|
-
} | {
|
|
33
|
-
type: 'apply-edit';
|
|
34
|
-
filePath: string;
|
|
35
|
-
diff: string;
|
|
36
|
-
} | {
|
|
37
|
-
type: 'apply-config';
|
|
38
|
-
configPath: string;
|
|
39
|
-
content: string;
|
|
40
|
-
} | {
|
|
41
|
-
type: 'get-status';
|
|
42
|
-
} | {
|
|
43
|
-
type: 'get-running-instances';
|
|
44
|
-
} | {
|
|
45
|
-
type: 'sync-config';
|
|
46
|
-
direction: ConfigSyncDirection;
|
|
47
|
-
} | {
|
|
48
|
-
type: 'diff-config';
|
|
49
|
-
} | {
|
|
50
|
-
type: 'read-workboard';
|
|
51
|
-
} | {
|
|
52
|
-
type: 'update-workboard';
|
|
53
|
-
sessionId: string;
|
|
54
|
-
task?: string;
|
|
55
|
-
files?: string[];
|
|
56
|
-
};
|
|
57
|
-
interface HarnessCommandResult {
|
|
58
|
-
success: boolean;
|
|
59
|
-
command: HarnessCommand['type'];
|
|
60
|
-
message?: string;
|
|
61
|
-
data?: unknown;
|
|
62
|
-
}
|
|
63
|
-
type HarnessEvent = {
|
|
64
|
-
type: 'harness-connected';
|
|
65
|
-
harnessId: string;
|
|
66
|
-
} | {
|
|
67
|
-
type: 'harness-disconnected';
|
|
68
|
-
harnessId: string;
|
|
69
|
-
} | {
|
|
70
|
-
type: 'generation-started';
|
|
71
|
-
taskId: string;
|
|
72
|
-
} | {
|
|
73
|
-
type: 'generation-completed';
|
|
74
|
-
taskId: string;
|
|
75
|
-
output: string;
|
|
76
|
-
} | {
|
|
77
|
-
type: 'error';
|
|
78
|
-
harnessId: string;
|
|
79
|
-
message: string;
|
|
80
|
-
};
|
|
81
|
-
interface HarnessInfo {
|
|
82
|
-
id: string;
|
|
83
|
-
name: string;
|
|
84
|
-
version?: string;
|
|
85
|
-
capabilities: HarnessCapabilities;
|
|
86
|
-
}
|
|
87
|
-
interface HarnessProcessInfo {
|
|
88
|
-
pid: number;
|
|
89
|
-
command: string;
|
|
90
|
-
harnessId: string;
|
|
91
|
-
}
|
|
92
|
-
interface ConfigSyncResult {
|
|
93
|
-
success: boolean;
|
|
94
|
-
harnessId: string;
|
|
95
|
-
direction: ConfigSyncDirection;
|
|
96
|
-
message?: string;
|
|
97
|
-
}
|
|
98
|
-
interface ConfigDiffEntry {
|
|
99
|
-
harnessId: string;
|
|
100
|
-
localExists: boolean;
|
|
101
|
-
ssdExists: boolean;
|
|
102
|
-
identical: boolean;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Contract every AI harness adapter must satisfy.
|
|
107
|
-
* Mirrors EditorAdapter from packages/editors.
|
|
108
|
-
*
|
|
109
|
-
* AI tools are external executables — never linked libraries.
|
|
110
|
-
* Communication is data-only: commands in, results out.
|
|
111
|
-
*/
|
|
112
|
-
interface HarnessAdapter {
|
|
113
|
-
/** Unique stable identifier, e.g. "claude-code", "cursor", "copilot" */
|
|
114
|
-
readonly id: string;
|
|
115
|
-
/** Human-readable display name */
|
|
116
|
-
readonly name: string;
|
|
117
|
-
/** Returns the static capability set for this harness */
|
|
118
|
-
getCapabilities(): HarnessCapabilities;
|
|
119
|
-
/** Returns live info (version, etc.) — may shell out */
|
|
120
|
-
getInfo(): Promise<HarnessInfo>;
|
|
121
|
-
/** True if the harness executable is on PATH and accessible */
|
|
122
|
-
isAvailable(): Promise<boolean>;
|
|
123
|
-
/** Execute a typed command against this harness */
|
|
124
|
-
execute(command: HarnessCommand): Promise<HarnessCommandResult>;
|
|
125
|
-
/** Subscribe to harness events; returns an unsubscribe function */
|
|
126
|
-
onEvent(handler: (event: HarnessEvent) => void): () => void;
|
|
127
|
-
/** Release all resources held by this adapter */
|
|
128
|
-
dispose(): Promise<void>;
|
|
129
|
-
}
|
|
3
|
+
export { ConflictResult, SessionType, WorkboardEntry, WorkboardSession, WorkboardState, acquireLock, atomicWriteSync, deriveSessionId, detectSessionType, lockPathFor, releaseLock, withLock, withLockAsync } from './workboard/index.js';
|
|
130
4
|
|
|
131
5
|
/**
|
|
132
6
|
* Adapter for Anthropic Claude Code (CLI: `claude`).
|
|
@@ -147,9 +21,13 @@ declare class ClaudeCodeAdapter implements HarnessAdapter {
|
|
|
147
21
|
getCapabilities(): HarnessCapabilities;
|
|
148
22
|
getInfo(): Promise<HarnessInfo>;
|
|
149
23
|
isAvailable(): Promise<boolean>;
|
|
24
|
+
notifyRegistered(): void;
|
|
25
|
+
notifyUnregistering(): void;
|
|
150
26
|
execute(command: HarnessCommand): Promise<HarnessCommandResult>;
|
|
27
|
+
private executeInner;
|
|
151
28
|
onEvent(handler: (event: HarnessEvent) => void): () => void;
|
|
152
29
|
dispose(): Promise<void>;
|
|
30
|
+
private emit;
|
|
153
31
|
}
|
|
154
32
|
|
|
155
33
|
/**
|
|
@@ -166,23 +44,66 @@ declare class CopilotAdapter implements HarnessAdapter {
|
|
|
166
44
|
getCapabilities(): HarnessCapabilities;
|
|
167
45
|
getInfo(): Promise<HarnessInfo>;
|
|
168
46
|
isAvailable(): Promise<boolean>;
|
|
47
|
+
notifyRegistered(): void;
|
|
48
|
+
notifyUnregistering(): void;
|
|
169
49
|
execute(command: HarnessCommand): Promise<HarnessCommandResult>;
|
|
170
50
|
onEvent(handler: (event: HarnessEvent) => void): () => void;
|
|
171
51
|
dispose(): Promise<void>;
|
|
52
|
+
private emit;
|
|
172
53
|
}
|
|
173
54
|
|
|
174
55
|
/**
|
|
175
|
-
*
|
|
56
|
+
* Adapter for Cursor IDE.
|
|
57
|
+
*
|
|
58
|
+
* Cursor is a VS Code fork with built-in AI capabilities.
|
|
59
|
+
* This adapter detects Cursor via `cursor --version` and provides
|
|
60
|
+
* workboard read/write when REVEALUI_WORKBOARD_PATH is set.
|
|
61
|
+
*/
|
|
62
|
+
declare class CursorAdapter implements HarnessAdapter {
|
|
63
|
+
readonly id = "cursor";
|
|
64
|
+
readonly name = "Cursor";
|
|
65
|
+
private readonly eventHandlers;
|
|
66
|
+
private readonly workboardPath;
|
|
67
|
+
constructor(workboardPath?: string);
|
|
68
|
+
getCapabilities(): HarnessCapabilities;
|
|
69
|
+
getInfo(): Promise<HarnessInfo>;
|
|
70
|
+
isAvailable(): Promise<boolean>;
|
|
71
|
+
notifyRegistered(): void;
|
|
72
|
+
notifyUnregistering(): void;
|
|
73
|
+
execute(command: HarnessCommand): Promise<HarnessCommandResult>;
|
|
74
|
+
private executeInner;
|
|
75
|
+
onEvent(handler: (event: HarnessEvent) => void): () => void;
|
|
76
|
+
dispose(): Promise<void>;
|
|
77
|
+
private emit;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Syncs harness config between local filesystem and root backup.
|
|
176
82
|
* Mirrors config-sync.ts from packages/editors.
|
|
177
83
|
*/
|
|
178
|
-
declare function syncConfig(harnessId: string, direction: ConfigSyncDirection,
|
|
179
|
-
/** Compares local vs
|
|
180
|
-
declare function diffConfig(harnessId: string,
|
|
84
|
+
declare function syncConfig(harnessId: string, direction: ConfigSyncDirection, root?: string): ConfigSyncResult;
|
|
85
|
+
/** Compares local vs root config for a harness. */
|
|
86
|
+
declare function diffConfig(harnessId: string, root?: string): ConfigDiffEntry;
|
|
87
|
+
/**
|
|
88
|
+
* Sync all known harness configs in a given direction.
|
|
89
|
+
* Returns results for each harness.
|
|
90
|
+
*/
|
|
91
|
+
declare function syncAllConfigs(direction: ConfigSyncDirection, root?: string): ConfigSyncResult[];
|
|
92
|
+
/**
|
|
93
|
+
* Diff all known harness configs.
|
|
94
|
+
* Returns diff entries for each harness.
|
|
95
|
+
*/
|
|
96
|
+
declare function diffAllConfigs(root?: string): ConfigDiffEntry[];
|
|
97
|
+
/**
|
|
98
|
+
* Validate that a config file contains parseable JSON.
|
|
99
|
+
* Returns null if valid, or an error message if invalid.
|
|
100
|
+
*/
|
|
101
|
+
declare function validateConfigJson(harnessId: string): string | null;
|
|
181
102
|
|
|
182
103
|
/** Returns the local config file path for a given harness id, or undefined if unknown. */
|
|
183
104
|
declare function getLocalConfigPath(harnessId: string): string | undefined;
|
|
184
|
-
/** Returns the
|
|
185
|
-
declare function
|
|
105
|
+
/** Returns the root config file path for a given harness id, or undefined if unknown. */
|
|
106
|
+
declare function getRootConfigPath(harnessId: string, root?: string): string | undefined;
|
|
186
107
|
/** Returns ids of all harnesses with known config paths. */
|
|
187
108
|
declare function getConfigurableHarnesses(): string[];
|
|
188
109
|
|
|
@@ -242,6 +163,8 @@ declare class HarnessCoordinator {
|
|
|
242
163
|
getWorkboard(): WorkboardManager;
|
|
243
164
|
/** Register a custom adapter (must be called before start()). */
|
|
244
165
|
registerAdapter(adapter: HarnessAdapter): void;
|
|
166
|
+
/** Run a health check across all registered harnesses and the workboard. */
|
|
167
|
+
healthCheck(): Promise<HealthCheckResult>;
|
|
245
168
|
}
|
|
246
169
|
|
|
247
170
|
/**
|
|
@@ -276,14 +199,18 @@ declare function findClaudeCodeSockets(): Promise<string[]>;
|
|
|
276
199
|
* harness.listRunning → HarnessProcessInfo[]
|
|
277
200
|
* harness.syncConfig → ConfigSyncResult
|
|
278
201
|
* harness.diffConfig → ConfigDiffEntry
|
|
202
|
+
* harness.health → HealthCheckResult
|
|
279
203
|
*/
|
|
280
204
|
declare class RpcServer {
|
|
281
205
|
private readonly registry;
|
|
282
206
|
private readonly socketPath;
|
|
283
207
|
private server;
|
|
208
|
+
private healthCheckFn;
|
|
284
209
|
constructor(registry: HarnessRegistry, socketPath: string);
|
|
285
210
|
private handleLine;
|
|
286
211
|
private dispatch;
|
|
212
|
+
/** Set the health check function (called by coordinator after construction). */
|
|
213
|
+
setHealthCheck(fn: () => Promise<unknown>): void;
|
|
287
214
|
start(): Promise<void>;
|
|
288
215
|
stop(): Promise<void>;
|
|
289
216
|
}
|
|
@@ -295,11 +222,11 @@ declare class RpcServer {
|
|
|
295
222
|
* integrating AI coding tools (Claude Code, Cursor, Copilot) into the
|
|
296
223
|
* RevealUI development workflow.
|
|
297
224
|
*
|
|
298
|
-
* Pro tier feature: gated behind isFeatureEnabled("
|
|
225
|
+
* Pro tier feature: gated behind isFeatureEnabled("ai").
|
|
299
226
|
*
|
|
300
227
|
* @packageDocumentation
|
|
301
228
|
*/
|
|
302
229
|
/** Check whether the harnesses feature is licensed for this installation. */
|
|
303
230
|
declare function checkHarnessesLicense(): Promise<boolean>;
|
|
304
231
|
|
|
305
|
-
export { ClaudeCodeAdapter,
|
|
232
|
+
export { ClaudeCodeAdapter, ConfigDiffEntry, ConfigSyncDirection, ConfigSyncResult, type CoordinatorOptions, CopilotAdapter, CursorAdapter, HarnessAdapter, HarnessCapabilities, HarnessCommand, HarnessCommandResult, HarnessCoordinator, HarnessEvent, HarnessInfo, HarnessProcessInfo, HarnessRegistry, HealthCheckResult, RpcServer, WorkboardManager, autoDetectHarnesses, checkHarnessesLicense, diffAllConfigs, diffConfig, findAllHarnessProcesses, findClaudeCodeSockets, findHarnessProcesses, findProcesses, getConfigurableHarnesses, getLocalConfigPath, getRootConfigPath, syncAllConfigs, syncConfig, validateConfigJson };
|