@ty_krystal/sei-ai 0.1.2 → 0.1.3

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,10 @@ 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");
735
772
  const info = isObject$2(openapiDoc.info) ? openapiDoc.info : {};
736
773
  const rawPaths = openapiDoc.paths;
737
- if (!isObject$2(rawPaths)) throw new Error("OpenAPI 文档缺少 paths 对象");
774
+ if (!isObject$2(rawPaths)) throw new SeiMcpError("OpenAPI 文档缺少 paths 对象", "INVALID_REQUEST");
738
775
  const normalizedKeyword = normalizeText(keyword).toLowerCase();
739
776
  const items = [];
740
777
  for (const [path, rawPathItem] of Object.entries(rawPaths)) {
@@ -880,132 +917,141 @@ function isObject$2(value) {
880
917
  //#endregion
881
918
  //#region src/cli.ts
882
919
  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") }));
920
+ try {
921
+ const command = firstPositional(argv);
922
+ if (!command || isHelpFlag(command) || hasFlag(argv, "--help") || hasFlag(argv, "-h")) {
923
+ process.stdout.write(`${renderCliHelp()}\n`);
978
924
  return;
979
925
  }
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;
926
+ const settings = createSeiAuthSettings(options.config, {
927
+ baseUrl: toUndefined(readOption(argv, "--base-url")),
928
+ token: toUndefined(readOption(argv, "--token")),
929
+ aiKey: toUndefined(readOption(argv, "--ai-key")),
930
+ role: toUndefined(readOption(argv, "--role") ?? readOption(argv, "--profile")),
931
+ account: toUndefined(readOption(argv, "--account")),
932
+ accountProject: toUndefined(readOption(argv, "--account-project")),
933
+ accountCheckcode: toUndefined(readOption(argv, "--account-checkcode")),
934
+ timeoutMs: readOptionNumber(argv, "--timeout")
935
+ });
936
+ const client = createCliSeiClient(settings, options.logger, options.fetchImpl ?? fetch);
937
+ switch (command) {
938
+ case "api-docs": {
939
+ const summary = buildOpenApiDocSummary(await client.readApiDocs(), readOption(argv, "--keyword") ?? void 0);
940
+ const output = normalizeFormat(readOption(argv, "--format")) === "json" ? `${JSON.stringify(summary, null, 2)}\n` : renderOpenApiMarkdown(summary);
941
+ await writeOutput(readOption(argv, "--output"), output);
942
+ return;
943
+ }
944
+ case "call": {
945
+ const [method, path] = positionalAfter(argv, command, 2);
946
+ if (!method || !path) throw new SeiMcpError("call 需要 METHOD 和 PATH", "INVALID_PARAMS");
947
+ const payload = await loadPayload(argv, {});
948
+ printJson(await client.call(method, path, payload, {
949
+ allowFailure: hasFlag(argv, "--allow-failure"),
950
+ allowEmpty: hasFlag(argv, "--allow-empty"),
951
+ skipAuth: hasFlag(argv, "--no-token")
952
+ }));
953
+ return;
954
+ }
955
+ case "query": {
956
+ const payload = await loadPayload(argv, {});
957
+ printJson(await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
958
+ return;
959
+ }
960
+ case "save": {
961
+ const payload = await loadPayload(argv, {});
962
+ printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
963
+ return;
964
+ }
965
+ case "query-sql":
966
+ case "sql": {
967
+ const [sql] = positionalAfter(argv, command, 1);
968
+ if (!sql) throw new SeiMcpError("query-sql 需要 SQL 参数", "INVALID_PARAMS");
969
+ printJson(await client.querySql(sql, readOptionNumber(argv, "--page"), readOptionNumber(argv, "--size"), { allowFailure: hasFlag(argv, "--allow-failure") }));
970
+ return;
971
+ }
972
+ case "dict-list": {
973
+ const payload = buildDictQueryPayload(readOption(argv, "--type") ?? void 0, readOption(argv, "--keyword") ?? void 0, readOptionNumber(argv, "--size") ?? 1e3);
974
+ const response = await client.query(payload, { allowFailure: hasFlag(argv, "--allow-failure") });
975
+ if (!hasFlag(argv, "--flat") && isObject$1(response) && isObject$1(response.data) && Array.isArray(response.data.rows)) response.data.tree = buildDictTree(response.data.rows);
976
+ printJson(response);
977
+ return;
978
+ }
979
+ case "dict-add-category": {
980
+ const [typeCode, name] = positionalAfter(argv, command, 2);
981
+ if (!typeCode || !name) throw new SeiMcpError("dict-add-category 需要 type_code 和 name", "INVALID_PARAMS");
982
+ const payload = buildDictSavePayload(buildDictCategoryRow({
983
+ body: readOption(argv, "--body") ?? void 0,
984
+ code: readOption(argv, "--code") ?? void 0,
985
+ ctype: readOption(argv, "--ctype") ?? void 0,
986
+ dictFlag: readOptionInteger(argv, "--dict-flag"),
987
+ ename: readOption(argv, "--ename") ?? void 0,
988
+ memo: readOption(argv, "--memo") ?? void 0,
989
+ name,
990
+ sort: readOptionInteger(argv, "--sort"),
991
+ sysid: readOption(argv, "--sysid") ?? void 0,
992
+ typeCode,
993
+ uuid: readOption(argv, "--uuid") ?? void 0
994
+ }));
995
+ printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
996
+ return;
997
+ }
998
+ case "dict-add-item": {
999
+ const [typeCode, code, name] = positionalAfter(argv, command, 3);
1000
+ if (!typeCode || !code || !name) throw new SeiMcpError("dict-add-item 需要 type_code、code 和 name", "INVALID_PARAMS");
1001
+ const payload = buildDictSavePayload(buildDictItemRow({
1002
+ body: readOption(argv, "--body") ?? void 0,
1003
+ code,
1004
+ ctype: readOption(argv, "--ctype") ?? void 0,
1005
+ dictFlag: readOptionInteger(argv, "--dict-flag"),
1006
+ ename: readOption(argv, "--ename") ?? void 0,
1007
+ memo: readOption(argv, "--memo") ?? void 0,
1008
+ name,
1009
+ parent: readOption(argv, "--parent") ?? void 0,
1010
+ sort: readOptionInteger(argv, "--sort"),
1011
+ sysid: readOption(argv, "--sysid") ?? void 0,
1012
+ typeCode,
1013
+ uuid: readOption(argv, "--uuid") ?? void 0
1014
+ }));
1015
+ printJson(await client.save(payload, { allowFailure: hasFlag(argv, "--allow-failure") }));
1016
+ return;
1017
+ }
1018
+ case "init-base-data": {
1019
+ const sqlFile = resolveBaseSeedSqlPath(readOption(argv, "--sql-file"));
1020
+ const statements = splitSqlStatements(removeSqlCommentLines(renderBaseSeedSql(await readFile(sqlFile, "utf8"), buildBaseSeedSqlVars({
1021
+ adminName: readOption(argv, "--admin-name") ?? void 0,
1022
+ adminPassword: readOption(argv, "--admin-password") ?? void 0,
1023
+ adminRole: readOption(argv, "--admin-role") ?? void 0,
1024
+ adminRoleName: readOption(argv, "--admin-role-name") ?? void 0,
1025
+ adminUid: readOption(argv, "--admin-uid") ?? void 0,
1026
+ resetAdminPassword: hasFlag(argv, "--reset-admin-password"),
1027
+ sysid: readOption(argv, "--sysid") ?? void 0
1028
+ }))));
1029
+ if (statements.length === 0) throw new SeiMcpError("初始化 SQL 文件没有可执行语句", "INVALID_PARAMS");
1030
+ const executed = await client.executeDdlStatements(statements, { allowFailure: hasFlag(argv, "--allow-failure") });
1031
+ await client.call("POST", "/api/sei/data/removeAllCache", {}, { allowFailure: false });
1032
+ printJson({
1033
+ success: true,
1034
+ code: 1,
1035
+ message: "基础源数据初始化完成",
1036
+ data: {
1037
+ executed: executed.length,
1038
+ sqlFile,
1039
+ admin: readOption(argv, "--admin-uid") ?? "admin",
1040
+ role: readOption(argv, "--admin-role") ?? "admin",
1041
+ baseUrl: settings.baseUrl
1042
+ }
1043
+ });
1044
+ return;
1045
+ }
1046
+ default: throw new SeiMcpError(`未知 CLI 子命令:${command}`, "INVALID_PARAMS");
1007
1047
  }
1008
- default: throw new SeiMcpError(`未知 CLI 子命令:${command}`, "INVALID_PARAMS");
1048
+ } catch (error) {
1049
+ options.logger.error("CLI command failed", {
1050
+ argv,
1051
+ error: toErrorLogMeta(error)
1052
+ });
1053
+ process.stderr.write(`${formatCliError(error)}\n`);
1054
+ throw error;
1009
1055
  }
1010
1056
  }
1011
1057
  function renderCliHelp() {
@@ -1238,6 +1284,25 @@ function toTargetMap(value) {
1238
1284
  return Object.fromEntries(Object.entries(value).filter(([, item]) => isObject$4(item)));
1239
1285
  }
1240
1286
  //#endregion
1287
+ //#region src/env.ts
1288
+ var DEFAULT_ENV_FILES = [".env.local", ".env"];
1289
+ async function loadWorkingDirEnv(cwd = process.cwd(), env = process.env) {
1290
+ for (const fileName of DEFAULT_ENV_FILES) await loadEnvFile(resolve(cwd, fileName), env);
1291
+ }
1292
+ async function loadEnvFile(path, env) {
1293
+ try {
1294
+ const parsed = parseEnv(await readFile(path, "utf8"));
1295
+ for (const [key, value] of Object.entries(parsed)) if (env[key] === void 0) env[key] = value;
1296
+ } catch (error) {
1297
+ if (isMissingFile(error)) return;
1298
+ throw error;
1299
+ }
1300
+ }
1301
+ function isMissingFile(error) {
1302
+ if (!error || typeof error !== "object") return false;
1303
+ return error.code === "ENOENT";
1304
+ }
1305
+ //#endregion
1241
1306
  //#region src/logger.ts
1242
1307
  var LEVEL_PRIORITY = {
1243
1308
  error: 0,
@@ -4881,7 +4946,7 @@ var $ZodAsyncError = class extends Error {
4881
4946
  }
4882
4947
  };
4883
4948
  var globalConfig = {};
4884
- function config$1(newConfig) {
4949
+ function config(newConfig) {
4885
4950
  if (newConfig) Object.assign(globalConfig, newConfig);
4886
4951
  return globalConfig;
4887
4952
  }
@@ -5224,7 +5289,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
5224
5289
  }, ctx);
5225
5290
  if (result instanceof Promise) throw new $ZodAsyncError();
5226
5291
  if (result.issues.length) {
5227
- const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())));
5292
+ const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
5228
5293
  captureStackTrace(e, _params?.callee);
