← Back to Skills Marketplace
alshowse-tech

Acp Adapter Layer

by alshowse-tech · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ✓ Security Clean
85
Downloads
0
Stars
1
Active Installs
1
Versions
Install in OpenClaw
/install acp-adapter-layer
Description
ACP (Agent Control Protocol) Adapter Layer for AI Native Full-Stack Software Factory - provides seamless integration between ASF multi-agent workflows and Op...
README (SKILL.md)

ACP Adapter Layer - ASF ↔ OpenClaw Bridge

Purpose in AI Native Full-Stack Software Factory

Position: System Infrastructure (Cross-Layer Integration)
Purpose: Bridge ASF multi-agent workflows with OpenClaw's Agent Control Protocol (ACP), enabling IDE integration, standardized session management, and tool interoperability.

OpenClaw Version: 2026.3.24+
ACP Specification: https://agentclientprotocol.com/

Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                  IDE / Editor                            │
│  (VS Code, JetBrains, etc. with ACP support)            │
└────────────────────┬────────────────────────────────────┘
                     │ ACP Protocol (stdio)
                     │ - initialize
                     │ - newSession
                     │ - prompt
                     │ - cancel
                     │ - tool calls
                     ▼
┌─────────────────────────────────────────────────────────┐
│              ACP ADAPTER LAYER                           │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  ┌─────────────────┐  ┌─────────────────┐               │
│  │  ACP Protocol   │  │  Session        │               │
│  │  Parser         │  │  Manager        │               │
│  │  - JSON-RPC     │  │  - ACP ↔ ASF    │               │
│  │  - Validation   │  │  - State sync   │               │
│  └────────┬────────┘  └────────┬────────┘               │
│           │                    │                         │
│           ▼                    ▼                         │
│  ┌─────────────────────────────────────────┐            │
│  │      Tool Protocol Converter            │            │
│  │      - ACP tools ↔ ASF tools            │            │
│  │      - Capability mapping               │            │
│  │      - Result transformation            │            │
│  └────────────────────┬────────────────────┘            │
│                       │                                  │
│                       ▼                                  │
│  ┌─────────────────────────────────────────┐            │
│  │      ASF Multi-Agent Router             │            │
│  │      - agentic-factory                  │            │
│  │      - role-namespace-engine            │            │
│  │      - OpenClaw Gateway                 │            │
│  └────────────────────┬────────────────────┘            │
│                       │                                  │
│                       ▼                                  │
│  ┌─────────────────────────────────────────┐            │
│  │      OpenClaw Gateway (WebSocket)       │            │
│  │      - ws://127.0.0.1:18789             │            │
│  │      - sessions, routing, tools         │            │
│  └─────────────────────────────────────────┘            │
│                                                          │
└─────────────────────────────────────────────────────────┘

ACP Protocol Support Matrix

ACP Feature Status Notes
initialize ✅ Implemented Handshake with capability negotiation
newSession ✅ Implemented Create ASF session, return ACP session ID
prompt ✅ Implemented Convert ACP prompt to ASF task
cancel ✅ Implemented Abort ASF task, notify ACP client
session/set_mode ✅ Implemented Map to ASF thinking/tool levels
session/info ✅ Implemented Return ASF session state
loadSession ✅ Implemented Replay ASF session history
listSessions ✅ Implemented List active ASF sessions
Tool calls ✅ Implemented ACP tools ↔ ASF tools mapping
Tool streaming ✅ Implemented Real-time tool status updates
Resource handling ✅ Implemented Images, files, embedded resources
Progress updates ✅ Implemented Task progress streaming
Usage updates ✅ Implemented Token usage from ASF

Core Capabilities

1. ACP Protocol Parser & Validator

interface ACPMessage {
  jsonrpc: '2.0';
  id?: string | number;
  method: string;
  params?: Record\x3Cstring, unknown>;
}

interface ACPResponse {
  jsonrpc: '2.0';
  id?: string | number;
  result?: unknown;
  error?: {
    code: number;
    message: string;
    data?: unknown;
  };
}

class ACPProtocolParser {
  private schema: Map\x3Cstring, ACPSchema>;
  
  constructor() {
    this.schema = this.loadACPSchema();
  }
  
