@ty_krystal/sei-ai 0.1.2 → 0.1.4
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 +15 -0
- package/dist/README.md +15 -0
- package/dist/index.js +269 -158
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,6 +63,13 @@ sei-ai cli api-docs --keyword login --format json
|
|
|
63
63
|
- `--token`
|
|
64
64
|
- `--timeout`
|
|
65
65
|
|
|
66
|
+
环境变量加载规则:
|
|
67
|
+
|
|
68
|
+
- 启动时会自动读取当前工作目录下的 `.env.local` 和 `.env`
|
|
69
|
+
- 加载顺序为 `.env.local` 再 `.env`
|
|
70
|
+
- 已经存在的进程环境变量不会被 `.env` 覆盖
|
|
71
|
+
- 显式 CLI 参数仍然优先于环境变量
|
|
72
|
+
|
|
66
73
|
载荷输入支持:
|
|
67
74
|
|
|
68
75
|
- `--json '<payload>'`
|
|
@@ -123,6 +130,14 @@ docker compose -f apps/sei-ai-mcp/docker-compose.yml up -d --build
|
|
|
123
130
|
- `SEI_MCP_PATH`
|
|
124
131
|
- `SEI_MCP_HTTP_JSON_RESPONSE`
|
|
125
132
|
|
|
133
|
+
例如可以在项目目录写:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
SEI_BASE_URL=http://127.0.0.1:8081
|
|
137
|
+
SEI_AI_LOGIN_KEY=dev-ai-secret
|
|
138
|
+
SEI_AI_LOGIN_ROLE=admin
|
|
139
|
+
```
|
|
140
|
+
|
|
126
141
|
## 权限模型
|
|
127
142
|
|
|
128
143
|
- 未配置目标时默认拒绝。
|
package/dist/README.md
CHANGED
|
@@ -63,6 +63,13 @@ sei-ai cli api-docs --keyword login --format json
|
|
|
63
63
|
- `--token`
|
|
64
64
|
- `--timeout`
|
|
65
65
|
|
|
66
|
+
环境变量加载规则:
|
|
67
|
+
|
|
68
|
+
- 启动时会自动读取当前工作目录下的 `.env.local` 和 `.env`
|
|
69
|
+
- 加载顺序为 `.env.local` 再 `.env`
|
|
70
|
+
- 已经存在的进程环境变量不会被 `.env` 覆盖
|
|
71
|
+
- 显式 CLI 参数仍然优先于环境变量
|
|
72
|
+
|
|
66
73
|
载荷输入支持:
|
|
67
74
|
|
|
68
75
|
- `--json '<payload>'`
|
|
@@ -123,6 +130,14 @@ docker compose -f apps/sei-ai-mcp/docker-compose.yml up -d --build
|
|
|
123
130
|
- `SEI_MCP_PATH`
|
|
124
131
|
- `SEI_MCP_HTTP_JSON_RESPONSE`
|
|
125
132
|
|
|
133
|
+
例如可以在项目目录写:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
SEI_BASE_URL=http://127.0.0.1:8081
|
|
137
|
+
SEI_AI_LOGIN_KEY=dev-ai-secret
|
|
138
|
+
SEI_AI_LOGIN_ROLE=admin
|
|
139
|
+
```
|
|
140
|
+
|
|
126
141
|
## 权限模型
|
|
127
142
|
|
|
128
143
|
- 未配置目标时默认拒绝。
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
|
+
import { parseEnv } from "node:util";
|
|
6
7
|
import { createServer } from "node:http";
|
|
7
8
|
import { ZodOptional, z } from "zod";
|
|
8
9
|
import process$1 from "node:process";
|
|
@@ -49,6 +50,7 @@ var PACKAGE_NAME = "sei-ai";
|
|
|
49
50
|
var BIN_NAME = "sei-ai";
|
|
50
51
|
var SERVER_NAME = "sei-ai-mcp-server";
|
|
51
52
|
var SERVER_VERSION = "0.2.0";
|
|
53
|
+
var DEFAULT_AI_KEY = "dev-ai-secret";
|
|
52
54
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
53
55
|
var DEFAULT_LOG_LEVEL = "info";
|
|
54
56
|
var MAX_RESPONSE_TEXT = 2e4;
|
|
@@ -78,6 +80,7 @@ var DICT_FIELDS = [
|
|
|
78
80
|
var BASE_SEED_DEFAULT_ADMIN_UID = "admin";
|
|
79
81
|
var BASE_SEED_DEFAULT_ADMIN_ROLE = "admin";
|
|
80
82
|
var BASE_SEED_DEFAULT_ADMIN_PASSWORD = "123456";
|
|
83
|
+
var ACCOUNT_LOGIN_DEFAULT_CHECKCODE = "abcd";
|
|
81
84
|
var ACCOUNT_LOGIN_DEFAULT_TYPE = "login";
|
|
82
85
|
var TOKEN_CACHE_DIR_NAME = "sei-ai";
|
|
83
86
|
var TOKEN_CACHE_FILE_PREFIX = "token-";
|
|
@@ -94,6 +97,35 @@ var SeiMcpError = class extends Error {
|
|
|
94
97
|
this.details = details;
|
|
95
98
|
}
|
|
96
99
|
};
|
|
100
|
+
function isSeiMcpError(error) {
|
|
101
|
+
return error instanceof SeiMcpError;
|
|
102
|
+
}
|
|
103
|
+
function normalizeErrorMessage(error) {
|
|
104
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
105
|
+
}
|
|
106
|
+
function formatCliError(error) {
|
|
107
|
+
if (error instanceof SeiMcpError) return `[${error.code}] ${error.message}`;
|
|
108
|
+
return `[INTERNAL_ERROR] ${normalizeErrorMessage(error)}`;
|
|
109
|
+
}
|
|
110
|
+
function toErrorLogMeta(error) {
|
|
111
|
+
if (error instanceof SeiMcpError) return {
|
|
112
|
+
code: error.code,
|
|
113
|
+
message: error.message,
|
|
114
|
+
details: toJsonValue(error.details)
|
|
115
|
+
};
|
|
116
|
+
if (error instanceof Error) return {
|
|
117
|
+
message: error.message,
|
|
118
|
+
stack: error.stack ?? ""
|
|
119
|
+
};
|
|
120
|
+
return { message: normalizeErrorMessage(error) };
|
|
121
|
+
}
|
|
122
|
+
function toJsonValue(value) {
|
|
123
|
+
if (value === null) return null;
|
|
124
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
|
|
125
|
+
if (Array.isArray(value)) return value.map((item) => toJsonValue(item));
|
|
126
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, toJsonValue(item)]));
|
|
127
|
+
return String(value);
|
|
128
|
+
}
|
|
97
129
|
//#endregion
|
|
98
130
|
//#region src/cli-helpers.ts
|
|
99
131
|
function buildDictQueryPayload(typeCode, keyword, size = 1e3) {
|
|
@@ -359,12 +391,12 @@ function createSeiClient(config, logger, fetchImpl = fetch) {
|
|
|
359
391
|
function createSeiAuthSettings(config, options = {}) {
|
|
360
392
|
return {
|
|
361
393
|
baseUrl: normalizeBaseUrl$1(options.baseUrl ?? config.baseUrl),
|
|
362
|
-
token:
|
|
363
|
-
aiKey:
|
|
394
|
+
token: firstText(options.token, config.auth.token, process.env.SEI_TOKEN),
|
|
395
|
+
aiKey: firstText(options.aiKey, config.auth.aiKey, process.env.SEI_AI_LOGIN_KEY, DEFAULT_AI_KEY),
|
|
364
396
|
role: resolveRole(options.role, config.auth.role),
|
|
365
|
-
account:
|
|
366
|
-
accountProject:
|
|
367
|
-
accountCheckcode:
|
|
397
|
+
account: firstText(options.account, config.auth.account, process.env.SEI_AI_LOGIN_ACCOUNT),
|
|
398
|
+
accountProject: firstText(options.accountProject, process.env.SEI_AI_LOGIN_ACCOUNT_PROJECT, "sys"),
|
|
399
|
+
accountCheckcode: firstText(options.accountCheckcode, process.env.SEI_AI_LOGIN_ACCOUNT_CHECKCODE, ACCOUNT_LOGIN_DEFAULT_CHECKCODE),
|
|
368
400
|
timeoutMs: normalizeTimeout(options.timeoutMs)
|
|
369
401
|
};
|
|
370
402
|
}
|
|
@@ -711,9 +743,14 @@ function normalizeTimeout(value) {
|
|
|
711
743
|
return DEFAULT_TIMEOUT_MS;
|
|
712
744
|
}
|
|
713
745
|
function resolveRole(role, fallback) {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
746
|
+
return firstText(role, fallback, process.env.SEI_AI_LOGIN_ROLE).toLowerCase();
|
|
747
|
+
}
|
|
748
|
+
function firstText(...values) {
|
|
749
|
+
for (const value of values) {
|
|
750
|
+
const text = toText(value);
|
|
751
|
+
if (text) return text;
|
|
752
|
+
}
|
|
753
|
+
return "";
|
|
717
754
|
}
|
|
718
755
|
function toText(value) {
|
|
719
756
|
return typeof value === "string" ? value.trim() : "";
|
|
@@ -731,10 +768,11 @@ var OPENAPI_HTTP_METHODS = [
|
|
|
731
768
|
"TRACE"
|
|
732
769
|
];
|
|
733
770
|
function buildOpenApiDocSummary(openapiDoc, keyword) {
|
|
734
|
-
if (!isObject$2(openapiDoc)) throw new
|
|
771
|
+
if (!isObject$2(openapiDoc)) throw new SeiMcpError("OpenAPI 文档不是 JSON 对象", "INVALID_REQUEST");
|
|
772
|
+
throwIfOpenApiErrorPayload(openapiDoc);
|
|
735
773
|
const info = isObject$2(openapiDoc.info) ? openapiDoc.info : {};
|
|
736
774
|
const rawPaths = openapiDoc.paths;
|
|
737
|
-
if (!isObject$2(rawPaths)) throw new
|
|
775
|
+
if (!isObject$2(rawPaths)) throw new SeiMcpError("OpenAPI 文档缺少 paths 对象", "INVALID_REQUEST");
|
|
738
776
|
const normalizedKeyword = normalizeText(keyword).toLowerCase();
|
|
739
777
|
const items = [];
|
|
740
778
|
for (const [path, rawPathItem] of Object.entries(rawPaths)) {
|
|
@@ -871,6 +909,42 @@ function summarizeSchema(schema) {
|
|
|
871
909
|
if (schemaType) return schemaType;
|
|
872
910
|
return Array.isArray(schema.enum) && schema.enum.length > 0 ? "enum" : "";
|
|
873
911
|
}
|
|
912
|
+
function throwIfOpenApiErrorPayload(openapiDoc) {
|
|
913
|
+
const rootCause = isObject$2(openapiDoc.rootCause) ? openapiDoc.rootCause : null;
|
|
914
|
+
const topFrame = (Array.isArray(rootCause?.stackTrace) ? rootCause.stackTrace : []).find(isObject$2);
|
|
915
|
+
const rootMessage = normalizeText(rootCause?.message);
|
|
916
|
+
const rootException = normalizeText(rootCause?.exception);
|
|
917
|
+
const message = normalizeText(openapiDoc.message);
|
|
918
|
+
const error = normalizeText(openapiDoc.error);
|
|
919
|
+
const path = normalizeText(openapiDoc.path);
|
|
920
|
+
if (!rootCause && !message && !error) return;
|
|
921
|
+
const parts = [
|
|
922
|
+
message || error,
|
|
923
|
+
rootException,
|
|
924
|
+
rootMessage,
|
|
925
|
+
formatStackFrame(topFrame),
|
|
926
|
+
path ? `path=${path}` : ""
|
|
927
|
+
].filter(Boolean);
|
|
928
|
+
throw new SeiMcpError(parts.length > 0 ? `OpenAPI 文档生成失败:${parts.join(" | ")}` : "OpenAPI 文档生成失败,服务端返回异常对象", "INVALID_REQUEST", {
|
|
929
|
+
error,
|
|
930
|
+
message,
|
|
931
|
+
path,
|
|
932
|
+
rootCause: rootCause ?? void 0
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
function formatStackFrame(frame) {
|
|
936
|
+
if (!isObject$2(frame)) return "";
|
|
937
|
+
const className = normalizeText(frame.className);
|
|
938
|
+
const methodName = normalizeText(frame.methodName);
|
|
939
|
+
const fileName = normalizeText(frame.fileName);
|
|
940
|
+
const lineNumber = Number(frame.lineNumber);
|
|
941
|
+
const locationParts = [className, methodName].filter(Boolean);
|
|
942
|
+
const sourceParts = [fileName, Number.isFinite(lineNumber) && lineNumber > 0 ? String(lineNumber) : ""].filter(Boolean);
|
|
943
|
+
const location = locationParts.join("#");
|
|
944
|
+
const source = sourceParts.join(":");
|
|
945
|
+
if (location && source) return `${location} (${source})`;
|
|
946
|
+
return location || source;
|
|
947
|
+
}
|
|
874
948
|
function normalizeText(value) {
|
|
875
949
|
return typeof value === "string" ? value.replace(/\s+/g, " ").trim() : "";
|
|
876
950
|
}
|
|
@@ -880,132 +954,141 @@ function isObject$2(value) {
|
|
|
880
954
|
//#endregion
|
|
881
955
|
//#region src/cli.ts
|
|
882
956
|
async function runCli(argv, options) {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
}
|
|
888
|
-
const settings = createSeiAuthSettings(options.config, {
|
|
889
|
-
baseUrl: toUndefined(readOption(argv, "--base-url")),
|
|
890
|
-
token: toUndefined(readOption(argv, "--token")),
|
|
891
|
-
aiKey: toUndefined(readOption(argv, "--ai-key")),
|
|
892
|
-
role: toUndefined(readOption(argv, "--role") ?? readOption(argv, "--profile")),
|
|
893
|
-
account: toUndefined(readOption(argv, "--account")),
|
|
894
|
-
accountProject: toUndefined(readOption(argv, "--account-project")),
|
|
895
|
-
accountCheckcode: toUndefined(readOption(argv, "--account-checkcode")),
|
|
896
|
-
timeoutMs: readOptionNumber(argv, "--timeout")
|
|
897
|
-
});
|
|
898
|
-
const client = createCliSeiClient(settings, options.logger, options.fetchImpl ?? fetch);
|
|
899
|
-
switch (command) {
|
|
900
|
-
case "api-docs": {
|
|
901
|
-
const summary = buildOpenApiDocSummary(await client.readApiDocs(), readOption(argv, "--keyword") ?? void 0);
|
|
902
|
-
const output = normalizeFormat(readOption(argv, "--format")) === "json" ? `${JSON.stringify(summary, null, 2)}\n` : renderOpenApiMarkdown(summary);
|
|
903
|
-
await writeOutput(readOption(argv, "--output"), output);
|
|
904
|
-
return;
|
|
905
|
-
}
|
|
906
|
-
case "call": {
|
|
907
|
-
const [method, path] = positionalAfter(argv, command, 2);
|
|
908
|
-
if (!method || !path) throw new SeiMcpError("call 需要 METHOD 和 PATH", "INVALID_PARAMS");
|
|
909
|
-
const payload = await loadPayload(argv, {});
|
|
910
|
-
printJson(await client.call(method, path, payload, {
|
|
911
|
-
allowFailure: hasFlag(argv, "--allow-failure"),
|
|
912
|
-
allowEmpty: hasFlag(argv, "--allow-empty"),
|
|
913
|
-
skipAuth: hasFlag(argv, "--no-token")
|
|
914
|
-
}));
|
|
915
|
-
return;
|
|
916
|
-
}
|
|
917
|
-
case "query": {
|
|
918
|
-
const payload = await loadPayload(argv, {});
|
|
919
|
-
printJson(await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
920
|
-
return;
|
|
921
|
-
}
|
|
922
|
-
case "save": {
|
|
923
|
-
const payload = await loadPayload(argv, {});
|
|
924
|
-
printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
925
|
-
return;
|
|
926
|
-
}
|
|
927
|
-
case "query-sql":
|
|
928
|
-
case "sql": {
|
|
929
|
-
const [sql] = positionalAfter(argv, command, 1);
|
|
930
|
-
if (!sql) throw new SeiMcpError("query-sql 需要 SQL 参数", "INVALID_PARAMS");
|
|
931
|
-
printJson(await client.querySql(sql, readOptionNumber(argv, "--page"), readOptionNumber(argv, "--size"), { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
case "dict-list": {
|
|
935
|
-
const payload = buildDictQueryPayload(readOption(argv, "--type") ?? void 0, readOption(argv, "--keyword") ?? void 0, readOptionNumber(argv, "--size") ?? 1e3);
|
|
936
|
-
const response = await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") });
|
|
937
|
-
if (!hasFlag(argv, "--flat") && isObject$1(response) && isObject$1(response.data) && Array.isArray(response.data.rows)) response.data.tree = buildDictTree(response.data.rows);
|
|
938
|
-
printJson(response);
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
941
|
-
case "dict-add-category": {
|
|
942
|
-
const [typeCode, name] = positionalAfter(argv, command, 2);
|
|
943
|
-
if (!typeCode || !name) throw new SeiMcpError("dict-add-category 需要 type_code 和 name", "INVALID_PARAMS");
|
|
944
|
-
const payload = buildDictSavePayload(buildDictCategoryRow({
|
|
945
|
-
body: readOption(argv, "--body") ?? void 0,
|
|
946
|
-
code: readOption(argv, "--code") ?? void 0,
|
|
947
|
-
ctype: readOption(argv, "--ctype") ?? void 0,
|
|
948
|
-
dictFlag: readOptionInteger(argv, "--dict-flag"),
|
|
949
|
-
ename: readOption(argv, "--ename") ?? void 0,
|
|
950
|
-
memo: readOption(argv, "--memo") ?? void 0,
|
|
951
|
-
name,
|
|
952
|
-
sort: readOptionInteger(argv, "--sort"),
|
|
953
|
-
sysid: readOption(argv, "--sysid") ?? void 0,
|
|
954
|
-
typeCode,
|
|
955
|
-
uuid: readOption(argv, "--uuid") ?? void 0
|
|
956
|
-
}));
|
|
957
|
-
printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
958
|
-
return;
|
|
959
|
-
}
|
|
960
|
-
case "dict-add-item": {
|
|
961
|
-
const [typeCode, code, name] = positionalAfter(argv, command, 3);
|
|
962
|
-
if (!typeCode || !code || !name) throw new SeiMcpError("dict-add-item 需要 type_code、code 和 name", "INVALID_PARAMS");
|
|
963
|
-
const payload = buildDictSavePayload(buildDictItemRow({
|
|
964
|
-
body: readOption(argv, "--body") ?? void 0,
|
|
965
|
-
code,
|
|
966
|
-
ctype: readOption(argv, "--ctype") ?? void 0,
|
|
967
|
-
dictFlag: readOptionInteger(argv, "--dict-flag"),
|
|
968
|
-
ename: readOption(argv, "--ename") ?? void 0,
|
|
969
|
-
memo: readOption(argv, "--memo") ?? void 0,
|
|
970
|
-
name,
|
|
971
|
-
parent: readOption(argv, "--parent") ?? void 0,
|
|
972
|
-
sort: readOptionInteger(argv, "--sort"),
|
|
973
|
-
sysid: readOption(argv, "--sysid") ?? void 0,
|
|
974
|
-
typeCode,
|
|
975
|
-
uuid: readOption(argv, "--uuid") ?? void 0
|
|
976
|
-
}));
|
|
977
|
-
printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
957
|
+
try {
|
|
958
|
+
const command = firstPositional(argv);
|
|
959
|
+
if (!command || isHelpFlag(command) || hasFlag(argv, "--help") || hasFlag(argv, "-h")) {
|
|
960
|
+
process.stdout.write(`${renderCliHelp()}\n`);
|
|
978
961
|
return;
|
|
979
962
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
963
|
+
const settings = createSeiAuthSettings(options.config, {
|
|
964
|
+
baseUrl: toUndefined(readOption(argv, "--base-url")),
|
|
965
|
+
token: toUndefined(readOption(argv, "--token")),
|
|
966
|
+
aiKey: toUndefined(readOption(argv, "--ai-key")),
|
|
967
|
+
role: toUndefined(readOption(argv, "--role") ?? readOption(argv, "--profile")),
|
|
968
|
+
account: toUndefined(readOption(argv, "--account")),
|
|
969
|
+
accountProject: toUndefined(readOption(argv, "--account-project")),
|
|
970
|
+
accountCheckcode: toUndefined(readOption(argv, "--account-checkcode")),
|
|
971
|
+
timeoutMs: readOptionNumber(argv, "--timeout")
|
|
972
|
+
});
|
|
973
|
+
const client = createCliSeiClient(settings, options.logger, options.fetchImpl ?? fetch);
|
|
974
|
+
switch (command) {
|
|
975
|
+
case "api-docs": {
|
|
976
|
+
const summary = buildOpenApiDocSummary(await client.readApiDocs(), readOption(argv, "--keyword") ?? void 0);
|
|
977
|
+
const output = normalizeFormat(readOption(argv, "--format")) === "json" ? `${JSON.stringify(summary, null, 2)}\n` : renderOpenApiMarkdown(summary);
|
|
978
|
+
await writeOutput(readOption(argv, "--output"), output);
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
case "call": {
|
|
982
|
+
const [method, path] = positionalAfter(argv, command, 2);
|
|
983
|
+
if (!method || !path) throw new SeiMcpError("call 需要 METHOD 和 PATH", "INVALID_PARAMS");
|
|
984
|
+
const payload = await loadPayload(argv, {});
|
|
985
|
+
printJson(await client.call(method, path, payload, {
|
|
986
|
+
allowFailure: hasFlag(argv, "--allow-failure"),
|
|
987
|
+
allowEmpty: hasFlag(argv, "--allow-empty"),
|
|
988
|
+
skipAuth: hasFlag(argv, "--no-token")
|
|
989
|
+
}));
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
case "query": {
|
|
993
|
+
const payload = await loadPayload(argv, {});
|
|
994
|
+
printJson(await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
case "save": {
|
|
998
|
+
const payload = await loadPayload(argv, {});
|
|
999
|
+
printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
case "query-sql":
|
|
1003
|
+
case "sql": {
|
|
1004
|
+
const [sql] = positionalAfter(argv, command, 1);
|
|
1005
|
+
if (!sql) throw new SeiMcpError("query-sql 需要 SQL 参数", "INVALID_PARAMS");
|
|
1006
|
+
printJson(await client.querySql(sql, readOptionNumber(argv, "--page"), readOptionNumber(argv, "--size"), { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
case "dict-list": {
|
|
1010
|
+
const payload = buildDictQueryPayload(readOption(argv, "--type") ?? void 0, readOption(argv, "--keyword") ?? void 0, readOptionNumber(argv, "--size") ?? 1e3);
|
|
1011
|
+
const response = await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") });
|
|
1012
|
+
if (!hasFlag(argv, "--flat") && isObject$1(response) && isObject$1(response.data) && Array.isArray(response.data.rows)) response.data.tree = buildDictTree(response.data.rows);
|
|
1013
|
+
printJson(response);
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
case "dict-add-category": {
|
|
1017
|
+
const [typeCode, name] = positionalAfter(argv, command, 2);
|
|
1018
|
+
if (!typeCode || !name) throw new SeiMcpError("dict-add-category 需要 type_code 和 name", "INVALID_PARAMS");
|
|
1019
|
+
const payload = buildDictSavePayload(buildDictCategoryRow({
|
|
1020
|
+
body: readOption(argv, "--body") ?? void 0,
|
|
1021
|
+
code: readOption(argv, "--code") ?? void 0,
|
|
1022
|
+
ctype: readOption(argv, "--ctype") ?? void 0,
|
|
1023
|
+
dictFlag: readOptionInteger(argv, "--dict-flag"),
|
|
1024
|
+
ename: readOption(argv, "--ename") ?? void 0,
|
|
1025
|
+
memo: readOption(argv, "--memo") ?? void 0,
|
|
1026
|
+
name,
|
|
1027
|
+
sort: readOptionInteger(argv, "--sort"),
|
|
1028
|
+
sysid: readOption(argv, "--sysid") ?? void 0,
|
|
1029
|
+
typeCode,
|
|
1030
|
+
uuid: readOption(argv, "--uuid") ?? void 0
|
|
1031
|
+
}));
|
|
1032
|
+
printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
case "dict-add-item": {
|
|
1036
|
+
const [typeCode, code, name] = positionalAfter(argv, command, 3);
|
|
1037
|
+
if (!typeCode || !code || !name) throw new SeiMcpError("dict-add-item 需要 type_code、code 和 name", "INVALID_PARAMS");
|
|
1038
|
+
const payload = buildDictSavePayload(buildDictItemRow({
|
|
1039
|
+
body: readOption(argv, "--body") ?? void 0,
|
|
1040
|
+
code,
|
|
1041
|
+
ctype: readOption(argv, "--ctype") ?? void 0,
|
|
1042
|
+
dictFlag: readOptionInteger(argv, "--dict-flag"),
|
|
1043
|
+
ename: readOption(argv, "--ename") ?? void 0,
|
|
1044
|
+
memo: readOption(argv, "--memo") ?? void 0,
|
|
1045
|
+
name,
|
|
1046
|
+
parent: readOption(argv, "--parent") ?? void 0,
|
|
1047
|
+
sort: readOptionInteger(argv, "--sort"),
|
|
1048
|
+
sysid: readOption(argv, "--sysid") ?? void 0,
|
|
1049
|
+
typeCode,
|
|
1050
|
+
uuid: readOption(argv, "--uuid") ?? void 0
|
|
1051
|
+
}));
|
|
1052
|
+
printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
case "init-base-data": {
|
|
1056
|
+
const sqlFile = resolveBaseSeedSqlPath(readOption(argv, "--sql-file"));
|
|
1057
|
+
const statements = splitSqlStatements(removeSqlCommentLines(renderBaseSeedSql(await readFile(sqlFile, "utf8"), buildBaseSeedSqlVars({
|
|
1058
|
+
adminName: readOption(argv, "--admin-name") ?? void 0,
|
|
1059
|
+
adminPassword: readOption(argv, "--admin-password") ?? void 0,
|
|
1060
|
+
adminRole: readOption(argv, "--admin-role") ?? void 0,
|
|
1061
|
+
adminRoleName: readOption(argv, "--admin-role-name") ?? void 0,
|
|
1062
|
+
adminUid: readOption(argv, "--admin-uid") ?? void 0,
|
|
1063
|
+
resetAdminPassword: hasFlag(argv, "--reset-admin-password"),
|
|
1064
|
+
sysid: readOption(argv, "--sysid") ?? void 0
|
|
1065
|
+
}))));
|
|
1066
|
+
if (statements.length === 0) throw new SeiMcpError("初始化 SQL 文件没有可执行语句", "INVALID_PARAMS");
|
|
1067
|
+
const executed = await client.executeDdlStatements(statements, { allowFailure: hasFlag(argv, "--allow-failure") });
|
|
1068
|
+
await client.call("POST", "/api/sei/data/removeAllCache", {}, { allowFailure: false });
|
|
1069
|
+
printJson({
|
|
1070
|
+
success: true,
|
|
1071
|
+
code: 1,
|
|
1072
|
+
message: "基础源数据初始化完成",
|
|
1073
|
+
data: {
|
|
1074
|
+
executed: executed.length,
|
|
1075
|
+
sqlFile,
|
|
1076
|
+
admin: readOption(argv, "--admin-uid") ?? "admin",
|
|
1077
|
+
role: readOption(argv, "--admin-role") ?? "admin",
|
|
1078
|
+
baseUrl: settings.baseUrl
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
default: throw new SeiMcpError(`未知 CLI 子命令:${command}`, "INVALID_PARAMS");
|
|
1007
1084
|
}
|
|
1008
|
-
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
options.logger.error("CLI command failed", {
|
|
1087
|
+
argv,
|
|
1088
|
+
error: toErrorLogMeta(error)
|
|
1089
|
+
});
|
|
1090
|
+
process.stderr.write(`${formatCliError(error)}\n`);
|
|
1091
|
+
throw error;
|
|
1009
1092
|
}
|
|
1010
1093
|
}
|
|
1011
1094
|
function renderCliHelp() {
|
|
@@ -1238,6 +1321,25 @@ function toTargetMap(value) {
|
|
|
1238
1321
|
return Object.fromEntries(Object.entries(value).filter(([, item]) => isObject$4(item)));
|
|
1239
1322
|
}
|
|
1240
1323
|
//#endregion
|
|
1324
|
+
//#region src/env.ts
|
|
1325
|
+
var DEFAULT_ENV_FILES = [".env.local", ".env"];
|
|
1326
|
+
async function loadWorkingDirEnv(cwd = process.cwd(), env = process.env) {
|
|
1327
|
+
for (const fileName of DEFAULT_ENV_FILES) await loadEnvFile(resolve(cwd, fileName), env);
|
|
1328
|
+
}
|
|
1329
|
+
async function loadEnvFile(path, env) {
|
|
1330
|
+
try {
|
|
1331
|
+
const parsed = parseEnv(await readFile(path, "utf8"));
|
|
1332
|
+
for (const [key, value] of Object.entries(parsed)) if (env[key] === void 0) env[key] = value;
|
|
1333
|
+
} catch (error) {
|
|
1334
|
+
if (isMissingFile(error)) return;
|
|
1335
|
+
throw error;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
function isMissingFile(error) {
|
|
1339
|
+
if (!error || typeof error !== "object") return false;
|
|
1340
|
+
return error.code === "ENOENT";
|
|
1341
|
+
}
|
|
1342
|
+
//#endregion
|
|
1241
1343
|
//#region src/logger.ts
|
|
1242
1344
|
var LEVEL_PRIORITY = {
|
|
1243
1345
|
error: 0,
|
|
@@ -4881,7 +4983,7 @@ var $ZodAsyncError = class extends Error {
|
|
|
4881
4983
|
}
|
|
4882
4984
|
};
|
|
4883
4985
|
var globalConfig = {};
|
|
4884
|
-
function config
|
|
4986
|
+
function config(newConfig) {
|
|
4885
4987
|
if (newConfig) Object.assign(globalConfig, newConfig);
|
|
4886
4988
|
return globalConfig;
|
|
4887
4989
|
}
|
|
@@ -5224,7 +5326,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
|
|
|
5224
5326
|
}, ctx);
|
|
5225
5327
|
if (result instanceof Promise) throw new $ZodAsyncError();
|
|
5226
5328
|
if (result.issues.length) {
|
|
5227
|
-
const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config
|
|
5329
|
+
const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
|
|
5228
5330
|
captureStackTrace(e, _params?.callee);
|
|
5229
5331
|
throw e;
|
|
5230
5332
|
}
|
|
@@ -5239,7 +5341,7 @@ var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
|
|
|
5239
5341
|
}, ctx);
|
|
5240
5342
|
if (result instanceof Promise) result = await result;
|
|
5241
5343
|
if (result.issues.length) {
|
|
5242
|
-
const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config
|
|
5344
|
+
const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
|
|
5243
5345
|
captureStackTrace(e, params?.callee);
|
|
5244
5346
|
throw e;
|
|
5245
5347
|
}
|
|
@@ -5258,7 +5360,7 @@ var _safeParse = (_Err) => (schema, value, _ctx) => {
|
|
|
5258
5360
|
if (result instanceof Promise) throw new $ZodAsyncError();
|
|
5259
5361
|
return result.issues.length ? {
|
|
5260
5362
|
success: false,
|
|
5261
|
-
error: new (_Err ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config
|
|
5363
|
+
error: new (_Err ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())))
|
|
5262
5364
|
} : {
|
|
5263
5365
|
success: true,
|
|
5264
5366
|
data: result.value
|
|
@@ -5274,7 +5376,7 @@ var _safeParseAsync = (_Err) => async (schema, value, _ctx) => {
|
|
|
5274
5376
|
if (result instanceof Promise) result = await result;
|
|
5275
5377
|
return result.issues.length ? {
|
|
5276
5378
|
success: false,
|
|
5277
|
-
error: new _Err(result.issues.map((iss) => finalizeIssue(iss, ctx, config
|
|
5379
|
+
error: new _Err(result.issues.map((iss) => finalizeIssue(iss, ctx, config())))
|
|
5278
5380
|
} : {
|
|
5279
5381
|
success: true,
|
|
5280
5382
|
data: result.value
|
|
@@ -6342,7 +6444,7 @@ function handleUnionResults(results, final, inst, ctx) {
|
|
|
6342
6444
|
code: "invalid_union",
|
|
6343
6445
|
input: final.value,
|
|
6344
6446
|
inst,
|
|
6345
|
-
errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config
|
|
6447
|
+
errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config())))
|
|
6346
6448
|
});
|
|
6347
6449
|
return final;
|
|
6348
6450
|
}
|
|
@@ -6571,7 +6673,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
|
6571
6673
|
payload.issues.push({
|
|
6572
6674
|
origin: "record",
|
|
6573
6675
|
code: "invalid_key",
|
|
6574
|
-
issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config
|
|
6676
|
+
issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
|
|
6575
6677
|
input: key,
|
|
6576
6678
|
path: [key],
|
|
6577
6679
|
inst
|
|
@@ -6739,7 +6841,7 @@ var $ZodCatch = /* @__PURE__ */ $constructor("$ZodCatch", (inst, def) => {
|
|
|
6739
6841
|
if (result.issues.length) {
|
|
6740
6842
|
payload.value = def.catchValue({
|
|
6741
6843
|
...payload,
|
|
6742
|
-
error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config
|
|
6844
|
+
error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config())) },
|
|
6743
6845
|
input: payload.value
|
|
6744
6846
|
});
|
|
6745
6847
|
payload.issues = [];
|
|
@@ -6750,7 +6852,7 @@ var $ZodCatch = /* @__PURE__ */ $constructor("$ZodCatch", (inst, def) => {
|
|
|
6750
6852
|
if (result.issues.length) {
|
|
6751
6853
|
payload.value = def.catchValue({
|
|
6752
6854
|
...payload,
|
|
6753
|
-
error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config
|
|
6855
|
+
error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config())) },
|
|
6754
6856
|
input: payload.value
|
|
6755
6857
|
});
|
|
6756
6858
|
payload.issues = [];
|
|
@@ -22221,6 +22323,7 @@ function toTrimmedString(value) {
|
|
|
22221
22323
|
}
|
|
22222
22324
|
//#endregion
|
|
22223
22325
|
//#region src/index.ts
|
|
22326
|
+
await loadWorkingDirEnv();
|
|
22224
22327
|
var logger = createLogger(process.env.SEI_MCP_LOG_LEVEL ?? "info");
|
|
22225
22328
|
var argv = process.argv.slice(2);
|
|
22226
22329
|
var runtime = parseRuntimeOptions(argv, process.env);
|
|
@@ -22228,23 +22331,31 @@ if (runtime.command === "help") {
|
|
|
22228
22331
|
process.stdout.write(`${renderHelpText()}\n`);
|
|
22229
22332
|
process.exit(0);
|
|
22230
22333
|
}
|
|
22231
|
-
|
|
22232
|
-
|
|
22233
|
-
|
|
22334
|
+
try {
|
|
22335
|
+
const config = await loadConfig(argv, process.env);
|
|
22336
|
+
if (runtime.command === "cli") {
|
|
22337
|
+
await runCli(runtime.cliArgs, {
|
|
22338
|
+
config,
|
|
22339
|
+
logger
|
|
22340
|
+
});
|
|
22341
|
+
process.exit(0);
|
|
22342
|
+
}
|
|
22343
|
+
const client = createSeiClient(config, logger);
|
|
22344
|
+
if (runtime.transport === "streamable-http") await startHttpServer(() => createMcpServer({
|
|
22234
22345
|
config,
|
|
22346
|
+
client,
|
|
22235
22347
|
logger
|
|
22236
|
-
});
|
|
22237
|
-
|
|
22348
|
+
}), runtime, logger);
|
|
22349
|
+
else await startStdioServer(createMcpServer({
|
|
22350
|
+
config,
|
|
22351
|
+
client,
|
|
22352
|
+
logger
|
|
22353
|
+
}));
|
|
22354
|
+
} catch (error) {
|
|
22355
|
+
if (!isSeiMcpError(error) || runtime.command !== "cli") {
|
|
22356
|
+
logger.error("sei-ai startup failed", toErrorLogMeta(error));
|
|
22357
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
22358
|
+
}
|
|
22359
|
+
process.exit(1);
|
|
22238
22360
|
}
|
|
22239
|
-
var client = createSeiClient(config, logger);
|
|
22240
|
-
if (runtime.transport === "streamable-http") await startHttpServer(() => createMcpServer({
|
|
22241
|
-
config,
|
|
22242
|
-
client,
|
|
22243
|
-
logger
|
|
22244
|
-
}), runtime, logger);
|
|
22245
|
-
else await startStdioServer(createMcpServer({
|
|
22246
|
-
config,
|
|
22247
|
-
client,
|
|
22248
|
-
logger
|
|
22249
|
-
}));
|
|
22250
22361
|
//#endregion
|