@kevisual/router 0.0.73 → 0.0.75
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/agent/routes/route-create.ts +37 -3
- package/dist/app.js +72 -9
- package/dist/opencode.d.ts +8 -1
- package/dist/opencode.js +27 -5
- package/dist/router-browser.d.ts +1 -0
- package/dist/router-browser.js +15 -1
- package/dist/router.d.ts +1 -0
- package/dist/router.js +15 -1
- package/package.json +2 -2
- package/src/opencode.ts +28 -6
- package/src/route.ts +17 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { app, createSkill, tool } from '../app.ts';
|
|
2
2
|
import * as docs from '../gen/index.ts'
|
|
3
3
|
import * as pkgs from '../../package.json' assert { type: 'json' };
|
|
4
|
+
import z from 'zod';
|
|
4
5
|
app.route({
|
|
5
6
|
path: 'router-skill',
|
|
6
7
|
key: 'create-route',
|
|
@@ -32,14 +33,47 @@ app.route({
|
|
|
32
33
|
}
|
|
33
34
|
}).addTo(app);
|
|
34
35
|
|
|
35
|
-
//
|
|
36
|
+
// 获取最新router版本号
|
|
36
37
|
app.route({
|
|
37
38
|
path: 'router-skill',
|
|
38
39
|
key: 'version',
|
|
39
|
-
description: '
|
|
40
|
+
description: '获取最新router版本号',
|
|
40
41
|
middleware: ['auth'],
|
|
42
|
+
metadata: {
|
|
43
|
+
tags: ['opencode'],
|
|
44
|
+
...createSkill({
|
|
45
|
+
skill: 'router-skill-version',
|
|
46
|
+
title: '获取最新router版本号',
|
|
47
|
+
summary: '获取最新router版本号',
|
|
48
|
+
args: {}
|
|
49
|
+
})
|
|
50
|
+
},
|
|
41
51
|
}).define(async (ctx) => {
|
|
42
52
|
ctx.body = {
|
|
43
53
|
content: pkgs.version || 'unknown'
|
|
44
54
|
}
|
|
45
|
-
}).addTo(app);
|
|
55
|
+
}).addTo(app);
|
|
56
|
+
|
|
57
|
+
// 执行技能test-route-skill,name为abearxiong
|
|
58
|
+
app.route({
|
|
59
|
+
path: 'route-skill',
|
|
60
|
+
key: 'test',
|
|
61
|
+
description: '测试路由技能',
|
|
62
|
+
middleware: ['auth'],
|
|
63
|
+
metadata: {
|
|
64
|
+
tags: ['opencode'],
|
|
65
|
+
...createSkill({
|
|
66
|
+
skill: 'test-route-skill',
|
|
67
|
+
title: '测试路由技能',
|
|
68
|
+
summary: '测试路由技能是否正常工作',
|
|
69
|
+
args: z.object({
|
|
70
|
+
name: z.string().describe('名字'),
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
},
|
|
74
|
+
}).define(async (ctx) => {
|
|
75
|
+
const name = ctx.query.name || 'unknown';
|
|
76
|
+
ctx.body = {
|
|
77
|
+
content: '测试成功,你好 ' + name
|
|
78
|
+
}
|
|
79
|
+
}).addTo(app)
|
package/dist/app.js
CHANGED
|
@@ -16745,6 +16745,9 @@ function date4(params) {
|
|
|
16745
16745
|
|
|
16746
16746
|
// node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
16747
16747
|
config(en_default());
|
|
16748
|
+
// node_modules/.pnpm/zod@4.3.6/node_modules/zod/index.js
|
|
16749
|
+
var zod_default = exports_external;
|
|
16750
|
+
|
|
16748
16751
|
// node_modules/.pnpm/nanoid@5.1.6/node_modules/nanoid/index.js
|
|
16749
16752
|
import { webcrypto as crypto } from "node:crypto";
|
|
16750
16753
|
var POOL_SIZE_MULTIPLIER = 128;
|
|
@@ -16799,6 +16802,12 @@ var tool = {
|
|
|
16799
16802
|
schema: exports_external
|
|
16800
16803
|
};
|
|
16801
16804
|
var createSkill = (skill) => {
|
|
16805
|
+
if (skill.tags) {
|
|
16806
|
+
const hasOpencode = skill.tags.includes("opencode");
|
|
16807
|
+
if (!hasOpencode) {
|
|
16808
|
+
skill.tags.push("opencode");
|
|
16809
|
+
}
|
|
16810
|
+
}
|
|
16802
16811
|
return {
|
|
16803
16812
|
args: {},
|
|
16804
16813
|
...skill
|
|
@@ -16919,6 +16928,10 @@ var toJSONSchema2 = (route) => {
|
|
|
16919
16928
|
const pickValues = pick(route, pickValue);
|
|
16920
16929
|
if (pickValues?.metadata?.args) {
|
|
16921
16930
|
const args = pickValues.metadata.args;
|
|
16931
|
+
if (args && typeof args === "object" && args.toJSONSchema && typeof args.toJSONSchema === "function") {
|
|
16932
|
+
pickValues.metadata.args = args.toJSONSchema();
|
|
16933
|
+
return pickValues;
|
|
16934
|
+
}
|
|
16922
16935
|
const keys = Object.keys(args);
|
|
16923
16936
|
const newArgs = {};
|
|
16924
16937
|
for (let key of keys) {
|
|
@@ -16937,6 +16950,10 @@ var fromJSONSchema2 = (route) => {
|
|
|
16937
16950
|
const args = route?.metadata?.args;
|
|
16938
16951
|
if (!args)
|
|
16939
16952
|
return route;
|
|
16953
|
+
if (args["$schema"] || args.type === "object" && args.properties && typeof args.properties === "object") {
|
|
16954
|
+
route.metadata.args = exports_external.fromJSONSchema(args);
|
|
16955
|
+
return route;
|
|
16956
|
+
}
|
|
16940
16957
|
const keys = Object.keys(args);
|
|
16941
16958
|
const newArgs = {};
|
|
16942
16959
|
for (let key of keys) {
|
|
@@ -17283,7 +17300,7 @@ class QueryRouter {
|
|
|
17283
17300
|
});
|
|
17284
17301
|
ctx.body = {
|
|
17285
17302
|
list: list.map((item) => {
|
|
17286
|
-
const route = pick(item, ["id", "path", "key", "description", "middleware"]);
|
|
17303
|
+
const route = pick(item, ["id", "path", "key", "description", "middleware", "metadata"]);
|
|
17287
17304
|
return toJSONSchema2(route);
|
|
17288
17305
|
}),
|
|
17289
17306
|
isUser
|
|
@@ -19175,8 +19192,12 @@ var addCallFn = (app2) => {
|
|
|
19175
19192
|
tags: ["opencode"],
|
|
19176
19193
|
...createSkill({
|
|
19177
19194
|
skill: "call-app",
|
|
19178
|
-
title: "调用app
|
|
19179
|
-
summary:
|
|
19195
|
+
title: "调用app应用,非技能模块",
|
|
19196
|
+
summary: `调用router的应用(非技能模块),适用于需要直接调用应用而不是技能的场景
|
|
19197
|
+
条件1: 参数path(string), key(string), payload(object)
|
|
19198
|
+
条件2: 当前的应用调用的模式不是技能
|
|
19199
|
+
|
|
19200
|
+
`,
|
|
19180
19201
|
args: {
|
|
19181
19202
|
path: tool.schema.string().describe("应用路径,例如 cnb"),
|
|
19182
19203
|
key: tool.schema.string().optional().describe("应用key,例如 list-repos"),
|
|
@@ -19196,6 +19217,7 @@ var addCallFn = (app2) => {
|
|
|
19196
19217
|
}).addTo(app2);
|
|
19197
19218
|
};
|
|
19198
19219
|
var createRouterAgentPluginFn = (opts) => {
|
|
19220
|
+
new Promise((resolve) => setTimeout(resolve, 100));
|
|
19199
19221
|
let router = opts?.router;
|
|
19200
19222
|
if (!router) {
|
|
19201
19223
|
const app2 = useContextKey("app");
|
|
@@ -19213,6 +19235,9 @@ var createRouterAgentPluginFn = (opts) => {
|
|
|
19213
19235
|
const _routes = filter(router.routes, opts?.query || "");
|
|
19214
19236
|
const routes = _routes.filter((r) => {
|
|
19215
19237
|
const metadata = r.metadata;
|
|
19238
|
+
if (metadata && metadata.skill && metadata.summary) {
|
|
19239
|
+
return true;
|
|
19240
|
+
}
|
|
19216
19241
|
if (metadata && metadata.tags && metadata.tags.includes("opencode")) {
|
|
19217
19242
|
return !!metadata.skill;
|
|
19218
19243
|
}
|
|
@@ -19226,15 +19251,16 @@ var createRouterAgentPluginFn = (opts) => {
|
|
|
19226
19251
|
tool: {
|
|
19227
19252
|
...routes.reduce((acc, route) => {
|
|
19228
19253
|
const metadata = route.metadata;
|
|
19254
|
+
let args = extractArgs(metadata?.args);
|
|
19229
19255
|
acc[metadata.skill] = {
|
|
19230
19256
|
name: metadata.title || metadata.skill,
|
|
19231
19257
|
description: metadata.summary || "",
|
|
19232
|
-
args
|
|
19233
|
-
async execute(
|
|
19258
|
+
args,
|
|
19259
|
+
async execute(args2) {
|
|
19234
19260
|
const res = await router.run({
|
|
19235
19261
|
path: route.path,
|
|
19236
19262
|
key: route.key,
|
|
19237
|
-
payload:
|
|
19263
|
+
payload: args2
|
|
19238
19264
|
}, { appId: router.appId });
|
|
19239
19265
|
if (res.code === 200) {
|
|
19240
19266
|
if (res.data?.content) {
|
|
@@ -19261,6 +19287,12 @@ var createRouterAgentPluginFn = (opts) => {
|
|
|
19261
19287
|
};
|
|
19262
19288
|
return AgentPlugin;
|
|
19263
19289
|
};
|
|
19290
|
+
var extractArgs = (args) => {
|
|
19291
|
+
if (args && typeof args === "object" && typeof args.shape === "object") {
|
|
19292
|
+
return args.shape;
|
|
19293
|
+
}
|
|
19294
|
+
return args || {};
|
|
19295
|
+
};
|
|
19264
19296
|
|
|
19265
19297
|
// agent/gen/index.ts
|
|
19266
19298
|
var readme = `# router
|
|
@@ -19453,7 +19485,7 @@ app
|
|
|
19453
19485
|
10. **中间件找不到会返回 404**,错误信息中会包含找不到的中间件列表。
|
|
19454
19486
|
`;
|
|
19455
19487
|
// package.json
|
|
19456
|
-
var version2 = "0.0.
|
|
19488
|
+
var version2 = "0.0.75";
|
|
19457
19489
|
|
|
19458
19490
|
// agent/routes/route-create.ts
|
|
19459
19491
|
app.route({
|
|
@@ -19493,13 +19525,44 @@ app.route({
|
|
|
19493
19525
|
app.route({
|
|
19494
19526
|
path: "router-skill",
|
|
19495
19527
|
key: "version",
|
|
19496
|
-
description: "
|
|
19497
|
-
middleware: ["auth"]
|
|
19528
|
+
description: "获取最新router版本号",
|
|
19529
|
+
middleware: ["auth"],
|
|
19530
|
+
metadata: {
|
|
19531
|
+
tags: ["opencode"],
|
|
19532
|
+
...createSkill({
|
|
19533
|
+
skill: "router-skill-version",
|
|
19534
|
+
title: "获取最新router版本号",
|
|
19535
|
+
summary: "获取最新router版本号",
|
|
19536
|
+
args: {}
|
|
19537
|
+
})
|
|
19538
|
+
}
|
|
19498
19539
|
}).define(async (ctx) => {
|
|
19499
19540
|
ctx.body = {
|
|
19500
19541
|
content: version2 || "unknown"
|
|
19501
19542
|
};
|
|
19502
19543
|
}).addTo(app);
|
|
19544
|
+
app.route({
|
|
19545
|
+
path: "route-skill",
|
|
19546
|
+
key: "test",
|
|
19547
|
+
description: "测试路由技能",
|
|
19548
|
+
middleware: ["auth"],
|
|
19549
|
+
metadata: {
|
|
19550
|
+
tags: ["opencode"],
|
|
19551
|
+
...createSkill({
|
|
19552
|
+
skill: "test-route-skill",
|
|
19553
|
+
title: "测试路由技能",
|
|
19554
|
+
summary: "测试路由技能是否正常工作",
|
|
19555
|
+
args: zod_default.object({
|
|
19556
|
+
name: zod_default.string().describe("名字")
|
|
19557
|
+
})
|
|
19558
|
+
})
|
|
19559
|
+
}
|
|
19560
|
+
}).define(async (ctx) => {
|
|
19561
|
+
const name = ctx.query.name || "unknown";
|
|
19562
|
+
ctx.body = {
|
|
19563
|
+
content: "测试成功,你好 " + name
|
|
19564
|
+
};
|
|
19565
|
+
}).addTo(app);
|
|
19503
19566
|
|
|
19504
19567
|
// agent/routes/index.ts
|
|
19505
19568
|
if (!app.hasRoute("auth", "")) {
|
package/dist/opencode.d.ts
CHANGED
|
@@ -719,5 +719,12 @@ declare const createRouterAgentPluginFn: (opts?: {
|
|
|
719
719
|
hooks?: (plugin: PluginInput) => Promise<Hooks>;
|
|
720
720
|
}) => Plugin;
|
|
721
721
|
declare const usePluginInput: () => PluginInput;
|
|
722
|
+
/**
|
|
723
|
+
* 如果args是z.object类型,拆分第一个Object的属性,比如z.object({ name: z.string(), age: z.number() }),拆分成{name: z.string(), age: z.number()}
|
|
724
|
+
* 如果args是普通对象,直接返回
|
|
725
|
+
* @param args
|
|
726
|
+
* @returns
|
|
727
|
+
*/
|
|
728
|
+
declare const extractArgs: (args: any) => any;
|
|
722
729
|
|
|
723
|
-
export { addCallFn, createRouterAgentPluginFn, usePluginInput };
|
|
730
|
+
export { addCallFn, createRouterAgentPluginFn, extractArgs, usePluginInput };
|
package/dist/opencode.js
CHANGED
|
@@ -14339,6 +14339,12 @@ var tool = {
|
|
|
14339
14339
|
schema: exports_external
|
|
14340
14340
|
};
|
|
14341
14341
|
var createSkill = (skill) => {
|
|
14342
|
+
if (skill.tags) {
|
|
14343
|
+
const hasOpencode = skill.tags.includes("opencode");
|
|
14344
|
+
if (!hasOpencode) {
|
|
14345
|
+
skill.tags.push("opencode");
|
|
14346
|
+
}
|
|
14347
|
+
}
|
|
14342
14348
|
return {
|
|
14343
14349
|
args: {},
|
|
14344
14350
|
...skill
|
|
@@ -14699,8 +14705,12 @@ var addCallFn = (app) => {
|
|
|
14699
14705
|
tags: ["opencode"],
|
|
14700
14706
|
...createSkill({
|
|
14701
14707
|
skill: "call-app",
|
|
14702
|
-
title: "调用app
|
|
14703
|
-
summary:
|
|
14708
|
+
title: "调用app应用,非技能模块",
|
|
14709
|
+
summary: `调用router的应用(非技能模块),适用于需要直接调用应用而不是技能的场景
|
|
14710
|
+
条件1: 参数path(string), key(string), payload(object)
|
|
14711
|
+
条件2: 当前的应用调用的模式不是技能
|
|
14712
|
+
|
|
14713
|
+
`,
|
|
14704
14714
|
args: {
|
|
14705
14715
|
path: tool.schema.string().describe("应用路径,例如 cnb"),
|
|
14706
14716
|
key: tool.schema.string().optional().describe("应用key,例如 list-repos"),
|
|
@@ -14720,6 +14730,7 @@ var addCallFn = (app) => {
|
|
|
14720
14730
|
}).addTo(app);
|
|
14721
14731
|
};
|
|
14722
14732
|
var createRouterAgentPluginFn = (opts) => {
|
|
14733
|
+
new Promise((resolve) => setTimeout(resolve, 100));
|
|
14723
14734
|
let router = opts?.router;
|
|
14724
14735
|
if (!router) {
|
|
14725
14736
|
const app = useContextKey("app");
|
|
@@ -14737,6 +14748,9 @@ var createRouterAgentPluginFn = (opts) => {
|
|
|
14737
14748
|
const _routes = filter(router.routes, opts?.query || "");
|
|
14738
14749
|
const routes = _routes.filter((r) => {
|
|
14739
14750
|
const metadata = r.metadata;
|
|
14751
|
+
if (metadata && metadata.skill && metadata.summary) {
|
|
14752
|
+
return true;
|
|
14753
|
+
}
|
|
14740
14754
|
if (metadata && metadata.tags && metadata.tags.includes("opencode")) {
|
|
14741
14755
|
return !!metadata.skill;
|
|
14742
14756
|
}
|
|
@@ -14750,15 +14764,16 @@ var createRouterAgentPluginFn = (opts) => {
|
|
|
14750
14764
|
tool: {
|
|
14751
14765
|
...routes.reduce((acc, route) => {
|
|
14752
14766
|
const metadata = route.metadata;
|
|
14767
|
+
let args = extractArgs(metadata?.args);
|
|
14753
14768
|
acc[metadata.skill] = {
|
|
14754
14769
|
name: metadata.title || metadata.skill,
|
|
14755
14770
|
description: metadata.summary || "",
|
|
14756
|
-
args
|
|
14757
|
-
async execute(
|
|
14771
|
+
args,
|
|
14772
|
+
async execute(args2) {
|
|
14758
14773
|
const res = await router.run({
|
|
14759
14774
|
path: route.path,
|
|
14760
14775
|
key: route.key,
|
|
14761
|
-
payload:
|
|
14776
|
+
payload: args2
|
|
14762
14777
|
}, { appId: router.appId });
|
|
14763
14778
|
if (res.code === 200) {
|
|
14764
14779
|
if (res.data?.content) {
|
|
@@ -14788,8 +14803,15 @@ var createRouterAgentPluginFn = (opts) => {
|
|
|
14788
14803
|
var usePluginInput = () => {
|
|
14789
14804
|
return useContextKey("plugin-input");
|
|
14790
14805
|
};
|
|
14806
|
+
var extractArgs = (args) => {
|
|
14807
|
+
if (args && typeof args === "object" && typeof args.shape === "object") {
|
|
14808
|
+
return args.shape;
|
|
14809
|
+
}
|
|
14810
|
+
return args || {};
|
|
14811
|
+
};
|
|
14791
14812
|
export {
|
|
14792
14813
|
usePluginInput,
|
|
14814
|
+
extractArgs,
|
|
14793
14815
|
createRouterAgentPluginFn,
|
|
14794
14816
|
addCallFn
|
|
14795
14817
|
};
|
package/dist/router-browser.d.ts
CHANGED
package/dist/router-browser.js
CHANGED
|
@@ -13966,6 +13966,12 @@ var tool = {
|
|
|
13966
13966
|
schema: exports_external
|
|
13967
13967
|
};
|
|
13968
13968
|
var createSkill = (skill) => {
|
|
13969
|
+
if (skill.tags) {
|
|
13970
|
+
const hasOpencode = skill.tags.includes("opencode");
|
|
13971
|
+
if (!hasOpencode) {
|
|
13972
|
+
skill.tags.push("opencode");
|
|
13973
|
+
}
|
|
13974
|
+
}
|
|
13969
13975
|
return {
|
|
13970
13976
|
args: {},
|
|
13971
13977
|
...skill
|
|
@@ -14086,6 +14092,10 @@ var toJSONSchema2 = (route) => {
|
|
|
14086
14092
|
const pickValues = pick(route, pickValue);
|
|
14087
14093
|
if (pickValues?.metadata?.args) {
|
|
14088
14094
|
const args = pickValues.metadata.args;
|
|
14095
|
+
if (args && typeof args === "object" && args.toJSONSchema && typeof args.toJSONSchema === "function") {
|
|
14096
|
+
pickValues.metadata.args = args.toJSONSchema();
|
|
14097
|
+
return pickValues;
|
|
14098
|
+
}
|
|
14089
14099
|
const keys = Object.keys(args);
|
|
14090
14100
|
const newArgs = {};
|
|
14091
14101
|
for (let key of keys) {
|
|
@@ -14104,6 +14114,10 @@ var fromJSONSchema2 = (route) => {
|
|
|
14104
14114
|
const args = route?.metadata?.args;
|
|
14105
14115
|
if (!args)
|
|
14106
14116
|
return route;
|
|
14117
|
+
if (args["$schema"] || args.type === "object" && args.properties && typeof args.properties === "object") {
|
|
14118
|
+
route.metadata.args = exports_external.fromJSONSchema(args);
|
|
14119
|
+
return route;
|
|
14120
|
+
}
|
|
14107
14121
|
const keys = Object.keys(args);
|
|
14108
14122
|
const newArgs = {};
|
|
14109
14123
|
for (let key of keys) {
|
|
@@ -14450,7 +14464,7 @@ class QueryRouter {
|
|
|
14450
14464
|
});
|
|
14451
14465
|
ctx.body = {
|
|
14452
14466
|
list: list.map((item) => {
|
|
14453
|
-
const route = pick(item, ["id", "path", "key", "description", "middleware"]);
|
|
14467
|
+
const route = pick(item, ["id", "path", "key", "description", "middleware", "metadata"]);
|
|
14454
14468
|
return toJSONSchema2(route);
|
|
14455
14469
|
}),
|
|
14456
14470
|
isUser
|
package/dist/router.d.ts
CHANGED
package/dist/router.js
CHANGED
|
@@ -16799,6 +16799,12 @@ var tool = {
|
|
|
16799
16799
|
schema: exports_external
|
|
16800
16800
|
};
|
|
16801
16801
|
var createSkill = (skill) => {
|
|
16802
|
+
if (skill.tags) {
|
|
16803
|
+
const hasOpencode = skill.tags.includes("opencode");
|
|
16804
|
+
if (!hasOpencode) {
|
|
16805
|
+
skill.tags.push("opencode");
|
|
16806
|
+
}
|
|
16807
|
+
}
|
|
16802
16808
|
return {
|
|
16803
16809
|
args: {},
|
|
16804
16810
|
...skill
|
|
@@ -16919,6 +16925,10 @@ var toJSONSchema2 = (route) => {
|
|
|
16919
16925
|
const pickValues = pick(route, pickValue);
|
|
16920
16926
|
if (pickValues?.metadata?.args) {
|
|
16921
16927
|
const args = pickValues.metadata.args;
|
|
16928
|
+
if (args && typeof args === "object" && args.toJSONSchema && typeof args.toJSONSchema === "function") {
|
|
16929
|
+
pickValues.metadata.args = args.toJSONSchema();
|
|
16930
|
+
return pickValues;
|
|
16931
|
+
}
|
|
16922
16932
|
const keys = Object.keys(args);
|
|
16923
16933
|
const newArgs = {};
|
|
16924
16934
|
for (let key of keys) {
|
|
@@ -16937,6 +16947,10 @@ var fromJSONSchema2 = (route) => {
|
|
|
16937
16947
|
const args = route?.metadata?.args;
|
|
16938
16948
|
if (!args)
|
|
16939
16949
|
return route;
|
|
16950
|
+
if (args["$schema"] || args.type === "object" && args.properties && typeof args.properties === "object") {
|
|
16951
|
+
route.metadata.args = exports_external.fromJSONSchema(args);
|
|
16952
|
+
return route;
|
|
16953
|
+
}
|
|
16940
16954
|
const keys = Object.keys(args);
|
|
16941
16955
|
const newArgs = {};
|
|
16942
16956
|
for (let key of keys) {
|
|
@@ -17283,7 +17297,7 @@ class QueryRouter {
|
|
|
17283
17297
|
});
|
|
17284
17298
|
ctx.body = {
|
|
17285
17299
|
list: list.map((item) => {
|
|
17286
|
-
const route = pick(item, ["id", "path", "key", "description", "middleware"]);
|
|
17300
|
+
const route = pick(item, ["id", "path", "key", "description", "middleware", "metadata"]);
|
|
17287
17301
|
return toJSONSchema2(route);
|
|
17288
17302
|
}),
|
|
17289
17303
|
isUser
|
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.
|
|
4
|
+
"version": "0.0.75",
|
|
5
5
|
"description": "",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/router.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@kevisual/dts": "^0.0.4",
|
|
29
29
|
"@kevisual/js-filter": "^0.0.5",
|
|
30
30
|
"@kevisual/local-proxy": "^0.0.8",
|
|
31
|
-
"@kevisual/query": "^0.0.
|
|
31
|
+
"@kevisual/query": "^0.0.46",
|
|
32
32
|
"@kevisual/use-config": "^1.0.30",
|
|
33
33
|
"@opencode-ai/plugin": "^1.2.6",
|
|
34
34
|
"@types/bun": "^1.3.9",
|
package/src/opencode.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useContextKey } from '@kevisual/context'
|
|
2
|
-
import { createSkill, type QueryRouterServer, tool, type
|
|
2
|
+
import { createSkill, type QueryRouterServer, tool, type Skill } from './route.ts'
|
|
3
3
|
import { type App } from './app.ts'
|
|
4
4
|
import { PluginInput, type Plugin, Hooks } from "@opencode-ai/plugin"
|
|
5
5
|
|
|
@@ -15,8 +15,12 @@ export const addCallFn = (app: App) => {
|
|
|
15
15
|
tags: ['opencode'],
|
|
16
16
|
...createSkill({
|
|
17
17
|
skill: 'call-app',
|
|
18
|
-
title: '调用app
|
|
19
|
-
summary:
|
|
18
|
+
title: '调用app应用,非技能模块',
|
|
19
|
+
summary: `调用router的应用(非技能模块),适用于需要直接调用应用而不是技能的场景
|
|
20
|
+
条件1: 参数path(string), key(string), payload(object)
|
|
21
|
+
条件2: 当前的应用调用的模式不是技能
|
|
22
|
+
|
|
23
|
+
`,
|
|
20
24
|
args: {
|
|
21
25
|
path: tool.schema.string().describe('应用路径,例如 cnb'),
|
|
22
26
|
key: tool.schema.string().optional().describe('应用key,例如 list-repos'),
|
|
@@ -42,7 +46,9 @@ export const createRouterAgentPluginFn = (opts?: {
|
|
|
42
46
|
query?: string,
|
|
43
47
|
hooks?: (plugin: PluginInput) => Promise<Hooks>
|
|
44
48
|
}) => {
|
|
49
|
+
new Promise(resolve => setTimeout(resolve, 100)) // 等待路由加载
|
|
45
50
|
let router = opts?.router
|
|
51
|
+
|
|
46
52
|
if (!router) {
|
|
47
53
|
const app = useContextKey<App>('app')
|
|
48
54
|
router = app
|
|
@@ -59,13 +65,15 @@ export const createRouterAgentPluginFn = (opts?: {
|
|
|
59
65
|
const _routes = filter(router.routes, opts?.query || '')
|
|
60
66
|
const routes = _routes.filter(r => {
|
|
61
67
|
const metadata = r.metadata as Skill
|
|
68
|
+
if (metadata && metadata.skill && metadata.summary) {
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
62
71
|
if (metadata && metadata.tags && metadata.tags.includes('opencode')) {
|
|
63
72
|
return !!metadata.skill
|
|
64
73
|
}
|
|
65
74
|
return false
|
|
66
75
|
});
|
|
67
|
-
|
|
68
|
-
// opencode run "查看系统信息"
|
|
76
|
+
// opencode run "使用技能查看系统信息"
|
|
69
77
|
const AgentPlugin: Plugin = async (pluginInput) => {
|
|
70
78
|
useContextKey<PluginInput>('plugin-input', () => pluginInput, true)
|
|
71
79
|
const hooks = opts?.hooks ? await opts.hooks(pluginInput) : {}
|
|
@@ -74,10 +82,11 @@ export const createRouterAgentPluginFn = (opts?: {
|
|
|
74
82
|
'tool': {
|
|
75
83
|
...routes.reduce((acc, route) => {
|
|
76
84
|
const metadata = route.metadata as Skill
|
|
85
|
+
let args = extractArgs(metadata?.args)
|
|
77
86
|
acc[metadata.skill!] = {
|
|
78
87
|
name: metadata.title || metadata.skill,
|
|
79
88
|
description: metadata.summary || '',
|
|
80
|
-
args:
|
|
89
|
+
args: args,
|
|
81
90
|
async execute(args: Record<string, any>) {
|
|
82
91
|
const res = await router.run({
|
|
83
92
|
path: route.path,
|
|
@@ -117,4 +126,17 @@ export const createRouterAgentPluginFn = (opts?: {
|
|
|
117
126
|
|
|
118
127
|
export const usePluginInput = (): PluginInput => {
|
|
119
128
|
return useContextKey<PluginInput>('plugin-input')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 如果args是z.object类型,拆分第一个Object的属性,比如z.object({ name: z.string(), age: z.number() }),拆分成{name: z.string(), age: z.number()}
|
|
133
|
+
* 如果args是普通对象,直接返回
|
|
134
|
+
* @param args
|
|
135
|
+
* @returns
|
|
136
|
+
*/
|
|
137
|
+
export const extractArgs = (args: any) => {
|
|
138
|
+
if (args && typeof args === 'object' && typeof args.shape === 'object') {
|
|
139
|
+
return args.shape
|
|
140
|
+
}
|
|
141
|
+
return args || {}
|
|
120
142
|
}
|
package/src/route.ts
CHANGED
|
@@ -97,6 +97,7 @@ export type Skill<T = SimpleObject> = {
|
|
|
97
97
|
skill: string;
|
|
98
98
|
title: string;
|
|
99
99
|
summary?: string;
|
|
100
|
+
tags?: string[];
|
|
100
101
|
args?: {
|
|
101
102
|
[key: string]: any
|
|
102
103
|
};
|
|
@@ -106,6 +107,12 @@ export const tool = {
|
|
|
106
107
|
}
|
|
107
108
|
/** */
|
|
108
109
|
export const createSkill = <T = SimpleObject>(skill: Skill<T>): Skill<T> => {
|
|
110
|
+
if (skill.tags) {
|
|
111
|
+
const hasOpencode = skill.tags.includes('opencode');
|
|
112
|
+
if (!hasOpencode) {
|
|
113
|
+
skill.tags.push('opencode');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
109
116
|
return {
|
|
110
117
|
args: {},
|
|
111
118
|
...skill
|
|
@@ -247,6 +254,10 @@ export const toJSONSchema = (route: RouteInfo) => {
|
|
|
247
254
|
const pickValues = pick(route, pickValue as any);
|
|
248
255
|
if (pickValues?.metadata?.args) {
|
|
249
256
|
const args = pickValues.metadata.args;
|
|
257
|
+
if (args && typeof args === 'object' && args.toJSONSchema && typeof args.toJSONSchema === 'function') {
|
|
258
|
+
pickValues.metadata.args = args.toJSONSchema();
|
|
259
|
+
return pickValues;
|
|
260
|
+
}
|
|
250
261
|
const keys = Object.keys(args);
|
|
251
262
|
const newArgs: { [key: string]: any } = {};
|
|
252
263
|
for (let key of keys) {
|
|
@@ -265,6 +276,11 @@ export const toJSONSchema = (route: RouteInfo) => {
|
|
|
265
276
|
export const fromJSONSchema = (route: RouteInfo): RouteInfo => {
|
|
266
277
|
const args = route?.metadata?.args;
|
|
267
278
|
if (!args) return route;
|
|
279
|
+
if (args["$schema"] || (args.type === 'object' && args.properties && typeof args.properties === 'object')) {
|
|
280
|
+
// 可能是整个schema
|
|
281
|
+
route.metadata.args = z.fromJSONSchema(args);
|
|
282
|
+
return route;
|
|
283
|
+
}
|
|
268
284
|
const keys = Object.keys(args);
|
|
269
285
|
const newArgs: { [key: string]: any } = {};
|
|
270
286
|
for (let key of keys) {
|
|
@@ -680,7 +696,7 @@ export class QueryRouter {
|
|
|
680
696
|
});
|
|
681
697
|
ctx.body = {
|
|
682
698
|
list: list.map((item) => {
|
|
683
|
-
const route = pick(item, ['id', 'path', 'key', 'description', 'middleware'] as const);
|
|
699
|
+
const route = pick(item, ['id', 'path', 'key', 'description', 'middleware', 'metadata'] as const);
|
|
684
700
|
return toJSONSchema(route);
|
|
685
701
|
}),
|
|
686
702
|
isUser
|