@eyeclaw/eyeclaw 2.0.15 → 2.2.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/index.ts +51 -19
- package/openclaw.plugin.json +7 -20
- package/package.json +5 -17
- package/src/cli.ts +18 -22
- package/src/http-handler.ts +208 -0
- package/src/types.ts +1 -36
- package/src/websocket-client.ts +316 -0
- package/src/channel.ts +0 -325
- package/src/client.ts +0 -377
package/index.ts
CHANGED
|
@@ -1,31 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EyeClaw SDK - OpenClaw Plugin
|
|
3
|
+
*
|
|
4
|
+
* 支持两种模式:
|
|
5
|
+
* 1. HTTP 端点:外部直接调用 POST /eyeclaw/chat,SSE 流式返回
|
|
6
|
+
* 2. WebSocket:连接 Rails 服务器,接收消息并流式返回
|
|
7
|
+
*/
|
|
1
8
|
import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
9
|
+
import { createHttpHandler, eyeclawConfigSchema } from './src/http-handler.js'
|
|
10
|
+
import { EyeClawWebSocketClient } from './src/websocket-client.js'
|
|
11
|
+
import type { EyeClawConfig } from './src/types.js'
|
|
4
12
|
|
|
5
13
|
/**
|
|
6
|
-
* EyeClaw
|
|
7
|
-
*
|
|
8
|
-
* Connects local OpenClaw instance to EyeClaw platform via WebSocket.
|
|
14
|
+
* EyeClaw 插件
|
|
9
15
|
*/
|
|
10
|
-
const
|
|
16
|
+
const eyeclawPlugin = {
|
|
11
17
|
id: 'eyeclaw',
|
|
12
18
|
name: 'EyeClaw',
|
|
13
|
-
description: 'EyeClaw
|
|
14
|
-
configSchema:
|
|
19
|
+
description: 'EyeClaw 消息转发插件 - HTTP SSE 流式 + WebSocket 客户端',
|
|
20
|
+
configSchema: eyeclawConfigSchema,
|
|
15
21
|
|
|
16
22
|
register(api: OpenClawPluginApi) {
|
|
17
|
-
|
|
18
|
-
// This allows channel.ts to access runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher
|
|
19
|
-
setRuntime(api.runtime)
|
|
23
|
+
const logger = api.logger
|
|
20
24
|
|
|
21
|
-
//
|
|
22
|
-
|
|
25
|
+
// 解析配置
|
|
26
|
+
const rawConfig = api.config?.plugins?.entries?.eyeclaw?.config
|
|
27
|
+
const config: EyeClawConfig = {
|
|
28
|
+
sdkToken: rawConfig?.sdkToken || '',
|
|
29
|
+
botId: rawConfig?.botId || '',
|
|
30
|
+
serverUrl: rawConfig?.serverUrl || '',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// HTTP 处理器(SSE 流式)
|
|
34
|
+
api.registerHttpHandler(createHttpHandler(api, () => config))
|
|
35
|
+
logger.info('[EyeClaw] HTTP handler registered: /eyeclaw/*')
|
|
36
|
+
|
|
37
|
+
// WebSocket 客户端(连接 Rails 接收消息)
|
|
38
|
+
if (config.sdkToken && config.botId && config.serverUrl) {
|
|
39
|
+
const wsClient = new EyeClawWebSocketClient(api, config)
|
|
40
|
+
wsClient.start()
|
|
41
|
+
logger.info('[EyeClaw] WebSocket client starting...')
|
|
42
|
+
|
|
43
|
+
// 存储客户端引用,防止被垃圾回收
|
|
44
|
+
;(global as any).__eyeclaw_ws = wsClient
|
|
45
|
+
} else {
|
|
46
|
+
logger.warn('[EyeClaw] WebSocket not started: missing config (sdkToken, botId, serverUrl)')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 打印启动信息
|
|
50
|
+
const gatewayPort = api.config?.gateway?.port ?? 18789
|
|
51
|
+
console.log('')
|
|
52
|
+
console.log('╔═══════════════════════════════════════════════════════════════════════╗')
|
|
53
|
+
console.log('║ EyeClaw Plugin 已启动 ║')
|
|
54
|
+
console.log('╠═══════════════════════════════════════════════════════════════════════╣')
|
|
55
|
+
console.log(`║ HTTP 端点: POST http://127.0.0.1:${gatewayPort}/eyeclaw/chat ║`)
|
|
56
|
+
console.log(`║ WebSocket: ${config.serverUrl ? '已配置' : '未配置'} ║`)
|
|
57
|
+
console.log(`║ SDK Token: ${config.sdkToken ? config.sdkToken.substring(0, 8) + '...' : '未配置'} ║`)
|
|
58
|
+
console.log('╚═══════════════════════════════════════════════════════════════════════╝')
|
|
59
|
+
console.log('')
|
|
23
60
|
},
|
|
24
61
|
}
|
|
25
62
|
|
|
26
|
-
export default
|
|
27
|
-
|
|
28
|
-
// Re-export for direct usage
|
|
29
|
-
export { EyeClawClient } from './src/client.js'
|
|
30
|
-
export * from './src/types.js'
|
|
31
|
-
export { eyeclawPlugin } from './src/channel.js'
|
|
63
|
+
export default eyeclawPlugin
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,34 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "eyeclaw",
|
|
3
|
-
"channels": ["eyeclaw"],
|
|
4
3
|
"configSchema": {
|
|
5
4
|
"type": "object",
|
|
6
|
-
"additionalProperties": true,
|
|
7
5
|
"properties": {
|
|
8
|
-
"enabled": {
|
|
9
|
-
"type": "boolean",
|
|
10
|
-
"description": "Enable/disable the plugin"
|
|
11
|
-
},
|
|
12
|
-
"botId": {
|
|
13
|
-
"type": ["string", "number"],
|
|
14
|
-
"description": "Bot ID from EyeClaw platform"
|
|
15
|
-
},
|
|
16
6
|
"sdkToken": {
|
|
17
7
|
"type": "string",
|
|
18
8
|
"description": "SDK token for authentication"
|
|
19
9
|
},
|
|
20
|
-
"
|
|
10
|
+
"botId": {
|
|
21
11
|
"type": "string",
|
|
22
|
-
"description": "EyeClaw
|
|
23
|
-
},
|
|
24
|
-
"reconnectInterval": {
|
|
25
|
-
"type": "number",
|
|
26
|
-
"description": "Reconnect interval in milliseconds"
|
|
12
|
+
"description": "Bot ID in EyeClaw Rails app"
|
|
27
13
|
},
|
|
28
|
-
"
|
|
29
|
-
"type": "
|
|
30
|
-
"description": "
|
|
14
|
+
"serverUrl": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "EyeClaw Rails server URL (e.g., http://localhost:3000)"
|
|
31
17
|
}
|
|
32
|
-
}
|
|
18
|
+
},
|
|
19
|
+
"required": ["sdkToken", "botId", "serverUrl"]
|
|
33
20
|
}
|
|
34
21
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eyeclaw/eyeclaw",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"description": "EyeClaw
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "EyeClaw plugin for OpenClaw - HTTP SSE streaming + WebSocket client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
7
7
|
"types": "./index.ts",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"openclaw",
|
|
17
17
|
"eyeclaw",
|
|
18
18
|
"plugin",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
19
|
+
"http",
|
|
20
|
+
"sse",
|
|
21
|
+
"streaming"
|
|
22
22
|
],
|
|
23
23
|
"author": "EyeClaw Team",
|
|
24
24
|
"license": "MIT",
|
|
@@ -35,24 +35,12 @@
|
|
|
35
35
|
"extensions": [
|
|
36
36
|
"./index.ts"
|
|
37
37
|
],
|
|
38
|
-
"channel": {
|
|
39
|
-
"id": "eyeclaw",
|
|
40
|
-
"label": "EyeClaw",
|
|
41
|
-
"selectionLabel": "EyeClaw Platform",
|
|
42
|
-
"blurb": "Connect local OpenClaw to EyeClaw platform via WebSocket.",
|
|
43
|
-
"order": 100
|
|
44
|
-
},
|
|
45
38
|
"install": {
|
|
46
39
|
"npmSpec": "@eyeclaw/eyeclaw",
|
|
47
40
|
"localPath": ".",
|
|
48
41
|
"defaultChoice": "npm"
|
|
49
42
|
}
|
|
50
43
|
},
|
|
51
|
-
"dependencies": {
|
|
52
|
-
"ws": "^8.16.0",
|
|
53
|
-
"@types/ws": "^8.5.10",
|
|
54
|
-
"zod": "^3.22.4"
|
|
55
|
-
},
|
|
56
44
|
"peerDependencies": {
|
|
57
45
|
"openclaw": ">=2026.2.3"
|
|
58
46
|
},
|
package/src/cli.ts
CHANGED
|
@@ -1,36 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import chalk from 'chalk'
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* EyeClaw SDK CLI
|
|
6
5
|
*
|
|
7
6
|
* This file provides a minimal CLI interface for the plugin.
|
|
8
|
-
* The actual installation is handled by: openclaw plugins install @eyeclaw/
|
|
7
|
+
* The actual installation is handled by: openclaw plugins install @eyeclaw/eyeclaw
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
const USAGE = `
|
|
12
|
-
${chalk.bold.blue('EyeClaw SDK')} - Connect your local OpenClaw to EyeClaw platform
|
|
11
|
+
${'Use chalk.bold.blue'}( 'EyeClaw SDK')} - Connect your local OpenClaw to EyeClaw platform
|
|
13
12
|
|
|
14
|
-
${chalk.bold('Installation:')}
|
|
15
|
-
${chalk.cyan('openclaw plugins install @eyeclaw/
|
|
13
|
+
${'Use chalk.bold'}( 'Installation:')}
|
|
14
|
+
${'Use chalk.cyan'}( 'openclaw plugins install @eyeclaw/eyeclaw')}
|
|
16
15
|
|
|
17
|
-
${chalk.bold('Configuration:')}
|
|
18
|
-
${chalk.cyan('openclaw config set
|
|
19
|
-
${chalk.cyan('openclaw config set channels.eyeclaw.botId "your-bot-id"')}
|
|
20
|
-
${chalk.cyan('openclaw config set channels.eyeclaw.sdkToken "your-sdk-token"')}
|
|
21
|
-
${chalk.cyan('openclaw config set channels.eyeclaw.serverUrl "https://eyeclaw.io"')}
|
|
16
|
+
${'Use chalk.bold'}( 'Configuration:')}
|
|
17
|
+
${'Use chalk.cyan'}( 'openclaw config set plugins.eyeclaw.sdkToken "your-sdk-token"')}
|
|
22
18
|
|
|
23
|
-
${chalk.bold('
|
|
24
|
-
${chalk.cyan('
|
|
19
|
+
${'Use chalk.bold'}( 'HTTP Endpoint:')}
|
|
20
|
+
${'Use chalk.cyan'}( 'POST http://127.0.0.1:18789/eyeclaw/chat')}
|
|
21
|
+
Headers: Authorization: Bearer <sdkToken>
|
|
22
|
+
Body: { "message": "Hello" }
|
|
25
23
|
|
|
26
|
-
${chalk.bold('
|
|
27
|
-
${chalk.cyan('openclaw plugins
|
|
24
|
+
${'Use chalk.bold'}( 'Upgrade:')}
|
|
25
|
+
${'Use chalk.cyan'}( 'openclaw plugins update eyeclaw')}
|
|
28
26
|
|
|
29
|
-
${chalk.bold('
|
|
30
|
-
${chalk.cyan('
|
|
27
|
+
${'Use chalk.bold'}( 'Uninstall:')}
|
|
28
|
+
${'Use chalk.cyan'}( 'openclaw plugins uninstall eyeclaw')}
|
|
31
29
|
|
|
32
|
-
${chalk.bold('
|
|
33
|
-
${chalk.cyan('https://
|
|
30
|
+
${'Use chalk.bold'}( 'Documentation:')}
|
|
31
|
+
${'Use chalk.cyan'}( 'https://eyeclaw.io/docs')}
|
|
34
32
|
`
|
|
35
33
|
|
|
36
34
|
function main() {
|
|
@@ -42,16 +40,14 @@ function main() {
|
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
if (args.includes('--version') || args.includes('-v')) {
|
|
45
|
-
// Read version from package.json
|
|
46
43
|
const pkg = require('../package.json')
|
|
47
44
|
console.log(`v${pkg.version}`)
|
|
48
45
|
process.exit(0)
|
|
49
46
|
}
|
|
50
47
|
|
|
51
|
-
// Default: show usage
|
|
52
48
|
console.log(USAGE)
|
|
53
|
-
console.log(
|
|
54
|
-
console.log(
|
|
49
|
+
console.log('ℹ This is an OpenClaw plugin. Please install it using:')
|
|
50
|
+
console.log(' openclaw plugins install @eyeclaw/eyeclaw\n')
|
|
55
51
|
}
|
|
56
52
|
|
|
57
53
|
main()
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EyeClaw SDK - OpenClaw HTTP Handler Plugin
|
|
3
|
+
*
|
|
4
|
+
* 直接注册 HTTP 端点,实现真正的 SSE 流式转发
|
|
5
|
+
*/
|
|
6
|
+
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
7
|
+
import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
|
|
8
|
+
import type { EyeClawConfig } from './types.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 读取 JSON 请求体
|
|
12
|
+
*/
|
|
13
|
+
async function readJsonBody(req: IncomingMessage): Promise<any> {
|
|
14
|
+
const chunks: Buffer[] = []
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
req.on('data', (chunk) => chunks.push(chunk))
|
|
17
|
+
req.on('end', () => {
|
|
18
|
+
try {
|
|
19
|
+
const body = Buffer.concat(chunks).toString('utf-8')
|
|
20
|
+
resolve(body ? JSON.parse(body) : {})
|
|
21
|
+
} catch (e) {
|
|
22
|
+
reject(e)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
req.on('error', (e) => reject(e))
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 验证 Authorization 头
|
|
31
|
+
*/
|
|
32
|
+
function verifyAuth(authHeader: string | string[] | undefined, expectedToken: string): boolean {
|
|
33
|
+
if (!expectedToken) return true
|
|
34
|
+
const header = Array.isArray(authHeader) ? authHeader[0] : authHeader
|
|
35
|
+
if (!header) return false
|
|
36
|
+
if (header.startsWith('Bearer ')) return header.slice(7) === expectedToken
|
|
37
|
+
return header === expectedToken
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 格式化 SSE 响应
|
|
42
|
+
*/
|
|
43
|
+
function formatSSE(event: string, data: any): string {
|
|
44
|
+
return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 创建 HTTP 处理器
|
|
49
|
+
*/
|
|
50
|
+
export function createHttpHandler(api: OpenClawPluginApi, getConfig: () => EyeClawConfig) {
|
|
51
|
+
return async function handler(req: IncomingMessage, res: ServerResponse): Promise<boolean> {
|
|
52
|
+
const url = new URL(req.url ?? '/', 'http://localhost')
|
|
53
|
+
if (!url.pathname.startsWith('/eyeclaw/')) return false
|
|
54
|
+
|
|
55
|
+
const logger = api.logger
|
|
56
|
+
const config = getConfig()
|
|
57
|
+
|
|
58
|
+
// 验证鉴权
|
|
59
|
+
const authHeader = req.headers.authorization
|
|
60
|
+
if (!verifyAuth(authHeader, config.sdkToken || '')) {
|
|
61
|
+
logger.warn('[EyeClaw] Unauthorized request')
|
|
62
|
+
res.statusCode = 401
|
|
63
|
+
res.setHeader('Content-Type', 'application/json')
|
|
64
|
+
res.end(JSON.stringify({ error: 'Unauthorized' }))
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (req.method !== 'POST') {
|
|
69
|
+
res.statusCode = 405
|
|
70
|
+
res.setHeader('Allow', 'POST')
|
|
71
|
+
res.end('Method Not Allowed')
|
|
72
|
+
return true
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const body = await readJsonBody(req)
|
|
77
|
+
const { message, session_id, stream_id } = body
|
|
78
|
+
|
|
79
|
+
if (!message) {
|
|
80
|
+
res.statusCode = 400
|
|
81
|
+
res.setHeader('Content-Type', 'application/json')
|
|
82
|
+
res.end(JSON.stringify({ error: 'Missing required field: message' }))
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
logger.info(`[EyeClaw] Chat: ${message.substring(0, 50)}...`)
|
|
87
|
+
|
|
88
|
+
// SSE 响应头
|
|
89
|
+
res.setHeader('Content-Type', 'text/event-stream')
|
|
90
|
+
res.setHeader('Cache-Control', 'no-cache')
|
|
91
|
+
res.setHeader('Connection', 'keep-alive')
|
|
92
|
+
res.setHeader('X-Accel-Buffering', 'no')
|
|
93
|
+
|
|
94
|
+
// 获取 Gateway 配置
|
|
95
|
+
const gatewayPort = api.config?.gateway?.port ?? 18789
|
|
96
|
+
const gatewayToken = api.config?.gateway?.auth?.token
|
|
97
|
+
const sessionKey = session_id ? `eyeclaw:${session_id}` : 'eyeclaw:default'
|
|
98
|
+
|
|
99
|
+
// 调用 OpenClaw
|
|
100
|
+
const openclawUrl = `http://127.0.0.1:${gatewayPort}/v1/chat/completions`
|
|
101
|
+
const openclawBody = {
|
|
102
|
+
model: 'openclaw:main',
|
|
103
|
+
stream: true,
|
|
104
|
+
messages: [{ role: 'user', content: message }],
|
|
105
|
+
user: sessionKey,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
|
|
109
|
+
if (gatewayToken) headers['Authorization'] = `Bearer ${gatewayToken}`
|
|
110
|
+
|
|
111
|
+
logger.info(`[EyeClaw] Calling OpenClaw: ${openclawUrl}`)
|
|
112
|
+
|
|
113
|
+
const openclawResponse = await fetch(openclawUrl, {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers,
|
|
116
|
+
body: JSON.stringify(openclawBody),
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
if (!openclawResponse.ok) {
|
|
120
|
+
const errorText = await openclawResponse.text()
|
|
121
|
+
throw new Error(`OpenClaw API error: ${openclawResponse.status} - ${errorText}`)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const reader = openclawResponse.body?.getReader()
|
|
125
|
+
if (!reader) throw new Error('No response body')
|
|
126
|
+
|
|
127
|
+
const decoder = new TextDecoder()
|
|
128
|
+
let buffer = ''
|
|
129
|
+
const currentStreamId = stream_id || Date.now().toString()
|
|
130
|
+
|
|
131
|
+
res.write(formatSSE('stream_start', { stream_id: currentStreamId }))
|
|
132
|
+
|
|
133
|
+
// 保活
|
|
134
|
+
const keepaliveInterval = setInterval(() => { try { res.write(': keepalive\n\n') } catch { clearInterval(keepaliveInterval) } }, 7000)
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
while (true) {
|
|
138
|
+
const { done, value } = await reader.read()
|
|
139
|
+
if (done) break
|
|
140
|
+
|
|
141
|
+
buffer += decoder.decode(value, { stream: true })
|
|
142
|
+
const lines = buffer.split('\n')
|
|
143
|
+
buffer = lines.pop() || ''
|
|
144
|
+
|
|
145
|
+
for (const line of lines) {
|
|
146
|
+
const trimmed = line.trim()
|
|
147
|
+
if (!trimmed.startsWith('data: ')) continue
|
|
148
|
+
const data = trimmed.slice(6)
|
|
149
|
+
if (data === '[DONE]') continue
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const chunk = JSON.parse(data)
|
|
153
|
+
const content = chunk.choices?.[0]?.delta?.content
|
|
154
|
+
if (content) {
|
|
155
|
+
res.write(formatSSE('stream_chunk', { stream_id: currentStreamId, content }))
|
|
156
|
+
}
|
|
157
|
+
} catch { /* ignore */ }
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} finally {
|
|
161
|
+
clearInterval(keepaliveInterval)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
res.write(formatSSE('stream_end', { stream_id: currentStreamId }))
|
|
165
|
+
res.end()
|
|
166
|
+
logger.info(`[EyeClaw] Completed: ${currentStreamId}`)
|
|
167
|
+
return true
|
|
168
|
+
|
|
169
|
+
} catch (error) {
|
|
170
|
+
const errorMsg = error instanceof Error ? error.message : String(error)
|
|
171
|
+
logger.error(`[EyeClaw] Error: ${errorMsg}`)
|
|
172
|
+
res.write(formatSSE('stream_error', { error: errorMsg }))
|
|
173
|
+
res.end()
|
|
174
|
+
return true
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 插件配置 Schema
|
|
181
|
+
*/
|
|
182
|
+
export const eyeclawConfigSchema = {
|
|
183
|
+
safeParse: (value: unknown) => {
|
|
184
|
+
if (!value || typeof value !== 'object') {
|
|
185
|
+
return { success: false, errors: ['Expected config object'] }
|
|
186
|
+
}
|
|
187
|
+
const cfg = value as any
|
|
188
|
+
if (!cfg.sdkToken) {
|
|
189
|
+
return { success: false, errors: ['sdkToken is required'] }
|
|
190
|
+
}
|
|
191
|
+
if (!cfg.botId) {
|
|
192
|
+
return { success: false, errors: ['botId is required'] }
|
|
193
|
+
}
|
|
194
|
+
if (!cfg.serverUrl) {
|
|
195
|
+
return { success: false, errors: ['serverUrl is required'] }
|
|
196
|
+
}
|
|
197
|
+
return { success: true, data: cfg }
|
|
198
|
+
},
|
|
199
|
+
jsonSchema: {
|
|
200
|
+
type: 'object',
|
|
201
|
+
properties: {
|
|
202
|
+
sdkToken: { type: 'string', description: 'EyeClaw SDK Token' },
|
|
203
|
+
botId: { type: 'string', description: 'Bot ID in EyeClaw Rails app' },
|
|
204
|
+
serverUrl: { type: 'string', description: 'EyeClaw Rails server URL' },
|
|
205
|
+
},
|
|
206
|
+
required: ['sdkToken', 'botId', 'serverUrl'],
|
|
207
|
+
},
|
|
208
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,36 +1,9 @@
|
|
|
1
|
-
// Type definitions for EyeClaw
|
|
1
|
+
// Type definitions for EyeClaw Plugin
|
|
2
2
|
|
|
3
3
|
export interface EyeClawConfig {
|
|
4
|
-
enabled?: boolean
|
|
5
|
-
botId: string | number
|
|
6
4
|
sdkToken: string
|
|
7
|
-
serverUrl?: string
|
|
8
|
-
reconnectInterval?: number
|
|
9
|
-
heartbeatInterval?: number
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface ResolvedEyeClawAccount {
|
|
13
|
-
accountId: string
|
|
14
|
-
enabled: boolean
|
|
15
|
-
configured: boolean
|
|
16
|
-
name: string
|
|
17
|
-
config?: EyeClawConfig
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface PluginConfig {
|
|
21
|
-
enabled: boolean
|
|
22
5
|
botId: string
|
|
23
|
-
sdkToken: string
|
|
24
6
|
serverUrl: string
|
|
25
|
-
reconnectInterval?: number
|
|
26
|
-
heartbeatInterval?: number
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface OpenClawContext {
|
|
30
|
-
config: PluginConfig
|
|
31
|
-
logger: Logger
|
|
32
|
-
emit: (event: string, data: unknown) => void
|
|
33
|
-
on: (event: string, handler: (data: unknown) => void) => void
|
|
34
7
|
}
|
|
35
8
|
|
|
36
9
|
export interface Logger {
|
|
@@ -40,14 +13,6 @@ export interface Logger {
|
|
|
40
13
|
debug: (message: string) => void
|
|
41
14
|
}
|
|
42
15
|
|
|
43
|
-
export interface ChannelMessage {
|
|
44
|
-
type: string
|
|
45
|
-
content?: string
|
|
46
|
-
role?: 'user' | 'assistant'
|
|
47
|
-
timestamp?: string
|
|
48
|
-
metadata?: Record<string, unknown>
|
|
49
|
-
}
|
|
50
|
-
|
|
51
16
|
export interface BotStatus {
|
|
52
17
|
online: boolean
|
|
53
18
|
status: string
|