  parse(message: string): ParsedACPMessage {
    try {
      const raw = JSON.parse(message);
      this.validate(raw);
      return this.transform(raw);
    } catch (error) {
      throw new ACPProtocolError(`Invalid ACP message: ${error.message}`);
    }
  }
  
  private validate(message: ACPMessage): void {
    // Validate JSON-RPC 2.0 structure
    if (message.jsonrpc !== '2.0') {
      throw new ACPProtocolError('Invalid JSON-RPC version');
    }
    
    // Validate method exists
    if (!this.schema.has(message.method)) {
      throw new ACPProtocolError(`Unknown method: ${message.method}`);
    }
    
    // Validate params against schema
    const schema = this.schema.get(message.method);
    this.validateParams(message.params, schema.params);
  }
  
  transform(raw: ACPMessage): ParsedACPMessage {
    switch (raw.method) {
      case 'initialize':
        return {
          type: 'initialize',
          capabilities: raw.params?.capabilities as ACPCapabilities,
          clientInfo: raw.params?.clientInfo as ClientInfo
        };
      
      case 'newSession':
        return {
          type: 'newSession',
          config: raw.params?.config as SessionConfig
        };
      
      case 'prompt':
        return {
          type: 'prompt',
          sessionId: raw.params?.sessionId as string,
          prompt: raw.params?.prompt as ACPPrompt,
          mode: raw.params?.mode as PromptMode
        };
      
      case 'cancel':
        return {
          type: 'cancel',
          sessionId: raw.params?.sessionId as string,
          reason: raw.params?.reason as string
        };
      
      default:
        throw new ACPProtocolError(`Unhandled method: ${raw.method}`);
    }
  }
}

2. Session Manager (ACP ↔ ASF)

interface ACPSession {
  acpSessionId: string;
  asfSessionKey: string;
  state: 'initializing' | 'active' | 'paused' | 'completed' | 'failed';
  createdAt: Date;
  lastActivityAt: Date;
  mode: ACPSessionMode;
  metadata: {
    clientInfo: ClientInfo;
    capabilities: ACPCapabilities;
    workspace?: string;
  };
}

interface ACPSessionMode {
  thinking: 'off' | 'low' | 'medium' | 'high';
  toolVerbosity: 'quiet' | 'normal' | 'verbose';
  reasoning: boolean;
  usageDetail: boolean;
  elevatedActions: boolean;
}

class ACPSessionManager {
  private sessions: Map\x3Cstring, ACPSession>;
  private gatewayClient: OpenClawGatewayClient;
  
  constructor(gatewayUrl: string) {
    this.gatewayClient = new OpenClawGatewayClient(gatewayUrl);
    this.sessions = new Map();
  }
  
  async createSession(config: SessionConfig, clientInfo: ClientInfo): Promise\x3CACPSession> {
    // Generate ACP session ID
    const acpSessionId = `acp:${crypto.randomUUID()}`;
    
    // Create corresponding ASF session
    const asfSessionKey = await this.gatewayClient.createSession({
      label: config.label || 'ACP Session',
      runtime: 'acp',
      mode: 'session',
      thinking: this.mapThinkingLevel(config.thinking),
      toolVerbosity: config.toolVerbosity || 'normal'
    });
    
    // Create session mapping
    const session: ACPSession = {
      acpSessionId,
      asfSessionKey,
      state: 'active',
      createdAt: new Date(),
      lastActivityAt: new Date(),
      mode: {
        thinking: config.thinking || 'medium',
        toolVerbosity: config.toolVerbosity || 'normal',
        reasoning: config.reasoning ?? false,
        usageDetail: config.usageDetail ?? false,
        elevatedActions: config.elevatedActions ?? false
      },
      metadata: {
        clientInfo,
        capabilities: config.capabilities,
        workspace: config.workspace
      }
    };
    
    this.sessions.set(acpSessionId, session);
    
    return session;
  }
  
  async getOrCreateSession(sessionId?: string, label?: string): Promise\x3CACPSession> {
    if (!sessionId) {
      // Create new session
      return this.createSession({ label }, {} as ClientInfo);
    }
    
    // Get existing session
    const session = this.sessions.get(sessionId);
    if (!session) {
      throw new ACPSessionError(`Session not found: ${sessionId}`);
    }
    
    return session;
  }
  
