@nickyzj2023/ai 1.4.2 → 1.4.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/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { a as get_time_default, c as runAgent, i as get_weather_default, o as defineModel, r as loadMCPTools } from "./src-izgP3Lfk.mjs";
2
- import { compactStr, extractErrorMessage } from "@nickyzj2023/utils";
1
+ import { a as get_time_default, c as runAgent, i as get_weather_default, o as defineModel, r as loadMCPTools } from "./src-CuZ-6Jye.mjs";
2
+ import { compactStr, extractErrorMessage, isObject } from "@nickyzj2023/utils";
3
3
  import readline from "node:readline";
4
4
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { homedir } from "node:os";
@@ -55,6 +55,121 @@ const ask = (question) => {
55
55
  });
56
56
  };
57
57
  /**
58
+ * 询问一个可留空的字段:回车或输入空白时沿用默认值
59
+ * @param question 提问文案
60
+ * @param fallback 默认值
61
+ * @returns 用户输入,空白时返回默认值
62
+ */
63
+ const askWithDefault = async (question, fallback) => {
64
+ return (await ask(question)).trim() || fallback;
65
+ };
66
+ /**
67
+ * 交互式录入一个MCP服务器配置
68
+ * @param current 编辑时传入现有服务器,回车表示沿用原值;只传name表示配置缺失、重新录入
69
+ * @returns 录入完成的配置;名称/URL为空或类型非法时返回null
70
+ */
71
+ const askMcpServer = async (current) => {
72
+ const name = await askWithDefault(`名称 [当前为${current?.name ?? "(新)"}]: `, current?.name);
73
+ if (!name) {
74
+ console.log("名称不能为空,已取消");
75
+ return null;
76
+ }
77
+ const defaultType = current?.type ?? "streamable_http";
78
+ const typeInput = await askWithDefault(`类型 streamable_http/sse [当前为${defaultType}]: `, defaultType);
79
+ if (typeInput !== "streamable_http" && typeInput !== "sse") {
80
+ console.log("类型无效,已取消");
81
+ return null;
82
+ }
83
+ const type = typeInput;
84
+ const url = await askWithDefault(`URL [当前为${current?.url ?? "无"}]: `, current?.url);
85
+ if (!url) {
86
+ console.log("URL不能为空,已取消");
87
+ return null;
88
+ }
89
+ let headers = current?.headers;
90
+ const headersInput = (await ask(`headers(JSON对象) [当前为${current?.headers ? JSON.stringify(current.headers) : "无"}]: `)).trim();
91
+ if (headersInput) try {
92
+ const parsed = JSON.parse(headersInput);
93
+ if (isObject(parsed)) headers = parsed;
94
+ else console.log("headers需为JSON对象,已回退到原先的值");
95
+ } catch {
96
+ console.log("headers解析失败,已回退到原先的值");
97
+ }
98
+ let ignoredToolNames = current?.ignoredToolNames;
99
+ const ignoredInput = (await ask(`忽略的工具名(逗号分隔) [当前为${current?.ignoredToolNames?.join(",") ?? "无"}]: `)).trim();
100
+ if (ignoredInput) ignoredToolNames = ignoredInput.split(",").map((item) => item.trim()).filter(Boolean);
101
+ return {
102
+ name,
103
+ type,
104
+ url,
105
+ ...headers ? { headers } : {},
106
+ ...ignoredToolNames?.length ? { ignoredToolNames } : {}
107
+ };
108
+ };
109
+ /**
110
+ * 把菜单输入的数字解析成对应的服务器名称
111
+ * @param input 用户输入的数字串
112
+ * @param names 当前菜单的服务器名称列表
113
+ * @returns 对应名称;非整数或越界返回undefined
114
+ */
115
+ const pickServerName = (input, names) => {
116
+ const index = Number(input) - 1;
117
+ return Number.isInteger(index) ? names[index] : void 0;
118
+ };
119
+ /**
120
+ * MCP配置菜单:列出现有服务器,数字键编辑、a键添加、d键删除、回车或q结束
121
+ */
122
+ const configMcp = async (mcpServers) => {
123
+ while (true) {
124
+ const names = Object.keys(mcpServers);
125
+ console.log("\n当前MCP服务器:");
126
+ if (names.length === 0) console.log("(空)");
127
+ else names.forEach((name, i) => {
128
+ const server = mcpServers[name];
129
+ console.log(` ${i + 1}. ${name} ${server ? `(${server.type} ${server.url})` : "(配置缺失)"}`);
130
+ });
131
+ const choice = (await ask("输入数字编辑对应服务器,a添加新服务器,d删除服务器,回车或q结束: ")).trim().toLowerCase();
132
+ if (!choice || choice === "q") break;
133
+ switch (choice) {
134
+ case "a": {
135
+ const result = await askMcpServer();
136
+ if (result) mcpServers[result.name] = result;
137
+ break;
138
+ }
139
+ case "d": {
140
+ const target = (await ask("输入要删除的服务器编号: ")).trim();
141
+ const oldName = pickServerName(target, names);
142
+ if (!oldName) {
143
+ console.log("无效编号,请输入列表中的数字");
144
+ break;
145
+ }
146
+ if ((await ask(`确认删除${oldName}?(y/n): `)).trim().toLowerCase() === "y") {
147
+ delete mcpServers[oldName];
148
+ console.log(`已删除${oldName}`);
149
+ } else console.log("已取消删除");
150
+ break;
151
+ }
152
+ default: {
153
+ const oldName = pickServerName(choice, names);
154
+ if (oldName) {
155
+ const server = mcpServers[oldName];
156
+ if (!server) console.log("该服务器配置缺失,将重新录入");
157
+ const result = await askMcpServer({
158
+ name: oldName,
159
+ ...server
160
+ });
161
+ if (result) {
162
+ if (result.name !== oldName) console.log(`已重命名 ${oldName} -> ${result.name}`);
163
+ delete mcpServers[oldName];
164
+ mcpServers[result.name] = result;
165
+ }
166
+ } else console.log("无效选择,请输入列表中的数字、a、d或回车");
167
+ break;
168
+ }
169
+ }
170
+ }
171
+ };
172
+ /**
58
173
  * setup入口:依次询问BASE_URL / MODEL / APIKEY,确认后写入全局配置
59
174
  */
