@eyeclaw/eyeclaw 1.0.5 β†’ 2.0.2

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 CHANGED
@@ -212,6 +212,112 @@ client.disconnect()
212
212
  - **Issues**: [https://github.com/eyeclaw/eyeclaw/issues](https://github.com/eyeclaw/eyeclaw/issues)
213
213
  - **Discord**: [https://discord.gg/eyeclaw](https://discord.gg/eyeclaw)
214
214
 
215
+ ## πŸ’¬ Chat Functionality
216
+
217
+ The EyeClaw SDK includes built-in chat functionality that allows you to interact with your bot through the web dashboard.
218
+
219
+ ### Testing Chat
220
+
221
+ 1. Start your bot with the EyeClaw plugin installed
222
+ 2. Go to your bot's detail page on EyeClaw Dashboard
223
+ 3. Click **"Test Chat"** button
224
+ 4. Send messages - the bot will respond with smart replies
225
+ 5. Use quick test buttons:
226
+ - **Ping** - Test bot connectivity
227
+ - **Status** - Get bot status (online, sessions, uptime)
228
+ - **Echo** - Echo back a test message
229
+ - **Help** - Show available commands
230
+
231
+ ### Supported Commands
232
+
233
+ The SDK automatically handles these commands from the web dashboard:
234
+
235
+ - **`chat`** - Receive and respond to chat messages
236
+ - Params: `{ message: string }`
237
+ - The bot uses pattern matching to generate smart responses
238
+ - Supports greetings, questions, thanks, farewells in Chinese and English
239
+
240
+ - **`ping`** - Test bot connectivity
241
+ - Response: "πŸ“ Pong! Bot is responding."
242
+
243
+ - **`status`** - Get current bot status
244
+ - Returns: online status, active sessions, total sessions, uptime
245
+
246
+ - **`echo`** - Echo back a message
247
+ - Params: `{ message: string }`
248
+ - Response: Echoes the message back
249
+
250
+ - **`help`** - Show available commands
251
+ - Response: Lists all available commands and their usage
252
+
253
+ ### Customizing Bot Responses
254
+
255
+ You can customize the bot's chat responses by modifying `generateBotReply()` in `sdk/src/client.ts`:
256
+
257
+ ```typescript
258
+ private generateBotReply(userMessage: string): string {
259
+ const lowerMessage = userMessage.toLowerCase()
260
+
261
+ // Add your custom patterns
262
+ if (lowerMessage.includes('weather')) {
263
+ return '🌀️ The weather is great today!'
264
+ }
265
+
266
+ if (lowerMessage.includes('time')) {
267
+ return `πŸ• Current time: ${new Date().toLocaleTimeString()}`
268
+ }
269
+
270
+ // Default response
271
+ return `πŸ’¬ You said: "${userMessage}"`
272
+ }
273
+ ```
274
+
275
+ ### Integrating AI/LLM Services
276
+
277
+ For production use, integrate your preferred AI service:
278
+
279
+ ```typescript
280
+ import OpenAI from 'openai'
281
+
282
+ private async generateBotReply(userMessage: string): Promise<string> {
283
+ const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
284
+
285
+ const completion = await openai.chat.completions.create({
286
+ model: 'gpt-4',
287
+ messages: [
288
+ { role: 'system', content: 'You are a helpful assistant.' },
289
+ { role: 'user', content: userMessage }
290
+ ]
291
+ })
292
+
293
+ return completion.choices[0].message.content || 'Sorry, I could not generate a response.'
294
+ }
295
+ ```
296
+
297
+ ### Message Flow
298
+
299
+ ```
300
+ User (Web Dashboard)
301
+ β”‚
302
+ β–Ό
303
+ DashboardChannel ──execute_command──► BotChannel
304
+ β”‚ β”‚
305
+ β”‚ β–Ό
306
+ β”‚ SDK receives command
307
+ β”‚ β”‚
308
+ β”‚ β–Ό
309
+ β”‚ handleExecuteCommand()
310
+ β”‚ β”‚
311
+ β”‚ β–Ό
312
+ β”‚ generateBotReply()
313
+ β”‚ β”‚
314
+ β”‚ β–Ό
315
+ │◄───────── sendLog() ──────────sendLog('info', reply)
316
+ β”‚
317
+ β–Ό
318
+ Chat Controller displays message
319
+ ```
320
+
215
321
  ## License
216
322
 
217
323
  MIT Β© EyeClaw Team
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.2",
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,253 @@
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
+ // Register OpenClaw Agent callback for chat messages
182
+ client.setSendAgentCallback(async (message: string) => {
183
+ try {
184
+ ctx.log?.info(`πŸ€– Sending message to OpenClaw Agent: ${message}`)
185
+
186
+ // Call OpenClaw Agent via CLI (since ctx.sendAgent doesn't exist)
187
+ const { spawn } = await import('child_process')
188
+ const { promisify } = await import('util')
189
+ const exec = promisify((await import('child_process')).exec)
190
+
191
+ try {
192
+ // Call openclaw agent CLI to process the message
193
+ const result = await exec(
194
+ `openclaw agent --session-id eyeclaw-web-chat --message ${JSON.stringify(message)} --json`,
195
+ { timeout: 60000 }
196
+ )
197
+
198
+ if (result.stdout) {
199
+ try {
200
+ const parsed = JSON.parse(result.stdout)
201
+ // OpenClaw CLI returns: { result: { payloads: [{ text: "...", mediaUrl: null }] } }
202
+ const response = parsed.result?.payloads?.[0]?.text || 'Agent completed (no text)'
203
+ ctx.log?.info(`βœ… Agent response: ${response.substring(0, 100)}...`)
204
+ client.sendLog('info', response)
205
+ } catch (e) {
206
+ // If not JSON, just send the stdout
207
+ ctx.log?.info(`βœ… Agent response (raw): ${result.stdout.substring(0, 100)}...`)
208
+ client.sendLog('info', result.stdout.trim())
209
+ }
210
+ } else {
211
+ client.sendLog('info', 'βœ… Agent completed successfully')
212
+ }
213
+ } catch (error) {
214
+ const errorMsg = error instanceof Error ? error.message : String(error)
215
+ ctx.log?.error(`Failed to execute openclaw agent: ${errorMsg}`)
216
+ client.sendLog('error', `❌ Agent error: ${errorMsg}`)
217
+ }
218
+ } catch (error) {
219
+ const errorMsg = error instanceof Error ? error.message : String(error)
220
+ ctx.log?.error(`Failed to call OpenClaw Agent: ${errorMsg}`)
221
+ client.sendLog('error', `❌ Failed to call agent: ${errorMsg}`)
222
+ }
223
+ })
224
+
225
+ try {
226
+ await client.connect()
227
+ ctx.log?.info('βœ… Successfully connected to EyeClaw platform')
228
+ ctx.setStatus({ accountId: ctx.accountId, running: true, lastStartAt: Date.now() })
229
+
230
+ // Wait for abort signal
231
+ await new Promise<void>((resolve) => {
232
+ ctx.abortSignal.addEventListener('abort', () => {
233
+ ctx.log?.info('πŸ›‘ Shutting down EyeClaw SDK...')
234
+ client.disconnect()
235
+ clients.delete(ctx.accountId)
236
+ ctx.setStatus({ accountId: ctx.accountId, running: false, lastStopAt: Date.now() })
237
+ resolve()
238
+ })
239
+ })
240
+ } catch (error) {
241
+ const errorMsg = error instanceof Error ? error.message : String(error)
242
+ ctx.log?.error(`Failed to connect to EyeClaw: ${errorMsg}`)
243
+ ctx.setStatus({
244
+ accountId: ctx.accountId,
245
+ running: false,
246
+ lastError: errorMsg,
247
+ lastStopAt: Date.now(),
248
+ })
249
+ throw error
250
+ }
251
+ },
252
+ },
253
+ }
package/src/client.ts CHANGED
@@ -105,6 +105,16 @@ export class EyeClawClient {
105
105
  this.logger.info(`Command received by server: ${message.command}`)
106
106
  break
107
107
 
108
+ case 'execute_command':
109
+ this.handleExecuteCommand(message)
110
+ break
111
+
112
+ case 'ping':
113
+ this.logger.debug('Received ping from dashboard')
114
+ // Send pong response
115
+ this.sendLog('info', 'πŸ“ Pong! Bot is alive.')
116
+ break
117
+
108
118
  case 'log':
109
119
  this.logger.info(`[Server Log] ${message.level}: ${message.message}`)
110
120
  break
@@ -114,6 +124,80 @@ export class EyeClawClient {
114
124
  }
115
125
  }
