@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 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: toText(options.token ?? config.auth.token ?? process.env.SEI_TOKEN),
363
- aiKey: toText(options.aiKey ?? config.auth.aiKey ?? process.env.SEI_AI_LOGIN_KEY ?? "dev-ai-secret"),
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: toText(options.account ?? config.auth.account ?? process.env.SEI_AI_LOGIN_ACCOUNT),
366
- accountProject: toText(options.accountProject ?? process.env.SEI_AI_LOGIN_ACCOUNT_PROJECT ?? "sys"),
367
- accountCheckcode: toText(options.accountCheckcode ?? process.env.SEI_AI_LOGIN_ACCOUNT_CHECKCODE ?? "abcd") || "abcd",
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
- const primary = toText(role);
715
- if (primary) return primary.toLowerCase();
716
- return toText(fallback ?? process.env.SEI_AI_LOGIN_ROLE).toLowerCase();
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 Error("OpenAPI 文档不是 JSON 对象");
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 Error("OpenAPI 文档缺少 paths 对象");
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
- const command = firstPositional(argv);
884
- if (!command || isHelpFlag(command) || hasFlag(argv, "--help") || hasFlag(argv, "-h")) {
885
- process.stdout.write(`${renderCliHelp()}\n`);
886
- return;
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
- case "init-base-data": {
981
- const sqlFile = resolveBaseSeedSqlPath(readOption(argv, "--sql-file"));
982
- const statements = splitSqlStatements(removeSqlCommentLines(renderBaseSeedSql(await readFile(sqlFile, "utf8"), buildBaseSeedSqlVars({
983
- adminName: readOption(argv, "--admin-name") ?? void 0,
984
- adminPassword: readOption(argv, "--admin-password") ?? void 0,
985
- adminRole: readOption(argv, "--admin-role") ?? void 0,
986
- adminRoleName: readOption(argv, "--admin-role-name") ?? void 0,
987
- adminUid: readOption(argv, "--admin-uid") ?? void 0,
988
- resetAdminPassword: hasFlag(argv, "--reset-admin-password"),
989
- sysid: readOption(argv, "--sysid") ?? void 0
990
- }))));
991
- if (statements.length === 0) throw new SeiMcpError("初始化 SQL 文件没有可执行语句", "INVALID_PARAMS");
992
- const executed = await client.executeDdlStatements(statements, { allowFailure: hasFlag(argv, "--allow-failure") });
993
- await client.call("POST", "/api/sei/data/removeAllCache", {}, { allowFailure: false });
994
- printJson({
995
- success: true,
996
- code: 1,
997
- message: "基础源数据初始化完成",
998
- data: {
999
- executed: executed.length,
1000
- sqlFile,
1001
- admin: readOption(argv, "--admin-uid") ?? "admin",
1002
- role: readOption(argv, "--admin-role") ?? "admin",
1003
- baseUrl: settings.baseUrl
1004
- }
1005
- });
1006
- return;
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
- default: throw new SeiMcpError(`未知 CLI 子命令:${command}`, "INVALID_PARAMS");
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$1(newConfig) {
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$1())));
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$1())));
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$1())))
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$1())))
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$1())))
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$1())),
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$1())) },
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$1())) },
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
- var config = await loadConfig(argv, process.env);
22232
- if (runtime.command === "cli") {
22233
- await runCli(runtime.cliArgs, {
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
- process.exit(0);
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ty_krystal/sei-ai",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "SEI MCP server and developer CLI for query, save, query-sql, and api-docs workflows",
5
5
  "keywords": [
6
6
  "sei",