@ty_krystal/sei-ai 0.1.5 → 0.1.6

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.
Files changed (53) hide show
  1. package/README.md +701 -175
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +5 -0
  6. package/dist/commands/api-docs.d.ts +20 -0
  7. package/dist/commands/api-docs.js +4 -5
  8. package/dist/commands/call.d.ts +27 -0
  9. package/dist/commands/call.js +12 -13
  10. package/dist/commands/dict/add-category.d.ts +31 -0
  11. package/dist/commands/dict/add-category.js +9 -10
  12. package/dist/commands/dict/add-item.d.ts +32 -0
  13. package/dist/commands/dict/add-item.js +11 -12
  14. package/dist/commands/dict/list.d.ts +22 -0
  15. package/dist/commands/dict/list.js +8 -9
  16. package/dist/commands/init/base-data.d.ts +26 -0
  17. package/dist/commands/init/base-data.js +14 -15
  18. package/dist/commands/query-sql.d.ts +24 -0
  19. package/dist/commands/query-sql.js +6 -8
  20. package/dist/commands/query.d.ts +21 -0
  21. package/dist/commands/query.js +6 -7
  22. package/dist/commands/relogin.d.ts +17 -0
  23. package/dist/commands/relogin.js +2 -3
  24. package/dist/commands/save.d.ts +21 -0
  25. package/dist/commands/save.js +6 -7
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +1 -2
  28. package/oclif.manifest.json +1302 -0
  29. package/package.json +63 -54
  30. package/config.example.json +0 -33
  31. package/dist/README.md +0 -239
  32. package/dist/cli-actions.js +0 -157
  33. package/dist/cli-helpers.js +0 -246
  34. package/dist/command-base/context.js +0 -10
  35. package/dist/command-base/output.js +0 -6
  36. package/dist/command-base/payload.js +0 -33
  37. package/dist/command-base/sei-command.js +0 -88
  38. package/dist/commands/stdio.js +0 -16
  39. package/dist/commands/streamable-http.js +0 -29
  40. package/dist/config.example.json +0 -33
  41. package/dist/config.js +0 -82
  42. package/dist/constants.js +0 -48
  43. package/dist/env.js +0 -33
  44. package/dist/errors.js +0 -55
  45. package/dist/logger.js +0 -71
  46. package/dist/openapi.js +0 -261
  47. package/dist/permissions.js +0 -209
  48. package/dist/schema.js +0 -112
  49. package/dist/sei-client.js +0 -535
  50. package/dist/server.js +0 -237
  51. package/dist/tools.js +0 -175
  52. package/dist/types.js +0 -1
  53. package/dist/utils.js +0 -53