  async loadSession(sessionId: string): Promise\x3CSessionHistory> {
    const session = this.sessions.get(sessionId);
    if (!session) {
      throw new ACPSessionError(`Session not found: ${sessionId}`);
    }
    
    // Fetch ASF session history
    const asfHistory = await this.gatewayClient.getSessionHistory(session.asfSessionKey);
    
    // Transform to ACP format
    return this.transformHistoryToACP(asfHistory);
  }
  
  async updateSessionMode(sessionId: string, mode: Partial\x3CACPSessionMode>): Promise\x3Cvoid> {
    const session = this.sessions.get(sessionId);
    if (!session) {
      throw new ACPSessionError(`Session not found: ${sessionId}`);
    }
    
    // Update mode
    session.mode = { ...session.mode, ...mode };
    
    // Update ASF session configuration
    await this.gatewayClient.updateSession(session.asfSessionKey, {
      thinking: mode.thinking,
      toolVerbosity: mode.toolVerbosity
    });
  }
  
  private mapThinkingLevel(level: string): string {
    const mapping: Record\x3Cstring, string> = {
      'off': 'off',
      'low': 'low',
      'medium': 'medium',
      'high': 'high'
    };
    return mapping[level] || 'medium';
  }
}

3. Tool Protocol Converter

interface ACPTool {
  name: string;
  description: string;
  inputSchema: JSONSchema;
  annotations?: {
    title?: string;
    readOnlyHint?: boolean;
    destructiveHint?: boolean;
    idempotentHint?: boolean;
    openWorldHint?: boolean;
  };
}

interface ASFTool {
  name: string;
  description: string;
  parameters: Record\x3Cstring, unknown>;
  handler: (args: unknown) => Promise\x3Cunknown>;
}

class ToolProtocolConverter {
  private acpTools: Map\x3Cstring, ACPTool>;
  private asfTools: Map\x3Cstring, ASFTool>;
  
  // ACP → ASF: Convert tool call
  async convertACPCallToASF(acpCall: ACPCall): Promise\x3CASFToolCall> {
    const asfTool = this.asfTools.get(acpCall.name);
    if (!asfTool) {
      throw new ToolNotFoundError(`ASF tool not found: ${acpCall.name}`);
    }
    
    return {
      toolName: asfTool.name,
      arguments: this.transformArguments(acpCall.arguments, asfTool.parameters),
      sessionId: acpCall.sessionId
    };
  }
  
  // ASF → ACP: Convert tool result
  async convertASFResultToACP(asfResult: ASFToolResult): Promise\x3CACPToolResult> {
    return {
      toolName: asfResult.toolName,
      result: this.transformResult(asfResult.result),
      content: this.extractContent(asfResult),
      isError: asfResult.isError,
      metadata: {
        duration: asfResult.duration,
        resources: asfResult.resources
      }
    };
  }
  
  // Register ASF tools as ACP tools
  registerASFTools(asfTools: ASFTool[]): ACPTool[] {
    const acpTools: ACPTool[] = [];
    
    for (const asfTool of asfTools) {
      const acpTool: ACPTool = {
        name: asfTool.name,
        description: asfTool.description,
        inputSchema: this.convertSchemaToJSONSchema(asfTool.parameters),
        annotations: {
          title: asfTool.name,
          readOnlyHint: this.isReadOnly(asfTool),
          destructiveHint: this.isDestructive(asfTool),
          idempotentHint: this.isIdempotent(asfTool),
          openWorldHint: true
        }
      };
      
      acpTools.push(acpTool);
      this.acpTools.set(acpTool.name, acpTool);
    }
    
    return acpTools;
  }
  
  private isReadOnly(tool: ASFTool): boolean {
    const readOnlyTools = ['read', 'fetch', 'search', 'list', 'get'];
    return readOnlyTools.some(keyword => tool.name.toLowerCase().includes(keyword));
  }
  
  private isDestructive(tool: ASFTool): boolean {
    const destructiveTools = ['delete', 'remove', 'destroy', 'drop'];
    return destructiveTools.some(keyword => tool.name.toLowerCase().includes(keyword));
  }
  