60
175
  async function runSetup() {
@@ -66,11 +181,14 @@ async function runSetup() {
66
181
  const baseUrl = await ask(`BASE_URL [当前为${config?.baseUrl}]: `) || config?.baseUrl;
67
182
  const apiKey = await ask(`APIKEY [当前为${config?.apiKey}]: `) || config?.apiKey;
68
183
  const model = await ask(`MODEL [当前为${config?.model}]: `) || config?.model;
184
+ const mcpServers = { ...config?.mcpServers };
185
+ if ((await ask("是否配置MCP服务器?(y/n): ")).trim().toLowerCase() === "y") await configMcp(mcpServers);
69
186
  try {
70
187
  saveConfig({
71
188
  baseUrl,
72
189
  apiKey,
73
- model
190
+ model,
191
+ mcpServers
74
192
  });
75
193
  console.log(`配置已保存到${getConfigPath()},直接运行ai即可开始对话`);
76
194
  } catch (e) {
package/dist/index.d.mts CHANGED
@@ -199,7 +199,7 @@ declare class MCPRouter {
199
199
  /**
200
200
  * 把传入的MCPServer列表转换成OpenAI API兼容的tools数组
201
201
  */
202
- declare const loadMCPTools: (mcpServers: Record<string, McpServer>) => Promise<ToolDefinition[]>;
202
+ declare const loadMCPTools: (mcpServers?: Record<string, McpServer>) => Promise<ToolDefinition[]>;
203
203
  //#endregion
204
204
  //#region src/utils/compact/types.d.ts
205
205
  declare namespace Compact {
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as get_time_default, c as runAgent, i as get_weather_default, n as MCPRouter, o as defineModel, r as loadMCPTools, s as defineTool, t as compact } from "./src-izgP3Lfk.mjs";
1
+ import { a as get_time_default, c as runAgent, i as get_weather_default, n as MCPRouter, o as defineModel, r as loadMCPTools, s as defineTool, t as compact } from "./src-CuZ-6Jye.mjs";
2
2
  export { MCPRouter, compact, defineModel, defineTool, get_time_default as getTime, get_weather_default as getWeather, loadMCPTools, runAgent };
@@ -273,7 +273,7 @@ var MCPRouter = class {
273
273
  /**
274
274
  * 把传入的MCPServer列表转换成OpenAI API兼容的tools数组
275
275
  */
276
- const loadMCPTools = async (mcpServers) => {
276
+ const loadMCPTools = async (mcpServers = {}) => {
277
277
  router ||= new MCPRouter();
278
278
  await Promise.allSettled(Object.entries(mcpServers).map(async ([name, server]) => {
279
279
  try {
@@ -327,7 +327,7 @@ const defaultReplacerOfToolResultContent = async (content, options) => {
327
327
  content
328
328
  }, {
329
329
  role: "user",
330
- content: "请用一两句话简述上条消息"
330
+ content: "请用一句话简述上面这条消息(直击要点,不要添加任何前言后语)"
331
331
  }];
332
332
  let simplifiedContent = "";
333
333
  for await (const e of runAgent(model, messages, [])) if (e.type === "content_delta") simplifiedContent += e.delta;
@@ -358,7 +358,7 @@ const defaultReplacerOfMediaContent = async (content, options) => {
358
358
  content
359
359
  }, {
360
360
  role: "user",
361
- content: "请用一两句话简述上方的多模态消息"
361
+ content: "请用一句话简述上面这条消息(直击要点,不要添加任何前言后语)"
362
362
  }];
363
363
  let simplifiedContent = "";
364
364
  for await (const e of runAgent(model, messages, [])) if (e.type === "content_delta") simplifiedContent += e.delta;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nickyzj2023/ai",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "我的“pi”,参考了pi-from-scratch",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",