package/package.json CHANGED
@@ -1,69 +1,78 @@
1
1
  {
2
2
  "name": "@ty_krystal/sei-ai",
3
- "version": "0.1.5",
4
- "description": "SEI MCP server and developer CLI for query, save, query-sql, and api-docs workflows",
5
- "keywords": [
6
- "sei",
7
- "mcp",
8
- "cli",
9
- "openapi"
10
- ],
11
- "homepage": "https://github.com/tanyu/sei-ai",
12
- "bugs": {
13
- "url": "https://github.com/tanyu/sei-ai/issues"
3
+ "description": "SEI CLI generated with oclif",
4
+ "version": "0.1.6",
5
+ "author": "tanyu",
6
+ "bin": {
7
+ "sei-ai": "./bin/run.js"
14
8
  },
15
- "repository": {
16
- "type": "git",
17
- "url": "git+https://github.com/tanyu/sei-ai.git"
9
+ "bugs": "https://github.com/tanyu/sei-ai/issues",
10
+ "dependencies": {
11
+ "@oclif/core": "^4",
12
+ "@oclif/plugin-help": "^6",
13
+ "@oclif/plugin-plugins": "^5",
14
+ "@ty_krystal/sei-ai-core": "workspace:*"
15
+ },
16
+ "devDependencies": {
17
+ "@eslint/compat": "^1",
18
+ "@oclif/prettier-config": "^0.2.1",
19
+ "@oclif/test": "^4",
20
+ "@types/chai": "^4",
21
+ "@types/mocha": "^10",
22
+ "@types/node": "^18",
23
+ "chai": "^4",
24
+ "eslint": "^9",
25
+ "eslint-config-oclif": "^6",
26
+ "eslint-config-prettier": "^10",
27
+ "mocha": "^11",
28
+ "oclif": "^4",
29
+ "shx": "^0.3.4",
30
+ "ts-node": "^10",
31
+ "typescript": "^5"
32
+ },
33
+ "engines": {
34
+ "node": ">=18.0.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
18
38
  },
19
- "license": "MIT",
20
- "type": "module",
21
39
  "files": [
22
- "dist",
23
- "README.md",
24
- "config.example.json"
40
+ "./bin",
41
+ "./dist",
42
+ "./oclif.manifest.json"
25
43
  ],
26
- "bin": {
27
- "sei-ai": "dist/index.js"
28
- },
44
+ "homepage": "https://github.com/tanyu/sei-ai",
45
+ "keywords": ["oclif", "sei", "cli"],
46
+ "license": "MIT",
47
+ "main": "dist/index.js",
48
+ "type": "module",
29
49
  "oclif": {
30
50
  "bin": "sei-ai",
31
51
  "dirname": "sei-ai",
32
- "commands": {
33
- "strategy": "pattern",
34
- "target": "./dist/commands",
35
- "globPatterns": [
36
- "**/*.js"
37
- ]
38
- }
52
+ "commands": "./dist/commands",
53
+ "plugins": [
54
+ "@oclif/plugin-help",
55
+ "@oclif/plugin-plugins"
56
+ ],
57
+ "topicSeparator": ":"
39
58
  },
40
- "main": "./dist/index.js",
41
- "exports": {
42
- ".": {
43
- "default": "./dist/index.js"
44
- }
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "git+https://github.com/tanyu/sei-ai.git"
45
62
  },
46
63
  "scripts": {
47
- "dev": "tsx src/index.ts",
48
- "start": "node dist/index.js",
49
- "start:stdio": "node dist/index.js stdio --config ./config.example.json",
50
- "start:http": "node dist/index.js streamable-http --host 0.0.0.0 --port 3000 --path /mcp --config ./config.example.json",
51
- "build": "tsc -p tsconfig.build.json && node ./scripts/copy-build-assets.mjs",
52
- "test": "node --import tsx --test test/**/*.test.ts",
53
- "check": "pnpm run typecheck && pnpm run test",
64
+ "build": "pnpm --filter @ty_krystal/sei-ai-core build && shx rm -rf dist && tsc -b",
65
+ "dev": "node ./bin/dev.js",
66
+ "lint": "eslint",
67
+ "postpack": "shx rm -f oclif.manifest.json",
68
+ "posttest": "pnpm run lint",
69
+ "prepack": "oclif manifest && oclif readme",
70
+ "prepublishOnly": "pnpm run build",
71
+ "publish:npm": "npm publish --access public",
72
+ "start": "node ./bin/run.js",
54
73
  "typecheck": "tsc -p tsconfig.json --noEmit",
55
- "prepublishOnly": "pnpm run build"
56
- },
57
- "engines": {
58
- "node": "^22.18.0 || ^24.0.0"
74
+ "test": "pnpm --filter @ty_krystal/sei-ai-core typecheck && mocha --forbid-only \"test/**/*.test.ts\"",
75
+ "version": "oclif readme && git add README.md"
59
76
  },
60
- "publishConfig": {
61
- "access": "public"
62
- },
63
- "dependencies": {
64
- "@modelcontextprotocol/sdk": "^1.29.0",
65
- "@oclif/core": "^4.11.10",
66
- "tsx": "^4.22.4",
67
- "zod": "^3.25.76"
68
- }
77
+ "types": "dist/index.d.ts"
69
78
  }
@@ -1,33 +0,0 @@
1
- {
2
- "baseUrl": "http://127.0.0.1:8081",
3
- "auth": {
4
- "mode": "ai-login",
5
- "aiKeyEnv": "SEI_AI_LOGIN_KEY",
6
- "roleEnv": "SEI_AI_LOGIN_ROLE"
7
- },
8
- "tools": {
9
- "query": true,
10
- "save": true,
11
- "schema": true,
12
- "querySql": false
13
- },
14
- "targets": {
15
- "modules": {
16
- "demo_order": {
17
- "mainTable": "demo_order",
18
- "query": {
19
- "fields": ["_ID", "ORDER_NO", "STATUS", "AMOUNT"],
20
- "defaultFields": ["_ID", "ORDER_NO", "STATUS"],
21
- "allowAllFields": false
22
- },
23
- "save": {
24
- "actions": ["add", "edit"],
25
- "fields": ["STATUS", "MEMO"]
26
- }
27
- }
28
- },
29
- "tables": {},
30
- "sources": {},
31
- "views": {}
32
- }
33
- }
package/dist/README.md DELETED
@@ -1,239 +0,0 @@
1
- # sei-ai
2
-
3
- 独立的 SEI MCP 服务,使用 `@modelcontextprotocol/sdk` 运行。
4
- 支持 `stdio`、`streamable-http` 和开发者 CLI 启动,默认只开放受控的低代码查询、保存和字段元数据工具,`querySql` 默认关闭。
5
- CLI 已迁移到 `oclif`,命令面改为顶层子命令。
6
-
7
- ## 安装
8
-
9
- ```bash
10
- npm install -g sei-ai
11
- ```
12
-
13
- ## 命令
14
-
15
- ```bash
16
- pnpm -F sei-ai-mcp dev -- --config ./apps/sei-ai-mcp/config.example.json
17
- pnpm -F sei-ai-mcp run start:stdio
18
- pnpm -F sei-ai-mcp run start:http
19
- pnpm -F sei-ai-mcp run build
20
- pnpm -F sei-ai-mcp run test
21
- ```
22
-
23
- 打包后 CLI:
24
-
25
- ```bash
26
- sei-ai help
27
- sei-ai stdio --config ./config.example.json
28
- sei-ai streamable-http --host 0.0.0.0 --port 3000 --path /mcp --config ./config.example.json
29
- sei-ai api-docs --format markdown
30
- sei-ai relogin
31
- sei-ai query-sql "SELECT _UID,_NAME FROM _SYS_USER"
32
- sei-ai call POST /api/sei/data/removeAllCache --json '{}'
33
- sei-ai dict:list --type status
34
- sei-ai dict:add-category sex 性别
35
- sei-ai dict:add-item sex male 男
36
- sei-ai init:base-data --sysid sys --admin-uid admin
37
- ```
38
-
39
- ## 开发者 CLI
40
-
41
- CLI 是新增的开发者命令面,不会改变现有 MCP server 的启动方式、tool 列表或 Host 集成配置。
42
-
43
- 命令示例:
44
-
45
- ```bash
46
- sei-ai query --json '{"head":{"module":"sys_user"},"option":{"fields":"_UID,_NAME"}}'
47
- sei-ai save --json '{"head":{"module":"sys_user"},"data":[{"action":"edit","rows":[{"keyVal":"admin","row":{"_NAME":"管理员"}}]}]}'
48
- sei-ai query-sql "SELECT _UID,_NAME FROM _SYS_USER" --page 1 --size 20
49
- sei-ai api-docs --keyword login --format json
50
- sei-ai relogin --role admin
51
- ```
52
-
53
- 常用认证参数:
54
-
55
- - `--base-url`
56
- - `--ai-key`
57
- - `--role`
58
- - `--account`
59
- - `--account-project`
60
- - `--account-checkcode`
61
- - `--token`
62
- - `--timeout`
63
-
64
- 环境变量加载规则:
65
-
66
- - 启动时会自动读取当前工作目录下的 `.env.local` 和 `.env`
67
- - 加载顺序为 `.env.local` 再 `.env`
68
- - 已经存在的进程环境变量不会被 `.env` 覆盖
69
- - 显式 CLI 参数仍然优先于环境变量
70
-
71
- 载荷输入支持:
72
-
73
- - `--json '<payload>'`
74
- - `--file ./payload.json`
75
- - `--stdin`
76
-
77
- 当前命令面:
78
-
79
- - `stdio`
80
- - `streamable-http`
81
- - `query`
82
- - `save`
83
- - `call`
84
- - `query-sql`
85
- - `sql`
86
- - `api-docs`
87
- - `relogin`
88
- - `dict:list`
89
- - `dict:add-category`
90
- - `dict:add-item`
91
- - `init:base-data`
92
-
93
- HTTP 模式:
94
-
95
- ```bash
96
- pnpm -F sei-ai-mcp dev -- --transport streamable-http --host 0.0.0.0 --port 3000 --path /mcp --config ./apps/sei-ai-mcp/config.example.json
97
- ```
98
-
99
- Docker Compose:
100
-
101
- ```bash
102
- docker compose -f apps/sei-ai-mcp/docker-compose.yml up -d --build
103
- ```
104
-
105
- 容器默认暴露 `http://127.0.0.1:3719/mcp`,并通过 `host.docker.internal` 访问宿主机上的 SEI 后端。
106
- 如果后端也在 compose 中运行,把 `SEI_BASE_URL` 改成对应服务名即可。
107
-
108
- ## 配置
109
-
110
- 支持的配置项:
111
-
112
- - `baseUrl`
113
- - `auth.mode`: `ai-login` 或 `token`
114
- - `auth.token`
115
- - `auth.aiKey`
116
- - `auth.account`
117
- - `auth.role`
118
- - `tools.query`
119
- - `tools.save`
120
- - `tools.schema`
121
- - `tools.querySql`
122
- - `targets.modules`
123
- - `targets.tables`
124
- - `targets.sources`
125
- - `targets.views`
126
-
127
- 敏感值可通过环境变量注入:
128
-
129
- - `SEI_BASE_URL`
130
- - `SEI_AI_LOGIN_KEY`
131
- - `SEI_AI_LOGIN_ACCOUNT`
132
- - `SEI_AI_LOGIN_ROLE`
133
- - `SEI_TOKEN`
134
- - `SEI_MCP_TRANSPORT`
135
- - `SEI_MCP_HOST`
136
- - `SEI_MCP_PORT`
137
- - `SEI_MCP_PATH`
138
- - `SEI_MCP_HTTP_JSON_RESPONSE`
139
-
140
- 例如可以在项目目录写:
141
-
142
- ```bash
143
- SEI_BASE_URL=http://127.0.0.1:8081
144
- SEI_AI_LOGIN_KEY=dev-ai-secret
145
- SEI_AI_LOGIN_ROLE=admin
146
- ```
147
-
148
- ## 权限模型
149
-
150
- - 未配置目标时默认拒绝。
151
- - 查询字段必须命中白名单。
152
- - 保存字段必须命中白名单。
153
- - `querySql` 默认关闭,只允许单条 `SELECT/WITH`。
154
- - stdio 日志只写 `stderr`,不污染 MCP 协议输出。
155
-
156
- 注意:
157
-
158
- - 上面的权限模型只约束 MCP tool 面。
159
- - 开发者 CLI 直接调用 SEI HTTP 接口,不走 MCP 白名单过滤,因此它是调试/开发入口,不是给模型暴露的 tool 面。
160
-
161
- ## 工具
162
-
163
- - `sei_query`
164
- - `sei_save`
165
- - `sei_schema`
166
- - `sei_query_sql`(默认关闭)
167
-
168
- ## MCP Host
169
-
170
- 可直接作为 stdio MCP server 启动,再由上层 Host 注册。
171
-
172
- stdio Host 配置示例:
173
-
174
- ```json
175
- {
176
- "mcpServers": {
177
- "sei-ai-mcp": {
178
- "command": "sei-ai",
179
- "args": [
180
- "stdio",
181
- "--config",
182
- "/abs/path/config.dev.json"
183
- ]
184
- }
185
- }
186
- }
187
- ```
188
-
189
- HTTP 模式可直接挂载到容器端口,Host 地址示例:
190
-
191
- ```json
192
- {
193
- "mcpServers": {
194
- "sei-ai-mcp": {
195
- "url": "http://127.0.0.1:3719/mcp"
196
- }
197
- }
198
- }
199
- ```
200
-
201
- ## Spring AI Java 接入
202
-
203
- 如果 `apps/backend` 通过 Spring AI MCP client 接入,直接使用 `streamable-http`:
204
-
205
- ```yaml
206
- spring:
207
- ai:
208
- mcp:
209
- client:
210
- enabled: true
211
- type: SYNC
212
- request-timeout: 30s
213
- toolcallback:
214
- enabled: true
215
- streamable-http:
216
- connections:
217
- sei-ai-mcp:
218
- url: http://127.0.0.1:3719
219
- endpoint: /mcp
220
- ```
221
-
222
- 如果后端默认直接访问宿主机暴露端口,可这样配置:
223
-
224
- ```bash
225
- SEI_AI_MCP_HTTP_URL=http://127.0.0.1:3719
226
- SEI_AI_MCP_HTTP_ENDPOINT=/mcp
227
- ```
228
-
229
- 如果是宿主机或外部服务直连容器暴露端口,则访问地址为:
230
-
231
- ```bash
232
- http://127.0.0.1:3719/mcp
233
- ```
234
-
235
-
236
-
237
- npm version patch --no-git-tag-version
238
- pnpm run build
239
- npm publish --access public --cache /tmp/sei-ai-npm-cache
@@ -1,157 +0,0 @@
1
- import { readFile, writeFile } from 'node:fs/promises';
2
- import { resolve } from 'node:path';
3
- import { BASE_SEED_DEFAULT_ADMIN_ROLE, BASE_SEED_DEFAULT_ADMIN_UID } from './constants.js';
4
- import { formatCliError, SeiMcpError } from './errors.js';
5
- import { buildBaseSeedSqlVars, buildDictCategoryRow, buildDictItemRow, buildDictQueryPayload, buildDictSavePayload, buildDictTree, removeSqlCommentLines, renderBaseSeedSql, splitSqlStatements, } from './cli-helpers.js';
6
- import { buildOpenApiDocSummary, renderOpenApiMarkdown } from './openapi.js';
7
- export async function runApiDocs(options) {
8
- const raw = await options.client.readApiDocs();
9
- const summary = buildOpenApiDocSummary(raw, options.keyword);
10
- const output = options.format === 'json' ? `${JSON.stringify(summary, null, 2)}\n` : renderOpenApiMarkdown(summary);
11
- await writeOutput(options.outputPath, output);
12
- }
13
- export async function runRelogin(options) {
14
- const token = await options.client.relogin();
15
- printJson({
16
- success: true,
17
- code: 1,
18
- message: '重新登录成功',
19
- data: { token },
20
- });
21
- }
22
- export async function runCall(options) {
23
- const response = await options.client.call(options.method, options.path, options.payload, {
24
- allowFailure: options.allowFailure,
25
- allowEmpty: options.allowEmpty,
26
- skipAuth: options.skipAuth,
27
- });
28
- printJson(response);
29
- }
30
- export async function runQuery(options) {
31
- const response = await options.client.query(options.payload, {
32
- allowFailure: options.allowFailure,
33
- });
34
- printJson(response);
35
- }
36
- export async function runSave(options) {
37
- const response = await options.client.save(options.payload, {
38
- allowFailure: options.allowFailure,
39
- });
40
- printJson(response);
41
- }
42
- export async function runQuerySql(options) {
43
- const response = await options.client.querySql(options.sql, options.page, options.size, {
44
- allowFailure: options.allowFailure,
45
- });
46
- printJson(response);
47
- }
48
- export async function runDictList(options) {
49
- const payload = buildDictQueryPayload(options.typeCode, options.keyword, options.size ?? 1000);
50
- const response = await options.client.query(payload, {
51
- allowFailure: options.allowFailure,
52
- });
53
- if (!options.flat && isObject(response) && isObject(response.data) && Array.isArray(response.data.rows)) {
54
- response.data.tree = buildDictTree(response.data.rows);
55
- }
56
- printJson(response);
57
- }
58
- export async function runDictAddCategory(options) {
59
- const payload = buildDictSavePayload(buildDictCategoryRow({
60
- body: stringOrUndefined(options.rowOptions?.body),
61
- code: stringOrUndefined(options.rowOptions?.code),
62
- ctype: stringOrUndefined(options.rowOptions?.ctype),
63
- dictFlag: numberOrUndefined(options.rowOptions?.dictFlag),
64
- ename: stringOrUndefined(options.rowOptions?.ename),
65
- memo: stringOrUndefined(options.rowOptions?.memo),
66
- name: options.name,
67
- sort: numberOrUndefined(options.rowOptions?.sort),
68
- sysid: stringOrUndefined(options.rowOptions?.sysid),
69
- typeCode: options.typeCode,
70
- uuid: stringOrUndefined(options.rowOptions?.uuid),
71
- }));
72
- const response = await options.client.save(payload, {
73
- allowFailure: options.allowFailure,
74
- });
75
- printJson(response);
76
- }
77
- export async function runDictAddItem(options) {
78
- const payload = buildDictSavePayload(buildDictItemRow({
79
- body: stringOrUndefined(options.rowOptions?.body),
80
- code: options.code,
81
- ctype: stringOrUndefined(options.rowOptions?.ctype),
82
- dictFlag: numberOrUndefined(options.rowOptions?.dictFlag),
83
- ename: stringOrUndefined(options.rowOptions?.ename),
84
- memo: stringOrUndefined(options.rowOptions?.memo),
85
- name: options.name,
86
- parent: stringOrUndefined(options.rowOptions?.parent),
87
- sort: numberOrUndefined(options.rowOptions?.sort),
88
- sysid: stringOrUndefined(options.rowOptions?.sysid),
89
- typeCode: options.typeCode,
90
- uuid: stringOrUndefined(options.rowOptions?.uuid),
91
- }));
92
- const response = await options.client.save(payload, {
93
- allowFailure: options.allowFailure,
94
- });
95
- printJson(response);
96
- }
97
- export async function runInitBaseData(options) {
98
- const sqlFile = resolveBaseSeedSqlPath(options.sqlFile);
99
- const template = await readFile(sqlFile, 'utf8');
100
- const sql = renderBaseSeedSql(template, buildBaseSeedSqlVars({
101
- adminName: options.adminName,
102
- adminPassword: options.adminPassword,
103
- adminRole: options.adminRole,
104
- adminRoleName: options.adminRoleName,
105
- adminUid: options.adminUid,
106
- resetAdminPassword: options.resetAdminPassword,
107
- sysid: options.sysid,
108
- }));
109
- const statements = splitSqlStatements(removeSqlCommentLines(sql));
110
- if (statements.length === 0) {
111
- throw new SeiMcpError('初始化 SQL 文件没有可执行语句', 'INVALID_PARAMS');
112
- }
113
- const executed = await options.client.executeDdlStatements(statements, {
114
- allowFailure: options.allowFailure,
115
- });
116
- await options.client.call('POST', '/api/sei/data/removeAllCache', {}, { allowFailure: false }).catch(() => undefined);
117
- printJson({
118
- success: true,
119
- code: 1,
120
- message: '基础源数据初始化完成',
121
- data: {
122
- executed: Array.isArray(executed) ? executed.length : 0,
123
- sqlFile,
124
- admin: options.adminUid ?? BASE_SEED_DEFAULT_ADMIN_UID,
125
- role: options.adminRole ?? BASE_SEED_DEFAULT_ADMIN_ROLE,
126
- },
127
- });
128
- }
129
- export async function writeOutput(path, text) {
130
- if (!path) {
131
- process.stdout.write(text);
132
- return;
133
- }
134
- await writeFile(path, text, 'utf8');
135
- process.stdout.write(`已写入:${path}\n`);
136
- }
137
- export function printJson(value) {
138
- process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
139
- }
140
- export function resolveBaseSeedSqlPath(path) {
141
- if (path) {
142
- return resolve(path);
143
- }
144
- return resolve(process.cwd(), '.codex/skills/sei-ai/scripts/init-base-data.sql');
145
- }
146
- export function formatActionError(error) {
147
- return formatCliError(error);
148
- }
149
- function stringOrUndefined(value) {
150
- return typeof value === 'string' && value.trim() ? value.trim() : undefined;
151
- }
152
- function numberOrUndefined(value) {
153
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
154
- }
155
- function isObject(value) {
156
- return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
157
- }