  private isIdempotent(tool: ASFTool): boolean {
    const idempotentTools = ['set', 'update', 'replace', 'write'];
    return idempotentTools.some(keyword => tool.name.toLowerCase().includes(keyword));
  }
}

4. ASF Multi-Agent Router Integration

interface ACPTaskRouter {
  // Route ACP prompt to appropriate ASF agent
  routePrompt(prompt: ACPPrompt, session: ACPSession): Promise\x3CRoutingDecision>;
  
  // Coordinate multi-agent work
  coordinateMultiAgent(task: ASFTask): Promise\x3CMultiAgentResult>;
  
  // Stream progress back to ACP client
  streamProgress(sessionId: string, progress: ProgressUpdate): Promise\x3Cvoid>;
}

class ACPTaskRouter {
  private sessionManager: ACPSessionManager;
  private gatewayClient: OpenClawGatewayClient;
  private factoryOrchestrator: AgenticFactoryOrchestrator;
  
  async routePrompt(prompt: ACPPrompt, session: ACPSession): Promise\x3CRoutingDecision> {
    // Analyze prompt complexity
    const analysis = await this.analyzePrompt(prompt);
    
    // Determine routing strategy
    if (analysis.complexity === 'simple') {
      // Direct execution
      return {
        strategy: 'direct',
        agent: 'builder',
        session: session.asfSessionKey
      };
    } else if (analysis.complexity === 'medium') {
      // Single specialist
      return {
        strategy: 'specialist',
        agent: this.selectSpecialist(analysis.domain),
        session: session.asfSessionKey
      };
    } else {
      // Multi-agent factory
      return {
        strategy: 'factory',
        agents: ['architect', 'builder', 'tester'],
        session: await this.factoryOrchestrator.createFactorySession()
      };
    }
  }
  
  async coordinateMultiAgent(task: ASFTask): Promise\x3CMultiAgentResult> {
    // Use agentic-factory for coordination
    const factoryResult = await this.factoryOrchestrator.execute(task);
    
    // Transform to ACP format
    return {
      result: factoryResult.output,
      agents: factoryResult.agents.map(a => ({
        name: a.role,
        contribution: a.output,
        duration: a.duration
      })),
      timeline: factoryResult.timeline,
      quality: factoryResult.quality
    };
  }
  
  async streamProgress(sessionId: string, progress: ProgressUpdate): Promise\x3Cvoid> {
    const session = this.sessionManager.sessions.get(sessionId);
    if (!session) {
      return;
    }
    
    // Convert ASF progress to ACP progress update
    const acpProgress: ACPProgressUpdate = {
      type: 'progress',
      sessionId: sessionId,
      status: progress.status,
      message: progress.message,
      percentage: progress.percentage,
      currentStep: progress.currentStep,
      totalSteps: progress.totalSteps
    };
    
    // Stream to ACP client
    await this.sendToACPClient(acpProgress);
  }
}

5. OpenClaw Gateway Integration

class OpenClawGatewayClient {
  private ws: WebSocket;
  private token: string;
  private messageQueue: Map\x3Cstring, ResolveReject>;
  
  constructor(gatewayUrl: string, token: string) {
    this.ws = new WebSocket(gatewayUrl);
    this.token = token;
    this.messageQueue = new Map();
    
    this.setupConnection();
  }
  
  async createSession(config: SessionConfig): Promise\x3Cstring> {
    const response = await this.send({
      type: 'session/create',
      payload: config
    });
    
    return response.sessionKey;
  }
  
  async sendPrompt(sessionKey: string, prompt: string): Promise\x3CAgentResponse> {
    const response = await this.send({
      type: 'chat/send',
      payload: {
        sessionKey,
        message: prompt
      }
    });
    
    return response;
  }
  
  async getSessionHistory(sessionKey: string): Promise\x3CSessionHistory> {
    const response = await this.send({
      type: 'session/history',
      payload: { sessionKey }
    });
    
    return response.history;
  }
  
  async updateSession(sessionKey: string, config: Partial\x3CSessionConfig>): Promise\x3Cvoid> {
    await this.send({
      type: 'session/update',
      payload: { sessionKey, config }
    });
  }
  
