@kevisual/router 0.0.56 → 0.0.57

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.
@@ -0,0 +1,331 @@
1
+ import { Plugin } from '@opencode-ai/plugin';
2
+
3
+ type RouterContextT = {
4
+ code?: number;
5
+ [key: string]: any;
6
+ };
7
+ type RouteContext<T = {
8
+ code?: number;
9
+ }, S = any> = {
10
+ /**
11
+ * 本地自己调用的时候使用,可以标识为当前自调用,那么 auth 就不许重复的校验
12
+ * 或者不需要登录的,直接调用
13
+ */
14
+ appId?: string;
15
+ query?: {
16
+ [key: string]: any;
17
+ };
18
+ /** return body */
19
+ body?: number | string | Object;
20
+ forward?: (response: {
21
+ code: number;
22
+ data?: any;
23
+ message?: any;
24
+ }) => void;
25
+ /** return code */
26
+ code?: number;
27
+ /** return msg */
28
+ message?: string;
29
+ state?: S;
30
+ /**
31
+ * 当前路径
32
+ */
33
+ currentPath?: string;
34
+ /**
35
+ * 当前key
36
+ */
37
+ currentKey?: string;
38
+ /**
39
+ * 当前route
40
+ */
41
+ currentRoute?: Route;
42
+ /**
43
+ * 进度
44
+ */
45
+ progress?: [string, string][];
46
+ nextQuery?: {
47
+ [key: string]: any;
48
+ };
49
+ end?: boolean;
50
+ app?: QueryRouter;
51
+ error?: any;
52
+ /** 请求 route的返回结果,不解析body为data */
53
+ call?: (message: {
54
+ path: string;
55
+ key?: string;
56
+ payload?: any;
57
+ [key: string]: any;
58
+ } | {
59
+ id: string;
60
+ apyload?: any;
61
+ [key: string]: any;
62
+ }, ctx?: RouteContext & {
63
+ [key: string]: any;
64
+ }) => Promise<any>;
65
+ /** 请求 route的返回结果,解析了body为data,就类同于 query.post获取的数据*/
66
+ run?: (message: {
67
+ path: string;
68
+ key?: string;
69
+ payload?: any;
70
+ }, ctx?: RouteContext & {
71
+ [key: string]: any;
72
+ }) => Promise<any>;
73
+ index?: number;
74
+ throw?: (code?: number | string, message?: string, tips?: string) => void;
75
+ /** 是否需要序列化, 使用JSON.stringify和JSON.parse */
76
+ needSerialize?: boolean;
77
+ } & T;
78
+ type SimpleObject = Record<string, any>;
79
+ type Run<T extends SimpleObject = {}> = (ctx: RouteContext<T>) => Promise<typeof ctx | null | void>;
80
+ type NextRoute = Pick<Route, 'id' | 'path' | 'key'>;
81
+ type RouteMiddleware = {
82
+ path: string;
83
+ key?: string;
84
+ id?: string;
85
+ } | string;
86
+ type RouteOpts<U = {}, T = SimpleObject> = {
87
+ path?: string;
88
+ key?: string;
89
+ id?: string;
90
+ run?: Run<U>;
91
+ nextRoute?: NextRoute;
92
+ description?: string;
93
+ metadata?: T;
94
+ middleware?: RouteMiddleware[];
95
+ type?: 'route' | 'middleware';
96
+ /**
97
+ * $#$ will be used to split path and key
98
+ */
99
+ idUsePath?: boolean;
100
+ /**
101
+ * id 合并的分隔符,默认为 $#$
102
+ */
103
+ delimiter?: string;
104
+ isDebug?: boolean;
105
+ };
106
+ type DefineRouteOpts = Omit<RouteOpts, 'idUsePath' | 'nextRoute'>;
107
+ declare const pickValue: readonly ["path", "key", "id", "description", "type", "middleware", "metadata"];
108
+ type RouteInfo = Pick<Route, (typeof pickValue)[number]>;
109
+ declare class Route<U = {
110
+ [key: string]: any;
111
+ }, T extends SimpleObject = SimpleObject> {
112
+ /**
113
+ * 一级路径
114
+ */
115
+ path?: string;
116
+ /**
117
+ * 二级路径
118
+ */
119
+ key?: string;
120
+ id?: string;
121
+ run?: Run;
122
+ nextRoute?: NextRoute;
123
+ description?: string;
124
+ metadata?: T;
125
+ middleware?: RouteMiddleware[];
126
+ type?: string;
127
+ data?: any;
128
+ /**
129
+ * 是否开启debug,开启后会打印错误信息
130
+ */
131
+ isDebug?: boolean;
132
+ constructor(path?: string, key?: string, opts?: RouteOpts);
133
+ prompt(description: string): this;
134
+ prompt(description: Function): this;
135
+ define<T extends {
136
+ [key: string]: any;
137
+ } = RouterContextT>(opts: DefineRouteOpts): this;
138
+ define<T extends {
139
+ [key: string]: any;
140
+ } = RouterContextT>(fn: Run<T & U>): this;
141
+ define<T extends {
142
+ [key: string]: any;
143
+ } = RouterContextT>(key: string, fn: Run<T & U>): this;
144
+ define<T extends {
145
+ [key: string]: any;
146
+ } = RouterContextT>(path: string, key: string, fn: Run<T & U>): this;
147
+ update(opts: DefineRouteOpts, checkList?: string[]): this;
148
+ addTo(router: QueryRouter | {
149
+ add: (route: Route) => void;
150
+ [key: string]: any;
151
+ }): void;
152
+ setData(data: any): this;
153
+ throw(code?: number | string, message?: string, tips?: string): void;
154
+ }
155
+ declare class QueryRouter {
156
+ appId: string;
157
+ routes: Route[];
158
+ maxNextRoute: number;
159
+ context?: RouteContext;
160
+ constructor();
161
+ add(route: Route): void;
162
+ /**
163
+ * remove route by path and key
164
+ * @param route
165
+ */
166
+ remove(route: Route | {
167
+ path: string;
168
+ key?: string;
169
+ }): void;
170
+ /**
171
+ * remove route by id
172
+ * @param uniqueId
173
+ */
174
+ removeById(unique: string): void;
175
+ /**
176
+ * 执行route
177
+ * @param path
178
+ * @param key
179
+ * @param ctx
180
+ * @returns
181
+ */
182
+ runRoute(path: string, key: string, ctx?: RouteContext): any;
183
+ /**
184
+ * 第一次执行
185
+ * @param message
186
+ * @param ctx
187
+ * @returns
188
+ */
189
+ parse(message: {
190
+ path: string;
191
+ key?: string;
192
+ payload?: any;
193
+ }, ctx?: RouteContext & {
194
+ [key: string]: any;
195
+ }): Promise<any>;
196
+ /**
197
+ * 返回的数据包含所有的context的请求返回的内容,可做其他处理
198
+ * @param message
199
+ * @param ctx
200
+ * @returns
201
+ */
202
+ call(message: {
203
+ id?: string;
204
+ path?: string;
205
+ key?: string;
206
+ payload?: any;
207
+ }, ctx?: RouteContext & {
208
+ [key: string]: any;
209
+ }): Promise<any>;
210
+ /**
211
+ * 请求 result 的数据
212
+ * @param message
213
+ * @param ctx
214
+ * @deprecated use run or call instead
215
+ * @returns
216
+ */
217
+ queryRoute(message: {
218
+ id?: string;
219
+ path: string;
220
+ key?: string;
221
+ payload?: any;
222
+ }, ctx?: RouteContext & {
223
+ [key: string]: any;
224
+ }): Promise<{
225
+ code: any;
226
+ data: any;
227
+ message: any;
228
+ }>;
229
+ /**
230
+ * Router Run获取数据
231
+ * @param message
232
+ * @param ctx
233
+ * @returns
234
+ */
235
+ run(message: {
236
+ id?: string;
237
+ path?: string;
238
+ key?: string;
239
+ payload?: any;
240
+ }, ctx?: RouteContext & {
241
+ [key: string]: any;
242
+ }): Promise<{
243
+ code: any;
244
+ data: any;
245
+ message: any;
246
+ }>;
247
+ /**
248
+ * 设置上下文
249
+ * @description 这里的上下文是为了在handle函数中使用
250
+ * @param ctx
251
+ */
252
+ setContext(ctx: RouteContext): void;
253
+ getList(filter?: (route: Route) => boolean): RouteInfo[];
254
+ /**
255
+ * 获取handle函数, 这里会去执行parse函数
256
+ */
257
+ getHandle<T = any>(router: QueryRouter, wrapperFn?: HandleFn<T>, ctx?: RouteContext): (msg: {
258
+ id?: string;
259
+ path?: string;
260
+ key?: string;
261
+ [key: string]: any;
262
+ }, handleContext?: RouteContext) => Promise<{
263
+ [key: string]: any;
264
+ code: string;
265
+ data?: any;
266
+ message?: string;
267
+ } | {
268
+ code: any;
269
+ data: any;
270
+ message: any;
271
+ } | {
272
+ code: number;
273
+ message: any;
274
+ data?: undefined;
275
+ }>;
276
+ exportRoutes(): Route<{
277
+ [key: string]: any;
278
+ }, SimpleObject>[];
279
+ importRoutes(routes: Route[]): void;
280
+ importRouter(router: QueryRouter): void;
281
+ throw(code?: number | string, message?: string, tips?: string): void;
282
+ hasRoute(path: string, key?: string): Route<{
283
+ [key: string]: any;
284
+ }, SimpleObject>;
285
+ findRoute(opts?: {
286
+ path?: string;
287
+ key?: string;
288
+ id?: string;
289
+ }): Route<{
290
+ [key: string]: any;
291
+ }, SimpleObject>;
292
+ createRouteList(force?: boolean, filter?: (route: Route) => boolean): void;
293
+ /**
294
+ * 等待程序运行, 获取到message的数据,就执行
295
+ *
296
+ * emitter = process
297
+ * -- .exit
298
+ * -- .on
299
+ * -- .send
300
+ */
301
+ wait(params?: {
302
+ path?: string;
303
+ key?: string;
304
+ payload?: any;
305
+ }, opts?: {
306
+ emitter?: any;
307
+ timeout?: number;
308
+ getList?: boolean;
309
+ force?: boolean;
310
+ filter?: (route: Route) => boolean;
311
+ }): Promise<void>;
312
+ }
313
+ interface HandleFn<T = any> {
314
+ (msg: {
315
+ path: string;
316
+ [key: string]: any;
317
+ }, ctx?: any): {
318
+ code: string;
319
+ data?: any;
320
+ message?: string;
321
+ [key: string]: any;
322
+ };
323
+ (res: RouteContext<T>): any;
324
+ }
325
+
326
+ declare const createRouterAgentPluginFn: (opts?: {
327
+ router?: QueryRouter;
328
+ query?: string;
329
+ }) => Plugin;
330
+
331
+ export { createRouterAgentPluginFn };
@@ -0,0 +1,969 @@
1
+ // src/utils/path-key.ts
2
+
3
+ // ../../node_modules/.pnpm/@kevisual+load@0.0.6/node_modules/@kevisual/load/dist/load.js
4
+ function getDefaultExportFromCjs(x) {
5
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
6
+ }
7
+ var eventemitter3 = { exports: {} };
8
+ var hasRequiredEventemitter3;
9
+ function requireEventemitter3() {
10
+ if (hasRequiredEventemitter3)
11
+ return eventemitter3.exports;
12
+ hasRequiredEventemitter3 = 1;
13
+ (function(module) {
14
+ var has = Object.prototype.hasOwnProperty, prefix = "~";
15
+ function Events() {}
16
+ if (Object.create) {
17
+ Events.prototype = Object.create(null);
18
+ if (!new Events().__proto__)
19
+ prefix = false;
20
+ }
21
+ function EE(fn, context, once) {
22
+ this.fn = fn;
23
+ this.context = context;
24
+ this.once = once || false;
25
+ }
26
+ function addListener(emitter, event, fn, context, once) {
27
+ if (typeof fn !== "function") {
28
+ throw new TypeError("The listener must be a function");
29
+ }
30
+ var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event;
31
+ if (!emitter._events[evt])
32
+ emitter._events[evt] = listener, emitter._eventsCount++;
33
+ else if (!emitter._events[evt].fn)
34
+ emitter._events[evt].push(listener);
35
+ else
36
+ emitter._events[evt] = [emitter._events[evt], listener];
37
+ return emitter;
38
+ }
39
+ function clearEvent(emitter, evt) {
40
+ if (--emitter._eventsCount === 0)
41
+ emitter._events = new Events;
42
+ else
43
+ delete emitter._events[evt];
44
+ }
45
+ function EventEmitter() {
46
+ this._events = new Events;
47
+ this._eventsCount = 0;
48
+ }
49
+ EventEmitter.prototype.eventNames = function eventNames() {
50
+ var names = [], events, name;
51
+ if (this._eventsCount === 0)
52
+ return names;
53
+ for (name in events = this._events) {
54
+ if (has.call(events, name))
55
+ names.push(prefix ? name.slice(1) : name);
56
+ }
57
+ if (Object.getOwnPropertySymbols) {
58
+ return names.concat(Object.getOwnPropertySymbols(events));
59
+ }
60
+ return names;
61
+ };
62
+ EventEmitter.prototype.listeners = function listeners(event) {
63
+ var evt = prefix ? prefix + event : event, handlers = this._events[evt];
64
+ if (!handlers)
65
+ return [];
66
+ if (handlers.fn)
67
+ return [handlers.fn];
68
+ for (var i = 0, l = handlers.length, ee = new Array(l);i < l; i++) {
69
+ ee[i] = handlers[i].fn;
70
+ }
71
+ return ee;
72
+ };
73
+ EventEmitter.prototype.listenerCount = function listenerCount(event) {
74
+ var evt = prefix ? prefix + event : event, listeners = this._events[evt];
75
+ if (!listeners)
76
+ return 0;
77
+ if (listeners.fn)
78
+ return 1;
79
+ return listeners.length;
80
+ };
81
+ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
82
+ var evt = prefix ? prefix + event : event;
83
+ if (!this._events[evt])
84
+ return false;
85
+ var listeners = this._events[evt], len = arguments.length, args, i;
86
+ if (listeners.fn) {
87
+ if (listeners.once)
88
+ this.removeListener(event, listeners.fn, undefined, true);
89
+ switch (len) {
90
+ case 1:
91
+ return listeners.fn.call(listeners.context), true;
92
+ case 2:
93
+ return listeners.fn.call(listeners.context, a1), true;
94
+ case 3:
95
+ return listeners.fn.call(listeners.context, a1, a2), true;
96
+ case 4:
97
+ return listeners.fn.call(listeners.context, a1, a2, a3), true;
98
+ case 5:
99
+ return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
100
+ case 6:
101
+ return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
102
+ }
103
+ for (i = 1, args = new Array(len - 1);i < len; i++) {
104
+ args[i - 1] = arguments[i];
105
+ }
106
+ listeners.fn.apply(listeners.context, args);
107
+ } else {
108
+ var length = listeners.length, j;
109
+ for (i = 0;i < length; i++) {
110
+ if (listeners[i].once)
111
+ this.removeListener(event, listeners[i].fn, undefined, true);
112
+ switch (len) {
113
+ case 1:
114
+ listeners[i].fn.call(listeners[i].context);
115
+ break;
116
+ case 2:
117
+ listeners[i].fn.call(listeners[i].context, a1);
118
+ break;
119
+ case 3:
120
+ listeners[i].fn.call(listeners[i].context, a1, a2);
121
+ break;
122
+ case 4:
123
+ listeners[i].fn.call(listeners[i].context, a1, a2, a3);
124
+ break;
125
+ default:
126
+ if (!args)
127
+ for (j = 1, args = new Array(len - 1);j < len; j++) {
128
+ args[j - 1] = arguments[j];
129
+ }
130
+ listeners[i].fn.apply(listeners[i].context, args);
131
+ }
132
+ }
133
+ }
134
+ return true;
135
+ };
136
+ EventEmitter.prototype.on = function on(event, fn, context) {
137
+ return addListener(this, event, fn, context, false);
138
+ };
139
+ EventEmitter.prototype.once = function once(event, fn, context) {
140
+ return addListener(this, event, fn, context, true);
141
+ };
142
+ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
143
+ var evt = prefix ? prefix + event : event;
144
+ if (!this._events[evt])
145
+ return this;
146
+ if (!fn) {
147
+ clearEvent(this, evt);
148
+ return this;
149
+ }
150
+ var listeners = this._events[evt];
151
+ if (listeners.fn) {
152
+ if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) {
153
+ clearEvent(this, evt);
154
+ }
155
+ } else {
156
+ for (var i = 0, events = [], length = listeners.length;i < length; i++) {
157
+ if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) {
158
+ events.push(listeners[i]);
159
+ }
160
+ }
161
+ if (events.length)
162
+ this._events[evt] = events.length === 1 ? events[0] : events;
163
+ else
164
+ clearEvent(this, evt);
165
+ }
166
+ return this;
167
+ };
168
+ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
169
+ var evt;
170
+ if (event) {
171
+ evt = prefix ? prefix + event : event;
172
+ if (this._events[evt])
173
+ clearEvent(this, evt);
174
+ } else {
175
+ this._events = new Events;
176
+ this._eventsCount = 0;
177
+ }
178
+ return this;
179
+ };
180
+ EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
181
+ EventEmitter.prototype.addListener = EventEmitter.prototype.on;
182
+ EventEmitter.prefixed = prefix;
183
+ EventEmitter.EventEmitter = EventEmitter;
184
+ {
185
+ module.exports = EventEmitter;
186
+ }
187
+ })(eventemitter3);
188
+ return eventemitter3.exports;
189
+ }
190
+ var eventemitter3Exports = requireEventemitter3();
191
+ var EventEmitter = /* @__PURE__ */ getDefaultExportFromCjs(eventemitter3Exports);
192
+ var reRunFn = (promiseOpts) => {
193
+ const timeout = promiseOpts.timeout || 5 * 60 * 1000;
194
+ const interval = promiseOpts.interval || 1000;
195
+ const checkSuccess = promiseOpts?.checkSuccess || (() => true);
196
+ const signal = promiseOpts.signal;
197
+ return new Promise(async (resolve, reject) => {
198
+ let intervalId;
199
+ let timeoutId = setTimeout(() => {
200
+ clearTimeout(intervalId);
201
+ resolve({
202
+ code: 500,
203
+ message: "timeout"
204
+ });
205
+ }, timeout);
206
+ const fn = promiseOpts.fn || (() => true);
207
+ const runFn = async () => {
208
+ if (signal?.aborted) {
209
+ clearInterval(intervalId);
210
+ clearTimeout(timeoutId);
211
+ return resolve({
212
+ code: 499,
213
+ message: "operation cancelled"
214
+ });
215
+ }
216
+ const res = await fn();
217
+ if (!!checkSuccess(res)) {
218
+ clearInterval(intervalId);
219
+ clearTimeout(timeoutId);
220
+ resolve({
221
+ code: 200,
222
+ data: res
223
+ });
224
+ } else {
225
+ setTimeout(() => {
226
+ runFn();
227
+ }, interval);
228
+ }
229
+ };
230
+ if (signal) {
231
+ signal.addEventListener("abort", () => {
232
+ clearInterval(intervalId);
233
+ clearTimeout(timeoutId);
234
+ resolve({
235
+ code: 499,
236
+ message: "operation cancelled"
237
+ });
238
+ });
239
+ }
240
+ runFn();
241
+ });
242
+ };
243
+
244
+ class BaseLoad {
245
+ modules = new Map;
246
+ event;
247
+ loading;
248
+ static reRunFn = reRunFn;
249
+ timeout = 5 * 60 * 1000;
250
+ constructor() {
251
+ this.event = new EventEmitter;
252
+ this.loading = false;
253
+ }
254
+ listenKey(key, listenOpts) {
255
+ const timeout = listenOpts?.timeout ?? this.timeout;
256
+ return new Promise((resolve) => {
257
+ const timeoutId = setTimeout(() => {
258
+ this.event.removeListener(key, onEvent);
259
+ resolve({
260
+ code: 500,
261
+ message: "timeout"
262
+ });
263
+ }, timeout);
264
+ const onEvent = (error) => {
265
+ clearTimeout(timeoutId);
266
+ if (error) {
267
+ return resolve({
268
+ code: 500,
269
+ message: error
270
+ });
271
+ }
272
+ const data = this.modules.get(key);
273
+ if (data?.loadSuccessClear) {
274
+ this.remove(key);
275
+ }
276
+ resolve({
277
+ code: 200,
278
+ data: data?.modules
279
+ });
280
+ };
281
+ this.event.once(key, onEvent);
282
+ });
283
+ }
284
+ async hasLoaded(key, hasLoadOpts) {
285
+ if (!key) {
286
+ return {
287
+ code: 404,
288
+ message: "key is required"
289
+ };
290
+ }
291
+ const has = this.modules.has(key);
292
+ if (!has) {
293
+ const isExist = hasLoadOpts?.isExist ?? true;
294
+ const timeout = hasLoadOpts?.timeout ?? this.timeout;
295
+ if (isExist) {
296
+ return await this.listenKey(key, { timeout });
297
+ }
298
+ return {
299
+ code: 404
300
+ };
301
+ }
302
+ const data = this.modules.get(key);
303
+ if (data?.status === "loaded") {
304
+ return {
305
+ code: 200,
306
+ data: data.modules
307
+ };
308
+ }
309
+ if (data?.status === "loading") {
310
+ return await this.listenKey(key, { timeout: hasLoadOpts?.timeout ?? this.timeout });
311
+ }
312
+ if (data?.status === "error") {
313
+ return {
314
+ code: 500,
315
+ message: "load error"
316
+ };
317
+ }
318
+ if (data?.status === "cancel") {
319
+ return {
320
+ code: 499,
321
+ message: "operation cancelled"
322
+ };
323
+ }
324
+ return {
325
+ code: 404
326
+ };
327
+ }
328
+ async loadFn(loadContent, opts) {
329
+ const key = opts.key;
330
+ if (!key) {
331
+ return {
332
+ code: 404,
333
+ message: "key is required"
334
+ };
335
+ }
336
+ const newModule = {
337
+ key: opts.key,
338
+ status: "loading",
339
+ loading: true,
340
+ loadSuccessClear: opts.loadSuccessClear ?? true
341
+ };
342
+ let errorMessage = "";
343
+ try {
344
+ const isReRun = opts.isReRun ?? false;
345
+ let res;
346
+ if (!isReRun) {
347
+ this.modules.set(key, newModule);
348
+ res = await loadContent();
349
+ } else {
350
+ newModule.controller = new AbortController;
351
+ const signal = newModule.controller.signal;
352
+ this.modules.set(key, newModule);
353
+ const data = await reRunFn({
354
+ timeout: opts.timeout,
355
+ interval: opts.interval,
356
+ checkSuccess: opts.checkSuccess,
357
+ fn: loadContent,
358
+ signal
359
+ });
360
+ newModule.controller = null;
361
+ if (data.code === 499) {
362
+ newModule.status = "cancel";
363
+ return {
364
+ code: 499,
365
+ message: "operation cancelled"
366
+ };
367
+ }
368
+ if (data.code !== 200) {
369
+ throw new Error(data.message);
370
+ }
371
+ res = data.data;
372
+ }
373
+ newModule.modules = res;
374
+ newModule.status = "loaded";
375
+ return {
376
+ code: 200,
377
+ data: res
378
+ };
379
+ } catch (error) {
380
+ errorMessage = error.message;
381
+ newModule.status = "error";
382
+ return {
383
+ code: 500,
384
+ message: error
385
+ };
386
+ } finally {
387
+ newModule.loading = false;
388
+ this.modules.set(opts.key, newModule);
389
+ if (!errorMessage) {
390
+ this.event.emit(opts.key);
391
+ } else {
392
+ this.event.emit(opts.key, errorMessage);
393
+ }
394
+ }
395
+ }
396
+ async load(loadContent, opts) {
397
+ this.loading = true;
398
+ const key = opts.key;
399
+ if (!key) {
400
+ return {
401
+ code: 404,
402
+ message: "key is required"
403
+ };
404
+ }
405
+ if (opts?.force) {
406
+ this.remove(key);
407
+ }
408
+ const has = this.modules.has(key);
409
+ if (has) {
410
+ return await this.hasLoaded(key);
411
+ }
412
+ if (typeof loadContent === "function") {
413
+ return this.loadFn(loadContent, opts);
414
+ }
415
+ console.error("loadContent is not a function and not has loaded");
416
+ }
417
+ remove(key) {
418
+ const has = this.modules.has(key);
419
+ if (has) {
420
+ this.checkRemoveController(key);
421
+ this.modules.delete(key);
422
+ }
423
+ }
424
+ emitLoaded(key) {
425
+ this.checkRemoveController(key);
426
+ this.event.emit(key);
427
+ }
428
+ setModule(key, data, loadData) {
429
+ const newModule = {
430
+ key,
431
+ status: "loaded",
432
+ loading: false,
433
+ modules: data || {},
434
+ ...loadData
435
+ };
436
+ this.modules.set(key, newModule);
437
+ this.emitLoaded(key);
438
+ return newModule;
439
+ }
440
+ cancel(key) {
441
+ this.checkRemoveController(key);
442
+ }
443
+ checkRemoveController(key) {
444
+ const data = this.modules.get(key);
445
+ if (data?.controller) {
446
+ data.controller?.abort?.();
447
+ delete data.controller;
448
+ this.modules.set(key, data);
449
+ }
450
+ }
451
+ }
452
+
453
+ // src/index.ts
454
+ var gt = globalThis || window || self;
455
+ var useEnv = (initEnv, initKey = "config", isOverwrite) => {
456
+ const env = gt[initKey];
457
+ const _env = env || initEnv;
458
+ if (!env) {
459
+ if (_env) {
460
+ gt[initKey] = _env;
461
+ } else {
462
+ gt[initKey] = {};
463
+ }
464
+ }
465
+ return gt[initKey];
466
+ };
467
+ var useEnvKey = (key, init, initKey = "config") => {
468
+ const _env = useEnv({}, initKey);
469
+ if (key && typeof _env[key] !== "undefined") {
470
+ return _env[key];
471
+ }
472
+ if (key && init) {
473
+ if (typeof init !== "function") {
474
+ _env[key] = init;
475
+ }
476
+ if (typeof init === "function") {
477
+ const result = init();
478
+ if (result instanceof Promise) {
479
+ return result.then((res) => {
480
+ _env[key] = res;
481
+ return res;
482
+ });
483
+ }
484
+ _env[key] = result;
485
+ }
486
+ return _env[key];
487
+ }
488
+ if (key) {
489
+ const baseLoad = new BaseLoad;
490
+ const voidFn = async () => {
491
+ return _env[key];
492
+ };
493
+ const checkFn = async () => {
494
+ const loadRes = await baseLoad.load(voidFn, {
495
+ key,
496
+ isReRun: true,
497
+ checkSuccess: () => _env[key],
498
+ timeout: 5 * 60 * 1000,
499
+ interval: 1000
500
+ });
501
+ if (loadRes.code !== 200) {
502
+ console.error("load key error");
503
+ return null;
504
+ }
505
+ return _env[key];
506
+ };
507
+ return checkFn();
508
+ }
509
+ console.error("key is empty ");
510
+ return null;
511
+ };
512
+ var useEnvKeyNew = (key, initKey = "config", opts) => {
513
+ const _env = useEnv({}, initKey);
514
+ if (key) {
515
+ delete _env[key];
516
+ }
517
+ if (opts?.getNew && opts.init) {
518
+ return useEnvKey(key, opts.init, initKey);
519
+ } else if (opts?.getNew) {
520
+ return useEnvKey(key, null, initKey);
521
+ }
522
+ };
523
+ var useContextKey = (key, init, isNew) => {
524
+ if (isNew) {
525
+ return useEnvKeyNew(key, "context", { getNew: true, init });
526
+ }
527
+ return useEnvKey(key, init, "context");
528
+ };
529
+ var use = useContextKey;
530
+ var useConfigKey = (key, init, isNew) => {
531
+ if (isNew) {
532
+ return useEnvKeyNew(key, "config", { getNew: true, init });
533
+ }
534
+ return useEnvKey(key, init, "config");
535
+ };
536
+
537
+ class InitEnv {
538
+ static isInit = false;
539
+ static init(opts) {
540
+ if (InitEnv.isInit) {
541
+ return;
542
+ }
543
+ const { load = true, page = false } = opts || {};
544
+ InitEnv.isInit = true;
545
+ gt.useConfigKey = useConfigKey;
546
+ gt.useContextKey = useContextKey;
547
+ gt.use = use;
548
+ gt.webEnv = { useConfigKey, useContextKey, use };
549
+ load && (gt.Load = BaseLoad);
550
+ }
551
+ }
552
+ InitEnv.init();
553
+
554
+ class Lexer {
555
+ constructor(input) {
556
+ this.pos = 0;
557
+ this.input = input.trim();
558
+ }
559
+ skipWhitespace() {
560
+ while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
561
+ this.pos++;
562
+ }
563
+ }
564
+ readString(quote) {
565
+ this.pos++;
566
+ let result = '';
567
+ while (this.pos < this.input.length && this.input[this.pos] !== quote) {
568
+ result += this.input[this.pos];
569
+ this.pos++;
570
+ }
571
+ this.pos++;
572
+ return result;
573
+ }
574
+ readNumber() {
575
+ let result = '';
576
+ while (this.pos < this.input.length && /[\d.]/.test(this.input[this.pos])) {
577
+ result += this.input[this.pos];
578
+ this.pos++;
579
+ }
580
+ return result;
581
+ }
582
+ readIdentifier() {
583
+ let result = '';
584
+ while (this.pos < this.input.length && /[\w.]/.test(this.input[this.pos])) {
585
+ result += this.input[this.pos];
586
+ this.pos++;
587
+ }
588
+ return result;
589
+ }
590
+ isKeyword(value) {
591
+ const keywords = ['WHERE', 'AND', 'OR', 'ORDER', 'BY', 'ASC', 'DESC', 'LIMIT', 'IN', 'CONTAINS', 'LIKE', 'NOT', 'IS', 'NULL'];
592
+ return keywords.includes(value.toUpperCase());
593
+ }
594
+ nextToken() {
595
+ this.skipWhitespace();
596
+ if (this.pos >= this.input.length) {
597
+ return { type: 'EOF', value: '', pos: this.pos };
598
+ }
599
+ const char = this.input[this.pos];
600
+ if (char === "'" || char === '"') {
601
+ return { type: 'STRING', value: this.readString(char), pos: this.pos };
602
+ }
603
+ if (/\d/.test(char)) {
604
+ return { type: 'NUMBER', value: this.readNumber(), pos: this.pos };
605
+ }
606
+ if (char === ',') {
607
+ this.pos++;
608
+ return { type: 'COMMA', value: ',', pos: this.pos };
609
+ }
610
+ if (char === '[') {
611
+ this.pos++;
612
+ return { type: 'LBRACKET', value: '[', pos: this.pos };
613
+ }
614
+ if (char === ']') {
615
+ this.pos++;
616
+ return { type: 'RBRACKET', value: ']', pos: this.pos };
617
+ }
618
+ if (/[=<>!]/.test(char)) {
619
+ this.pos++;
620
+ if (char === '=' && this.pos < this.input.length && this.input[this.pos] === '=') {
621
+ this.pos++;
622
+ return { type: 'EQ', value: '==', pos: this.pos };
623
+ }
624
+ if (char === '!' && this.pos < this.input.length && this.input[this.pos] === '=') {
625
+ this.pos++;
626
+ return { type: 'NEQ', value: '!=', pos: this.pos };
627
+ }
628
+ if (char === '>') {
629
+ if (this.pos < this.input.length && this.input[this.pos] === '=') {
630
+ this.pos++;
631
+ return { type: 'GTE', value: '>=', pos: this.pos };
632
+ }
633
+ return { type: 'GT', value: '>', pos: this.pos };
634
+ }
635
+ if (char === '<') {
636
+ if (this.pos < this.input.length && this.input[this.pos] === '=') {
637
+ this.pos++;
638
+ return { type: 'LTE', value: '<=', pos: this.pos };
639
+ }
640
+ return { type: 'LT', value: '<', pos: this.pos };
641
+ }
642
+ return { type: 'EQ', value: char, pos: this.pos };
643
+ }
644
+ const identifier = this.readIdentifier();
645
+ const upperIdentifier = identifier.toUpperCase();
646
+ const keywords = {
647
+ 'WHERE': 'WHERE',
648
+ 'AND': 'AND',
649
+ 'OR': 'OR',
650
+ 'ORDER': 'ORDER',
651
+ 'BY': 'BY',
652
+ 'ASC': 'ASC',
653
+ 'DESC': 'DESC',
654
+ 'LIMIT': 'LIMIT',
655
+ 'IN': 'IN',
656
+ 'CONTAINS': 'CONTAINS',
657
+ 'LIKE': 'LIKE',
658
+ 'NOT': 'NOT',
659
+ 'IS': 'IS',
660
+ 'NULL': 'NULL'
661
+ };
662
+ if (keywords[upperIdentifier]) {
663
+ return { type: keywords[upperIdentifier], value: upperIdentifier, pos: this.pos };
664
+ }
665
+ return { type: 'IDENTIFIER', value: identifier, pos: this.pos };
666
+ }
667
+ }
668
+ class Parser {
669
+ constructor(lexer) {
670
+ this.lexer = lexer;
671
+ this.currentToken = this.lexer.nextToken();
672
+ }
673
+ eat(type) {
674
+ if (this.currentToken.type === type) {
675
+ this.currentToken = this.lexer.nextToken();
676
+ }
677
+ else {
678
+ throw new Error(`Expected ${type}, got ${this.currentToken.type}`);
679
+ }
680
+ }
681
+ parse() {
682
+ const statements = [];
683
+ if (this.currentToken.type === 'WHERE') {
684
+ statements.push(this.parseWhere());
685
+ }
686
+ if (this.currentToken.type === 'ORDER') {
687
+ statements.push(this.parseOrder());
688
+ }
689
+ if (this.currentToken.type === 'LIMIT') {
690
+ statements.push(this.parseLimit());
691
+ }
692
+ return statements;
693
+ }
694
+ parseWhere() {
695
+ this.eat('WHERE');
696
+ return { type: 'WhereClause', left: this.parseCondition() };
697
+ }
698
+ parseCondition() {
699
+ let left = this.parseExpression();
700
+ while (this.currentToken.type === 'AND' || this.currentToken.type === 'OR') {
701
+ const op = this.currentToken.type;
702
+ this.eat(op);
703
+ const right = this.parseExpression();
704
+ left = { type: 'LogicalOp', left, right, operator: op };
705
+ }
706
+ return left;
707
+ }
708
+ parseExpression() {
709
+ const field = this.currentToken.value;
710
+ this.eat('IDENTIFIER');
711
+ let operator;
712
+ let value;
713
+ if (this.currentToken.type === 'NOT') {
714
+ this.eat('NOT');
715
+ operator = 'NOT LIKE';
716
+ this.eat('LIKE');
717
+ value = this.parseValue();
718
+ }
719
+ else if (this.currentToken.type === 'LIKE') {
720
+ operator = 'LIKE';
721
+ this.eat('LIKE');
722
+ value = this.parseValue();
723
+ }
724
+ else if (this.currentToken.type === 'IS') {
725
+ this.eat('IS');
726
+ if (this.currentToken.type === 'NOT') {
727
+ operator = 'IS NOT NULL';
728
+ this.eat('NOT');
729
+ this.eat('NULL');
730
+ }
731
+ else {
732
+ operator = 'IS NULL';
733
+ this.eat('NULL');
734
+ }
735
+ value = null;
736
+ }
737
+ else if (this.currentToken.type === 'CONTAINS') {
738
+ operator = 'CONTAINS';
739
+ this.eat('CONTAINS');
740
+ value = this.parseValue();
741
+ }
742
+ else if (this.currentToken.type === 'IN') {
743
+ operator = 'IN';
744
+ this.eat('IN');
745
+ value = this.parseInList();
746
+ }
747
+ else {
748
+ const opType = this.currentToken.type;
749
+ this.eat(opType);
750
+ operator = opType;
751
+ value = this.parseValue();
752
+ }
753
+ return { type: 'Condition', field, operator, value };
754
+ }
755
+ parseInList() {
756
+ this.eat('LBRACKET');
757
+ const values = [];
758
+ while (this.currentToken.type !== 'RBRACKET') {
759
+ const node = this.parseValue();
760
+ values.push(node.value);
761
+ if (this.currentToken.type === 'COMMA') {
762
+ this.eat('COMMA');
763
+ }
764
+ }
765
+ this.eat('RBRACKET');
766
+ return values;
767
+ }
768
+ parseValue() {
769
+ if (this.currentToken.type === 'STRING') {
770
+ const value = this.currentToken.value;
771
+ this.eat('STRING');
772
+ return { type: 'Value', value };
773
+ }
774
+ if (this.currentToken.type === 'NUMBER') {
775
+ const value = parseFloat(this.currentToken.value);
776
+ this.eat('NUMBER');
777
+ return { type: 'Value', value };
778
+ }
779
+ throw new Error(`Expected value, got ${this.currentToken.type}`);
780
+ }
781
+ parseOrder() {
782
+ this.eat('ORDER');
783
+ this.eat('BY');
784
+ const field = this.currentToken.value;
785
+ this.eat('IDENTIFIER');
786
+ let direction = 'ASC';
787
+ if (this.currentToken.type === 'ASC' || this.currentToken.type === 'DESC') {
788
+ direction = this.currentToken.value;
789
+ this.eat(this.currentToken.type);
790
+ }
791
+ return { type: 'OrderClause', field, value: direction };
792
+ }
793
+ parseLimit() {
794
+ this.eat('LIMIT');
795
+ const value = parseFloat(this.currentToken.value);
796
+ this.eat('NUMBER');
797
+ return { type: 'LimitClause', value };
798
+ }
799
+ }
800
+ class Executor {
801
+ getValueByPath(obj, path) {
802
+ return path.split('.').reduce((acc, part) => acc?.[part], obj);
803
+ }
804
+ likeToRegex(pattern) {
805
+ let regex = '';
806
+ for (let i = 0; i < pattern.length; i++) {
807
+ const char = pattern[i];
808
+ if (char === '%') {
809
+ regex += '.*';
810
+ }
811
+ else if (char === '_') {
812
+ regex += '.';
813
+ }
814
+ else if (/[.*+?^${}()|[\]\\]/.test(char)) {
815
+ regex += '\\' + char;
816
+ }
817
+ else {
818
+ regex += char;
819
+ }
820
+ }
821
+ return new RegExp(`^${regex}$`, 'i');
822
+ }
823
+ evaluateCondition(node, item) {
824
+ if (node.type === 'Condition') {
825
+ const fieldValue = this.getValueByPath(item, node.field);
826
+ const { operator, value } = node;
827
+ const actualValue = value?.type === 'Value' ? value.value : value;
828
+ switch (operator) {
829
+ case 'EQ':
830
+ return fieldValue === actualValue;
831
+ case 'NEQ':
832
+ return fieldValue !== actualValue;
833
+ case 'GT':
834
+ return fieldValue > actualValue;
835
+ case 'LT':
836
+ return fieldValue < actualValue;
837
+ case 'GTE':
838
+ return fieldValue >= actualValue;
839
+ case 'LTE':
840
+ return fieldValue <= actualValue;
841
+ case 'IN':
842
+ return Array.isArray(actualValue) && actualValue.includes(fieldValue);
843
+ case 'CONTAINS':
844
+ return Array.isArray(fieldValue) && fieldValue.includes(actualValue);
845
+ case 'LIKE':
846
+ if (typeof fieldValue !== 'string' || typeof actualValue !== 'string') {
847
+ return false;
848
+ }
849
+ return this.likeToRegex(actualValue).test(fieldValue);
850
+ case 'NOT LIKE':
851
+ if (typeof fieldValue !== 'string' || typeof actualValue !== 'string') {
852
+ return true;
853
+ }
854
+ return !this.likeToRegex(actualValue).test(fieldValue);
855
+ case 'IS NULL':
856
+ return fieldValue === null || fieldValue === undefined;
857
+ case 'IS NOT NULL':
858
+ return fieldValue !== null && fieldValue !== undefined;
859
+ default:
860
+ return false;
861
+ }
862
+ }
863
+ if (node.type === 'LogicalOp') {
864
+ const leftResult = this.evaluateCondition(node.left, item);
865
+ const rightResult = this.evaluateCondition(node.right, item);
866
+ return node.operator === 'AND'
867
+ ? leftResult && rightResult
868
+ : leftResult || rightResult;
869
+ }
870
+ return true;
871
+ }
872
+ execute(statements, data) {
873
+ let result = [...data];
874
+ for (const stmt of statements) {
875
+ if (stmt.type === 'WhereClause') {
876
+ result = result.filter(item => this.evaluateCondition(stmt.left, item));
877
+ }
878
+ else if (stmt.type === 'OrderClause') {
879
+ const field = stmt.field;
880
+ const direction = stmt.value;
881
+ result.sort((a, b) => {
882
+ const aVal = this.getValueByPath(a, field);
883
+ const bVal = this.getValueByPath(b, field);
884
+ if (aVal === bVal)
885
+ return 0;
886
+ const cmp = aVal > bVal ? 1 : -1;
887
+ return direction === 'ASC' ? cmp : -cmp;
888
+ });
889
+ }
890
+ else if (stmt.type === 'LimitClause') {
891
+ result = result.slice(0, stmt.value);
892
+ }
893
+ }
894
+ return result;
895
+ }
896
+ }
897
+ function filter(data, query) {
898
+ if (!query || query.trim() === '') {
899
+ return data;
900
+ }
901
+ const lexer = new Lexer(query);
902
+ const parser = new Parser(lexer);
903
+ const ast = parser.parse();
904
+ const executor = new Executor();
905
+ return executor.execute(ast, data);
906
+ }
907
+
908
+ const createRouterAgentPluginFn = (opts) => {
909
+ let router = opts?.router;
910
+ if (!router) {
911
+ const app = useContextKey('app');
912
+ router = app.router;
913
+ }
914
+ if (!router) {
915
+ throw new Error('Router 参数缺失');
916
+ }
917
+ const _routes = filter(router.routes, opts?.query || '');
918
+ const routes = _routes.filter(r => {
919
+ const metadata = r.metadata;
920
+ if (metadata && metadata.tags && metadata.tags.includes('opencode')) {
921
+ return !!metadata.skill;
922
+ }
923
+ return false;
924
+ });
925
+ // opencode run "查看系统信息"
926
+ const AgentPlugin = async ({ project, client, $, directory, worktree }) => {
927
+ return {
928
+ 'tool': {
929
+ ...routes.reduce((acc, route) => {
930
+ const metadata = route.metadata;
931
+ acc[metadata.skill] = {
932
+ name: metadata.title || metadata.skill,
933
+ description: metadata.summary || '',
934
+ args: metadata.args || {},
935
+ async execute(args) {
936
+ const res = await router.run({
937
+ path: route.path,
938
+ key: route.key,
939
+ payload: args
940
+ }, { appId: router.appId });
941
+ if (res.code === 200) {
942
+ if (res.data?.content) {
943
+ return res.data.content;
944
+ }
945
+ if (res.data?.final) {
946
+ return '调用程序成功';
947
+ }
948
+ const str = JSON.stringify(res.data || res, null, 2);
949
+ if (str.length > 10000) {
950
+ return str.slice(0, 10000) + '... (truncated)';
951
+ }
952
+ return str;
953
+ }
954
+ return `Error: ${res?.message || '无法获取结果'}`;
955
+ }
956
+ };
957
+ return acc;
958
+ }, {})
959
+ },
960
+ 'tool.execute.before': async (opts) => {
961
+ // console.log('CnbPlugin: tool.execute.before', opts.tool);
962
+ // delete toolSkills['cnb-login-verify']
963
+ }
964
+ };
965
+ };
966
+ return AgentPlugin;
967
+ };
968
+
969
+ export { createRouterAgentPluginFn };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@kevisual/router",
4
- "version": "0.0.56",
4
+ "version": "0.0.57",
5
5
  "description": "",
