@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 CHANGED
@@ -1,9 +1,6 @@
1
1
  import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'
2
2
  import { emptyPluginConfigSchema } from 'openclaw/plugin-sdk'
3
- import { EyeClawClient } from './src/client.js'
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
- const runtime = api.runtime
21
- const logger = runtime.logging.getChildLogger({ plugin: 'eyeclaw' })
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eyeclaw/eyeclaw",
3
- "version": "1.0.5",
3
+ "version": "2.0.0",
4
4
  "description": "EyeClaw channel plugin for OpenClaw - Connect your local OpenClaw instance to EyeClaw platform",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
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 OpenClaw plugin system
2
- // Reference: clawdbot-feishu architecture
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