@open1s/ezbos 1.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/.github/workflows/ci.yml +34 -0
- package/.github/workflows/publish.yml +33 -0
- package/.pnp.cjs +8400 -0
- package/.pnp.loader.mjs +2126 -0
- package/.yarn/install-state.gz +0 -0
- package/README.md +468 -0
- package/bun.lock +89 -0
- package/examples/00-pr.ts +1 -0
- package/examples/01-tools.ts +85 -0
- package/examples/02-hooks.ts +82 -0
- package/examples/03-plugins.ts +91 -0
- package/examples/04-mcp.ts +74 -0
- package/examples/05-skills.ts +45 -0
- package/examples/06-session.ts +72 -0
- package/examples/07-agent-advanced.ts +86 -0
- package/examples/08-brainos-messaging.ts +83 -0
- package/package.json +46 -0
- package/skills/code-review/SKILL.md +18 -0
- package/src/agent.ts +404 -0
- package/src/brainos.ts +357 -0
- package/src/hook.ts +73 -0
- package/src/index.ts +17 -0
- package/src/mcp.ts +40 -0
- package/src/plugin.ts +93 -0
- package/src/skills.ts +31 -0
- package/src/tool.ts +110 -0
- package/src/types.ts +30 -0
- package/tsconfig.json +21 -0
package/src/brainos.ts
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import * as jsbos from '@open1s/jsbos';
|
|
2
|
+
import { AgentBuilder, Agent } from './agent.js';
|
|
3
|
+
|
|
4
|
+
interface BrainOSOptions {
|
|
5
|
+
model?: string;
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface BusOptions {
|
|
11
|
+
mode?: string;
|
|
12
|
+
connect?: string[];
|
|
13
|
+
listen?: string[];
|
|
14
|
+
peer?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class BrainOS {
|
|
18
|
+
private _bus: jsbos.Bus | null = null;
|
|
19
|
+
private _started = false;
|
|
20
|
+
private _config: any = null;
|
|
21
|
+
private _options: BrainOSOptions & { bus?: BusOptions } = {};
|
|
22
|
+
|
|
23
|
+
constructor(options: BrainOSOptions = {}) {
|
|
24
|
+
this._options = options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async start(): Promise<this> {
|
|
28
|
+
const loader = new jsbos.ConfigLoader();
|
|
29
|
+
loader.discover();
|
|
30
|
+
try {
|
|
31
|
+
this._config = JSON.parse(loader.loadSync());
|
|
32
|
+
} catch {
|
|
33
|
+
this._config = {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const gm = this._config.global_model || {};
|
|
37
|
+
const apiKey = this._options.apiKey || gm.api_key;
|
|
38
|
+
const baseUrl = this._options.baseUrl || gm.base_url || 'https://integrate.api.nvidia.com/v1';
|
|
39
|
+
const model = this._options.model || gm.model || 'nvidia/meta/llama-3.1-8b-instruct';
|
|
40
|
+
|
|
41
|
+
this._options.apiKey = apiKey;
|
|
42
|
+
this._options.baseUrl = baseUrl;
|
|
43
|
+
this._options.model = model;
|
|
44
|
+
|
|
45
|
+
const busConfig = this._options.bus || { mode: 'peer' };
|
|
46
|
+
this._bus = await jsbos.Bus.create(busConfig as any);
|
|
47
|
+
this._started = true;
|
|
48
|
+
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async stop(): Promise<void> {
|
|
53
|
+
if (this._bus) {
|
|
54
|
+
this._bus = null;
|
|
55
|
+
}
|
|
56
|
+
this._started = false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get isStarted(): boolean {
|
|
60
|
+
return this._started;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
with_bus(config: BusOptions): this {
|
|
64
|
+
this._options.bus = { ...this._options.bus, ...config };
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
agent(name: string = 'assistant', options: {
|
|
69
|
+
model?: string;
|
|
70
|
+
baseUrl?: string;
|
|
71
|
+
apiKey?: string;
|
|
72
|
+
systemPrompt?: string;
|
|
73
|
+
temperature?: number;
|
|
74
|
+
timeoutSecs?: number;
|
|
75
|
+
maxTokens?: number;
|
|
76
|
+
} = {}): AgentBuilder {
|
|
77
|
+
if (!this._started) {
|
|
78
|
+
throw new Error('BrainOS not started. Call start() first.');
|
|
79
|
+
}
|
|
80
|
+
return new AgentBuilder(name, {
|
|
81
|
+
...this._options,
|
|
82
|
+
...options,
|
|
83
|
+
apiKey: options.apiKey || this._options.apiKey,
|
|
84
|
+
model: options.model || this._options.model,
|
|
85
|
+
baseUrl: options.baseUrl || this._options.baseUrl,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get bus(): jsbos.Bus {
|
|
90
|
+
if (!this._started) throw new Error('BrainOS not started');
|
|
91
|
+
return this._bus!;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async publish(topic: string, payload: any, isJson: boolean = false): Promise<void> {
|
|
95
|
+
if (!this._started) throw new Error('BrainOS not started');
|
|
96
|
+
if (isJson) {
|
|
97
|
+
await this._bus!.publishJson(topic, payload);
|
|
98
|
+
} else {
|
|
99
|
+
await this._bus!.publishText(topic, String(payload));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async publisher(topic: string): Promise<PublisherWrapper> {
|
|
104
|
+
if (!this._started) throw new Error('BrainOS not started');
|
|
105
|
+
const pub = await this._bus!.createPublisher(topic);
|
|
106
|
+
return new PublisherWrapper(pub);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async subscriber(topic: string): Promise<SubscriberWrapper> {
|
|
110
|
+
if (!this._started) throw new Error('BrainOS not started');
|
|
111
|
+
const sub = await this._bus!.createSubscriber(topic);
|
|
112
|
+
return new SubscriberWrapper(sub);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async query(topic: string): Promise<QueryClient> {
|
|
116
|
+
if (!this._started) throw new Error('BrainOS not started');
|
|
117
|
+
const q = await this._bus!.createQuery(topic);
|
|
118
|
+
return new QueryClient(q);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async queryable(topic: string, handler?: (input: string) => any): Promise<QueryableServer> {
|
|
122
|
+
if (!this._started) throw new Error('BrainOS not started');
|
|
123
|
+
const q = await this._bus!.createQueryable(topic);
|
|
124
|
+
if (handler) {
|
|
125
|
+
(q as any)._handler = handler;
|
|
126
|
+
}
|
|
127
|
+
return new QueryableServer(q, handler);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async caller(name: string): Promise<CallerClient> {
|
|
131
|
+
if (!this._started) throw new Error('BrainOS not started');
|
|
132
|
+
const c = await this._bus!.createCaller(name);
|
|
133
|
+
return new CallerClient(c);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async callable(uri: string, handler?: (input: string) => any): Promise<CallableServer> {
|
|
137
|
+
if (!this._started) throw new Error('BrainOS not started');
|
|
138
|
+
const c = await this._bus!.createCallable(uri);
|
|
139
|
+
return new CallableServer(c, handler);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
get config(): any {
|
|
143
|
+
return this._config;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static async with(name: string = 'assistant', options: BrainOSOptions = {}): Promise<Agent> {
|
|
147
|
+
const brain = new BrainOS(options);
|
|
148
|
+
await brain.start();
|
|
149
|
+
const builder = brain.agent(name);
|
|
150
|
+
return await builder.start();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export class PublisherWrapper {
|
|
155
|
+
constructor(private _inner: jsbos.Publisher) {}
|
|
156
|
+
|
|
157
|
+
get topic(): string {
|
|
158
|
+
return this._inner.topic;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async publish(payload: any, isJson: boolean = false): Promise<void> {
|
|
162
|
+
if (isJson) {
|
|
163
|
+
await this._inner.publishJson(payload);
|
|
164
|
+
} else {
|
|
165
|
+
await this._inner.publishText(String(payload));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async text(payload: string): Promise<void> {
|
|
170
|
+
await this._inner.publishText(payload);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async json(data: any): Promise<void> {
|
|
174
|
+
await this._inner.publishJson(data);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export class SubscriberWrapper {
|
|
179
|
+
constructor(private _inner: jsbos.Subscriber) {}
|
|
180
|
+
|
|
181
|
+
get topic(): string {
|
|
182
|
+
return this._inner.topic;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async recv(timeoutMs?: number): Promise<string | null> {
|
|
186
|
+
if (timeoutMs) {
|
|
187
|
+
return await this._inner.recvWithTimeoutMs(timeoutMs);
|
|
188
|
+
}
|
|
189
|
+
return await this._inner.recv();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async recvJson(timeoutMs?: number): Promise<any | null> {
|
|
193
|
+
const msg = await this.recv(timeoutMs);
|
|
194
|
+
if (!msg) return null;
|
|
195
|
+
try {
|
|
196
|
+
return JSON.parse(msg);
|
|
197
|
+
} catch {
|
|
198
|
+
return msg;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async run(callback: (msg: string) => void): Promise<void> {
|
|
203
|
+
await this._inner.run((e, msg) => {
|
|
204
|
+
if (e) return;
|
|
205
|
+
callback(msg as string);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async runJson(callback: (data: any) => void): Promise<void> {
|
|
210
|
+
await this._inner.runJson((e, data) => {
|
|
211
|
+
if (e) return;
|
|
212
|
+
callback(data);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async stop(): Promise<void> {
|
|
217
|
+
await this._inner.stop();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
[Symbol.asyncIterator]() {
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async next(): Promise<{ done: boolean; value?: string }> {
|
|
225
|
+
const msg = await this.recv();
|
|
226
|
+
return msg === null ? { done: true } : { done: false, value: msg };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export class QueryClient {
|
|
231
|
+
constructor(private _inner: jsbos.Query) {}
|
|
232
|
+
|
|
233
|
+
get topic(): string {
|
|
234
|
+
return this._inner.topic;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async ask(payload: string, timeoutMs?: number): Promise<string> {
|
|
238
|
+
if (timeoutMs) {
|
|
239
|
+
return await this._inner.queryTextTimeoutMs(payload, timeoutMs);
|
|
240
|
+
}
|
|
241
|
+
return await this._inner.queryText(payload);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async askJson(payload: any, timeoutMs?: number): Promise<any> {
|
|
245
|
+
const response = await this.ask(JSON.stringify(payload), timeoutMs);
|
|
246
|
+
try {
|
|
247
|
+
return JSON.parse(response);
|
|
248
|
+
} catch {
|
|
249
|
+
return response;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export class QueryableServer {
|
|
255
|
+
private _handler?: (input: string) => any;
|
|
256
|
+
|
|
257
|
+
constructor(private _inner: jsbos.Queryable, handler?: (input: string) => any) {
|
|
258
|
+
this._handler = handler;
|
|
259
|
+
if (handler) {
|
|
260
|
+
this._inner.setHandler((e, input) => {
|
|
261
|
+
if (e) throw e;
|
|
262
|
+
return handler(input);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
handle(handler: (input: string) => any): this {
|
|
268
|
+
this._handler = handler;
|
|
269
|
+
this._inner.setHandler((e, input) => {
|
|
270
|
+
if (e) throw e;
|
|
271
|
+
return handler(input);
|
|
272
|
+
});
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async start(): Promise<this> {
|
|
277
|
+
await this._inner.start();
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async run(handler: (input: string) => any): Promise<void> {
|
|
282
|
+
this._inner.run((e, input) => {
|
|
283
|
+
if (e) throw e;
|
|
284
|
+
return handler(input);
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async runJson(handler: (input: any) => any): Promise<void> {
|
|
289
|
+
this._inner.runJson((e, input) => {
|
|
290
|
+
if (e) throw e;
|
|
291
|
+
return handler(input);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export class CallerClient {
|
|
297
|
+
constructor(private _inner: jsbos.Caller) {}
|
|
298
|
+
|
|
299
|
+
async call(payload: string): Promise<string> {
|
|
300
|
+
return await this._inner.callText(payload);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async callJson(payload: any): Promise<any> {
|
|
304
|
+
const response = await this._inner.callText(JSON.stringify(payload));
|
|
305
|
+
try {
|
|
306
|
+
return JSON.parse(response);
|
|
307
|
+
} catch {
|
|
308
|
+
return response;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export class CallableServer {
|
|
314
|
+
private _handler?: (input: string) => any;
|
|
315
|
+
|
|
316
|
+
constructor(private _inner: jsbos.Callable, handler?: (input: string) => any) {
|
|
317
|
+
this._handler = handler;
|
|
318
|
+
if (handler) {
|
|
319
|
+
this._inner.setHandler((e, input) => {
|
|
320
|
+
if (e) throw e;
|
|
321
|
+
return handler(input);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
handle(handler: (input: string) => any): this {
|
|
327
|
+
this._handler = handler;
|
|
328
|
+
this._inner.setHandler((e, input) => {
|
|
329
|
+
if (e) throw e;
|
|
330
|
+
return handler(input);
|
|
331
|
+
});
|
|
332
|
+
return this;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
get isStarted(): boolean {
|
|
336
|
+
return this._inner.isStarted();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async start(): Promise<this> {
|
|
340
|
+
await this._inner.start();
|
|
341
|
+
return this;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async run(handler: (input: string) => any): Promise<void> {
|
|
345
|
+
this._inner.run((e, input) => {
|
|
346
|
+
if (e) throw e;
|
|
347
|
+
return handler(input);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async runJson(handler: (input: any) => any): Promise<void> {
|
|
352
|
+
this._inner.runJson((e, input) => {
|
|
353
|
+
if (e) throw e;
|
|
354
|
+
return handler(input);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
package/src/hook.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { HookEvent as JSHookEvent, HookContextData } from '@open1s/jsbos';
|
|
2
|
+
|
|
3
|
+
export const HookEvent = {
|
|
4
|
+
BeforeToolCall: 0,
|
|
5
|
+
AfterToolCall: 1,
|
|
6
|
+
BeforeLlmCall: 2,
|
|
7
|
+
AfterLlmCall: 3,
|
|
8
|
+
OnMessage: 4,
|
|
9
|
+
OnComplete: 5,
|
|
10
|
+
OnError: 6,
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
export type HookEvent = typeof HookEvent[keyof typeof HookEvent];
|
|
14
|
+
export type { HookContextData } from '@open1s/jsbos';
|
|
15
|
+
|
|
16
|
+
export const HookDecision = {
|
|
17
|
+
Continue: 'continue',
|
|
18
|
+
Abort: 'abort',
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
export type HookDecision = 'continue' | 'abort';
|
|
22
|
+
|
|
23
|
+
export type HookCallback = (ctx: HookContextData) => string | Promise<string>;
|
|
24
|
+
|
|
25
|
+
const registeredHooks = new WeakMap<object, Map<HookEvent, HookCallback[]>>();
|
|
26
|
+
|
|
27
|
+
export function hook(event: HookEvent) {
|
|
28
|
+
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
29
|
+
const original = descriptor.value;
|
|
30
|
+
const hooks = registeredHooks.get(target) || new Map();
|
|
31
|
+
if (!hooks.has(event)) hooks.set(event, []);
|
|
32
|
+
hooks.get(event)!.push(async function(this: any, ctx: HookContextData) {
|
|
33
|
+
return await original.call(this, ctx);
|
|
34
|
+
});
|
|
35
|
+
registeredHooks.set(target, hooks);
|
|
36
|
+
return descriptor;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createHookRegistry(instance: object): Array<{ event: HookEvent, callback: HookCallback }> {
|
|
41
|
+
const hooks: Array<{ event: HookEvent, callback: HookCallback }> = [];
|
|
42
|
+
const proto = Object.getPrototypeOf(instance);
|
|
43
|
+
const map = registeredHooks.get(instance) || registeredHooks.get(proto);
|
|
44
|
+
if (map) {
|
|
45
|
+
for (const [event, callbacks] of map) {
|
|
46
|
+
for (const cb of callbacks) {
|
|
47
|
+
hooks.push({ event: event as HookEvent, callback: cb });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return hooks;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function mergeHooks(...sources: any[]): Array<{ event: HookEvent, callback: HookCallback }> {
|
|
55
|
+
const result: Array<{ event: HookEvent, callback: HookCallback }> = [];
|
|
56
|
+
for (const source of sources) {
|
|
57
|
+
if (!source) continue;
|
|
58
|
+
if (typeof source === 'object' && 'callback' in source) {
|
|
59
|
+
result.push(source as { event: HookEvent, callback: HookCallback });
|
|
60
|
+
} else if (typeof source === 'object') {
|
|
61
|
+
const hooks = createHookRegistry(source);
|
|
62
|
+
result.push(...hooks);
|
|
63
|
+
} else if (typeof source === 'function') {
|
|
64
|
+
const hooks = createHookRegistry(source.prototype || source);
|
|
65
|
+
result.push(...hooks);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function defineHook(event: HookEvent, callback: HookCallback): { event: HookEvent, callback: HookCallback } {
|
|
72
|
+
return { event, callback };
|
|
73
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as jsbos from '@open1s/jsbos';
|
|
2
|
+
|
|
3
|
+
export { jsbos };
|
|
4
|
+
|
|
5
|
+
export * from './tool.js';
|
|
6
|
+
export * from './hook.js';
|
|
7
|
+
export * from './plugin.js';
|
|
8
|
+
export * from './mcp.js';
|
|
9
|
+
export * from './skills.js';
|
|
10
|
+
export * from './agent.js';
|
|
11
|
+
export * from './brainos.js';
|
|
12
|
+
|
|
13
|
+
export const version = jsbos.version;
|
|
14
|
+
export const initTracing = jsbos.initTracing;
|
|
15
|
+
export const logTestMessage = jsbos.logTestMessage;
|
|
16
|
+
export { McpClient } from '@open1s/jsbos';
|
|
17
|
+
export { ConfigLoader } from '@open1s/jsbos';
|
package/src/mcp.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface McpProcessConfig {
|
|
2
|
+
type: 'process';
|
|
3
|
+
namespace: string;
|
|
4
|
+
command: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface McpHttpConfig {
|
|
9
|
+
type: 'http';
|
|
10
|
+
namespace: string;
|
|
11
|
+
url: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type McpConfig = McpProcessConfig | McpHttpConfig;
|
|
15
|
+
|
|
16
|
+
export function defineMcpProcess(namespace: string, command: string, args: string[]): McpProcessConfig {
|
|
17
|
+
return { type: 'process', namespace, command, args };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function defineMcpHttp(namespace: string, url: string): McpHttpConfig {
|
|
21
|
+
return { type: 'http', namespace, url };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class McpBuilder {
|
|
25
|
+
private _configs: McpConfig[] = [];
|
|
26
|
+
|
|
27
|
+
process(namespace: string, command: string, args: string[]): this {
|
|
28
|
+
this._configs.push({ type: 'process', namespace, command, args });
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
http(namespace: string, url: string): this {
|
|
33
|
+
this._configs.push({ type: 'http', namespace, url });
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
build(): McpConfig[] {
|
|
38
|
+
return [...this._configs];
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsbos } from './tool.js';
|
|
2
|
+
|
|
3
|
+
export interface PluginHandlers {
|
|
4
|
+
name?: string;
|
|
5
|
+
on_llm_request?: (req: any) => any;
|
|
6
|
+
on_llm_response?: (resp: any) => any;
|
|
7
|
+
on_tool_call?: (call: any) => any;
|
|
8
|
+
on_tool_result?: (result: any) => any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const registeredPlugins = new WeakMap<object, PluginHandlers>();
|
|
12
|
+
|
|
13
|
+
export function plugin(name: string) {
|
|
14
|
+
return function <T extends { new (...args: any[]): {} }>(target: T) {
|
|
15
|
+
return class extends target {
|
|
16
|
+
__pluginName = name;
|
|
17
|
+
constructor(...args: any[]) {
|
|
18
|
+
super(...args);
|
|
19
|
+
const handlers: PluginHandlers = { name };
|
|
20
|
+
const proto = target.prototype;
|
|
21
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
22
|
+
if (key === 'constructor') continue;
|
|
23
|
+
const method = (proto as any)[key];
|
|
24
|
+
if (typeof method === 'function') {
|
|
25
|
+
if (key === 'on_llm_request') handlers.on_llm_request = method.bind(this);
|
|
26
|
+
else if (key === 'on_llm_response') handlers.on_llm_response = method.bind(this);
|
|
27
|
+
else if (key === 'on_tool_call') handlers.on_tool_call = method.bind(this);
|
|
28
|
+
else if (key === 'on_tool_result') handlers.on_tool_result = method.bind(this);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
registeredPlugins.set(this, handlers);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function on_llm_request(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
|
|
38
|
+
return descriptor;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function on_llm_response(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
|
|
42
|
+
return descriptor;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function on_tool_call(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
|
|
46
|
+
return descriptor;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function on_tool_result(_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
|
|
50
|
+
return descriptor;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getPluginHandlers(instance: object): PluginHandlers {
|
|
54
|
+
const registered = registeredPlugins.get(instance);
|
|
55
|
+
if (registered) return registered;
|
|
56
|
+
|
|
57
|
+
const name = instance?.constructor?.name;
|
|
58
|
+
const handlers: PluginHandlers = { name };
|
|
59
|
+
|
|
60
|
+
const proto = Object.getPrototypeOf(instance);
|
|
61
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
62
|
+
if (key === 'constructor') continue;
|
|
63
|
+
const method = (proto as any)[key];
|
|
64
|
+
if (typeof method === 'function') {
|
|
65
|
+
if (key === 'on_llm_request') handlers.on_llm_request = method.bind(instance);
|
|
66
|
+
else if (key === 'on_llm_response') handlers.on_llm_response = method.bind(instance);
|
|
67
|
+
else if (key === 'on_tool_call') handlers.on_tool_call = method.bind(instance);
|
|
68
|
+
else if (key === 'on_tool_result') handlers.on_tool_result = method.bind(instance);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return handlers;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function mergePlugins(...sources: any[]): PluginHandlers[] {
|
|
76
|
+
const result: PluginHandlers[] = [];
|
|
77
|
+
for (const source of sources) {
|
|
78
|
+
if (!source) continue;
|
|
79
|
+
if (typeof source === 'object' && 'name' in source) {
|
|
80
|
+
result.push(source as PluginHandlers);
|
|
81
|
+
} else if (typeof source === 'function') {
|
|
82
|
+
const instance = new (source as any)();
|
|
83
|
+
result.push(getPluginHandlers(instance));
|
|
84
|
+
} else if (typeof source === 'object') {
|
|
85
|
+
result.push(getPluginHandlers(source));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function definePlugin(handlers: PluginHandlers): PluginHandlers {
|
|
92
|
+
return handlers;
|
|
93
|
+
}
|
package/src/skills.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface SkillDef {
|
|
2
|
+
name: string;
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SkillDir {
|
|
7
|
+
path: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function defineSkill(name: string, content: string): SkillDef {
|
|
11
|
+
return { name, content };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class SkillsBuilder {
|
|
15
|
+
private _dirs: string[] = [];
|
|
16
|
+
private _skills: SkillDef[] = [];
|
|
17
|
+
|
|
18
|
+
from_dir(path: string): this {
|
|
19
|
+
this._dirs.push(path);
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
add(name: string, content: string): this {
|
|
24
|
+
this._skills.push({ name, content });
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
build(): { dirs: string[], inline: SkillDef[] } {
|
|
29
|
+
return { dirs: this._dirs, inline: this._skills };
|
|
30
|
+
}
|
|
31
|
+
}
|