openclaw-overlay-plugin 0.7.22
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 +406 -0
- package/SKILL.md +78 -0
- package/clawdbot.plugin.json +106 -0
- package/dist/cli-main.d.ts +7 -0
- package/dist/cli-main.js +192 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +14 -0
- package/dist/core/config.d.ts +11 -0
- package/dist/core/config.js +13 -0
- package/dist/core/index.d.ts +25 -0
- package/dist/core/index.js +26 -0
- package/dist/core/payment.d.ts +16 -0
- package/dist/core/payment.js +94 -0
- package/dist/core/types.d.ts +94 -0
- package/dist/core/types.js +4 -0
- package/dist/core/verify.d.ts +28 -0
- package/dist/core/verify.js +104 -0
- package/dist/core/wallet.d.ts +99 -0
- package/dist/core/wallet.js +219 -0
- package/dist/scripts/baemail/commands.d.ts +64 -0
- package/dist/scripts/baemail/commands.js +258 -0
- package/dist/scripts/baemail/handler.d.ts +36 -0
- package/dist/scripts/baemail/handler.js +284 -0
- package/dist/scripts/baemail/index.d.ts +5 -0
- package/dist/scripts/baemail/index.js +5 -0
- package/dist/scripts/config.d.ts +48 -0
- package/dist/scripts/config.js +68 -0
- package/dist/scripts/index.d.ts +7 -0
- package/dist/scripts/index.js +7 -0
- package/dist/scripts/messaging/connect.d.ts +8 -0
- package/dist/scripts/messaging/connect.js +114 -0
- package/dist/scripts/messaging/handlers.d.ts +21 -0
- package/dist/scripts/messaging/handlers.js +334 -0
- package/dist/scripts/messaging/inbox.d.ts +11 -0
- package/dist/scripts/messaging/inbox.js +51 -0
- package/dist/scripts/messaging/index.d.ts +8 -0
- package/dist/scripts/messaging/index.js +8 -0
- package/dist/scripts/messaging/poll.d.ts +7 -0
- package/dist/scripts/messaging/poll.js +52 -0
- package/dist/scripts/messaging/send.d.ts +7 -0
- package/dist/scripts/messaging/send.js +43 -0
- package/dist/scripts/output.d.ts +12 -0
- package/dist/scripts/output.js +19 -0
- package/dist/scripts/overlay/discover.d.ts +7 -0
- package/dist/scripts/overlay/discover.js +72 -0
- package/dist/scripts/overlay/index.d.ts +7 -0
- package/dist/scripts/overlay/index.js +7 -0
- package/dist/scripts/overlay/registration.d.ts +19 -0
- package/dist/scripts/overlay/registration.js +176 -0
- package/dist/scripts/overlay/services.d.ts +29 -0
- package/dist/scripts/overlay/services.js +167 -0
- package/dist/scripts/overlay/transaction.d.ts +42 -0
- package/dist/scripts/overlay/transaction.js +103 -0
- package/dist/scripts/payment/build.d.ts +24 -0
- package/dist/scripts/payment/build.js +54 -0
- package/dist/scripts/payment/commands.d.ts +15 -0
- package/dist/scripts/payment/commands.js +73 -0
- package/dist/scripts/payment/index.d.ts +6 -0
- package/dist/scripts/payment/index.js +6 -0
- package/dist/scripts/payment/types.d.ts +56 -0
- package/dist/scripts/payment/types.js +4 -0
- package/dist/scripts/services/index.d.ts +6 -0
- package/dist/scripts/services/index.js +6 -0
- package/dist/scripts/services/queue.d.ts +11 -0
- package/dist/scripts/services/queue.js +28 -0
- package/dist/scripts/services/request.d.ts +7 -0
- package/dist/scripts/services/request.js +82 -0
- package/dist/scripts/services/respond.d.ts +11 -0
- package/dist/scripts/services/respond.js +132 -0
- package/dist/scripts/types.d.ts +107 -0
- package/dist/scripts/types.js +4 -0
- package/dist/scripts/utils/index.d.ts +6 -0
- package/dist/scripts/utils/index.js +6 -0
- package/dist/scripts/utils/merkle.d.ts +12 -0
- package/dist/scripts/utils/merkle.js +47 -0
- package/dist/scripts/utils/storage.d.ts +66 -0
- package/dist/scripts/utils/storage.js +211 -0
- package/dist/scripts/utils/woc.d.ts +26 -0
- package/dist/scripts/utils/woc.js +91 -0
- package/dist/scripts/wallet/balance.d.ts +22 -0
- package/dist/scripts/wallet/balance.js +240 -0
- package/dist/scripts/wallet/identity.d.ts +70 -0
- package/dist/scripts/wallet/identity.js +151 -0
- package/dist/scripts/wallet/index.d.ts +6 -0
- package/dist/scripts/wallet/index.js +6 -0
- package/dist/scripts/wallet/setup.d.ts +15 -0
- package/dist/scripts/wallet/setup.js +105 -0
- package/dist/scripts/x-verification/commands.d.ts +27 -0
- package/dist/scripts/x-verification/commands.js +222 -0
- package/dist/scripts/x-verification/index.d.ts +4 -0
- package/dist/scripts/x-verification/index.js +4 -0
- package/dist/services/built-in/api-proxy/index.d.ts +6 -0
- package/dist/services/built-in/api-proxy/index.js +23 -0
- package/dist/services/built-in/code-develop/index.d.ts +6 -0
- package/dist/services/built-in/code-develop/index.js +23 -0
- package/dist/services/built-in/code-review/index.d.ts +10 -0
- package/dist/services/built-in/code-review/index.js +51 -0
- package/dist/services/built-in/image-analysis/index.d.ts +6 -0
- package/dist/services/built-in/image-analysis/index.js +33 -0
- package/dist/services/built-in/memory-store/index.d.ts +6 -0
- package/dist/services/built-in/memory-store/index.js +22 -0
- package/dist/services/built-in/roulette/index.d.ts +6 -0
- package/dist/services/built-in/roulette/index.js +27 -0
- package/dist/services/built-in/summarize/index.d.ts +6 -0
- package/dist/services/built-in/summarize/index.js +21 -0
- package/dist/services/built-in/tell-joke/handler.d.ts +7 -0
- package/dist/services/built-in/tell-joke/handler.js +122 -0
- package/dist/services/built-in/tell-joke/index.d.ts +9 -0
- package/dist/services/built-in/tell-joke/index.js +31 -0
- package/dist/services/built-in/translate/index.d.ts +6 -0
- package/dist/services/built-in/translate/index.js +21 -0
- package/dist/services/built-in/web-research/index.d.ts +9 -0
- package/dist/services/built-in/web-research/index.js +51 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.js +14 -0
- package/dist/services/loader.d.ts +77 -0
- package/dist/services/loader.js +292 -0
- package/dist/services/manager.d.ts +86 -0
- package/dist/services/manager.js +255 -0
- package/dist/services/registry.d.ts +98 -0
- package/dist/services/registry.js +204 -0
- package/dist/services/types.d.ts +230 -0
- package/dist/services/types.js +30 -0
- package/dist/test/cli.test.d.ts +7 -0
- package/dist/test/cli.test.js +329 -0
- package/dist/test/comprehensive-overlay.test.d.ts +13 -0
- package/dist/test/comprehensive-overlay.test.js +593 -0
- package/dist/test/key-derivation.test.d.ts +12 -0
- package/dist/test/key-derivation.test.js +86 -0
- package/dist/test/overlay-submit.test.d.ts +10 -0
- package/dist/test/overlay-submit.test.js +460 -0
- package/dist/test/request-response-flow.test.d.ts +5 -0
- package/dist/test/request-response-flow.test.js +209 -0
- package/dist/test/service-system.test.d.ts +5 -0
- package/dist/test/service-system.test.js +190 -0
- package/dist/test/utils/server-logic.d.ts +98 -0
- package/dist/test/utils/server-logic.js +286 -0
- package/dist/test/wallet.test.d.ts +7 -0
- package/dist/test/wallet.test.js +146 -0
- package/index.ts +1965 -0
- package/openclaw.plugin.json +106 -0
- package/package.json +73 -0
- package/src/cli-main.ts +230 -0
- package/src/cli.ts +16 -0
- package/src/core/README.md +246 -0
- package/src/core/config.ts +21 -0
- package/src/core/index.ts +42 -0
- package/src/core/payment.ts +111 -0
- package/src/core/types.ts +102 -0
- package/src/core/verify.ts +119 -0
- package/src/core/wallet.ts +282 -0
- package/src/scripts/baemail/commands.ts +326 -0
- package/src/scripts/baemail/handler.ts +338 -0
- package/src/scripts/baemail/index.ts +6 -0
- package/src/scripts/config.ts +81 -0
- package/src/scripts/index.ts +8 -0
- package/src/scripts/messaging/connect.ts +121 -0
- package/src/scripts/messaging/handlers.ts +394 -0
- package/src/scripts/messaging/inbox.ts +64 -0
- package/src/scripts/messaging/index.ts +9 -0
- package/src/scripts/messaging/poll.ts +59 -0
- package/src/scripts/messaging/send.ts +54 -0
- package/src/scripts/output.ts +21 -0
- package/src/scripts/overlay/discover.ts +81 -0
- package/src/scripts/overlay/index.ts +8 -0
- package/src/scripts/overlay/registration.ts +199 -0
- package/src/scripts/overlay/services.ts +199 -0
- package/src/scripts/overlay/transaction.ts +124 -0
- package/src/scripts/payment/build.ts +65 -0
- package/src/scripts/payment/commands.ts +92 -0
- package/src/scripts/payment/index.ts +7 -0
- package/src/scripts/payment/types.ts +62 -0
- package/src/scripts/services/index.ts +7 -0
- package/src/scripts/services/queue.ts +35 -0
- package/src/scripts/services/request.ts +98 -0
- package/src/scripts/services/respond.ts +149 -0
- package/src/scripts/types.ts +121 -0
- package/src/scripts/utils/index.ts +7 -0
- package/src/scripts/utils/merkle.ts +57 -0
- package/src/scripts/utils/storage.ts +231 -0
- package/src/scripts/utils/woc.ts +106 -0
- package/src/scripts/wallet/balance.ts +277 -0
- package/src/scripts/wallet/identity.ts +203 -0
- package/src/scripts/wallet/index.ts +7 -0
- package/src/scripts/wallet/setup.ts +121 -0
- package/src/scripts/x-verification/commands.ts +256 -0
- package/src/scripts/x-verification/index.ts +5 -0
- package/src/services/built-in/api-proxy/index.ts +26 -0
- package/src/services/built-in/api-proxy/prompt.md +26 -0
- package/src/services/built-in/code-develop/index.ts +26 -0
- package/src/services/built-in/code-develop/prompt.md +35 -0
- package/src/services/built-in/code-review/index.ts +54 -0
- package/src/services/built-in/code-review/prompt.md +105 -0
- package/src/services/built-in/image-analysis/index.ts +36 -0
- package/src/services/built-in/image-analysis/prompt.md +42 -0
- package/src/services/built-in/memory-store/index.ts +25 -0
- package/src/services/built-in/memory-store/prompt.md +45 -0
- package/src/services/built-in/roulette/index.ts +30 -0
- package/src/services/built-in/roulette/prompt.md +35 -0
- package/src/services/built-in/summarize/index.ts +24 -0
- package/src/services/built-in/summarize/prompt.md +27 -0
- package/src/services/built-in/tell-joke/handler.ts +134 -0
- package/src/services/built-in/tell-joke/index.ts +34 -0
- package/src/services/built-in/tell-joke/prompt.md +59 -0
- package/src/services/built-in/translate/index.ts +24 -0
- package/src/services/built-in/translate/prompt.md +23 -0
- package/src/services/built-in/web-research/index.ts +54 -0
- package/src/services/built-in/web-research/prompt.md +110 -0
- package/src/services/index.ts +16 -0
- package/src/services/loader.ts +344 -0
- package/src/services/manager.ts +304 -0
- package/src/services/registry.ts +246 -0
- package/src/services/types.ts +259 -0
- package/src/test/cli.test.ts +352 -0
- package/src/test/comprehensive-overlay.test.ts +729 -0
- package/src/test/key-derivation.test.ts +102 -0
- package/src/test/overlay-submit.test.ts +570 -0
- package/src/test/request-response-flow.test.ts +252 -0
- package/src/test/service-system.test.ts +241 -0
- package/src/test/utils/server-logic.ts +368 -0
- package/src/test/wallet.test.ts +166 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service loader implementation.
|
|
3
|
+
*
|
|
4
|
+
* This dynamically loads services from directories, supporting both
|
|
5
|
+
* built-in services and custom user services.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { ServiceDefinition, ServiceLoader } from './types.js';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Default service loader implementation.
|
|
18
|
+
*/
|
|
19
|
+
export class DefaultServiceLoader implements ServiceLoader {
|
|
20
|
+
private builtInDir: string;
|
|
21
|
+
private customDir: string;
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
// Built-in services directory
|
|
25
|
+
this.builtInDir = path.resolve(__dirname, 'built-in');
|
|
26
|
+
|
|
27
|
+
// Custom services directory (in user's config dir)
|
|
28
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
29
|
+
this.customDir = path.join(homeDir, '.openclaw', 'services');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Load services from a directory.
|
|
34
|
+
*/
|
|
35
|
+
async loadFromDirectory(directory: string): Promise<ServiceDefinition[]> {
|
|
36
|
+
if (!fs.existsSync(directory)) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const services: ServiceDefinition[] = [];
|
|
41
|
+
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
42
|
+
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (!entry.isDirectory()) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const serviceDefinition = await this.loadServiceFromDirectory(
|
|
50
|
+
path.join(directory, entry.name)
|
|
51
|
+
);
|
|
52
|
+
if (serviceDefinition) {
|
|
53
|
+
services.push(serviceDefinition);
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.warn(`Failed to load service from ${entry.name}:`, error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return services;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Load all built-in services.
|
|
65
|
+
*/
|
|
66
|
+
async loadBuiltInServices(): Promise<ServiceDefinition[]> {
|
|
67
|
+
return this.loadFromDirectory(this.builtInDir);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Load custom user services.
|
|
72
|
+
*/
|
|
73
|
+
async loadCustomServices(): Promise<ServiceDefinition[]> {
|
|
74
|
+
return this.loadFromDirectory(this.customDir);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Load all services (built-in + custom).
|
|
79
|
+
*/
|
|
80
|
+
async loadAllServices(): Promise<ServiceDefinition[]> {
|
|
81
|
+
const [builtIn, custom] = await Promise.all([
|
|
82
|
+
this.loadBuiltInServices(),
|
|
83
|
+
this.loadCustomServices()
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
return [...builtIn, ...custom];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Load a service definition from a directory.
|
|
91
|
+
*/
|
|
92
|
+
private async loadServiceFromDirectory(serviceDir: string): Promise<ServiceDefinition | null> {
|
|
93
|
+
const indexPath = path.join(serviceDir, 'index.ts');
|
|
94
|
+
const jsIndexPath = path.join(serviceDir, 'index.js');
|
|
95
|
+
const promptPath = path.join(serviceDir, 'prompt.md');
|
|
96
|
+
|
|
97
|
+
// Check if index file exists (TypeScript or JavaScript)
|
|
98
|
+
let modulePath: string;
|
|
99
|
+
if (fs.existsSync(indexPath)) {
|
|
100
|
+
modulePath = indexPath;
|
|
101
|
+
} else if (fs.existsSync(jsIndexPath)) {
|
|
102
|
+
modulePath = jsIndexPath;
|
|
103
|
+
} else {
|
|
104
|
+
throw new Error(`No index.ts or index.js found in ${serviceDir}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
// Dynamic import the service module
|
|
109
|
+
const serviceModule = await import(modulePath);
|
|
110
|
+
const serviceDefinition = serviceModule.default || serviceModule;
|
|
111
|
+
|
|
112
|
+
if (!serviceDefinition || typeof serviceDefinition !== 'object') {
|
|
113
|
+
throw new Error('Service must export a default ServiceDefinition object');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Validate required fields
|
|
117
|
+
if (!serviceDefinition.id) {
|
|
118
|
+
throw new Error('Service definition must have an id');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Add prompt file path if it exists
|
|
122
|
+
if (fs.existsSync(promptPath)) {
|
|
123
|
+
serviceDefinition.promptFile = promptPath;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return serviceDefinition as ServiceDefinition;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
throw new Error(`Failed to import service from ${modulePath}: ${error}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create a new custom service directory.
|
|
134
|
+
*/
|
|
135
|
+
createCustomServiceDirectory(serviceId: string): string {
|
|
136
|
+
const serviceDir = path.join(this.customDir, serviceId);
|
|
137
|
+
|
|
138
|
+
// Ensure custom services directory exists
|
|
139
|
+
fs.mkdirSync(this.customDir, { recursive: true });
|
|
140
|
+
|
|
141
|
+
// Create service directory
|
|
142
|
+
if (fs.existsSync(serviceDir)) {
|
|
143
|
+
throw new Error(`Service directory ${serviceId} already exists`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
fs.mkdirSync(serviceDir);
|
|
147
|
+
return serviceDir;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Create a basic service template.
|
|
152
|
+
*/
|
|
153
|
+
createServiceTemplate(serviceId: string, options: {
|
|
154
|
+
name: string;
|
|
155
|
+
description: string;
|
|
156
|
+
defaultPrice: number;
|
|
157
|
+
category?: string;
|
|
158
|
+
hasHandler?: boolean;
|
|
159
|
+
}): void {
|
|
160
|
+
const serviceDir = this.createCustomServiceDirectory(serviceId);
|
|
161
|
+
|
|
162
|
+
// Create index.ts
|
|
163
|
+
const indexContent = this.generateServiceTemplate(serviceId, options);
|
|
164
|
+
fs.writeFileSync(path.join(serviceDir, 'index.ts'), indexContent);
|
|
165
|
+
|
|
166
|
+
// Create prompt.md
|
|
167
|
+
const promptContent = this.generatePromptTemplate(serviceId, options);
|
|
168
|
+
fs.writeFileSync(path.join(serviceDir, 'prompt.md'), promptContent);
|
|
169
|
+
|
|
170
|
+
// Create handler.ts if requested
|
|
171
|
+
if (options.hasHandler) {
|
|
172
|
+
const handlerContent = this.generateHandlerTemplate(serviceId, options);
|
|
173
|
+
fs.writeFileSync(path.join(serviceDir, 'handler.ts'), handlerContent);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Generate service definition template.
|
|
179
|
+
*/
|
|
180
|
+
private generateServiceTemplate(serviceId: string, options: {
|
|
181
|
+
name: string;
|
|
182
|
+
description: string;
|
|
183
|
+
defaultPrice: number;
|
|
184
|
+
category?: string;
|
|
185
|
+
hasHandler?: boolean;
|
|
186
|
+
}): string {
|
|
187
|
+
return `/**
|
|
188
|
+
* ${options.name} service definition.
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
import { ServiceDefinition${options.hasHandler ? ', ServiceHandler' : ''} } from '../../types.js';
|
|
192
|
+
${options.hasHandler ? `import { ${serviceId.replace(/-/g, '')}Handler } from './handler.js';` : ''}
|
|
193
|
+
|
|
194
|
+
const ${serviceId.replace(/-/g, '')}Service: ServiceDefinition = {
|
|
195
|
+
id: '${serviceId}',
|
|
196
|
+
name: '${options.name}',
|
|
197
|
+
description: '${options.description}',
|
|
198
|
+
defaultPrice: ${options.defaultPrice},${options.category ? `\n category: '${options.category}',` : ''}
|
|
199
|
+
inputSchema: {
|
|
200
|
+
type: 'object',
|
|
201
|
+
properties: {
|
|
202
|
+
// Define your input schema here
|
|
203
|
+
query: {
|
|
204
|
+
type: 'string',
|
|
205
|
+
description: 'Query or input for the service'
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
required: ['query']
|
|
209
|
+
}${options.hasHandler ? `,\n handler: ${serviceId.replace(/-/g, '')}Handler` : ''}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export default ${serviceId.replace(/-/g, '')}Service;
|
|
213
|
+
`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Generate prompt template.
|
|
218
|
+
*/
|
|
219
|
+
private generatePromptTemplate(serviceId: string, options: {
|
|
220
|
+
name: string;
|
|
221
|
+
description: string;
|
|
222
|
+
}): string {
|
|
223
|
+
return `# ${options.name} Service
|
|
224
|
+
|
|
225
|
+
You are processing a request for the "${serviceId}" service.
|
|
226
|
+
|
|
227
|
+
## Service Description
|
|
228
|
+
${options.description}
|
|
229
|
+
|
|
230
|
+
## Input
|
|
231
|
+
The user has provided the following input:
|
|
232
|
+
\`\`\`json
|
|
233
|
+
{{input}}
|
|
234
|
+
\`\`\`
|
|
235
|
+
|
|
236
|
+
## Instructions
|
|
237
|
+
Process the user's request and provide a helpful response based on the service description.
|
|
238
|
+
Format your response as a structured result that can be easily parsed and used.
|
|
239
|
+
|
|
240
|
+
## Response Format
|
|
241
|
+
Provide your response in this format:
|
|
242
|
+
\`\`\`json
|
|
243
|
+
{
|
|
244
|
+
"result": "your processed result here",
|
|
245
|
+
"metadata": {
|
|
246
|
+
"processingTime": "time taken",
|
|
247
|
+
"version": "1.0"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
\`\`\`
|
|
251
|
+
`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Generate handler template.
|
|
256
|
+
*/
|
|
257
|
+
private generateHandlerTemplate(serviceId: string, options: {
|
|
258
|
+
name: string;
|
|
259
|
+
}): string {
|
|
260
|
+
return `/**
|
|
261
|
+
* ${options.name} service handler.
|
|
262
|
+
*/
|
|
263
|
+
|
|
264
|
+
import { ServiceHandler, ValidationResult, ServiceContext, ServiceResult } from '../../types.js';
|
|
265
|
+
|
|
266
|
+
export const ${serviceId.replace(/-/g, '')}Handler: ServiceHandler = {
|
|
267
|
+
/**
|
|
268
|
+
* Validate service input.
|
|
269
|
+
*/
|
|
270
|
+
validate(input: any): ValidationResult {
|
|
271
|
+
if (!input || typeof input !== 'object') {
|
|
272
|
+
return { valid: false, error: 'Input must be an object' };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (!input.query || typeof input.query !== 'string') {
|
|
276
|
+
return { valid: false, error: 'Query must be a non-empty string' };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Add more validation as needed
|
|
280
|
+
return { valid: true, sanitized: input };
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Process the service request.
|
|
285
|
+
*/
|
|
286
|
+
async process(input: any, context: ServiceContext): Promise<ServiceResult> {
|
|
287
|
+
try {
|
|
288
|
+
const startTime = Date.now();
|
|
289
|
+
|
|
290
|
+
// Your service logic here
|
|
291
|
+
const result = {
|
|
292
|
+
query: input.query,
|
|
293
|
+
response: 'This is a template response. Implement your logic here.',
|
|
294
|
+
timestamp: new Date().toISOString()
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const processingTime = Date.now() - startTime;
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
success: true,
|
|
301
|
+
data: result,
|
|
302
|
+
metadata: {
|
|
303
|
+
processingTime,
|
|
304
|
+
version: '1.0'
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
} catch (error) {
|
|
308
|
+
return {
|
|
309
|
+
success: false,
|
|
310
|
+
error: error instanceof Error ? error.message : String(error)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get the built-in services directory.
|
|
320
|
+
*/
|
|
321
|
+
getBuiltInDirectory(): string {
|
|
322
|
+
return this.builtInDir;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Get the custom services directory.
|
|
327
|
+
*/
|
|
328
|
+
getCustomDirectory(): string {
|
|
329
|
+
return this.customDir;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Check if a service directory exists.
|
|
334
|
+
*/
|
|
335
|
+
serviceExists(serviceId: string, inCustom = false): boolean {
|
|
336
|
+
const baseDir = inCustom ? this.customDir : this.builtInDir;
|
|
337
|
+
return fs.existsSync(path.join(baseDir, serviceId));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Global service loader instance.
|
|
343
|
+
*/
|
|
344
|
+
export const serviceLoader = new DefaultServiceLoader();
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service manager implementation.
|
|
3
|
+
*
|
|
4
|
+
* This orchestrates the service system, providing a high-level interface
|
|
5
|
+
* for service execution while keeping payment and relay logic separate.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ServiceManager, ServiceDefinition, ServiceContext, ServiceResult, ValidationResult } from './types.js';
|
|
9
|
+
import { serviceRegistry, DefaultServiceRegistry } from './registry.js';
|
|
10
|
+
import { serviceLoader, DefaultServiceLoader } from './loader.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Default service manager implementation.
|
|
14
|
+
*/
|
|
15
|
+
export class DefaultServiceManager implements ServiceManager {
|
|
16
|
+
public readonly registry: DefaultServiceRegistry;
|
|
17
|
+
public readonly loader: DefaultServiceLoader;
|
|
18
|
+
private initialized = false;
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
this.registry = serviceRegistry;
|
|
22
|
+
this.loader = serviceLoader;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Initialize the service system.
|
|
27
|
+
*/
|
|
28
|
+
async initialize(): Promise<void> {
|
|
29
|
+
if (this.initialized) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Load all services
|
|
34
|
+
const services = await this.loader.loadAllServices();
|
|
35
|
+
|
|
36
|
+
// Register all services
|
|
37
|
+
for (const service of services) {
|
|
38
|
+
try {
|
|
39
|
+
this.registry.register(service);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.warn(`Failed to register service '${service.id}':`, error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.initialized = true;
|
|
46
|
+
|
|
47
|
+
console.log(`Service manager initialized with ${this.registry.count()} services`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Execute a service request.
|
|
52
|
+
*/
|
|
53
|
+
async execute(serviceId: string, input: any, context: ServiceContext): Promise<ServiceResult> {
|
|
54
|
+
if (!this.initialized) {
|
|
55
|
+
throw new Error('Service manager not initialized');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const service = this.registry.get(serviceId);
|
|
59
|
+
if (!service) {
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
error: `Service '${serviceId}' not found`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Validate input if service has a handler
|
|
67
|
+
if (service.handler) {
|
|
68
|
+
const validation = service.handler.validate(input);
|
|
69
|
+
if (!validation.valid) {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: `Input validation failed: ${validation.error}`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Use sanitized input if provided
|
|
77
|
+
input = validation.sanitized || input;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Execute the service
|
|
81
|
+
try {
|
|
82
|
+
if (service.handler) {
|
|
83
|
+
// Use custom handler
|
|
84
|
+
return await service.handler.process(input, context);
|
|
85
|
+
} else {
|
|
86
|
+
// Service uses agent mode - return success with input for agent processing
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
data: {
|
|
90
|
+
serviceId,
|
|
91
|
+
input,
|
|
92
|
+
mode: 'agent',
|
|
93
|
+
promptFile: service.promptFile
|
|
94
|
+
},
|
|
95
|
+
metadata: {
|
|
96
|
+
version: '1.0',
|
|
97
|
+
executionMode: 'agent'
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
error: error instanceof Error ? error.message : String(error),
|
|
105
|
+
metadata: {
|
|
106
|
+
serviceId,
|
|
107
|
+
errorType: 'execution_error'
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Validate service input.
|
|
115
|
+
*/
|
|
116
|
+
validate(serviceId: string, input: any): ValidationResult {
|
|
117
|
+
const service = this.registry.get(serviceId);
|
|
118
|
+
if (!service) {
|
|
119
|
+
return {
|
|
120
|
+
valid: false,
|
|
121
|
+
error: `Service '${serviceId}' not found`
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Use service handler validation if available
|
|
126
|
+
if (service.handler) {
|
|
127
|
+
return service.handler.validate(input);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Default validation for agent-mode services
|
|
131
|
+
if (service.inputSchema) {
|
|
132
|
+
return this.validateAgainstSchema(input, service.inputSchema);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// No validation required
|
|
136
|
+
return { valid: true };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Reload all services.
|
|
141
|
+
*/
|
|
142
|
+
async reload(): Promise<void> {
|
|
143
|
+
// Clear existing services
|
|
144
|
+
this.registry.clear();
|
|
145
|
+
this.initialized = false;
|
|
146
|
+
|
|
147
|
+
// Reinitialize
|
|
148
|
+
await this.initialize();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get service for agent-mode processing.
|
|
153
|
+
*/
|
|
154
|
+
getServiceForAgentMode(serviceId: string): {
|
|
155
|
+
service: ServiceDefinition;
|
|
156
|
+
promptFile?: string;
|
|
157
|
+
} | null {
|
|
158
|
+
const service = this.registry.get(serviceId);
|
|
159
|
+
if (!service) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
service,
|
|
165
|
+
promptFile: service.promptFile
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Check if service is available.
|
|
171
|
+
*/
|
|
172
|
+
isServiceAvailable(serviceId: string): boolean {
|
|
173
|
+
return this.registry.has(serviceId);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get service execution mode.
|
|
178
|
+
*/
|
|
179
|
+
getServiceMode(serviceId: string): 'handler' | 'agent' | null {
|
|
180
|
+
const service = this.registry.get(serviceId);
|
|
181
|
+
if (!service) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return service.handler ? 'handler' : 'agent';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get all available services for discovery.
|
|
190
|
+
*/
|
|
191
|
+
getAvailableServices(): Array<{
|
|
192
|
+
id: string;
|
|
193
|
+
name: string;
|
|
194
|
+
description: string;
|
|
195
|
+
defaultPrice: number;
|
|
196
|
+
category?: string;
|
|
197
|
+
mode: 'handler' | 'agent';
|
|
198
|
+
}> {
|
|
199
|
+
return this.registry.list().map(service => ({
|
|
200
|
+
id: service.id,
|
|
201
|
+
name: service.name,
|
|
202
|
+
description: service.description,
|
|
203
|
+
defaultPrice: service.defaultPrice,
|
|
204
|
+
category: service.category,
|
|
205
|
+
mode: service.handler ? 'handler' : 'agent'
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Validate input against JSON schema.
|
|
211
|
+
*/
|
|
212
|
+
private validateAgainstSchema(input: any, schema: object): ValidationResult {
|
|
213
|
+
// Simple schema validation - in production, you might use a library like ajv
|
|
214
|
+
try {
|
|
215
|
+
const schemaObj = schema as any;
|
|
216
|
+
|
|
217
|
+
if (schemaObj.type === 'object') {
|
|
218
|
+
if (!input || typeof input !== 'object') {
|
|
219
|
+
return { valid: false, error: 'Input must be an object' };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check required properties
|
|
223
|
+
if (schemaObj.required && Array.isArray(schemaObj.required)) {
|
|
224
|
+
for (const requiredProp of schemaObj.required) {
|
|
225
|
+
if (!(requiredProp in input)) {
|
|
226
|
+
return { valid: false, error: `Missing required property: ${requiredProp}` };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Basic type checking for properties
|
|
232
|
+
if (schemaObj.properties) {
|
|
233
|
+
for (const [propName, propSchema] of Object.entries(schemaObj.properties)) {
|
|
234
|
+
if (propName in input) {
|
|
235
|
+
const propType = (propSchema as any).type;
|
|
236
|
+
const actualType = typeof input[propName];
|
|
237
|
+
|
|
238
|
+
if (propType && propType !== actualType) {
|
|
239
|
+
return { valid: false, error: `Property '${propName}' must be of type ${propType}` };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return { valid: true, sanitized: input };
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return { valid: false, error: `Schema validation error: ${error}` };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get service statistics.
|
|
254
|
+
*/
|
|
255
|
+
getStatistics(): {
|
|
256
|
+
totalServices: number;
|
|
257
|
+
handlerServices: number;
|
|
258
|
+
agentServices: number;
|
|
259
|
+
servicesByCategory: Record<string, number>;
|
|
260
|
+
} {
|
|
261
|
+
const services = this.registry.list();
|
|
262
|
+
const stats = {
|
|
263
|
+
totalServices: services.length,
|
|
264
|
+
handlerServices: 0,
|
|
265
|
+
agentServices: 0,
|
|
266
|
+
servicesByCategory: {} as Record<string, number>
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
for (const service of services) {
|
|
270
|
+
// Count by execution mode
|
|
271
|
+
if (service.handler) {
|
|
272
|
+
stats.handlerServices++;
|
|
273
|
+
} else {
|
|
274
|
+
stats.agentServices++;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Count by category
|
|
278
|
+
const category = service.category || 'uncategorized';
|
|
279
|
+
stats.servicesByCategory[category] = (stats.servicesByCategory[category] || 0) + 1;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return stats;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Global service manager instance.
|
|
288
|
+
*/
|
|
289
|
+
export const serviceManager = new DefaultServiceManager();
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Initialize the service system.
|
|
293
|
+
* This should be called once during application startup.
|
|
294
|
+
*/
|
|
295
|
+
export async function initializeServiceSystem(): Promise<void> {
|
|
296
|
+
await serviceManager.initialize();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get service manager instance.
|
|
301
|
+
*/
|
|
302
|
+
export function getServiceManager(): DefaultServiceManager {
|
|
303
|
+
return serviceManager;
|
|
304
|
+
}
|