6
6
  "type": "module",
7
7
  "main": "./dist/router.js",
@@ -21,11 +21,14 @@
21
21
  "keywords": [],
22
22
  "author": "abearxiong",
23
23
  "license": "MIT",
24
- "packageManager": "pnpm@10.28.0",
24
+ "packageManager": "pnpm@10.28.1",
25
25
  "devDependencies": {
26
- "@kevisual/js-filter": "^0.0.4",
26
+ "@kevisual/context": "^0.0.4",
27
+ "@kevisual/js-filter": "^0.0.5",
27
28
  "@kevisual/local-proxy": "^0.0.8",
28
29
  "@kevisual/query": "^0.0.35",
30
+ "@kevisual/use-config": "^1.0.28",
31
+ "@opencode-ai/plugin": "^1.1.26",
29
32
  "@rollup/plugin-alias": "^6.0.0",
30
33
  "@rollup/plugin-commonjs": "29.0.0",
31
34
  "@rollup/plugin-node-resolve": "^16.0.3",
@@ -35,10 +38,10 @@
35
38
  "@types/send": "^1.2.1",
36
39
  "@types/ws": "^8.18.1",
37
40
  "@types/xml2js": "^0.4.14",
38
- "eventemitter3": "^5.0.1",
41
+ "eventemitter3": "^5.0.4",
39
42
  "nanoid": "^5.1.6",
40
43
  "path-to-regexp": "^8.3.0",
41
- "rollup": "^4.55.1",
44
+ "rollup": "^4.55.2",
42
45
  "rollup-plugin-dts": "^6.3.0",
43
46
  "send": "^1.2.1",
44
47
  "ts-loader": "^9.5.4",
@@ -76,6 +79,7 @@
76
79
  "require": "./dist/router-simple.js",
77
80
  "types": "./dist/router-simple.d.ts"
78
81
  },