  private send(message: GatewayMessage): Promise\x3Cunknown> {
    return new Promise((resolve, reject) => {
      const id = crypto.randomUUID();
      this.messageQueue.set(id, { resolve, reject });
      
      this.ws.send(JSON.stringify({ id, ...message }));
      
      // Timeout after 30 seconds
      setTimeout(() => {
        this.messageQueue.delete(id);
        reject(new Error('Gateway timeout'));
      }, 30000);
    });
  }
  
  private setupConnection(): void {
    this.ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      const pending = this.messageQueue.get(message.id);
      if (pending) {
        pending.resolve(message.payload);
        this.messageQueue.delete(message.id);
      }
    };
    
    this.ws.onerror = (error) => {
      console.error('Gateway connection error:', error);
    };
  }
}

Integration with ASF Components

With agentic-factory (L6)

interface AgenticFactoryIntegration {
  // Create factory session for ACP task
  createFactoryForACP(acpSession: ACPSession): Promise\x3CFactorySession>;
  
  // Route ACP prompt to factory
  routeToFactory(prompt: ACPPrompt): Promise\x3CFactoryResult>;
  
  // Stream factory progress to ACP
  streamFactoryProgress(factorySession: FactorySession): AsyncIterable\x3CProgressUpdate>;
}

With role-namespace-engine (L6)

interface RoleNamespaceIntegration {
  // Map ACP client to ASF namespace
  mapClientToNamespace(clientInfo: ClientInfo): Promise\x3Cstring>;
  
  // Enforce namespace isolation for ACP sessions
  enforceIsolation(acpSession: ACPSession): Promise\x3Cvoid>;
  
  // Check cross-namespace access
  checkAccess(session: ACPSession, target: string): Promise\x3CAccessDecision>;
}

Configuration

{
  "acpAdapter": {
    "enabled": true,
    "gateway": {
      "url": "ws://127.0.0.1:18789",
      "token": "~/.openclaw/gateway.token",
      "reconnectAttempts": 3,
      "reconnectDelay": 1000
    },
    "session": {
      "defaultMode": {
        "thinking": "medium",
        "toolVerbosity": "normal",
        "reasoning": false,
        "usageDetail": false,
        "elevatedActions": false
      },
      "timeout": 3600,
      "maxConcurrent": 10
    },
    "tools": {
      "autoRegister": true,
      "excludePatterns": ["internal.*", "debug.*"],
      "timeout": 30000
    },
    "logging": {
      "level": "info",
      "protocol": false,
      "tools": true
    }
  }
}

Usage Examples

Example 1: Initialize ACP Connection

const adapter = new ACPAdapterLayer({
  gatewayUrl: 'ws://127.0.0.1:18789',
  token: 'your-gateway-token'
});

// Handle ACP initialize
const initializeRequest = {
  jsonrpc: '2.0',
  id: 1,
  method: 'initialize',
  params: {
    protocolVersion: '2024-11-05',
    capabilities: {
      roots: { listChanged: true },
      sampling: {},
      tools: { listChanged: true }
    },
    clientInfo: {
      name: 'vscode',
      version: '1.85.0'
    }
  }
};

const response = await adapter.handle(initializeRequest);

// Output:
{
  jsonrpc: '2.0',
  id: 1,
  result: {
    protocolVersion: '2024-11-05',
    capabilities: {
      tools: {
        listChanged: true,
        tools: [/* ASF tools */]
      }
    },
    serverInfo: {
      name: 'asf-acp-adapter',
      version: '1.0.0'
    }
  }
}

Example 2: Create ACP Session

const newSessionRequest = {
  jsonrpc: '2.0',
  id: 2,
  method: 'newSession',
  params: {
    config: {
      label: 'Code Review Session',
      thinking: 'high',
      toolVerbosity: 'verbose'
    }
  }
};

const response = await adapter.handle(newSessionRequest);

// Output:
{
  jsonrpc: '2.0',
  id: 2,
  result: {
    sessionId: 'acp:uuid-here',
    state: 'active',
    mode: {
      thinking: 'high',
      toolVerbosity: 'verbose'
    }
  }
}

Example 3: Process ACP Prompt

const promptRequest = {
  jsonrpc: '2.0',
  id: 3,
  method: 'prompt',
  params: {
    sessionId: 'acp:uuid-here',
    prompt: {
      role: 'user',
      content: [{ type: 'text', text: 'Review this code for performance issues' }]
    }
  }
};