5229
5294
  throw e;
5230
5295
  }
@@ -5239,7 +5304,7 @@ var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
5239
5304
  }, ctx);
5240
5305
  if (result instanceof Promise) result = await result;
5241
5306
  if (result.issues.length) {
5242
- const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())));
5307
+ const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())));
5243
5308
  captureStackTrace(e, params?.callee);
5244
5309
  throw e;
5245
5310
  }
@@ -5258,7 +5323,7 @@ var _safeParse = (_Err) => (schema, value, _ctx) => {
5258
5323
  if (result instanceof Promise) throw new $ZodAsyncError();
5259
5324
  return result.issues.length ? {
5260
5325
  success: false,
5261
- error: new (_Err ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())))
5326
+ error: new (_Err ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config())))
5262
5327
  } : {
5263
5328
  success: true,
5264
5329
  data: result.value
@@ -5274,7 +5339,7 @@ var _safeParseAsync = (_Err) => async (schema, value, _ctx) => {
5274
5339
  if (result instanceof Promise) result = await result;
5275
5340
  return result.issues.length ? {
5276
5341
  success: false,
5277
- error: new _Err(result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())))
5342
+ error: new _Err(result.issues.map((iss) => finalizeIssue(iss, ctx, config())))
5278
5343
  } : {
5279
5344
  success: true,
5280
5345
  data: result.value
@@ -6342,7 +6407,7 @@ function handleUnionResults(results, final, inst, ctx) {
6342
6407
  code: "invalid_union",
6343
6408
  input: final.value,
6344
6409
  inst,
6345
- errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())))
6410
+ errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config())))
6346
6411
  });