82
+ "./opencode": "./dist/opencode.js",
79
83
  "./define": {
80
84
  "import": "./dist/router-define.js",
81
85
  "require": "./dist/router-define.js",
@@ -0,0 +1,72 @@
1
+ import { useContextKey } from '@kevisual/context'
2
+ import { type QueryRouter, type Skill } from './route.ts'
3
+ import { type App } from './app.ts'
4
+ import { type Plugin } from "@opencode-ai/plugin"
5
+
6
+ import { filter } from '@kevisual/js-filter';
7
+
8
+ export const createRouterAgentPluginFn = (opts?: {
9
+ router?: QueryRouter,
10
+ //** 过滤比如,WHERE metadata.tags includes 'opencode' */
11
+ query?: string
12
+ }) => {
13
+ let router = opts?.router
14
+ if (!router) {
15
+ const app = useContextKey<App>('app')
16
+ router = app.router
17
+ }
18
+ if (!router) {
19
+ throw new Error('Router 参数缺失')
20
+ }
21
+ const _routes = filter(router.routes, opts?.query || '')
22
+ const routes = _routes.filter(r => {
23
+ const metadata = r.metadata as Skill
24
+ if (metadata && metadata.tags && metadata.tags.includes('opencode')) {
25
+ return !!metadata.skill
26
+ }
27
+ return false
28
+ })
29
+ // opencode run "查看系统信息"
30
+ const AgentPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
31
+ return {
32
+ 'tool': {
33
+ ...routes.reduce((acc, route) => {
34
+ const metadata = route.metadata as Skill
35
+ acc[metadata.skill!] = {
36
+ name: metadata.title || metadata.skill,
37
+ description: metadata.summary || '',
38
+ args: metadata.args || {},
39
+ async execute(args: Record<string, any>) {
40
+ const res = await router.run({
41
+ path: route.path,
42
+ key: route.key,
43
+ payload: args
44
+ },
45
+ { appId: router.appId! });
46
+ if (res.code === 200) {
47
+ if (res.data?.content) {
48
+ return res.data.content;
49
+ }
50
+ if (res.data?.final) {
51
+ return '调用程序成功';
52
+ }
53
+ const str = JSON.stringify(res.data || res, null, 2);
54
+ if (str.length > 10000) {
55
+ return str.slice(0, 10000) + '... (truncated)';
56
+ }
57
+ return str;
58
+ }
59
+ return `Error: ${res?.message || '无法获取结果'}`;
60
+ }
61
+ }
62
+ return acc;
63
+ }, {} as Record<string, any>)
64
+ },
65
+ 'tool.execute.before': async (opts) => {
66
+ // console.log('CnbPlugin: tool.execute.before', opts.tool);
67
+ // delete toolSkills['cnb-login-verify']
68
+ }
69
+ }
70
+ }
71
+ return AgentPlugin
72
+ }