const response = await adapter.handle(promptRequest);

// Streams progress updates, then returns:
{
  jsonrpc: '2.0',
  id: 3,
  result: {
    content: [{ type: 'text', text: 'Code review complete...' }],
    toolCalls: [...],
    usage: {
      inputTokens: 1500,
      outputTokens: 800
    }
  }
}

Remember: ACP is the bridge between IDEs and AI. Make it seamless.

Usage Guidance
This skill appears to be what it says: an adapter for ACP↔ASF integration. It does not request credentials or perform network calls to remote hosts by default (it references a local WebSocket gateway). Before deploying in production, review whether you will point the gateway client at a remote server — that would require secure credentials and network controls. Also note the OpenClaw client currently uses a placeholder token; if you wire this to a real gateway, provide and protect appropriate credentials and review any network endpoints the adapter will contact.
Capability Analysis
Type: OpenClaw Skill Name: acp-adapter-layer Version: 1.0.0 The skill bundle implements a protocol adapter layer designed to bridge the Agent Control Protocol (ACP) used by IDEs with the OpenClaw Agentic Software Factory (ASF). The implementation in index.ts and the documentation in SKILL.md describe standard JSON-RPC parsing, session management, and tool mapping logic. The code interacts with a local service gateway via WebSockets (ws://127.0.0.1:18789) and follows the stated architectural purpose without any indicators of malicious intent, data exfiltration, or unauthorized execution.
Capability Tags
crypto
Capability Assessment
Purpose & Capability
Name and description claim an ACP adapter; code implements an ACP parser, session manager, tool converter, and a local OpenClaw gateway client. Required resources (none) match the adapter role — nothing asks for unrelated cloud or system credentials.
Instruction Scope
SKILL.md and included code focus on parsing ACP messages, session lifecycle, and mapping tools; they reference a local gateway (ws://127.0.0.1:18789) which is consistent with bridging to a local OpenClaw gateway. There are no instructions to read arbitrary host files, exfiltrate data, or call external endpoints beyond the documented local gateway.
Install Mechanism
No install spec is present (instruction-only behavior), so nothing is downloaded or written to disk by the install process. This is low-risk and proportional for a library/adapter.
Credentials
The skill declares no required environment variables, credentials, or config paths. The implementation uses a hard-coded local gateway URL and a placeholder 'token' value in the client constructor; it does not request or access sensitive environment secrets.
Persistence & Privilege
always is false and the skill does not request elevated platform privileges or modify other skills' configs. It does not persist or demand system-wide presence beyond its own code.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install acp-adapter-layer
  3. After installation, invoke the skill by name or use /acp-adapter-layer
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.0.0
acp-adapter-layer 1.0.0 - Initial release of the ACP Adapter Layer, bridging ASF multi-agent workflows with OpenClaw's Agent Control Protocol (ACP). - Enables seamless IDE integration (VS Code, JetBrains) via ACP, providing standardized session management and protocol conversion. - Implements full ACP protocol support, including session creation, prompt processing, tool calls, and real-time streaming updates. - Provides a robust ACP protocol parser/validator and session manager for cross-layer integration in AI-native development environments. - Supports bi-directional tool mapping and capability negotiation between ASF and ACP clients.
Metadata
Slug acp-adapter-layer
Version 1.0.0
License MIT-0
All-time Installs 1
Active Installs 1
Total Versions 1
Frequently Asked Questions

What is Acp Adapter Layer?

ACP (Agent Control Protocol) Adapter Layer for AI Native Full-Stack Software Factory - provides seamless integration between ASF multi-agent workflows and Op... It is an AI Agent Skill for Claude Code / OpenClaw, with 85 downloads so far.

How do I install Acp Adapter Layer?

Run "/install acp-adapter-layer" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is Acp Adapter Layer free?

Yes, Acp Adapter Layer is completely free, licensed under MIT-0. You can download, install and use it at no cost.

Which platforms does Acp Adapter Layer support?

Acp Adapter Layer is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created Acp Adapter Layer?

It is built and maintained by alshowse-tech (@alshowse-tech); the current version is v1.0.0.

💬 Comments