6347
6412
  return final;
6348
6413
  }
@@ -6571,7 +6636,7 @@ var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
6571
6636
  payload.issues.push({
6572
6637
  origin: "record",
6573
6638
  code: "invalid_key",
6574
- issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config$1())),
6639
+ issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
6575
6640
  input: key,
6576
6641
  path: [key],
6577
6642
  inst
@@ -6739,7 +6804,7 @@ var $ZodCatch = /* @__PURE__ */ $constructor("$ZodCatch", (inst, def) => {
6739
6804
  if (result.issues.length) {
6740
6805
  payload.value = def.catchValue({
6741
6806
  ...payload,
6742
- error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())) },
6807
+ error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config())) },
6743
6808
  input: payload.value
6744
6809
  });
6745
6810
  payload.issues = [];
@@ -6750,7 +6815,7 @@ var $ZodCatch = /* @__PURE__ */ $constructor("$ZodCatch", (inst, def) => {
6750
6815
  if (result.issues.length) {
6751
6816
  payload.value = def.catchValue({
6752
6817
  ...payload,
6753
- error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config$1())) },
6818
+ error: { issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config())) },
6754
6819
  input: payload.value
6755
6820
  });
6756
6821
  payload.issues = [];
@@ -22221,6 +22286,7 @@ function toTrimmedString(value) {
22221
22286
  }
