@eyeclaw/eyeclaw 1.0.5 → 2.0.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 +4 -92
- package/package.json +1 -1
- package/src/channel.ts +209 -0
- package/src/types.ts +18 -2
package/index.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
|
|
2
2
|
import { emptyPluginConfigSchema } from 'openclaw/plugin-sdk'
|
|
3
|
-
import {
|
|
4
|
-
import type { PluginConfig } from './src/types.js'
|
|
5
|
-
|
|
6
|
-
let client: EyeClawClient | null = null
|
|
3
|
+
import { eyeclawPlugin } from './src/channel.js'
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
6
|
* EyeClaw SDK - OpenClaw Channel Plugin
|
|
@@ -17,94 +14,8 @@ const plugin = {
|
|
|
17
14
|
configSchema: emptyPluginConfigSchema(),
|
|
18
15
|
|
|
19
16
|
register(api: OpenClawPluginApi) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Try multiple ways to access config
|
|
24
|
-
const runtimeConfig = runtime.config as any
|
|
25
|
-
const apiConfig = (api as any).config
|
|
26
|
-
const pluginConfig = (api as any).pluginConfig
|
|
27
|
-
|
|
28
|
-
// Debug: log entire config structure from different sources
|
|
29
|
-
logger.info('🔍 runtime.config:', JSON.stringify(runtimeConfig, null, 2))
|
|
30
|
-
logger.info('🔍 api.config:', JSON.stringify(apiConfig, null, 2))
|
|
31
|
-
logger.info('🔍 api.pluginConfig:', JSON.stringify(pluginConfig, null, 2))
|
|
32
|
-
logger.info('🔍 api keys:', Object.keys(api).join(', '))
|
|
33
|
-
logger.info('🔍 runtime keys:', Object.keys(runtime).join(', '))
|
|
34
|
-
|
|
35
|
-
// Try to get config from different sources
|
|
36
|
-
let eyeclawConfig: PluginConfig =
|
|
37
|
-
pluginConfig ||
|
|
38
|
-
apiConfig ||
|
|
39
|
-
runtimeConfig?.channels?.eyeclaw ||
|
|
40
|
-
runtimeConfig?.plugins?.eyeclaw ||
|
|
41
|
-
{}
|
|
42
|
-
|
|
43
|
-
// Debug: log eyeclaw config
|
|
44
|
-
logger.info('🔍 EyeClaw config:', JSON.stringify(eyeclawConfig, null, 2))
|
|
45
|
-
logger.info('🔍 botId type:', typeof eyeclawConfig.botId)
|
|
46
|
-
logger.info('🔍 botId value:', eyeclawConfig.botId)
|
|
47
|
-
logger.info('🔍 sdkToken type:', typeof eyeclawConfig.sdkToken)
|
|
48
|
-
logger.info('🔍 sdkToken exists:', !!eyeclawConfig.sdkToken)
|
|
49
|
-
|
|
50
|
-
// Check if enabled
|
|
51
|
-
if (eyeclawConfig.enabled === false) {
|
|
52
|
-
logger.info('Plugin disabled in config')
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Validate required fields with detailed logging
|
|
57
|
-
if (!eyeclawConfig.botId) {
|
|
58
|
-
logger.warn('❌ botId is missing or falsy:', eyeclawConfig.botId)
|
|
59
|
-
logger.warn('Configure with: openclaw config set channels.eyeclaw.botId "YOUR_BOT_ID"')
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!eyeclawConfig.sdkToken) {
|
|
64
|
-
logger.warn('❌ sdkToken is missing or falsy')
|
|
65
|
-
logger.warn('Configure with: openclaw config set channels.eyeclaw.sdkToken "YOUR_SDK_TOKEN"')
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
logger.info('✅ Config validation passed')
|
|
70
|
-
|
|
71
|
-
// Convert botId to string (OpenClaw may auto-convert "1" to number 1)
|
|
72
|
-
eyeclawConfig.botId = String(eyeclawConfig.botId)
|
|
73
|
-
|
|
74
|
-
// Set defaults
|
|
75
|
-
eyeclawConfig.serverUrl = eyeclawConfig.serverUrl || 'http://localhost:3000'
|
|
76
|
-
eyeclawConfig.reconnectInterval = eyeclawConfig.reconnectInterval || 5000
|
|
77
|
-
eyeclawConfig.heartbeatInterval = eyeclawConfig.heartbeatInterval || 30000
|
|
78
|
-
|
|
79
|
-
logger.info('🦞 Starting EyeClaw SDK...', { botId: eyeclawConfig.botId, serverUrl: eyeclawConfig.serverUrl })
|
|
80
|
-
|
|
81
|
-
// Create logger adapter for client (simple console logger)
|
|
82
|
-
const clientLogger = {
|
|
83
|
-
debug: (msg: string) => logger.debug(msg),
|
|
84
|
-
info: (msg: string) => logger.info(msg),
|
|
85
|
-
warn: (msg: string) => logger.warn(msg),
|
|
86
|
-
error: (msg: string) => logger.error(msg),
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Create and connect WebSocket client
|
|
90
|
-
client = new EyeClawClient(eyeclawConfig, clientLogger)
|
|
91
|
-
|
|
92
|
-
client.connect().then(() => {
|
|
93
|
-
logger.info('✅ Successfully connected to EyeClaw platform')
|
|
94
|
-
}).catch((error: Error) => {
|
|
95
|
-
logger.error('Failed to connect to EyeClaw', { error: error.message })
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
// Handle shutdown
|
|
99
|
-
const shutdown = () => {
|
|
100
|
-
if (client) {
|
|
101
|
-
logger.info('🛑 Shutting down EyeClaw SDK...')
|
|
102
|
-
client.disconnect()
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
process.on('SIGINT', shutdown)
|
|
107
|
-
process.on('SIGTERM', shutdown)
|
|
17
|
+
// Register EyeClaw as a channel plugin
|
|
18
|
+
api.registerChannel({ plugin: eyeclawPlugin })
|
|
108
19
|
},
|
|
109
20
|
}
|
|
110
21
|
|
|
@@ -113,3 +24,4 @@ export default plugin
|
|
|
113
24
|
// Re-export for direct usage
|
|
114
25
|
export { EyeClawClient } from './src/client.js'
|
|
115
26
|
export * from './src/types.js'
|
|
27
|
+
export { eyeclawPlugin } from './src/channel.js'
|
package/package.json
CHANGED
package/src/channel.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import type { ChannelPlugin } from 'openclaw/plugin-sdk'
|
|
2
|
+
import { DEFAULT_ACCOUNT_ID } from 'openclaw/plugin-sdk'
|
|
3
|
+
import type { EyeClawConfig, ResolvedEyeClawAccount } from './types.js'
|
|
4
|
+
import { EyeClawClient } from './client.js'
|
|
5
|
+
|
|
6
|
+
// Active clients map (accountId -> client)
|
|
7
|
+
const clients = new Map<string, EyeClawClient>()
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolve EyeClaw account configuration
|
|
11
|
+
*/
|
|
12
|
+
function resolveEyeClawAccount(cfg: any, accountId: string): ResolvedEyeClawAccount {
|
|
13
|
+
const eyeclawConfig: EyeClawConfig = cfg?.channels?.eyeclaw || {}
|
|
14
|
+
|
|
15
|
+
// Default account uses top-level config
|
|
16
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
17
|
+
return {
|
|
18
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
19
|
+
enabled: eyeclawConfig.enabled !== false,
|
|
20
|
+
configured: !!(eyeclawConfig.botId && eyeclawConfig.sdkToken),
|
|
21
|
+
name: 'Default',
|
|
22
|
+
config: eyeclawConfig,
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Named accounts not supported yet
|
|
27
|
+
throw new Error(`Named accounts not yet supported for EyeClaw`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* List all EyeClaw account IDs
|
|
32
|
+
*/
|
|
33
|
+
function listEyeClawAccountIds(cfg: any): string[] {
|
|
34
|
+
return [DEFAULT_ACCOUNT_ID]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* EyeClaw Channel Plugin
|
|
39
|
+
*/
|
|
40
|
+
export const eyeclawPlugin: ChannelPlugin<ResolvedEyeClawAccount> = {
|
|
41
|
+
id: 'eyeclaw',
|
|
42
|
+
|
|
43
|
+
meta: {
|
|
44
|
+
id: 'eyeclaw',
|
|
45
|
+
label: 'EyeClaw',
|
|
46
|
+
selectionLabel: 'EyeClaw Platform',
|
|
47
|
+
docsPath: '/channels/eyeclaw',
|
|
48
|
+
docsLabel: 'eyeclaw',
|
|
49
|
+
blurb: 'EyeClaw platform integration via WebSocket.',
|
|
50
|
+
order: 100,
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
capabilities: {
|
|
54
|
+
chatTypes: ['direct', 'channel'],
|
|
55
|
+
polls: false,
|
|
56
|
+
threads: false,
|
|
57
|
+
media: false,
|
|
58
|
+
reactions: false,
|
|
59
|
+
edit: false,
|
|
60
|
+
reply: false,
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
reload: { configPrefixes: ['channels.eyeclaw'] },
|
|
64
|
+
|
|
65
|
+
configSchema: {
|
|
66
|
+
schema: {
|
|
67
|
+
type: 'object',
|
|
68
|
+
additionalProperties: true,
|
|
69
|
+
properties: {
|
|
70
|
+
enabled: {
|
|
71
|
+
type: 'boolean',
|
|
72
|
+
description: 'Enable/disable the plugin',
|
|
73
|
+
},
|
|
74
|
+
botId: {
|
|
75
|
+
type: ['string', 'number'],
|
|
76
|
+
description: 'Bot ID from EyeClaw platform',
|
|
77
|
+
},
|
|
78
|
+
sdkToken: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
description: 'SDK token for authentication',
|
|
81
|
+
},
|
|
82
|
+
serverUrl: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
description: 'EyeClaw server URL',
|
|
85
|
+
},
|
|
86
|
+
reconnectInterval: {
|
|
87
|
+
type: 'number',
|
|
88
|
+
description: 'Reconnect interval in milliseconds',
|
|
89
|
+
},
|
|
90
|
+
heartbeatInterval: {
|
|
91
|
+
type: 'number',
|
|
92
|
+
description: 'Heartbeat interval in milliseconds',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
config: {
|
|
99
|
+
listAccountIds: (cfg) => listEyeClawAccountIds(cfg),
|
|
100
|
+
resolveAccount: (cfg, accountId) => resolveEyeClawAccount(cfg, accountId || DEFAULT_ACCOUNT_ID),
|
|
101
|
+
defaultAccountId: () => DEFAULT_ACCOUNT_ID,
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
status: {
|
|
105
|
+
defaultRuntime: {
|
|
106
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
107
|
+
running: false,
|
|
108
|
+
lastStartAt: null,
|
|
109
|
+
lastStopAt: null,
|
|
110
|
+
lastError: null,
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
buildChannelSummary: ({ snapshot }) => ({
|
|
114
|
+
configured: snapshot.configured ?? false,
|
|
115
|
+
running: snapshot.running ?? false,
|
|
116
|
+
lastStartAt: snapshot.lastStartAt ?? null,
|
|
117
|
+
lastStopAt: snapshot.lastStopAt ?? null,
|
|
118
|
+
lastError: snapshot.lastError ?? null,
|
|
119
|
+
}),
|
|
120
|
+
|
|
121
|
+
probeAccount: async ({ account }) => {
|
|
122
|
+
// Simple probe - check if configured
|
|
123
|
+
return {
|
|
124
|
+
ok: account.configured,
|
|
125
|
+
message: account.configured ? 'Configured' : 'Not configured',
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
buildAccountSnapshot: ({ account, runtime, probe }) => ({
|
|
130
|
+
accountId: account.accountId,
|
|
131
|
+
enabled: account.enabled,
|
|
132
|
+
configured: account.configured,
|
|
133
|
+
name: account.name,
|
|
134
|
+
running: runtime?.running ?? false,
|
|
135
|
+
lastStartAt: runtime?.lastStartAt ?? null,
|
|
136
|
+
lastStopAt: runtime?.lastStopAt ?? null,
|
|
137
|
+
lastError: runtime?.lastError ?? null,
|
|
138
|
+
probe,
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
gateway: {
|
|
143
|
+
startAccount: async (ctx) => {
|
|
144
|
+
const account = resolveEyeClawAccount(ctx.cfg, ctx.accountId)
|
|
145
|
+
|
|
146
|
+
if (!account.configured || !account.config) {
|
|
147
|
+
throw new Error('EyeClaw not configured. Please set botId and sdkToken.')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const config = account.config
|
|
151
|
+
|
|
152
|
+
// Validate required fields
|
|
153
|
+
if (!config.botId || !config.sdkToken) {
|
|
154
|
+
throw new Error('botId and sdkToken are required')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Set defaults
|
|
158
|
+
const clientConfig = {
|
|
159
|
+
botId: String(config.botId),
|
|
160
|
+
sdkToken: config.sdkToken,
|
|
161
|
+
serverUrl: config.serverUrl || 'http://localhost:3000',
|
|
162
|
+
reconnectInterval: config.reconnectInterval || 5000,
|
|
163
|
+
heartbeatInterval: config.heartbeatInterval || 30000,
|
|
164
|
+
enabled: true,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
ctx.log?.info(`🦞 Starting EyeClaw SDK... botId=${clientConfig.botId}, serverUrl=${clientConfig.serverUrl}`)
|
|
168
|
+
|
|
169
|
+
// Create logger adapter
|
|
170
|
+
const logger = {
|
|
171
|
+
debug: (msg: string) => ctx.log?.debug?.(msg),
|
|
172
|
+
info: (msg: string) => ctx.log?.info(msg),
|
|
173
|
+
warn: (msg: string) => ctx.log?.warn(msg),
|
|
174
|
+
error: (msg: string) => ctx.log?.error(msg),
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Create and connect client
|
|
178
|
+
const client = new EyeClawClient(clientConfig, logger)
|
|
179
|
+
clients.set(ctx.accountId, client)
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
await client.connect()
|
|
183
|
+
ctx.log?.info('✅ Successfully connected to EyeClaw platform')
|
|
184
|
+
ctx.setStatus({ accountId: ctx.accountId, running: true, lastStartAt: Date.now() })
|
|
185
|
+
|
|
186
|
+
// Wait for abort signal
|
|
187
|
+
await new Promise<void>((resolve) => {
|
|
188
|
+
ctx.abortSignal.addEventListener('abort', () => {
|
|
189
|
+
ctx.log?.info('🛑 Shutting down EyeClaw SDK...')
|
|
190
|
+
client.disconnect()
|
|
191
|
+
clients.delete(ctx.accountId)
|
|
192
|
+
ctx.setStatus({ accountId: ctx.accountId, running: false, lastStopAt: Date.now() })
|
|
193
|
+
resolve()
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
} catch (error) {
|
|
197
|
+
const errorMsg = error instanceof Error ? error.message : String(error)
|
|
198
|
+
ctx.log?.error(`Failed to connect to EyeClaw: ${errorMsg}`)
|
|
199
|
+
ctx.setStatus({
|
|
200
|
+
accountId: ctx.accountId,
|
|
201
|
+
running: false,
|
|
202
|
+
lastError: errorMsg,
|
|
203
|
+
lastStopAt: Date.now(),
|
|
204
|
+
})
|
|
205
|
+
throw error
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
|
-
// Type definitions for
|
|
2
|
-
|
|
1
|
+
// Type definitions for EyeClaw Channel Plugin
|
|
2
|
+
|
|
3
|
+
export interface EyeClawConfig {
|
|
4
|
+
enabled?: boolean
|
|
5
|
+
botId: string | number
|
|
6
|
+
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
|
+
}
|
|
3
19
|
|
|
4
20
|
export interface PluginConfig {
|
|
5
21
|
enabled: boolean
|