116
126
 
127
+ private handleExecuteCommand(message: Record<string, unknown>): void {
128
+ const command = message.command as string
129
+ const params = (message.params as Record<string, unknown>) || {}
130
+
131
+ this.logger.info(`πŸ“₯ Executing command: ${command}`)
132
+
133
+ switch (command) {
134
+ case 'chat': {
135
+ const userMessage = params.message as string
136
+ this.logger.info(`πŸ’¬ Chat message: ${userMessage}`)
137
+
138
+ // Send user message acknowledgment
139
+ this.sendLog('info', `ζ”Άεˆ°ζΆˆζ―: ${userMessage}`)
140
+
141
+ // Call OpenClaw Agent via callback
142
+ this.handleChatMessage(userMessage)
143
+ break
144
+ }
145
+
146
+ case 'ping': {
147
+ this.sendLog('info', 'πŸ“ Pong! Bot is responding.')
148
+ break
149
+ }
150
+
151
+ case 'status': {
152
+ this.requestStatus()
153
+ break
154
+ }
155
+
156
+ case 'echo': {
157
+ const echoMessage = params.message as string
158
+ this.sendLog('info', `Echo: ${echoMessage}`)
159
+ break
160
+ }
161
+
162
+ case 'help': {
163
+ const helpMessage = [
164
+ 'πŸ€– Available Commands:',
165
+ 'β€’ chat - Send a chat message',
166
+ 'β€’ ping - Test connection',
167
+ 'β€’ status - Get bot status',
168
+ 'β€’ echo - Echo a message',
169
+ 'β€’ help - Show this help',
170
+ ].join('\n')
171
+ this.sendLog('info', helpMessage)
172
+ break
173
+ }
174
+
175
+ default:
176
+ this.logger.warn(`Unknown command: ${command}`)
177
+ this.sendLog('error', `❌ Unknown command: ${command}`)
178
+ }
179
+ }
180
+
181
+ private handleChatMessage(userMessage: string): void {
182
+ // This will be called by OpenClaw channel plugin via sendAgent
183
+ if (this.sendAgentCallback) {
184
+ this.logger.info('πŸ€– Calling OpenClaw Agent...')
185
+ this.sendAgentCallback(userMessage)
186
+ } else {
187
+ // Fallback: simple echo if not running in OpenClaw context
188
+ this.logger.warn('No OpenClaw Agent available, using echo mode')
189
+ this.sendLog('info', `πŸ’¬ Echo: "${userMessage}" (OpenClaw Agent not connected)`)
190
+ }
191
+ }
192
+
193
+ // Callback to send message to OpenClaw Agent (injected by channel plugin)
194
+ private sendAgentCallback: ((message: string) => Promise<void>) | null = null
195
+
196
+ setSendAgentCallback(callback: (message: string) => Promise<void>): void {
197
+ this.sendAgentCallback = callback
198
+ this.logger.info('βœ… OpenClaw Agent callback registered')
199
+ }
200
+
117
201
  private handleStatusResponse(status: BotStatus): void {
118
202
  this.logger.info(`Bot status: online=${status.online}, status=${status.status}, sessions=${status.active_sessions}, uptime=${Math.floor(status.uptime / 60)}m`)
119
203
  }
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