22222
22287
  //#endregion
22223
22288
  //#region src/index.ts
22289
+ await loadWorkingDirEnv();
22224
22290
  var logger = createLogger(process.env.SEI_MCP_LOG_LEVEL ?? "info");
22225
22291
  var argv = process.argv.slice(2);
22226
22292
  var runtime = parseRuntimeOptions(argv, process.env);
@@ -22228,23 +22294,31 @@ if (runtime.command === "help") {
22228
22294
  process.stdout.write(`${renderHelpText()}\n`);
22229
22295
  process.exit(0);
22230
22296
  }
22231
- var config = await loadConfig(argv, process.env);
22232
- if (runtime.command === "cli") {
22233
- await runCli(runtime.cliArgs, {
22297
+ try {
22298
+ const config = await loadConfig(argv, process.env);
22299
+ if (runtime.command === "cli") {
22300
+ await runCli(runtime.cliArgs, {
22301
+ config,
22302
+ logger
22303
+ });
22304
+ process.exit(0);
22305
+ }
22306
+ const client = createSeiClient(config, logger);
22307
+ if (runtime.transport === "streamable-http") await startHttpServer(() => createMcpServer({
22234
22308
  config,
22309
+ client,
22235
22310
  logger
22236
- });
22237
- process.exit(0);
22311
+ }), runtime, logger);
22312
+ else await startStdioServer(createMcpServer({
22313
+ config,
22314
+ client,
22315
+ logger
22316
+ }));
22317
+ } catch (error) {
22318
+ if (!isSeiMcpError(error) || runtime.command !== "cli") {
22319
+ logger.error("sei-ai startup failed", toErrorLogMeta(error));
22320
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
22321
+ }
22322
+ process.exit(1);
22238
22323
  }
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
22324
  //#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.3",
4
4
  "description": "SEI MCP server and developer CLI for query, save, query-sql, and api-docs workflows",
5
5
  "keywords": [
6
6
  "sei",