@ty_krystal/sei-ai 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -21
- package/dist/README.md +28 -21
- package/dist/cli-actions.js +157 -0
- package/dist/cli-helpers.js +246 -0
- package/dist/command-base/context.js +10 -0
- package/dist/command-base/output.js +6 -0
- package/dist/command-base/payload.js +33 -0
- package/dist/command-base/sei-command.js +88 -0
- package/dist/commands/api-docs.js +23 -0
- package/dist/commands/call.js +38 -0
- package/dist/commands/dict/add-category.js +45 -0
- package/dist/commands/dict/add-item.js +47 -0
- package/dist/commands/dict/list.js +27 -0
- package/dist/commands/init/base-data.js +38 -0
- package/dist/commands/query-sql.js +30 -0
- package/dist/commands/query.js +28 -0
- package/dist/commands/relogin.js +16 -0
- package/dist/commands/save.js +28 -0
- package/dist/commands/stdio.js +16 -0
- package/dist/commands/streamable-http.js +29 -0
- package/dist/config.js +82 -0
- package/dist/constants.js +48 -0
- package/dist/env.js +33 -0
- package/dist/errors.js +55 -0
- package/dist/index.js +2 -22361
- package/dist/logger.js +71 -0
- package/dist/openapi.js +261 -0
- package/dist/permissions.js +209 -0
- package/dist/schema.js +112 -0
- package/dist/sei-client.js +535 -0
- package/dist/server.js +237 -0
- package/dist/tools.js +175 -0
- package/dist/types.js +1 -0
- package/dist/utils.js +53 -0
- package/package.json +14 -2
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { loadConfig } from '../config.js';
|
|
3
|
+
import { DEFAULT_LOG_LEVEL } from '../constants.js';
|
|
4
|
+
import { formatCliError, toErrorLogMeta } from '../errors.js';
|
|
5
|
+
import { loadWorkingDirEnv } from '../env.js';
|
|
6
|
+
import { createLogger } from '../logger.js';
|
|
7
|
+
import { createCliSeiClient, createSeiAuthSettings } from '../sei-client.js';
|
|
8
|
+
import { loadPayload } from './payload.js';
|
|
9
|
+
import { printJson, writeText } from './output.js';
|
|
10
|
+
export class SeiCommand extends Command {
|
|
11
|
+
static baseFlags = {
|
|
12
|
+
config: Flags.string({ description: '配置文件路径' }),
|
|
13
|
+
'base-url': Flags.string({ description: 'SEI 基础地址' }),
|
|
14
|
+
'ai-key': Flags.string({ description: 'AI 登录密钥' }),
|
|
15
|
+
role: Flags.string({ aliases: ['profile'], description: 'AI 登录角色' }),
|
|
16
|
+
account: Flags.string({ description: 'AI 登录账号' }),
|
|
17
|
+
'account-project': Flags.string({ description: 'AI 登录项目' }),
|
|
18
|
+
'account-checkcode': Flags.string({ description: 'AI 登录验证码' }),
|
|
19
|
+
token: Flags.string({ description: '固定 token' }),
|
|
20
|
+
timeout: Flags.integer({ description: '请求超时(毫秒)' }),
|
|
21
|
+
};
|
|
22
|
+
logger;
|
|
23
|
+
configData;
|
|
24
|
+
async init() {
|
|
25
|
+
await loadWorkingDirEnv();
|
|
26
|
+
this.logger = createLogger(process.env.SEI_MCP_LOG_LEVEL ?? DEFAULT_LOG_LEVEL);
|
|
27
|
+
this.configData = await loadConfig(this.argv, process.env);
|
|
28
|
+
await super.init();
|
|
29
|
+
}
|
|
30
|
+
async resolveConfig() {
|
|
31
|
+
return this.configData;
|
|
32
|
+
}
|
|
33
|
+
async createCliClient(options = {}) {
|
|
34
|
+
const config = await this.resolveConfig();
|
|
35
|
+
const settings = createSeiAuthSettings(config, {
|
|
36
|
+
baseUrl: options.baseUrl,
|
|
37
|
+
token: options.token,
|
|
38
|
+
aiKey: options.aiKey,
|
|
39
|
+
role: options.role,
|
|
40
|
+
account: options.account,
|
|
41
|
+
accountProject: options.accountProject,
|
|
42
|
+
accountCheckcode: options.accountCheckcode,
|
|
43
|
+
timeoutMs: options.timeoutMs,
|
|
44
|
+
});
|
|
45
|
+
return createCliSeiClient(settings, this.logger, globalThis.fetch);
|
|
46
|
+
}
|
|
47
|
+
async loadPayload(input) {
|
|
48
|
+
return loadPayload(input);
|
|
49
|
+
}
|
|
50
|
+
printJson(value) {
|
|
51
|
+
printJson(value);
|
|
52
|
+
}
|
|
53
|
+
writeText(text) {
|
|
54
|
+
writeText(text);
|
|
55
|
+
}
|
|
56
|
+
createAuthOptions(flags) {
|
|
57
|
+
return {
|
|
58
|
+
baseUrl: stringFlag(flags, 'base-url'),
|
|
59
|
+
token: stringFlag(flags, 'token'),
|
|
60
|
+
aiKey: stringFlag(flags, 'ai-key'),
|
|
61
|
+
role: stringFlag(flags, 'role') ?? stringFlag(flags, 'profile'),
|
|
62
|
+
account: stringFlag(flags, 'account'),
|
|
63
|
+
accountProject: stringFlag(flags, 'account-project'),
|
|
64
|
+
accountCheckcode: stringFlag(flags, 'account-checkcode'),
|
|
65
|
+
timeoutMs: numberFlag(flags, 'timeout'),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async createClientFromFlags(flags) {
|
|
69
|
+
return this.createCliClient(this.createAuthOptions(flags));
|
|
70
|
+
}
|
|
71
|
+
async catch(error) {
|
|
72
|
+
this.logger?.error('CLI command failed', {
|
|
73
|
+
argv: this.argv,
|
|
74
|
+
error: toErrorLogMeta(error),
|
|
75
|
+
});
|
|
76
|
+
process.stderr.write(`${formatCliError(error)}\n`);
|
|
77
|
+
process.exitCode = 1;
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function stringFlag(flags, name) {
|
|
82
|
+
const value = flags[name];
|
|
83
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
84
|
+
}
|
|
85
|
+
function numberFlag(flags, name) {
|
|
86
|
+
const value = flags[name];
|
|
87
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
88
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../command-base/sei-command.js';
|
|
3
|
+
import { runApiDocs } from '../cli-actions.js';
|
|
4
|
+
export default class ApiDocsCommand extends SeiCommand {
|
|
5
|
+
static summary = 'Export and render OpenAPI docs';
|
|
6
|
+
static description = 'Fetches the SEI OpenAPI document and renders a markdown or JSON summary.';
|
|
7
|
+
static flags = {
|
|
8
|
+
...SeiCommand.baseFlags,
|
|
9
|
+
keyword: Flags.string({ description: '过滤关键字' }),
|
|
10
|
+
format: Flags.string({ default: 'markdown', description: '输出格式' }),
|
|
11
|
+
output: Flags.string({ description: '输出文件路径' }),
|
|
12
|
+
};
|
|
13
|
+
async run() {
|
|
14
|
+
const { flags } = await this.parse(ApiDocsCommand);
|
|
15
|
+
const client = await this.createClientFromFlags(flags);
|
|
16
|
+
await runApiDocs({
|
|
17
|
+
client: { readApiDocs: () => client.readApiDocs() },
|
|
18
|
+
keyword: flags.keyword,
|
|
19
|
+
format: flags.format === 'json' ? 'json' : 'markdown',
|
|
20
|
+
outputPath: flags.output,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../command-base/sei-command.js';
|
|
3
|
+
import { runCall } from '../cli-actions.js';
|
|
4
|
+
export default class CallCommand extends SeiCommand {
|
|
5
|
+
static summary = 'Call a SEI endpoint';
|
|
6
|
+
static description = 'Makes an authenticated request to a SEI endpoint.';
|
|
7
|
+
static args = {
|
|
8
|
+
method: Args.string({ required: true, description: 'HTTP method' }),
|
|
9
|
+
path: Args.string({ required: true, description: 'Request path' }),
|
|
10
|
+
};
|
|
11
|
+
static flags = {
|
|
12
|
+
...SeiCommand.baseFlags,
|
|
13
|
+
json: Flags.string({ description: 'JSON payload' }),
|
|
14
|
+
file: Flags.string({ description: 'JSON file path' }),
|
|
15
|
+
stdin: Flags.boolean({ default: false, description: 'Read JSON payload from stdin' }),
|
|
16
|
+
'allow-failure': Flags.boolean({ default: false, description: '允许失败响应' }),
|
|
17
|
+
'allow-empty': Flags.boolean({ default: false, description: '允许空响应' }),
|
|
18
|
+
'no-token': Flags.boolean({ default: false, description: '不附带 token' }),
|
|
19
|
+
};
|
|
20
|
+
async run() {
|
|
21
|
+
const { args, flags } = await this.parse(CallCommand);
|
|
22
|
+
const client = await this.createClientFromFlags(flags);
|
|
23
|
+
const payload = await this.loadPayload({
|
|
24
|
+
jsonText: flags.json,
|
|
25
|
+
filePath: flags.file,
|
|
26
|
+
stdin: flags.stdin,
|
|
27
|
+
});
|
|
28
|
+
await runCall({
|
|
29
|
+
client: { call: (method, path, body, requestOptions) => client.call(method, path, body, requestOptions) },
|
|
30
|
+
method: args.method,
|
|
31
|
+
path: args.path,
|
|
32
|
+
payload,
|
|
33
|
+
allowFailure: flags['allow-failure'],
|
|
34
|
+
allowEmpty: flags['allow-empty'],
|
|
35
|
+
skipAuth: flags['no-token'],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../../command-base/sei-command.js';
|
|
3
|
+
import { runDictAddCategory } from '../../cli-actions.js';
|
|
4
|
+
export default class DictAddCategoryCommand extends SeiCommand {
|
|
5
|
+
static summary = 'Add a dictionary category';
|
|
6
|
+
static description = 'Builds and saves a SEI dictionary category row.';
|
|
7
|
+
static args = {
|
|
8
|
+
typeCode: Args.string({ required: true, description: '字典类别编码' }),
|
|
9
|
+
name: Args.string({ required: true, description: '字典名称' }),
|
|
10
|
+
};
|
|
11
|
+
static flags = {
|
|
12
|
+
...SeiCommand.baseFlags,
|
|
13
|
+
code: Flags.string({ description: '字典代码' }),
|
|
14
|
+
ctype: Flags.string({ description: '分类类型' }),
|
|
15
|
+
'dict-flag': Flags.integer({ description: '字典标记' }),
|
|
16
|
+
ename: Flags.string({ description: '英文名' }),
|
|
17
|
+
memo: Flags.string({ description: '备注' }),
|
|
18
|
+
sort: Flags.integer({ description: '排序号' }),
|
|
19
|
+
sysid: Flags.string({ description: '系统编号' }),
|
|
20
|
+
uuid: Flags.string({ description: 'UUID' }),
|
|
21
|
+
body: Flags.string({ description: '附加内容' }),
|
|
22
|
+
'allow-failure': Flags.boolean({ default: false, description: '允许失败响应' }),
|
|
23
|
+
};
|
|
24
|
+
async run() {
|
|
25
|
+
const { args, flags } = await this.parse(DictAddCategoryCommand);
|
|
26
|
+
const client = await this.createClientFromFlags(flags);
|
|
27
|
+
await runDictAddCategory({
|
|
28
|
+
client: { save: (payload, requestOptions) => client.save(payload, requestOptions) },
|
|
29
|
+
typeCode: args.typeCode,
|
|
30
|
+
name: args.name,
|
|
31
|
+
rowOptions: {
|
|
32
|
+
body: flags.body,
|
|
33
|
+
code: flags.code,
|
|
34
|
+
ctype: flags.ctype,
|
|
35
|
+
dictFlag: flags['dict-flag'],
|
|
36
|
+
ename: flags.ename,
|
|
37
|
+
memo: flags.memo,
|
|
38
|
+
sort: flags.sort,
|
|
39
|
+
sysid: flags.sysid,
|
|
40
|
+
uuid: flags.uuid,
|
|
41
|
+
},
|
|
42
|
+
allowFailure: flags['allow-failure'],
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../../command-base/sei-command.js';
|
|
3
|
+
import { runDictAddItem } from '../../cli-actions.js';
|
|
4
|
+
export default class DictAddItemCommand extends SeiCommand {
|
|
5
|
+
static summary = 'Add a dictionary item';
|
|
6
|
+
static description = 'Builds and saves a SEI dictionary item row.';
|
|
7
|
+
static args = {
|
|
8
|
+
typeCode: Args.string({ required: true, description: '字典类别编码' }),
|
|
9
|
+
code: Args.string({ required: true, description: '字典项编码' }),
|
|
10
|
+
name: Args.string({ required: true, description: '字典项名称' }),
|
|
11
|
+
};
|
|
12
|
+
static flags = {
|
|
13
|
+
...SeiCommand.baseFlags,
|
|
14
|
+
parent: Flags.string({ description: '父级编号' }),
|
|
15
|
+
ctype: Flags.string({ description: '分类类型' }),
|
|
16
|
+
'dict-flag': Flags.integer({ description: '字典标记' }),
|
|
17
|
+
ename: Flags.string({ description: '英文名' }),
|
|
18
|
+
memo: Flags.string({ description: '备注' }),
|
|
19
|
+
sort: Flags.integer({ description: '排序号' }),
|
|
20
|
+
sysid: Flags.string({ description: '系统编号' }),
|
|
21
|
+
uuid: Flags.string({ description: 'UUID' }),
|
|
22
|
+
body: Flags.string({ description: '附加内容' }),
|
|
23
|
+
'allow-failure': Flags.boolean({ default: false, description: '允许失败响应' }),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
const { args, flags } = await this.parse(DictAddItemCommand);
|
|
27
|
+
const client = await this.createClientFromFlags(flags);
|
|
28
|
+
await runDictAddItem({
|
|
29
|
+
client: { save: (payload, requestOptions) => client.save(payload, requestOptions) },
|
|
30
|
+
typeCode: args.typeCode,
|
|
31
|
+
code: args.code,
|
|
32
|
+
name: args.name,
|
|
33
|
+
rowOptions: {
|
|
34
|
+
body: flags.body,
|
|
35
|
+
ctype: flags.ctype,
|
|
36
|
+
dictFlag: flags['dict-flag'],
|
|
37
|
+
ename: flags.ename,
|
|
38
|
+
memo: flags.memo,
|
|
39
|
+
parent: flags.parent,
|
|
40
|
+
sort: flags.sort,
|
|
41
|
+
sysid: flags.sysid,
|
|
42
|
+
uuid: flags.uuid,
|
|
43
|
+
},
|
|
44
|
+
allowFailure: flags['allow-failure'],
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../../command-base/sei-command.js';
|
|
3
|
+
import { runDictList } from '../../cli-actions.js';
|
|
4
|
+
export default class DictListCommand extends SeiCommand {
|
|
5
|
+
static summary = 'List dictionary entries';
|
|
6
|
+
static description = 'Queries SEI dictionary data and optionally renders the tree structure.';
|
|
7
|
+
static flags = {
|
|
8
|
+
...SeiCommand.baseFlags,
|
|
9
|
+
type: Flags.string({ description: '字典类型' }),
|
|
10
|
+
keyword: Flags.string({ description: '过滤关键字' }),
|
|
11
|
+
size: Flags.integer({ default: 1000, description: '返回条数' }),
|
|
12
|
+
flat: Flags.boolean({ default: false, description: '只输出平铺结果' }),
|
|
13
|
+
'allow-failure': Flags.boolean({ default: false, description: '允许失败响应' }),
|
|
14
|
+
};
|
|
15
|
+
async run() {
|
|
16
|
+
const { flags } = await this.parse(DictListCommand);
|
|
17
|
+
const client = await this.createClientFromFlags(flags);
|
|
18
|
+
await runDictList({
|
|
19
|
+
client: { query: (payload, requestOptions) => client.query(payload, requestOptions) },
|
|
20
|
+
typeCode: flags.type,
|
|
21
|
+
keyword: flags.keyword,
|
|
22
|
+
size: flags.size,
|
|
23
|
+
flat: flags.flat,
|
|
24
|
+
allowFailure: flags['allow-failure'],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../../command-base/sei-command.js';
|
|
3
|
+
import { runInitBaseData } from '../../cli-actions.js';
|
|
4
|
+
export default class InitBaseDataCommand extends SeiCommand {
|
|
5
|
+
static summary = 'Initialize base data';
|
|
6
|
+
static description = 'Executes the base seed SQL and clears SEI caches.';
|
|
7
|
+
static flags = {
|
|
8
|
+
...SeiCommand.baseFlags,
|
|
9
|
+
'admin-uid': Flags.string({ description: '管理员 UID' }),
|
|
10
|
+
'admin-name': Flags.string({ description: '管理员名称' }),
|
|
11
|
+
'admin-role': Flags.string({ description: '管理员角色' }),
|
|
12
|
+
'admin-role-name': Flags.string({ description: '管理员角色名称' }),
|
|
13
|
+
'admin-password': Flags.string({ description: '管理员密码' }),
|
|
14
|
+
sysid: Flags.string({ description: '系统编号' }),
|
|
15
|
+
'reset-admin-password': Flags.boolean({ default: false, description: '重置管理员密码' }),
|
|
16
|
+
'sql-file': Flags.string({ description: '初始化 SQL 文件' }),
|
|
17
|
+
'allow-failure': Flags.boolean({ default: false, description: '允许失败响应' }),
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const { flags } = await this.parse(InitBaseDataCommand);
|
|
21
|
+
const client = await this.createClientFromFlags(flags);
|
|
22
|
+
await runInitBaseData({
|
|
23
|
+
client: {
|
|
24
|
+
executeDdlStatements: (statements, requestOptions) => client.executeDdlStatements(statements, requestOptions),
|
|
25
|
+
call: (method, path, payload, requestOptions) => client.call(method, path, payload, requestOptions),
|
|
26
|
+
},
|
|
27
|
+
sqlFile: flags['sql-file'],
|
|
28
|
+
sysid: flags.sysid,
|
|
29
|
+
adminUid: flags['admin-uid'],
|
|
30
|
+
adminName: flags['admin-name'],
|
|
31
|
+
adminRole: flags['admin-role'],
|
|
32
|
+
adminRoleName: flags['admin-role-name'],
|
|
33
|
+
adminPassword: flags['admin-password'],
|
|
34
|
+
resetAdminPassword: flags['reset-admin-password'],
|
|
35
|
+
allowFailure: flags['allow-failure'],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../command-base/sei-command.js';
|
|
3
|
+
import { runQuerySql } from '../cli-actions.js';
|
|
4
|
+
import { buildQuerySqlPayload } from '../sei-client.js';
|
|
5
|
+
export default class QuerySqlCommand extends SeiCommand {
|
|
6
|
+
static summary = 'Query SEI with SQL';
|
|
7
|
+
static description = 'Executes querySQL against SEI using a SQL string.';
|
|
8
|
+
static aliases = ['sql'];
|
|
9
|
+
static args = {
|
|
10
|
+
sql: Args.string({ description: 'SQL 语句', required: true }),
|
|
11
|
+
};
|
|
12
|
+
static flags = {
|
|
13
|
+
...SeiCommand.baseFlags,
|
|
14
|
+
page: Flags.integer({ default: 1, description: '页码' }),
|
|
15
|
+
size: Flags.integer({ default: 20, description: '每页条数' }),
|
|
16
|
+
'allow-failure': Flags.boolean({ default: false, description: '允许失败响应' }),
|
|
17
|
+
};
|
|
18
|
+
async run() {
|
|
19
|
+
const { args, flags } = await this.parse(QuerySqlCommand);
|
|
20
|
+
const client = await this.createClientFromFlags(flags);
|
|
21
|
+
const payload = buildQuerySqlPayload(args.sql, flags.page, flags.size);
|
|
22
|
+
await runQuerySql({
|
|
23
|
+
client: { querySql: (sql, page, size, requestOptions) => client.querySql(sql, page, size, requestOptions) },
|
|
24
|
+
sql: payload.sql,
|
|
25
|
+
page: payload.page,
|
|
26
|
+
size: payload.size,
|
|
27
|
+
allowFailure: flags['allow-failure'],
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../command-base/sei-command.js';
|
|
3
|
+
import { runQuery } from '../cli-actions.js';
|
|
4
|
+
export default class QueryCommand extends SeiCommand {
|
|
5
|
+
static summary = 'Query SEI data';
|
|
6
|
+
static description = 'Sends a SEI query payload.';
|
|
7
|
+
static flags = {
|
|
8
|
+
...SeiCommand.baseFlags,
|
|
9
|
+
json: Flags.string({ description: 'JSON payload' }),
|
|
10
|
+
file: Flags.string({ description: 'JSON file path' }),
|
|
11
|
+
stdin: Flags.boolean({ default: false, description: 'Read JSON payload from stdin' }),
|
|
12
|
+
'allow-failure': Flags.boolean({ default: false, description: '允许失败响应' }),
|
|
13
|
+
};
|
|
14
|
+
async run() {
|
|
15
|
+
const { flags } = await this.parse(QueryCommand);
|
|
16
|
+
const client = await this.createClientFromFlags(flags);
|
|
17
|
+
const payload = await this.loadPayload({
|
|
18
|
+
jsonText: flags.json,
|
|
19
|
+
filePath: flags.file,
|
|
20
|
+
stdin: flags.stdin,
|
|
21
|
+
});
|
|
22
|
+
await runQuery({
|
|
23
|
+
client: { query: (input, requestOptions) => client.query(input, requestOptions) },
|
|
24
|
+
payload,
|
|
25
|
+
allowFailure: flags['allow-failure'],
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SeiCommand } from '../command-base/sei-command.js';
|
|
2
|
+
import { runRelogin } from '../cli-actions.js';
|
|
3
|
+
export default class ReloginCommand extends SeiCommand {
|
|
4
|
+
static summary = 'Refresh the login token';
|
|
5
|
+
static description = 'Forces a fresh ai-login and prints the new token.';
|
|
6
|
+
static flags = {
|
|
7
|
+
...SeiCommand.baseFlags,
|
|
8
|
+
};
|
|
9
|
+
async run() {
|
|
10
|
+
const { flags } = await this.parse(ReloginCommand);
|
|
11
|
+
const client = await this.createClientFromFlags(flags);
|
|
12
|
+
await runRelogin({
|
|
13
|
+
client: { relogin: () => client.relogin() },
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../command-base/sei-command.js';
|
|
3
|
+
import { runSave } from '../cli-actions.js';
|
|
4
|
+
export default class SaveCommand extends SeiCommand {
|
|
5
|
+
static summary = 'Save SEI data';
|
|
6
|
+
static description = 'Sends a SEI save payload.';
|
|
7
|
+
static flags = {
|
|
8
|
+
...SeiCommand.baseFlags,
|
|
9
|
+
json: Flags.string({ description: 'JSON payload' }),
|
|
10
|
+
file: Flags.string({ description: 'JSON file path' }),
|
|
11
|
+
stdin: Flags.boolean({ default: false, description: 'Read JSON payload from stdin' }),
|
|
12
|
+
'allow-failure': Flags.boolean({ default: false, description: '允许失败响应' }),
|
|
13
|
+
};
|
|
14
|
+
async run() {
|
|
15
|
+
const { flags } = await this.parse(SaveCommand);
|
|
16
|
+
const client = await this.createClientFromFlags(flags);
|
|
17
|
+
const payload = await this.loadPayload({
|
|
18
|
+
jsonText: flags.json,
|
|
19
|
+
filePath: flags.file,
|
|
20
|
+
stdin: flags.stdin,
|
|
21
|
+
});
|
|
22
|
+
await runSave({
|
|
23
|
+
client: { save: (input, requestOptions) => client.save(input, requestOptions) },
|
|
24
|
+
payload,
|
|
25
|
+
allowFailure: flags['allow-failure'],
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { SeiCommand } from '../command-base/sei-command.js';
|
|
2
|
+
import { createMcpServer, startStdioServer } from '../server.js';
|
|
3
|
+
import { createSeiClient } from '../sei-client.js';
|
|
4
|
+
export default class StdioCommand extends SeiCommand {
|
|
5
|
+
static summary = 'Start MCP server over stdio';
|
|
6
|
+
static description = 'Starts the SEI MCP server using stdio transport.';
|
|
7
|
+
static flags = {
|
|
8
|
+
...SeiCommand.baseFlags,
|
|
9
|
+
};
|
|
10
|
+
async run() {
|
|
11
|
+
const config = await this.resolveConfig();
|
|
12
|
+
const client = createSeiClient(config, this.logger);
|
|
13
|
+
const server = createMcpServer({ config, client, logger: this.logger });
|
|
14
|
+
await startStdioServer(server);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { SeiCommand } from '../command-base/sei-command.js';
|
|
3
|
+
import { createMcpServer, startHttpServer } from '../server.js';
|
|
4
|
+
import { createSeiClient } from '../sei-client.js';
|
|
5
|
+
export default class StreamableHttpCommand extends SeiCommand {
|
|
6
|
+
static summary = 'Start MCP server over streamable HTTP';
|
|
7
|
+
static description = 'Starts the SEI MCP server using the streamable HTTP transport.';
|
|
8
|
+
static flags = {
|
|
9
|
+
...SeiCommand.baseFlags,
|
|
10
|
+
host: Flags.string({ default: '127.0.0.1', description: 'HTTP bind host' }),
|
|
11
|
+
port: Flags.integer({ default: 3000, description: 'HTTP bind port' }),
|
|
12
|
+
path: Flags.string({ default: '/mcp', description: 'HTTP endpoint path' }),
|
|
13
|
+
'http-json-response': Flags.boolean({ default: false, description: 'Enable JSON response mode' }),
|
|
14
|
+
};
|
|
15
|
+
async run() {
|
|
16
|
+
const { flags } = await this.parse(StreamableHttpCommand);
|
|
17
|
+
const config = await this.resolveConfig();
|
|
18
|
+
const client = createSeiClient(config, this.logger);
|
|
19
|
+
await startHttpServer(() => createMcpServer({ config, client, logger: this.logger }), {
|
|
20
|
+
command: 'serve',
|
|
21
|
+
transport: 'streamable-http',
|
|
22
|
+
host: flags.host,
|
|
23
|
+
port: flags.port,
|
|
24
|
+
path: flags.path,
|
|
25
|
+
enableJsonResponse: flags['http-json-response'],
|
|
26
|
+
cliArgs: [],
|
|
27
|
+
}, this.logger);
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { DEFAULT_BASE_URL } from './constants.js';
|
|
3
|
+
import { SeiMcpError } from './errors.js';
|
|
4
|
+
import { isObject, toTrimmedString } from './utils.js';
|
|
5
|
+
export async function loadConfig(argv = process.argv.slice(2), env = process.env) {
|
|
6
|
+
const configPath = resolveArg(argv, '--config') ?? (toTrimmedString(env.SEI_AI_MCP_CONFIG) || null);
|
|
7
|
+
const fileConfig = configPath ? await readConfigFile(configPath) : {};
|
|
8
|
+
return normalizeConfig(fileConfig, env);
|
|
9
|
+
}
|
|
10
|
+
export function normalizeConfig(input = {}, env = process.env) {
|
|
11
|
+
const config = isObject(input) ? input : {};
|
|
12
|
+
const auth = isObject(config.auth) ? config.auth : {};
|
|
13
|
+
const tools = isObject(config.tools) ? config.tools : {};
|
|
14
|
+
const targets = isObject(config.targets) ? config.targets : {};
|
|
15
|
+
return {
|
|
16
|
+
baseUrl: normalizeBaseUrl(config.baseUrl, env.SEI_BASE_URL),
|
|
17
|
+
auth: {
|
|
18
|
+
mode: normalizeMode(auth.mode, env.SEI_AI_MCP_AUTH_MODE),
|
|
19
|
+
token: stringOrEnv(auth.token, env.SEI_TOKEN),
|
|
20
|
+
tokenEnv: stringOrEnv(auth.tokenEnv, env.SEI_TOKEN ? 'SEI_TOKEN' : 'SEI_TOKEN'),
|
|
21
|
+
aiKey: stringOrEnv(auth.aiKey, env.SEI_AI_LOGIN_KEY),
|
|
22
|
+
aiKeyEnv: stringOrEnv(auth.aiKeyEnv, env.SEI_AI_LOGIN_KEY ? 'SEI_AI_LOGIN_KEY' : 'SEI_AI_LOGIN_KEY'),
|
|
23
|
+
account: stringOrEnv(auth.account, env.SEI_AI_LOGIN_ACCOUNT),
|
|
24
|
+
accountEnv: stringOrEnv(auth.accountEnv, env.SEI_AI_LOGIN_ACCOUNT ? 'SEI_AI_LOGIN_ACCOUNT' : 'SEI_AI_LOGIN_ACCOUNT'),
|
|
25
|
+
role: stringOrEnv(auth.role, env.SEI_AI_LOGIN_ROLE),
|
|
26
|
+
roleEnv: stringOrEnv(auth.roleEnv, env.SEI_AI_LOGIN_ROLE ? 'SEI_AI_LOGIN_ROLE' : 'SEI_AI_LOGIN_ROLE'),
|
|
27
|
+
},
|
|
28
|
+
tools: {
|
|
29
|
+
query: tools.query !== false,
|
|
30
|
+
save: tools.save !== false,
|
|
31
|
+
schema: tools.schema !== false,
|
|
32
|
+
querySql: tools.querySql === true,
|
|
33
|
+
},
|
|
34
|
+
targets: {
|
|
35
|
+
modules: toTargetMap(targets.modules),
|
|
36
|
+
tables: toTargetMap(targets.tables),
|
|
37
|
+
sources: toTargetMap(targets.sources),
|
|
38
|
+
views: toTargetMap(targets.views),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async function readConfigFile(path) {
|
|
43
|
+
try {
|
|
44
|
+
const text = await readFile(path, 'utf8');
|
|
45
|
+
return JSON.parse(text);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
throw new SeiMcpError(`无法读取配置文件: ${path}`, 'INVALID_REQUEST', {
|
|
49
|
+
path,
|
|
50
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function resolveArg(argv, name) {
|
|
55
|
+
const index = argv.indexOf(name);
|
|
56
|
+
if (index === -1) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const next = argv[index + 1];
|
|
60
|
+
return next && !next.startsWith('-') ? next : null;
|
|
61
|
+
}
|
|
62
|
+
function normalizeBaseUrl(configValue, envValue) {
|
|
63
|
+
const raw = stringOrEnv(configValue, envValue);
|
|
64
|
+
return raw || DEFAULT_BASE_URL;
|
|
65
|
+
}
|
|
66
|
+
function normalizeMode(configValue, envValue) {
|
|
67
|
+
const raw = stringOrEnv(configValue, envValue).toLowerCase();
|
|
68
|
+
return raw === 'token' ? 'token' : 'ai-login';
|
|
69
|
+
}
|
|
70
|
+
function stringOrEnv(value, envValue) {
|
|
71
|
+
const candidate = toTrimmedString(value);
|
|
72
|
+
if (candidate) {
|
|
73
|
+
return candidate;
|
|
74
|
+
}
|
|
75
|
+
return toTrimmedString(envValue);
|
|
76
|
+
}
|
|
77
|
+
function toTargetMap(value) {
|
|
78
|
+
if (!isObject(value)) {
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => isObject(item)));
|
|
82
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const PACKAGE_NAME = 'sei-ai';
|
|
2
|
+
export const BIN_NAME = 'sei-ai';
|
|
3
|
+
export const SERVER_NAME = 'sei-ai-mcp-server';
|
|
4
|
+
export const SERVER_VERSION = '0.2.0';
|
|
5
|
+
export const DEFAULT_BASE_URL = 'http://127.0.0.1:8081';
|
|
6
|
+
export const DEFAULT_AI_KEY = 'dev-ai-secret';
|
|
7
|
+
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
8
|
+
export const DEFAULT_LOG_LEVEL = 'info';
|
|
9
|
+
export const DEFAULT_QUERY_LIMIT = 20;
|
|
10
|
+
export const DEFAULT_QUERY_SQL_PAGE = 1;
|
|
11
|
+
export const DEFAULT_QUERY_SQL_SIZE = 20;
|
|
12
|
+
export const MAX_RESPONSE_TEXT = 20_000;
|
|
13
|
+
export const MAX_LOG_TEXT = 2_000;
|
|
14
|
+
export const LOGIN_PATH = '/api/sei/public/login';
|
|
15
|
+
export const ACCOUNT_CHECK_CODE_PATH = '/api/sei/public/checkCode';
|
|
16
|
+
export const QUERY_PATH = '/api/sei/data/public/query';
|
|
17
|
+
export const SAVE_PATH = '/api/sei/data/public/save';
|
|
18
|
+
export const QUERY_SQL_PATH = '/api/sei/data/querySQL';
|
|
19
|
+
export const DDL_EXECUTE_PATH = '/api/sei/mcp/ddl/execute';
|
|
20
|
+
export const OPENAPI_DOCS_PATH = '/v3/api-docs';
|
|
21
|
+
export const DICT_MODULE = 'sys_dic';
|
|
22
|
+
export const DICT_DEFAULT_SYSID = 'sys';
|
|
23
|
+
export const DICT_FIELDS = [
|
|
24
|
+
'_UUID',
|
|
25
|
+
'_PID',
|
|
26
|
+
'_SYSID',
|
|
27
|
+
'_TYPE',
|
|
28
|
+
'_CTYPE',
|
|
29
|
+
'_CODE',
|
|
30
|
+
'_NAME',
|
|
31
|
+
'_ENAME',
|
|
32
|
+
'_DICT',
|
|
33
|
+
'_SORT',
|
|
34
|
+
'_BODY',
|
|
35
|
+
'_MEMO',
|
|
36
|
+
];
|
|
37
|
+
export const BASE_SEED_DEFAULT_SYSID = 'sys';
|
|
38
|
+
export const BASE_SEED_DEFAULT_ADMIN_UID = 'admin';
|
|
39
|
+
export const BASE_SEED_DEFAULT_ADMIN_ROLE = 'admin';
|
|
40
|
+
export const BASE_SEED_DEFAULT_ADMIN_PASSWORD = '123456';
|
|
41
|
+
export const ACCOUNT_LOGIN_DEFAULT_PROJECT = 'sys';
|
|
42
|
+
export const ACCOUNT_LOGIN_DEFAULT_CHECKCODE = 'abcd';
|
|
43
|
+
export const ACCOUNT_LOGIN_DEFAULT_TYPE = 'login';
|
|
44
|
+
export const ACCOUNT_LOGIN_DEFAULT_OS = 0;
|
|
45
|
+
export const TOKEN_CACHE_DIR_NAME = 'sei-ai';
|
|
46
|
+
export const TOKEN_CACHE_FILE_PREFIX = 'token-';
|
|
47
|
+
export const TOKEN_CACHE_FILE_SUFFIX = '.json';
|
|
48
|
+
export const TOKEN_CACHE_VERSION = 1;
|
package/dist/env.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { parseEnv } from 'node:util';
|
|
4
|
+
const DEFAULT_ENV_FILES = ['.env.local', '.env'];
|
|
5
|
+
export async function loadWorkingDirEnv(cwd = process.cwd(), env = process.env) {
|
|
6
|
+
for (const fileName of DEFAULT_ENV_FILES) {
|
|
7
|
+
await loadEnvFile(resolve(cwd, fileName), env);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
async function loadEnvFile(path, env) {
|
|
11
|
+
try {
|
|
12
|
+
const text = await readFile(path, 'utf8');
|
|
13
|
+
const parsed = parseEnv(text);
|
|
14
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
15
|
+
if (env[key] === undefined) {
|
|
16
|
+
env[key] = value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (isMissingFile(error)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function isMissingFile(error) {
|
|
28
|
+
if (!error || typeof error !== 'object') {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const withCode = error;
|
|
32
|
+
return withCode.code === 'ENOENT';
|
|
33
|
+
}
|