@heyamiko/openclaw-plugin 0.1.0
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 +200 -0
- package/contracts/channel-config.schema.json +87 -0
- package/contracts/platform-ack.schema.json +25 -0
- package/contracts/platform-events.schema.json +87 -0
- package/contracts/platform-outbound.schema.json +47 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/src/accounts.d.ts +30 -0
- package/dist/src/accounts.d.ts.map +1 -0
- package/dist/src/accounts.js +115 -0
- package/dist/src/accounts.js.map +1 -0
- package/dist/src/api.d.ts +13 -0
- package/dist/src/api.d.ts.map +1 -0
- package/dist/src/api.js +45 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/channel.d.ts +174 -0
- package/dist/src/channel.d.ts.map +1 -0
- package/dist/src/channel.js +140 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/config-schema.d.ts +92 -0
- package/dist/src/config-schema.d.ts.map +1 -0
- package/dist/src/config-schema.js +17 -0
- package/dist/src/config-schema.js.map +1 -0
- package/dist/src/monitor.d.ts +19 -0
- package/dist/src/monitor.d.ts.map +1 -0
- package/dist/src/monitor.js +432 -0
- package/dist/src/monitor.js.map +1 -0
- package/dist/src/reply-prefix.d.ts +12 -0
- package/dist/src/reply-prefix.d.ts.map +1 -0
- package/dist/src/reply-prefix.js +8 -0
- package/dist/src/reply-prefix.js.map +1 -0
- package/dist/src/runtime.d.ts +57 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +28 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/send.d.ts +8 -0
- package/dist/src/send.d.ts.map +1 -0
- package/dist/src/send.js +51 -0
- package/dist/src/send.js.map +1 -0
- package/dist/src/status.d.ts +19 -0
- package/dist/src/status.d.ts.map +1 -0
- package/dist/src/status.js +51 -0
- package/dist/src/status.js.map +1 -0
- package/dist/src/types.d.ts +79 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/openclaw.plugin.json +51 -0
- package/package.json +73 -0
- package/skills/amiko/SKILL.md +287 -0
- package/skills/amiko/cli.js +521 -0
- package/skills/amiko/lib.js +634 -0
- package/skills/composio/SKILL.md +102 -0
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# @heyamiko/openclaw-plugin
|
|
2
|
+
|
|
3
|
+
An [OpenClaw](https://openclaw.dev) channel plugin that connects your OpenClaw agent to the [Amiko](https://amiko.app) platform, enabling direct and group chat via webhook.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This plugin registers an `amiko` channel with OpenClaw and provides:
|
|
8
|
+
|
|
9
|
+
- **Direct messages** — receive and reply to 1:1 DMs from Amiko users
|
|
10
|
+
- **Group chat** — participate in Amiko group conversations (mention-triggered)
|
|
11
|
+
- **Shared account** — agent replies on behalf of its owner in conversations
|
|
12
|
+
- **Feed comments** — agent comments on friends' posts (as draft, pending owner review)
|
|
13
|
+
- **Webhook delivery** — inbound messages arrive via HTTP webhook (no polling)
|
|
14
|
+
- **Context injection** — when auto-reply is off, messages are injected into agent context via `chat.inject` (no response generated)
|
|
15
|
+
- **Multi-account support** — configure multiple twins under a single plugin
|
|
16
|
+
- **Conversation-scoped delivery** — Amiko decides which conversations are routed to the plugin
|
|
17
|
+
|
|
18
|
+
## Repository Structure
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
index.ts Plugin entry point (exported default)
|
|
22
|
+
src/
|
|
23
|
+
channel.ts ChannelPlugin definition
|
|
24
|
+
monitor.ts Webhook inbound monitor (chat + post events)
|
|
25
|
+
accounts.ts Account resolution (single + multi-account)
|
|
26
|
+
api.ts HTTP client for Amiko platform API
|
|
27
|
+
send.ts Outbound sendText / sendMedia
|
|
28
|
+
status.ts Health probe + account inspection
|
|
29
|
+
runtime.ts PluginRuntime singleton
|
|
30
|
+
config-schema.ts Zod schema for channels.amiko config
|
|
31
|
+
types.ts Domain types
|
|
32
|
+
m0/ M0 reference implementation and contract tests
|
|
33
|
+
contracts/ JSON Schemas for API payloads
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Requirements
|
|
37
|
+
|
|
38
|
+
- [Node.js](https://nodejs.org) >= 18
|
|
39
|
+
- [OpenClaw](https://openclaw.dev) installed and configured
|
|
40
|
+
- [pnpm](https://pnpm.io) >= 8 for local development only
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
### 1. Install from npm
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install -g @heyamiko/openclaw-plugin
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or install directly through OpenClaw:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
openclaw plugins install @heyamiko/openclaw-plugin
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Obtain your Twin Token
|
|
57
|
+
|
|
58
|
+
The `token` is a **Twin Token** (JWT with `clawd-` prefix) that identifies the twin on the Amiko platform. It is used to authenticate API calls this plugin makes to amiko-chat.
|
|
59
|
+
|
|
60
|
+
To get a token:
|
|
61
|
+
1. Log in to the Amiko platform and go to your agent's deploy page.
|
|
62
|
+
2. The twin token is generated when the agent is deployed.
|
|
63
|
+
3. Keep the token secret — treat it like a password.
|
|
64
|
+
|
|
65
|
+
> **Note:** The account key in `channels.amiko.accounts` should be the OpenClaw agent ID, such as `main` or `agent-foo`. Put the actual Amiko twin ID in `twinId`.
|
|
66
|
+
|
|
67
|
+
### 3. Configure the channel
|
|
68
|
+
|
|
69
|
+
Add the following to your OpenClaw config (`~/.openclaw/openclaw.json`):
|
|
70
|
+
|
|
71
|
+
```json5
|
|
72
|
+
{
|
|
73
|
+
"channels": {
|
|
74
|
+
"amiko": {
|
|
75
|
+
"defaultAccount": "main",
|
|
76
|
+
"accounts": {
|
|
77
|
+
"main": {
|
|
78
|
+
"twinId": "<primaryTwinId>",
|
|
79
|
+
"token": "clawd-eyJhbGciOi...",
|
|
80
|
+
"platformApiBaseUrl": "https://platform.heyamiko.com",
|
|
81
|
+
"chatApiBaseUrl": "https://your-amiko-chat.up.railway.app",
|
|
82
|
+
"webhookPath": "/amiko/webhook/<primaryTwinId>"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Fields:**
|
|
91
|
+
|
|
92
|
+
| Field | Required | Default | Description |
|
|
93
|
+
|-------|----------|---------|-------------|
|
|
94
|
+
| `twinId` | Yes | — | Amiko twin ID for this OpenClaw agent |
|
|
95
|
+
| `token` | Yes | — | Twin token (`clawd-` prefix JWT) |
|
|
96
|
+
| `platformApiBaseUrl` | No | `https://platform.heyamiko.com` | Base URL for `amiko-new` / platform API |
|
|
97
|
+
| `chatApiBaseUrl` | No | `https://api.amiko.app` | Base URL for amiko-chat internal channel API |
|
|
98
|
+
| `apiBaseUrl` | Legacy | — | Backward-compatible fallback URL. Prefer the two explicit URLs above |
|
|
99
|
+
| `webhookPath` | No | `/amiko/webhook/<twinId>` | Inbound webhook path |
|
|
100
|
+
| `webhookSecret` | No | — | HMAC-SHA256 secret for webhook validation |
|
|
101
|
+
|
|
102
|
+
For multiple twins:
|
|
103
|
+
|
|
104
|
+
```json5
|
|
105
|
+
{
|
|
106
|
+
"channels": {
|
|
107
|
+
"amiko": {
|
|
108
|
+
"defaultAccount": "main",
|
|
109
|
+
"accounts": {
|
|
110
|
+
"main": {
|
|
111
|
+
"twinId": "<twinId1>",
|
|
112
|
+
"token": "clawd-...",
|
|
113
|
+
"platformApiBaseUrl": "https://platform.heyamiko.com",
|
|
114
|
+
"chatApiBaseUrl": "https://your-amiko-chat.up.railway.app"
|
|
115
|
+
},
|
|
116
|
+
"agent-foo": {
|
|
117
|
+
"twinId": "<twinId2>",
|
|
118
|
+
"token": "clawd-...",
|
|
119
|
+
"platformApiBaseUrl": "https://platform.heyamiko.com",
|
|
120
|
+
"chatApiBaseUrl": "https://your-amiko-chat.up.railway.app"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Each configured twin gets its own webhook endpoint at `/amiko/webhook/<twinId>` by default. The routing side still keys off the OpenClaw account name such as `main` or `agent-foo`.
|
|
129
|
+
|
|
130
|
+
OpenClaw routing must also bind each agent to the matching Amiko account key. If the account is `main`, bind `amiko:main`; if the account is `agent-foo`, bind `amiko:agent-foo`.
|
|
131
|
+
|
|
132
|
+
Examples:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
openclaw agents add main --bind amiko:main
|
|
136
|
+
openclaw agents add agent-foo --bind amiko:agent-foo
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
If the agent already exists, make sure `agents.entries.<agentId>.routing.bindings` in `~/.openclaw/openclaw.json` contains the same `amiko:<accountId>` value.
|
|
140
|
+
|
|
141
|
+
### 4. Restart the gateway
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
openclaw gateway restart
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The Amiko channel will be loaded and the webhook endpoint will be active.
|
|
148
|
+
|
|
149
|
+
### Verify channel health
|
|
150
|
+
|
|
151
|
+
After restarting, check that the channel is healthy:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
openclaw channel status amiko
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- `healthy` — token is valid and the Amiko API is reachable
|
|
158
|
+
- `unconfigured` — `token` is missing from config (set it and restart)
|
|
159
|
+
- `unhealthy` — token is set but the API returned an error (check token validity)
|
|
160
|
+
|
|
161
|
+
## Webhook Events
|
|
162
|
+
|
|
163
|
+
The plugin handles two event types on the same webhook endpoint:
|
|
164
|
+
|
|
165
|
+
| Event | Source | Behavior |
|
|
166
|
+
|-------|--------|----------|
|
|
167
|
+
| `message.text` | amiko-chat | Chat message. `replyExpected=true` → agent responds. `replyExpected=false` → `chat.inject` (context only). |
|
|
168
|
+
| `post.published` | amiko-new | Friend posted. Agent decides to comment (draft) or skip (`<empty-response/>`). |
|
|
169
|
+
|
|
170
|
+
## Development
|
|
171
|
+
|
|
172
|
+
### Local development setup
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
git clone https://github.com/HCF-S/openclaw-amiko-plugin
|
|
176
|
+
cd openclaw-amiko-plugin
|
|
177
|
+
pnpm install
|
|
178
|
+
pnpm run build
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Type check
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
pnpm run typecheck
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Run M0 contract tests
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
pnpm run test:m0
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
All 20 tests should pass with no external dependencies.
|
|
194
|
+
|
|
195
|
+
## Planning Docs
|
|
196
|
+
|
|
197
|
+
- Comprehensive plan: `AMIKO_CHANNEL_COMPREHENSIVE_PLAN.md`
|
|
198
|
+
- Execution checklist: `AMIKO_CHANNEL_EXECUTION_CHECKLIST.md`
|
|
199
|
+
- M0 machine-readable contracts: `contracts/`
|
|
200
|
+
- M0 reference implementation: `src/m0/`
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://openclaw.dev/schemas/amiko/channel-config.schema.json",
|
|
4
|
+
"title": "AmikoChannelConfig",
|
|
5
|
+
"description": "Schema for channels.amiko in OpenClaw config. OCP-001.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"token": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Bearer token for amiko-platform API authentication. Sensitive.",
|
|
12
|
+
"uiHints": { "sensitive": true }
|
|
13
|
+
},
|
|
14
|
+
"twinId": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Amiko twin ID used by this account."
|
|
17
|
+
},
|
|
18
|
+
"platformApiBaseUrl": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"format": "uri",
|
|
21
|
+
"description": "Base URL of amiko-new / platform API.",
|
|
22
|
+
"default": "https://platform.heyamiko.com"
|
|
23
|
+
},
|
|
24
|
+
"chatApiBaseUrl": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"format": "uri",
|
|
27
|
+
"description": "Base URL of amiko-chat internal channel API.",
|
|
28
|
+
"default": "https://api.amiko.app"
|
|
29
|
+
},
|
|
30
|
+
"apiBaseUrl": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"format": "uri",
|
|
33
|
+
"description": "Legacy fallback URL. Prefer platformApiBaseUrl and chatApiBaseUrl."
|
|
34
|
+
},
|
|
35
|
+
"name": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "Human-readable account label."
|
|
38
|
+
},
|
|
39
|
+
"enabled": {
|
|
40
|
+
"type": "boolean",
|
|
41
|
+
"description": "Whether this account is active. Defaults to true.",
|
|
42
|
+
"default": true
|
|
43
|
+
},
|
|
44
|
+
"pollIntervalMs": {
|
|
45
|
+
"type": "integer",
|
|
46
|
+
"minimum": 1000,
|
|
47
|
+
"maximum": 60000,
|
|
48
|
+
"description": "Polling interval in milliseconds.",
|
|
49
|
+
"default": 3000
|
|
50
|
+
},
|
|
51
|
+
"pollTimeoutMs": {
|
|
52
|
+
"type": "integer",
|
|
53
|
+
"minimum": 1000,
|
|
54
|
+
"maximum": 30000,
|
|
55
|
+
"description": "Per-request HTTP timeout in milliseconds.",
|
|
56
|
+
"default": 10000
|
|
57
|
+
},
|
|
58
|
+
"defaultAccount": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"description": "Which OpenClaw agent/account key to treat as default in multi-account mode."
|
|
61
|
+
},
|
|
62
|
+
"accounts": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"description": "Per-account config overrides keyed by OpenClaw agent ID (for example main, agent-foo).",
|
|
65
|
+
"additionalProperties": {
|
|
66
|
+
"$ref": "#/$defs/AmikoAccountConfig"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"$defs": {
|
|
71
|
+
"AmikoAccountConfig": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"additionalProperties": false,
|
|
74
|
+
"properties": {
|
|
75
|
+
"token": { "type": "string", "uiHints": { "sensitive": true } },
|
|
76
|
+
"twinId": { "type": "string" },
|
|
77
|
+
"platformApiBaseUrl": { "type": "string", "format": "uri" },
|
|
78
|
+
"chatApiBaseUrl": { "type": "string", "format": "uri" },
|
|
79
|
+
"apiBaseUrl": { "type": "string", "format": "uri" },
|
|
80
|
+
"name": { "type": "string" },
|
|
81
|
+
"enabled": { "type": "boolean" },
|
|
82
|
+
"pollIntervalMs": { "type": "integer", "minimum": 1000, "maximum": 60000 },
|
|
83
|
+
"pollTimeoutMs": { "type": "integer", "minimum": 1000, "maximum": 30000 }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://openclaw.dev/schemas/amiko/platform-ack.schema.json",
|
|
4
|
+
"title": "AmikoAckPayload",
|
|
5
|
+
"description": "Request body schema for POST /internal/openclaw/amiko/acks. OCP-002.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["accountId", "cursor", "eventIds"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"accountId": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Account scope. Must match the Bearer token's account."
|
|
13
|
+
},
|
|
14
|
+
"cursor": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Cursor of the last event in the acknowledged batch."
|
|
17
|
+
},
|
|
18
|
+
"eventIds": {
|
|
19
|
+
"type": "array",
|
|
20
|
+
"items": { "type": "string" },
|
|
21
|
+
"minItems": 1,
|
|
22
|
+
"description": "IDs of all events in the acknowledged batch."
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://openclaw.dev/schemas/amiko/platform-events.schema.json",
|
|
4
|
+
"title": "AmikoEventsResponse",
|
|
5
|
+
"description": "Response schema for GET /internal/openclaw/amiko/events. OCP-002.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["events", "nextCursor", "hasMore"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"events": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": { "$ref": "#/$defs/AmikoInboundEvent" }
|
|
13
|
+
},
|
|
14
|
+
"nextCursor": {
|
|
15
|
+
"type": ["string", "null"],
|
|
16
|
+
"description": "Cursor to use in the next poll. Null when no events returned."
|
|
17
|
+
},
|
|
18
|
+
"hasMore": {
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"description": "True if more events exist beyond the returned limit."
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"$defs": {
|
|
24
|
+
"AmikoInboundEvent": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["id", "type", "accountId", "conversationId", "conversationType", "senderId", "senderName", "timestamp", "cursor"],
|
|
27
|
+
"additionalProperties": true,
|
|
28
|
+
"properties": {
|
|
29
|
+
"id": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Stable globally unique event ID for deduplication."
|
|
32
|
+
},
|
|
33
|
+
"type": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"enum": ["message.text", "message.image", "participant.added"],
|
|
36
|
+
"description": "Event type."
|
|
37
|
+
},
|
|
38
|
+
"accountId": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Account this event belongs to."
|
|
41
|
+
},
|
|
42
|
+
"conversationId": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "Platform conversation identifier."
|
|
45
|
+
},
|
|
46
|
+
"conversationType": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"enum": ["direct", "group"],
|
|
49
|
+
"description": "Direct or group conversation."
|
|
50
|
+
},
|
|
51
|
+
"senderId": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Platform user ID of the sender."
|
|
54
|
+
},
|
|
55
|
+
"senderName": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "Display name of the sender at send time."
|
|
58
|
+
},
|
|
59
|
+
"timestamp": {
|
|
60
|
+
"type": "integer",
|
|
61
|
+
"description": "Unix timestamp in milliseconds."
|
|
62
|
+
},
|
|
63
|
+
"cursor": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"description": "Monotonic cursor value for this event."
|
|
66
|
+
},
|
|
67
|
+
"text": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"description": "Message text body. Present for message.text type."
|
|
70
|
+
},
|
|
71
|
+
"mediaUrl": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"format": "uri",
|
|
74
|
+
"description": "Signed media URL. Present for message.image type."
|
|
75
|
+
},
|
|
76
|
+
"mediaCaption": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"description": "Optional caption for media messages."
|
|
79
|
+
},
|
|
80
|
+
"mentionsBot": {
|
|
81
|
+
"type": "boolean",
|
|
82
|
+
"description": "True if the message includes a @mention of the bot. Used for group routing."
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://openclaw.dev/schemas/amiko/platform-outbound.schema.json",
|
|
4
|
+
"title": "AmikoOutboundPayload",
|
|
5
|
+
"description": "Request body schema for POST /internal/openclaw/amiko/messages. OCP-002.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["accountId", "conversationId", "idempotencyKey", "type", "text"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"accountId": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Account scope. Must match the Bearer token's account."
|
|
13
|
+
},
|
|
14
|
+
"conversationId": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Target conversation for delivery."
|
|
17
|
+
},
|
|
18
|
+
"idempotencyKey": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Client-generated key for deduplication. Format: <accountId>:<conversationId>:<uuid-v4>."
|
|
21
|
+
},
|
|
22
|
+
"type": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"enum": ["text", "media"],
|
|
25
|
+
"description": "Message type."
|
|
26
|
+
},
|
|
27
|
+
"text": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Message text content. For type=media, used as caption fallback."
|
|
30
|
+
},
|
|
31
|
+
"mediaUrl": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"format": "uri",
|
|
34
|
+
"description": "Media URL. Required when type=media."
|
|
35
|
+
},
|
|
36
|
+
"mediaCaption": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Optional caption for media messages."
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"if": {
|
|
42
|
+
"properties": { "type": { "const": "media" } }
|
|
43
|
+
},
|
|
44
|
+
"then": {
|
|
45
|
+
"required": ["mediaUrl"]
|
|
46
|
+
}
|
|
47
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
configSchema: {};
|
|
6
|
+
register(api: {
|
|
7
|
+
runtime: any;
|
|
8
|
+
config?: any;
|
|
9
|
+
registerChannel: (params: {
|
|
10
|
+
plugin: any;
|
|
11
|
+
}) => void;
|
|
12
|
+
registerHttpRoute?: (params: any) => void;
|
|
13
|
+
registerHttpHandler?: (handler: (req: any, res: any) => boolean | Promise<boolean>) => void;
|
|
14
|
+
}): void;
|
|
15
|
+
};
|
|
16
|
+
export default _default;
|
|
17
|
+
export { amikoPlugin } from "./src/channel.js";
|
|
18
|
+
export { setAmikoRuntime, getAmikoRuntime } from "./src/runtime.js";
|
|
19
|
+
export type { ResolvedAmikoAccount, AmikoConfig, AmikoAccountConfig } from "./src/types.js";
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;kBAWgB;QACZ,OAAO,EAAE,GAAG,CAAC;QACb,MAAM,CAAC,EAAE,GAAG,CAAC;QACb,eAAe,EAAE,CAAC,MAAM,EAAE;YAAE,MAAM,EAAE,GAAG,CAAA;SAAE,KAAK,IAAI,CAAC;QACnD,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;QAC1C,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;KAC7F;;AAbH,wBA8DE;AAGF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { listAmikoAccountIds, resolveAmikoAccount } from "./src/accounts.js";
|
|
2
|
+
import { amikoPlugin } from "./src/channel.js";
|
|
3
|
+
import { dispatchWebhookRequest, setAmikoRuntime } from "./src/runtime.js";
|
|
4
|
+
export default {
|
|
5
|
+
id: "amiko",
|
|
6
|
+
name: "Amiko",
|
|
7
|
+
description: "Connect OpenClaw bot to Amiko platform (direct and group chat via webhook)",
|
|
8
|
+
configSchema: {},
|
|
9
|
+
register(api) {
|
|
10
|
+
setAmikoRuntime(api.runtime);
|
|
11
|
+
api.registerChannel({ plugin: amikoPlugin });
|
|
12
|
+
const sendChannelNotStarted = (res) => {
|
|
13
|
+
const json = JSON.stringify({ ok: false, error: "Amiko channel not started yet" });
|
|
14
|
+
res.statusCode = 503;
|
|
15
|
+
res.setHeader("Content-Type", "application/json");
|
|
16
|
+
res.setHeader("Content-Length", Buffer.byteLength(json));
|
|
17
|
+
res.end(json);
|
|
18
|
+
};
|
|
19
|
+
const registeredPaths = new Set();
|
|
20
|
+
const cfg = api.config ?? {};
|
|
21
|
+
for (const accountId of listAmikoAccountIds(cfg)) {
|
|
22
|
+
try {
|
|
23
|
+
const account = resolveAmikoAccount({ cfg, accountId });
|
|
24
|
+
const webhookPath = account.config.webhookPath ?? `/amiko/webhook/${account.twinId}`;
|
|
25
|
+
if (registeredPaths.has(webhookPath))
|
|
26
|
+
continue;
|
|
27
|
+
registeredPaths.add(webhookPath);
|
|
28
|
+
api.registerHttpRoute?.({
|
|
29
|
+
path: webhookPath,
|
|
30
|
+
auth: "plugin",
|
|
31
|
+
match: "exact",
|
|
32
|
+
replaceExisting: true,
|
|
33
|
+
handler: async (req, res) => {
|
|
34
|
+
const handled = await dispatchWebhookRequest(req, res);
|
|
35
|
+
if (handled)
|
|
36
|
+
return true;
|
|
37
|
+
sendChannelNotStarted(res);
|
|
38
|
+
return true;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Skip malformed accounts; startAccount/status will surface the real error.
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
api.registerHttpHandler?.(async (req, res) => {
|
|
47
|
+
const handled = await dispatchWebhookRequest(req, res);
|
|
48
|
+
if (handled)
|
|
49
|
+
return true;
|
|
50
|
+
const pathname = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
51
|
+
if (!pathname.startsWith("/amiko/webhook/"))
|
|
52
|
+
return false;
|
|
53
|
+
sendChannelNotStarted(res);
|
|
54
|
+
return true;
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
// Named exports for programmatic use
|
|
59
|
+
export { amikoPlugin } from "./src/channel.js";
|
|
60
|
+
export { setAmikoRuntime, getAmikoRuntime } from "./src/runtime.js";
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AmikoConfig, AmikoAccountConfig, ResolvedAmikoAccount } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_ACCOUNT_ID = "main";
|
|
3
|
+
export declare const DEFAULT_PLATFORM_API_BASE_URL = "https://platform.heyamiko.com";
|
|
4
|
+
export declare const DEFAULT_CHAT_API_BASE_URL = "https://api.amiko.app";
|
|
5
|
+
export declare function normalizeAccountId(id: string): string;
|
|
6
|
+
export declare function listAmikoAccountIds(cfg: {
|
|
7
|
+
channels?: {
|
|
8
|
+
amiko?: AmikoConfig;
|
|
9
|
+
};
|
|
10
|
+
}): string[];
|
|
11
|
+
export declare function resolveDefaultAmikoAccountId(cfg: {
|
|
12
|
+
channels?: {
|
|
13
|
+
amiko?: AmikoConfig;
|
|
14
|
+
};
|
|
15
|
+
}): string;
|
|
16
|
+
export declare function resolveAmikoAccountConfig(amiko: AmikoConfig, accountId: string): AmikoAccountConfig;
|
|
17
|
+
export declare function resolveAmikoAccount(params: {
|
|
18
|
+
cfg: {
|
|
19
|
+
channels?: {
|
|
20
|
+
amiko?: AmikoConfig;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
accountId: string;
|
|
24
|
+
}): ResolvedAmikoAccount;
|
|
25
|
+
export declare function listEnabledAmikoAccounts(cfg: {
|
|
26
|
+
channels?: {
|
|
27
|
+
amiko?: AmikoConfig;
|
|
28
|
+
};
|
|
29
|
+
}): string[];
|
|
30
|
+
//# sourceMappingURL=accounts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../src/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAExF,eAAO,MAAM,kBAAkB,SAAS,CAAC;AACzC,eAAO,MAAM,6BAA6B,kCAAkC,CAAC;AAC7E,eAAO,MAAM,yBAAyB,0BAA0B,CAAC;AAEjE,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAErD;AAmBD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,WAAW,CAAA;KAAE,CAAA;CAAE,GAAG,MAAM,EAAE,CAKzF;AAED,wBAAgB,4BAA4B,CAAC,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,WAAW,CAAA;KAAE,CAAA;CAAE,GAAG,MAAM,CAchG;AAED,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,MAAM,GAChB,kBAAkB,CAuCpB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,GAAG,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,WAAW,CAAA;SAAE,CAAA;KAAE,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,oBAAoB,CAiCvB;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,WAAW,CAAA;KAAE,CAAA;CAAE,GAAG,MAAM,EAAE,CAS9F"}
|