@eyeclaw/eyeclaw 1.0.4 → 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 -80
- package/package.json +1 -1
- package/src/channel.ts +209 -0
- package/src/cli.ts +1 -1
- 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,82 +14,8 @@ const plugin = {
|
|
|
17
14
|
configSchema: emptyPluginConfigSchema(),
|
|
18
15
|
|
|
19
16
|
register(api: OpenClawPluginApi) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const config = runtime.config as any
|
|
23
|
-
|
|
24
|
-
// Debug: log entire config structure
|
|
25
|
-
logger.info('🔍 Full config:', JSON.stringify(config, null, 2))
|
|
26
|
-
logger.info('🔍 Channels config:', JSON.stringify(config?.channels, null, 2))
|
|
27
|
-
|
|
28
|
-
// Get EyeClaw config from channels.eyeclaw
|
|
29
|
-
const eyeclawConfig: PluginConfig = config?.channels?.eyeclaw || {}
|
|
30
|
-
|
|
31
|
-
// Debug: log eyeclaw config
|
|
32
|
-
logger.info('🔍 EyeClaw config:', JSON.stringify(eyeclawConfig, null, 2))
|
|
33
|
-
logger.info('🔍 botId type:', typeof eyeclawConfig.botId)
|
|
34
|
-
logger.info('🔍 botId value:', eyeclawConfig.botId)
|
|
35
|
-
logger.info('🔍 sdkToken type:', typeof eyeclawConfig.sdkToken)
|
|
36
|
-
logger.info('🔍 sdkToken exists:', !!eyeclawConfig.sdkToken)
|
|
37
|
-
|
|
38
|
-
// Check if enabled
|
|
39
|
-
if (eyeclawConfig.enabled === false) {
|
|
40
|
-
logger.info('Plugin disabled in config')
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Validate required fields with detailed logging
|
|
45
|
-
if (!eyeclawConfig.botId) {
|
|
46
|
-
logger.warn('❌ botId is missing or falsy:', eyeclawConfig.botId)
|
|
47
|
-
logger.warn('Configure with: openclaw config set channels.eyeclaw.botId "YOUR_BOT_ID"')
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (!eyeclawConfig.sdkToken) {
|
|
52
|
-
logger.warn('❌ sdkToken is missing or falsy')
|
|
53
|
-
logger.warn('Configure with: openclaw config set channels.eyeclaw.sdkToken "YOUR_SDK_TOKEN"')
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
logger.info('✅ Config validation passed')
|
|
58
|
-
|
|
59
|
-
// Convert botId to string (OpenClaw may auto-convert "1" to number 1)
|
|
60
|
-
eyeclawConfig.botId = String(eyeclawConfig.botId)
|
|
61
|
-
|
|
62
|
-
// Set defaults
|
|
63
|
-
eyeclawConfig.serverUrl = eyeclawConfig.serverUrl || 'http://localhost:3000'
|
|
64
|
-
eyeclawConfig.reconnectInterval = eyeclawConfig.reconnectInterval || 5000
|
|
65
|
-
eyeclawConfig.heartbeatInterval = eyeclawConfig.heartbeatInterval || 30000
|
|
66
|
-
|
|
67
|
-
logger.info('🦞 Starting EyeClaw SDK...', { botId: eyeclawConfig.botId, serverUrl: eyeclawConfig.serverUrl })
|
|
68
|
-
|
|
69
|
-
// Create logger adapter for client (simple console logger)
|
|
70
|
-
const clientLogger = {
|
|
71
|
-
debug: (msg: string) => logger.debug(msg),
|
|
72
|
-
info: (msg: string) => logger.info(msg),
|
|
73
|
-
warn: (msg: string) => logger.warn(msg),
|
|
74
|
-
error: (msg: string) => logger.error(msg),
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Create and connect WebSocket client
|
|
78
|
-
client = new EyeClawClient(eyeclawConfig, clientLogger)
|
|
79
|
-
|
|
80
|
-
client.connect().then(() => {
|
|
81
|
-
logger.info('✅ Successfully connected to EyeClaw platform')
|
|
82
|
-
}).catch((error: Error) => {
|
|
83
|
-
logger.error('Failed to connect to EyeClaw', { error: error.message })
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
// Handle shutdown
|
|
87
|
-
const shutdown = () => {
|
|
88
|
-
if (client) {
|
|
89
|
-
logger.info('🛑 Shutting down EyeClaw SDK...')
|
|
90
|
-
client.disconnect()
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
process.on('SIGINT', shutdown)
|
|
95
|
-
process.on('SIGTERM', shutdown)
|
|
17
|
+
// Register EyeClaw as a channel plugin
|
|
18
|
+
api.registerChannel({ plugin: eyeclawPlugin })
|
|
96
19
|
},
|
|
97
20
|
}
|
|
98
21
|
|
|
@@ -101,3 +24,4 @@ export default plugin
|
|
|
101
24
|
// Re-export for direct usage
|
|
102
25
|
export { EyeClawClient } from './src/client.js'
|
|
103
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/cli.ts
CHANGED
|
@@ -51,7 +51,7 @@ function main() {
|
|
|
51
51
|
// Default: show usage
|
|
52
52
|
console.log(USAGE)
|
|
53
53
|
console.log(chalk.yellow('ℹ This is an OpenClaw plugin. Please install it using:'))
|
|
54
|
-
console.log(chalk.cyan(' openclaw plugins install @eyeclaw/
|
|
54
|
+
console.log(chalk.cyan(' openclaw plugins install @eyeclaw/eyeclaw\n'))
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
main()
|
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
|