@numa-tech/numa 1.14.52 → 1.16.0

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.
Files changed (78) hide show
  1. package/README.md +161 -24
  2. package/dist/ai-registry/commands.d.ts +8 -1
  3. package/dist/ai-registry/commands.js +70 -15
  4. package/dist/ai-registry/commands.js.map +1 -1
  5. package/dist/ai-registry/mcp-installer.d.ts +1 -1
  6. package/dist/ai-registry/mcp-installer.js +26 -0
  7. package/dist/ai-registry/mcp-installer.js.map +1 -1
  8. package/dist/ai-registry/sync.js +3 -2
  9. package/dist/ai-registry/sync.js.map +1 -1
  10. package/dist/cli.js +42 -14
  11. package/dist/cli.js.map +1 -1
  12. package/dist/command-catalog.js +28 -3
  13. package/dist/command-catalog.js.map +1 -1
  14. package/dist/interactive/entry.d.ts +2 -0
  15. package/dist/interactive/entry.js +91 -0
  16. package/dist/interactive/entry.js.map +1 -0
  17. package/dist/interactive/mcp.d.ts +4 -0
  18. package/dist/interactive/mcp.js +88 -0
  19. package/dist/interactive/mcp.js.map +1 -0
  20. package/dist/interactive/prompts.d.ts +26 -0
  21. package/dist/interactive/prompts.js +79 -0
  22. package/dist/interactive/prompts.js.map +1 -0
  23. package/dist/interactive/registry.d.ts +28 -0
  24. package/dist/interactive/registry.js +147 -0
  25. package/dist/interactive/registry.js.map +1 -0
  26. package/dist/maintenance/modules.js +1 -1
  27. package/dist/maintenance/modules.js.map +1 -1
  28. package/dist/mcp/catalog.d.ts +35 -0
  29. package/dist/mcp/catalog.js +203 -0
  30. package/dist/mcp/catalog.js.map +1 -0
  31. package/dist/mcp/commands.d.ts +10 -0
  32. package/dist/mcp/commands.js +96 -0
  33. package/dist/mcp/commands.js.map +1 -0
  34. package/dist/mcp/environment.d.ts +5 -0
  35. package/dist/mcp/environment.js +35 -0
  36. package/dist/mcp/environment.js.map +1 -0
  37. package/dist/mcp/inspect.d.ts +35 -0
  38. package/dist/mcp/inspect.js +17 -0
  39. package/dist/mcp/inspect.js.map +1 -0
  40. package/dist/mcp/install.d.ts +55 -0
  41. package/dist/mcp/install.js +151 -0
  42. package/dist/mcp/install.js.map +1 -0
  43. package/dist/mcp/modules.d.ts +4 -0
  44. package/dist/mcp/modules.js +11 -0
  45. package/dist/mcp/modules.js.map +1 -0
  46. package/dist/oss/commands.d.ts +3 -0
  47. package/dist/oss/commands.js +74 -7
  48. package/dist/oss/commands.js.map +1 -1
  49. package/dist/oss/search.d.ts +70 -0
  50. package/dist/oss/search.js +172 -0
  51. package/dist/oss/search.js.map +1 -0
  52. package/dist/oss/tools.d.ts +8 -0
  53. package/dist/oss/tools.js +69 -0
  54. package/dist/oss/tools.js.map +1 -0
  55. package/dist/oss/transfers.d.ts +17 -2
  56. package/dist/oss/transfers.js +29 -7
  57. package/dist/oss/transfers.js.map +1 -1
  58. package/dist/server.d.ts +4 -1
  59. package/dist/server.js +192 -169
  60. package/dist/server.js.map +1 -1
  61. package/dist/wecom/tools.js +15 -4
  62. package/dist/wecom/tools.js.map +1 -1
  63. package/package.json +4 -4
  64. package/skills/numa-cli/SKILL.md +4 -2
  65. package/skills/numa-cli/agents/openai.yaml +1 -1
  66. package/skills/numa-cli/evals/evals.json +31 -1
  67. package/skills/numa-cli/numa-skill.json +1 -1
  68. package/skills/numa-cli/references/cli-mcp.md +121 -0
  69. package/skills/numa-oss-access/SKILL.md +7 -3
  70. package/skills/numa-oss-access/agents/openai.yaml +1 -1
  71. package/skills/numa-oss-access/evals/evals.json +18 -0
  72. package/skills/numa-oss-access/numa-skill.json +2 -2
  73. package/skills/numa-oss-access/references/cli-bootstrap.md +2 -2
  74. package/skills/numa-oss-access/references/search-and-mcp.md +63 -0
  75. package/skills/numa-wecom/SKILL.md +7 -3
  76. package/skills/numa-wecom/numa-skill.json +2 -2
  77. package/skills/numa-wecom/references/cli-bootstrap.md +2 -2
  78. package/skills/numa-wecom/references/mcp.md +12 -0
@@ -0,0 +1,96 @@
1
+ import { loadAppConfig } from "../app-config.js";
2
+ import { safeMcpOutput, collectEnvironment } from "./environment.js";
3
+ import { applyCliMcp, planCliMcp } from "./install.js";
4
+ import { parseMcpModules } from "./modules.js";
5
+ import { searchModules, selectMcpTools } from "./catalog.js";
6
+ import { inspectMcpTools } from "./inspect.js";
7
+ import { completeMcpInstall } from "../interactive/mcp.js";
8
+ import { terminalUi, useInteractive, requireInput, confirmInstall, } from "../interactive/prompts.js";
9
+ export function registerLocalMcpCommands(program, dependencies = {}) {
10
+ const mcp = program.commands.find((command) => command.name() === "mcp") ??
11
+ program.command("mcp");
12
+ const write = dependencies.write ??
13
+ ((text) => {
14
+ process.stdout.write(text);
15
+ });
16
+ const emit = async (command, value, text) => {
17
+ const config = await loadAppConfig();
18
+ write(`${command.optsWithGlobals().json || config.output === "json" ? JSON.stringify(safeMcpOutput(value), null, 2) : text}\n`);
19
+ };
20
+ for (const parent of [program, mcp])
21
+ parent
22
+ .command("modules [query]")
23
+ .description("按用途、关键词或 Skill 名称检索 MCP 模块(本地,无需登录)")
24
+ .option("--search <query>", "用途关键词,例如 文件、通知、笔记")
25
+ .action(async (query, options, command) => {
26
+ const modules = searchModules(options.search ?? query);
27
+ await emit(command, { ok: true, modules }, modules
28
+ .map((m) => `${m.id} · ${m.title} · ${m.tools.length} tools\n ${m.description}\n Skills: ${m.skills.join(", ")}`)
29
+ .join("\n") || "没有匹配模块;运行 numa modules 查看全部。");
30
+ });
31
+ mcp
32
+ .command("install-cli")
33
+ .description("安装 CLI 自身的 MCP;缺少目标时使用交互向导")
34
+ .option("--agent <agent>", "workbuddy|claude|claude-desktop|codex|cursor|openclaw|custom|chatgpt")
35
+ .option("--scope <scope>", "user 或 project", "user")
36
+ .option("--name <name>", "Agent 中的配置名称", "numa-cli")
37
+ .option("--modules <modules>", "默认 auth,oss,wecom;可选 notion、platform")
38
+ .option("--tools <names>", "仅启用指定工具,逗号分隔,须属于所选模块")
39
+ .option("--exclude-tools <names>", "排除指定工具,逗号分隔")
40
+ .option("--server-name <name>", "MCP 协议中的服务名称")
41
+ .option("--server-description <text>", "服务用途说明;自动附加真实模块和工具范围")
42
+ .option("--auth-mode <mode>", "browser 或 client_credentials", "browser")
43
+ .option("--client-id <id>", "机器 Client ID,仅 client_credentials 模式")
44
+ .option("--client-secret-file <path>", "机器 Secret 文件绝对路径;不会读取或复制内容")
45
+ .option("--workspace <path>", "项目范围配置目录")
46
+ .option("--custom-config <path>", "custom Agent 的 MCP JSON 路径")
47
+ .option("--env <NAME=VALUE>", "追加启动环境变量,可重复;仅写入目标 MCP 配置", collectEnvironment)
48
+ .option("--force", "备份并替换 JSON 配置中的同名服务")
49
+ .option("--tunnel-id <id>", "ChatGPT Secure MCP Tunnel ID")
50
+ .option("--endpoint <url>", "ChatGPT 已部署的 HTTPS MCP endpoint")
51
+ .option("--plan", "仅展示配置与步骤,不修改客户端或 Tunnel")
52
+ .option("--interactive", "使用交互安装向导,保留已给定参数")
53
+ .option("--no-interactive", "禁止交互,缺少必要参数时报错")
54
+ .action(async (input, command) => {
55
+ const json = Boolean(command.optsWithGlobals().json ||
56
+ (await loadAppConfig()).output === "json");
57
+ const missing = !input.agent ||
58
+ (input.agent === "custom" && !input.customConfig) ||
59
+ (input.authMode === "client_credentials" &&
60
+ (!input.clientId || !input.clientSecretFile));
61
+ const interactive = useInteractive(input, missing, json, dependencies.tty), ui = dependencies.ui ?? terminalUi;
62
+ let options = { ...input };
63
+ if (interactive) {
64
+ for (const key of ["scope", "name", "authMode"])
65
+ if (command.getOptionValueSource(key) === "default")
66
+ delete options[key];
67
+ options = { ...options, ...(await completeMcpInstall(options, ui)) };
68
+ }
69
+ requireInput(options.agent, "缺少 --agent");
70
+ const plan = planCliMcp(options);
71
+ if (options.plan)
72
+ return emit(command, { ok: true, plan }, `MCP 安装计划:${plan.agent} / ${plan.scope}\n${JSON.stringify(safeMcpOutput(plan), null, 2)}`);
73
+ if (interactive && (plan.status === "ready" || plan.tunnel))
74
+ await confirmInstall(ui, plan);
75
+ const result = await (dependencies.apply ?? applyCliMcp)(plan);
76
+ await emit(command, { ok: true, ...result, plan }, `${result.status === "installed" ? "Numa MCP 配置已安装" : result.status === "manual_required" ? "已生成连接指引,尚未安装 ChatGPT 应用" : "Tunnel 配置已初始化,仍需运行 Tunnel 并在 ChatGPT 完成连接"}${result.installation?.configPath ? `:${result.installation.configPath}` : ""}\n${result.instructions.join("\n")}`);
77
+ });
78
+ mcp
79
+ .command("tools")
80
+ .description("读取本地 MCP tools/list 及服务自描述,不执行业务操作")
81
+ .option("--modules <modules>", "逗号分隔模块名", parseMcpModules)
82
+ .option("--tools <names>", "仅保留指定工具")
83
+ .option("--exclude-tools <names>", "排除指定工具")
84
+ .option("--server-name <name>", "服务名称")
85
+ .option("--server-description <text>", "服务用途说明")
86
+ .action(async (options, command) => {
87
+ const selection = selectMcpTools(options), tools = await inspectMcpTools(options);
88
+ await emit(command, {
89
+ ok: true,
90
+ modules: selection.modules,
91
+ instructions: selection.instructions,
92
+ tools,
93
+ }, `${selection.instructions}\n\n${tools.map((tool) => `${tool.name}\n ${tool.description}`).join("\n")}`);
94
+ });
95
+ }
96
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/mcp/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { McpClientDefinition } from "../ai-registry/mcp-installer.js";
2
+ export declare function collectEnvironment(value: string, previous?: string[]): string[];
3
+ export declare function parseEnvironment(values?: string[]): Record<string, string>;
4
+ export declare function appendEnvironment(definition: McpClientDefinition, values?: string[]): McpClientDefinition;
5
+ export declare function safeMcpOutput(value: unknown): unknown;
@@ -0,0 +1,35 @@
1
+ import { AiRegistryError, sanitizeRegistryOutput, } from "../ai-registry/errors.js";
2
+ import { sanitizeOssOutput } from "../oss/errors.js";
3
+ export function collectEnvironment(value, previous = []) {
4
+ return [...previous, value];
5
+ }
6
+ export function parseEnvironment(values = []) {
7
+ if (values.length > 64)
8
+ throw new AiRegistryError("At most 64 --env entries are allowed.", "ai_registry_mcp_invalid");
9
+ const entries = [], names = new Set();
10
+ for (const entry of values) {
11
+ const split = entry.indexOf("="), name = entry.slice(0, split), value = entry.slice(split + 1);
12
+ if (split < 1 ||
13
+ !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name) ||
14
+ value.includes("\0") ||
15
+ entry.length > 32768)
16
+ throw new AiRegistryError("--env requires NAME=VALUE with a valid environment name and no NUL. Values may contain = and may be empty.", "ai_registry_mcp_invalid");
17
+ if (names.has(name.toUpperCase()))
18
+ throw new AiRegistryError(`Duplicate --env name: ${name}.`, "ai_registry_mcp_invalid");
19
+ names.add(name.toUpperCase());
20
+ entries.push([name, value]);
21
+ }
22
+ return Object.fromEntries(entries);
23
+ }
24
+ export function appendEnvironment(definition, values = []) {
25
+ const env = parseEnvironment(values);
26
+ if (!values.length)
27
+ return definition;
28
+ if (!("command" in definition))
29
+ throw new AiRegistryError("--env configures a local MCP process only; remote HTTPS MCP has no local environment. Use --transport bridge for a supported Registry client.", "ai_registry_mcp_invalid");
30
+ return { ...definition, env: { ...definition.env, ...env } };
31
+ }
32
+ export function safeMcpOutput(value) {
33
+ return sanitizeRegistryOutput(sanitizeOssOutput(value));
34
+ }
35
+ //# sourceMappingURL=environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/mcp/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,MAAM,UAAU,kBAAkB,CAChC,KAAa,EACb,QAAQ,GAAa,EAAE;IAEvB,OAAO,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC;AACD,MAAM,UAAU,gBAAgB,CAC9B,MAAM,GAAa,EAAE;IAErB,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE;QACpB,MAAM,IAAI,eAAe,CACvB,uCAAuC,EACvC,yBAAyB,CAC1B,CAAC;IACJ,MAAM,OAAO,GAA4B,EAAE,EACzC,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAC9B,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAC5B,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QACjC,IACE,KAAK,GAAG,CAAC;YACT,CAAC,2BAA2B,CAAC,IAAI,CAAC,IAAI,CAAC;YACvC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YACpB,KAAK,CAAC,MAAM,GAAG,KAAK;YAEpB,MAAM,IAAI,eAAe,CACvB,4GAA4G,EAC5G,yBAAyB,CAC1B,CAAC;QACJ,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAC/B,MAAM,IAAI,eAAe,CACvB,yBAAyB,IAAI,GAAG,EAChC,yBAAyB,CAC1B,CAAC;QACJ,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AACD,MAAM,UAAU,iBAAiB,CAC/B,UAA+B,EAC/B,MAAM,GAAa,EAAE;IAErB,MAAM,GAAG,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,UAAU,CAAC;IACtC,IAAI,CAAC,CAAC,SAAS,IAAI,UAAU,CAAC;QAC5B,MAAM,IAAI,eAAe,CACvB,+IAA+I,EAC/I,yBAAyB,CAC1B,CAAC;IACJ,OAAO,EAAE,GAAG,UAAU,EAAE,GAAG,EAAE,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,CAAC;AAC/D,CAAC;AACD,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,sBAAsB,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { McpSelectionOptions } from "./catalog.js";
2
+ export declare function inspectMcpTools(options?: McpSelectionOptions): Promise<{
3
+ inputSchema: {
4
+ [x: string]: unknown;
5
+ type: "object";
6
+ properties?: Record<string, object> | undefined;
7
+ required?: string[] | undefined;
8
+ };
9
+ name: string;
10
+ description?: string | undefined;
11
+ outputSchema?: {
12
+ [x: string]: unknown;
13
+ type: "object";
14
+ properties?: Record<string, object> | undefined;
15
+ required?: string[] | undefined;
16
+ } | undefined;
17
+ annotations?: {
18
+ title?: string | undefined;
19
+ readOnlyHint?: boolean | undefined;
20
+ destructiveHint?: boolean | undefined;
21
+ idempotentHint?: boolean | undefined;
22
+ openWorldHint?: boolean | undefined;
23
+ } | undefined;
24
+ execution?: {
25
+ taskSupport?: "optional" | "required" | "forbidden" | undefined;
26
+ } | undefined;
27
+ _meta?: Record<string, unknown> | undefined;
28
+ icons?: {
29
+ src: string;
30
+ mimeType?: string | undefined;
31
+ sizes?: string[] | undefined;
32
+ theme?: "light" | "dark" | undefined;
33
+ }[] | undefined;
34
+ title?: string | undefined;
35
+ }[]>;
@@ -0,0 +1,17 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
3
+ import { createServer } from "../server.js";
4
+ export async function inspectMcpTools(options = {}) {
5
+ const server = createServer(options), client = new Client({ name: "numa-tool-inspection", version: "1.0.0" });
6
+ const [a, b] = InMemoryTransport.createLinkedPair();
7
+ try {
8
+ await server.connect(b);
9
+ await client.connect(a);
10
+ return (await client.listTools()).tools;
11
+ }
12
+ finally {
13
+ await client.close();
14
+ await server.close();
15
+ }
16
+ }
17
+ //# sourceMappingURL=inspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect.js","sourceRoot":"","sources":["../../src/mcp/inspect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAO,GAAwB,EAAE;IACrE,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,EAClC,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1E,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,iBAAiB,CAAC,gBAAgB,EAAE,CAAC;IACpD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACxB,OAAO,CAAC,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC;IAC1C,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { installMcpClientDefinition, type McpClientDefinition, type McpInstallResult } from "../ai-registry/mcp-installer.js";
2
+ import { type McpModule } from "./modules.js";
3
+ export declare const CLI_MCP_AGENTS: readonly ["workbuddy", "claude", "claude-desktop", "codex", "cursor", "openclaw", "custom", "chatgpt"];
4
+ export type CliMcpAgent = typeof CLI_MCP_AGENTS[number];
5
+ export interface CliMcpOptions {
6
+ agent?: string;
7
+ env?: string[];
8
+ tools?: string;
9
+ excludeTools?: string;
10
+ serverName?: string;
11
+ serverDescription?: string;
12
+ scope?: string;
13
+ name?: string;
14
+ modules?: string;
15
+ authMode?: string;
16
+ clientId?: string;
17
+ clientSecretFile?: string;
18
+ workspace?: string;
19
+ customConfig?: string;
20
+ force?: boolean;
21
+ tunnelId?: string;
22
+ endpoint?: string;
23
+ }
24
+ export interface CliMcpPlan {
25
+ agent: CliMcpAgent;
26
+ scope: "project" | "user";
27
+ name: string;
28
+ modules: McpModule[];
29
+ tools: string[];
30
+ instructionsDescription: string;
31
+ definition: McpClientDefinition;
32
+ status: "ready" | "manual_required";
33
+ workspace?: string;
34
+ customConfig?: string;
35
+ force?: boolean;
36
+ tunnel?: {
37
+ id: string;
38
+ profile: string;
39
+ launcherPath: string;
40
+ };
41
+ instructions: string[];
42
+ }
43
+ export interface CliMcpInstallDependencies {
44
+ install?: typeof installMcpClientDefinition;
45
+ run?: (command: string, args: string[]) => Promise<void>;
46
+ home?: string;
47
+ platform?: NodeJS.Platform;
48
+ }
49
+ export declare function planCliMcp(options: CliMcpOptions, dependencies?: CliMcpInstallDependencies): CliMcpPlan;
50
+ export declare function applyCliMcp(plan: CliMcpPlan, dependencies?: CliMcpInstallDependencies): Promise<{
51
+ status: string;
52
+ installation?: McpInstallResult;
53
+ instructions: string[];
54
+ launcherPath?: string;
55
+ }>;
@@ -0,0 +1,151 @@
1
+ import { spawn } from "node:child_process";
2
+ import { homedir } from "node:os";
3
+ import { dirname, isAbsolute, join } from "node:path";
4
+ import { lstat, mkdir, unlink, writeFile } from "node:fs/promises";
5
+ import { PACKAGE_NAME, PACKAGE_VERSION } from "../branding.js";
6
+ import { installMcpClientDefinition } from "../ai-registry/mcp-installer.js";
7
+ import { AiRegistryError } from "../ai-registry/errors.js";
8
+ import { appendEnvironment, parseEnvironment } from "./environment.js";
9
+ import { selectMcpTools } from "./catalog.js";
10
+ import { DEFAULT_INSTALL_MODULES, parseMcpModules } from "./modules.js";
11
+ export const CLI_MCP_AGENTS = ["workbuddy", "claude", "claude-desktop", "codex", "cursor", "openclaw", "custom", "chatgpt"];
12
+ function invalid(message) { throw new AiRegistryError(message, "ai_registry_mcp_invalid"); }
13
+ function httpsEndpoint(value) {
14
+ let url;
15
+ try {
16
+ url = new URL(value);
17
+ }
18
+ catch {
19
+ return invalid("Endpoint must be a valid HTTPS URL.");
20
+ }
21
+ if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash || ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))
22
+ invalid("Endpoint must be a reachable HTTPS MCP URL without credentials, query or fragment.");
23
+ return url.toString();
24
+ }
25
+ export function planCliMcp(options, dependencies = {}) {
26
+ if (!CLI_MCP_AGENTS.includes(options.agent))
27
+ invalid(`Agent must be one of ${CLI_MCP_AGENTS.join(",")}.`);
28
+ const agent = options.agent, scope = options.scope ?? "user", name = options.name ?? "numa-cli";
29
+ if (scope !== "user" && scope !== "project")
30
+ invalid("Scope must be user or project.");
31
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u.test(name))
32
+ invalid("Name must contain 1-64 safe letters, digits, dots, underscores or hyphens.");
33
+ if (["chatgpt", "claude-desktop", "codex", "openclaw"].includes(agent) && scope !== "user")
34
+ invalid(`${agent} requires --scope user.`);
35
+ if (agent === "custom" && !options.customConfig)
36
+ invalid("custom requires --custom-config.");
37
+ if (options.customConfig && agent !== "custom")
38
+ invalid("--custom-config is only valid for custom.");
39
+ if (agent !== "chatgpt" && (options.tunnelId || options.endpoint))
40
+ invalid("Tunnel and endpoint options are only valid for chatgpt.");
41
+ if (options.tunnelId && options.endpoint)
42
+ invalid("Choose a tunnel-id or an endpoint, not both.");
43
+ if (agent === "chatgpt" && options.force)
44
+ invalid("--force does not apply to ChatGPT or Tunnel profiles; review an existing profile with tunnel-client.");
45
+ if (["codex", "openclaw"].includes(agent) && options.force)
46
+ invalid("--force only applies to JSON configuration adapters; manage native CLI entries with the corresponding official CLI.");
47
+ const modules = options.modules === undefined ? [...DEFAULT_INSTALL_MODULES] : parseMcpModules(options.modules);
48
+ const extra = parseEnvironment(options.env);
49
+ if (Object.keys(extra).some(k => ["NUMA_KEYCLOAK_AUTH_MODE", "NUMA_KEYCLOAK_CLIENT_ID", "NUMA_KEYCLOAK_CLIENT_SECRET", "NUMA_KEYCLOAK_CLIENT_SECRET_FILE"].includes(k.toUpperCase())))
50
+ invalid("Use --auth-mode, --client-id and --client-secret-file for Numa identity; --env must not override these explicit identity controls.");
51
+ const selection = selectMcpTools({ ...options, modules });
52
+ const mode = options.authMode ?? "browser";
53
+ if (!["browser", "client_credentials"].includes(mode))
54
+ invalid("Auth mode must be browser or client_credentials.");
55
+ if (mode === "browser" && (options.clientId || options.clientSecretFile))
56
+ invalid("Machine client options require --auth-mode client_credentials.");
57
+ const env = { NUMA_KEYCLOAK_AUTH_MODE: mode };
58
+ if (mode === "client_credentials") {
59
+ if (!options.clientId?.trim() || !options.clientSecretFile || !isAbsolute(options.clientSecretFile))
60
+ invalid("Machine mode requires --client-id and an absolute --client-secret-file; never pass a literal secret.");
61
+ if (options.clientId === "mcp-client")
62
+ invalid("The public browser mcp-client cannot be used as a machine identity.");
63
+ if (/[\u0000-\u001f\u007f]/u.test(options.clientId + options.clientSecretFile))
64
+ invalid("Machine configuration contains invalid controls.");
65
+ env.NUMA_KEYCLOAK_CLIENT_ID = options.clientId;
66
+ env.NUMA_KEYCLOAK_CLIENT_SECRET_FILE = options.clientSecretFile;
67
+ // An inherited literal secret must not conflict with this explicitly selected file.
68
+ env.NUMA_KEYCLOAK_CLIENT_SECRET = "";
69
+ }
70
+ const platform = dependencies.platform ?? process.platform;
71
+ let definition = { command: platform === "win32" ? "npx.cmd" : "npx", args: ["-y", `${PACKAGE_NAME}@${PACKAGE_VERSION}`, "serve", "--modules", modules.join(","), "--auth-mode", mode], env };
72
+ if ("command" in definition)
73
+ for (const [flag, value] of [["--tools", options.tools], ["--exclude-tools", options.excludeTools], ["--server-name", options.serverName], ["--server-description", options.serverDescription]])
74
+ if (value !== undefined)
75
+ definition.args.push(flag, value);
76
+ definition = appendEnvironment(definition, options.env);
77
+ if (agent === "chatgpt" && options.endpoint && options.env?.length)
78
+ invalid("--env is only supported for local stdio or Tunnel, not a remote endpoint.");
79
+ const instructions = [mode === "browser" ? "先运行 numa login;MCP 使用同一用户的登录缓存与 platform profile。" : "MCP 进程使用指定机器 Client 和 Secret 文件;Issuer/API 地址从 platform profile 获取。", `重启或刷新 Agent 的 MCP 列表,信任 ${name} 后核对工具。发送消息、上传、下载和删除仍需用户明确授权。`];
80
+ const plan = { agent, scope, name, modules, tools: selection.tools, instructionsDescription: selection.instructions, definition, status: "ready", workspace: options.workspace, customConfig: options.customConfig, force: options.force, instructions };
81
+ if (["codex", "openclaw"].includes(agent))
82
+ instructions.push(`此适配器使用 ${agent} 官方 CLI 管理 ${name},同名条目按其原生命令语义处理,不提供 JSON 备份;安装前核对同名配置。`);
83
+ if (agent === "chatgpt") {
84
+ plan.status = "manual_required";
85
+ if (options.endpoint) {
86
+ plan.definition = { type: "http", url: httpsEndpoint(options.endpoint) };
87
+ instructions.push("该 endpoint 必须已经部署并暴露 Numa MCP;本命令不会部署或验证远端服务器。", "在 ChatGPT Plugins 创建开发者应用,填写此 HTTPS endpoint 并完成该服务要求的认证与工具审核。");
88
+ }
89
+ else {
90
+ if (options.tunnelId) {
91
+ if (!/^tunnel_[a-f0-9]{32}$/u.test(options.tunnelId))
92
+ invalid("tunnel-id must be tunnel_ followed by 32 lowercase hex characters.");
93
+ plan.tunnel = { id: options.tunnelId, profile: name, launcherPath: join(dependencies.home ?? homedir(), ".config", "numa", "mcp", `${name}-tunnel.mjs`) };
94
+ if (platform === "win32")
95
+ invalid("Automatic tunnel initialization currently supports macOS/Linux; on Windows use the documented tunnel-client setup or --endpoint.");
96
+ instructions.push(`准备官方 tunnel-client 后,安装命令将初始化 ${name} profile。运行时由环境提供 CONTROL_PLANE_API_KEY,不写入 Numa 配置。`, `运行 tunnel-client doctor --profile ${name} --explain,然后保持 tunnel-client run --profile ${name} 运行。`);
97
+ }
98
+ else
99
+ instructions.push("先在 OpenAI Platform tunnel settings 创建或选择可访问的 Tunnel,再带 --tunnel-id 重跑;也可使用已部署的 --endpoint。", "本次仅生成配置指引,没有安装或连接 ChatGPT 应用。");
100
+ instructions.push("在 ChatGPT Plugins 创建开发者应用,Connection 选择 Tunnel;选择对应 tunnel_id,并完成工作区授权与工具审核。", "官方指引:https://developers.openai.com/api/docs/guides/secure-mcp-tunnels");
101
+ }
102
+ }
103
+ return plan;
104
+ }
105
+ async function run(command, args) {
106
+ await new Promise((resolvePromise, reject) => {
107
+ const child = spawn(command, args, { shell: false, stdio: ["ignore", "ignore", "ignore"], env: process.env });
108
+ child.on("error", () => reject(new AiRegistryError(`${command} could not start; install the official client and retry.`, "ai_registry_mcp_cli_failed")));
109
+ child.on("exit", code => code === 0 ? resolvePromise() : reject(new AiRegistryError(`${command} failed (exit ${code}); inspect its configuration locally without sharing secrets.`, "ai_registry_mcp_cli_failed")));
110
+ });
111
+ }
112
+ export async function applyCliMcp(plan, dependencies = {}) {
113
+ if (plan.agent === "chatgpt" && !plan.tunnel)
114
+ return { status: "manual_required", instructions: plan.instructions };
115
+ if (plan.tunnel) {
116
+ const parent = dirname(plan.tunnel.launcherPath);
117
+ // Reject symlink ancestors before creating a private launcher, never touch existing profiles with --force.
118
+ let current = dependencies.home ?? homedir();
119
+ for (const part of [".config", "numa", "mcp"]) {
120
+ current = join(current, part);
121
+ const info = await lstat(current).catch((error) => { if (error.code === "ENOENT")
122
+ return undefined; throw error; });
123
+ if (info?.isSymbolicLink())
124
+ invalid("Refusing a symbolic-link tunnel launcher directory.");
125
+ }
126
+ if (!("command" in plan.definition))
127
+ invalid("Tunnel requires a local stdio definition.");
128
+ await mkdir(parent, { recursive: true, mode: 0o700 });
129
+ const launcher = `import { spawn } from 'node:child_process';\nconst d = ${JSON.stringify(plan.definition)};\nconst child = spawn(d.command, d.args, {stdio:'inherit',env:{...process.env,...d.env},shell:false});\nchild.on('error',()=>process.exit(1));\nchild.on('exit',code=>process.exit(code??1));\nfor(const signal of ['SIGINT','SIGTERM'])process.on(signal,()=>child.kill(signal));\n`;
130
+ try {
131
+ await writeFile(plan.tunnel.launcherPath, launcher, { flag: "wx", mode: 0o600 });
132
+ }
133
+ catch (error) {
134
+ if (error.code === "EEXIST")
135
+ invalid("Tunnel launcher already exists; review the existing profile before replacing it.");
136
+ throw error;
137
+ }
138
+ const quote = (value) => `'${value.replace(/'/gu, `'"'"'`)}'`;
139
+ try {
140
+ await (dependencies.run ?? run)("tunnel-client", ["init", "--sample", "sample_mcp_stdio_local", "--profile", plan.tunnel.profile, "--tunnel-id", plan.tunnel.id, "--mcp-command", `${quote(process.execPath)} ${quote(plan.tunnel.launcherPath)}`]);
141
+ }
142
+ catch {
143
+ await unlink(plan.tunnel.launcherPath);
144
+ throw new AiRegistryError(`Tunnel initialization failed; the new launcher was removed. Check that official tunnel-client is installed and inspect profile ${plan.tunnel.profile} for a partial initialization before retrying.`, "ai_registry_mcp_cli_failed");
145
+ }
146
+ return { status: "tunnel_configured_manual_connection_required", launcherPath: plan.tunnel.launcherPath, instructions: plan.instructions };
147
+ }
148
+ const installation = await (dependencies.install ?? installMcpClientDefinition)(plan.name, plan.definition, { agent: plan.agent, scope: plan.scope, workspace: plan.workspace, customConfig: plan.customConfig, force: plan.force });
149
+ return { status: "installed", installation, instructions: plan.instructions };
150
+ }
151
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/mcp/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare const MCP_MODULES: readonly ["auth", "oss", "wecom", "notion", "platform"];
2
+ export type McpModule = typeof MCP_MODULES[number];
3
+ export declare const DEFAULT_INSTALL_MODULES: McpModule[];
4
+ export declare function parseMcpModules(value: string): McpModule[];
@@ -0,0 +1,11 @@
1
+ import { AiRegistryError } from "../ai-registry/errors.js";
2
+ export const MCP_MODULES = ["auth", "oss", "wecom", "notion", "platform"];
3
+ export const DEFAULT_INSTALL_MODULES = ["auth", "oss", "wecom"];
4
+ export function parseMcpModules(value) {
5
+ const names = value.split(",").map(name => name.trim());
6
+ if (!names.length || names.some(name => !MCP_MODULES.includes(name))) {
7
+ throw new AiRegistryError(`MCP modules must be selected from ${MCP_MODULES.join(",")}.`, "ai_registry_mcp_invalid");
8
+ }
9
+ return [...new Set(names)];
10
+ }
11
+ //# sourceMappingURL=modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.js","sourceRoot":"","sources":["../../src/mcp/modules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAU,CAAC;AAEnF,MAAM,CAAC,MAAM,uBAAuB,GAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AAC7E,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAiB,CAAC,CAAC,EAAE,CAAC;QAClF,MAAM,IAAI,eAAe,CAAC,qCAAqC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;IACtH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAgB,CAAC;AAC5C,CAAC"}
@@ -1,9 +1,12 @@
1
1
  import type { Command } from "commander";
2
2
  import { OssClient } from "./client.js";
3
3
  import type { AccessGrantsResponse } from "./schemas.js";
4
+ import { OssTransferClient } from "./transfers.js";
4
5
  export interface OssCommandDependencies {
5
6
  clientFactory?: () => OssClient;
6
7
  write?: (text: string) => void;
8
+ diagnosticWrite?: (text: string) => void;
9
+ transferFactory?: () => OssTransferClient;
7
10
  }
8
11
  export declare function formatOssGrants(result: AccessGrantsResponse): string;
9
12
  export declare function registerOssCommands(program: Command, dependencies?: OssCommandDependencies): void;
@@ -4,6 +4,7 @@ import { loadConfig } from "../config.js";
4
4
  import { OssClient } from "./client.js";
5
5
  import { OssError, sanitizeOssOutput } from "./errors.js";
6
6
  import { sessionMetadata, OssTransferClient } from "./transfers.js";
7
+ import { boundedInteger, selectedFields, validateSearchOptions, searchObjects, projectEntries, renderEntries } from "./search.js";
7
8
  function jsonEnabled(command, configuredOutput) {
8
9
  return Boolean(command.optsWithGlobals().json || configuredOutput === "json");
9
10
  }
@@ -99,19 +100,85 @@ export function registerOssCommands(program, dependencies = {}) {
99
100
  const session = await client().issueSession(grantId(grant), options.ttl);
100
101
  await emit(command, { ok: true, session: sessionMetadata(session) }, `OSS STS 已签发: ${session.roleName},到期 ${session.expiration}`);
101
102
  });
102
- const transfer = new OssTransferClient();
103
+ const transfer = dependencies.transferFactory?.() ?? new OssTransferClient();
104
+ const write = dependencies.write ?? ((text) => { process.stdout.write(text); });
105
+ const diagnostic = dependencies.diagnosticWrite ?? ((text) => { process.stderr.write(text); });
106
+ const listingOutput = async (command, options, payload, entries, summary) => {
107
+ const config = await loadAppConfig();
108
+ const format = options.format ?? (jsonEnabled(command, config.output) ? "json" : "table");
109
+ const fields = selectedFields(options.fields);
110
+ if (format === "json")
111
+ write(`${JSON.stringify(sanitizeOssOutput(payload), null, 2)}\n`);
112
+ else {
113
+ const rendered = renderEntries(entries, fields, format);
114
+ if (format === "table")
115
+ write(`${sanitizeOssOutput(summary)}\n${rendered}\n`);
116
+ else {
117
+ if (rendered)
118
+ write(`${rendered}\n`);
119
+ diagnostic(`${sanitizeOssOutput(summary)}\n`);
120
+ }
121
+ }
122
+ };
123
+ const checkFormat = (command, options) => {
124
+ if (command.optsWithGlobals().json && options.format && options.format !== "json") {
125
+ throw new OssError("--json cannot be combined with another --format.", "oss_input_invalid");
126
+ }
127
+ };
103
128
  oss
104
129
  .command("ls <grantId> [prefix]")
105
- .description("列出授权前缀下的 OSS 对象")
106
- .option("-l, --limit <number>", "返回条数,1-1000", limit, 100)
130
+ .description("列出授权前缀下的一页 OSS 对象或目录")
131
+ .option("-l, --limit <number>", "单页条数,1-1000(对象和目录合计)", limit, 100)
132
+ .option("--marker <key>", "使用上页 nextMarker 继续列举")
133
+ .option("--delimiter <separator>", "设置 /,按目录层级列举")
134
+ .option("--fields <fields>", "输出字段,逗号分隔:key,kind,sizeBytes,lastModified,etag,type,storageClass")
135
+ .option("--format <format>", "table、json、jsonl 或 keys;后两种的摘要写入 stderr")
107
136
  .option("--ttl <seconds>", "STS 有效期,900-3600 秒", ttl)
108
137
  .action(async (grant, prefix, options, command) => {
109
138
  const parsedGrantId = grantId(grant);
110
139
  const parsedPrefix = prefix?.trim() ? key(prefix, "prefix") : undefined;
111
- const runtime = client();
112
- const session = await runtime.issueSession(parsedGrantId, options.ttl);
113
- const result = await transfer.list(session, parsedPrefix, options.limit);
114
- await emit(command, { ok: true, session: sessionMetadata(session), listing: result }, `OSS 对象 ${result.objects.length} 个${result.truncated ? "(还有更多)" : ""}`);
140
+ const validated = validateSearchOptions(options);
141
+ if (validated.needsHead)
142
+ throw new OssError("Use oss search or oss stat to retrieve custom metadata and contentType.", "oss_input_invalid");
143
+ checkFormat(command, options);
144
+ const session = await client().issueSession(parsedGrantId, options.ttl);
145
+ const result = await transfer.list(session, parsedPrefix, options.limit, options);
146
+ const entries = [...result.directories.map(key => ({ key, kind: "directory" })), ...result.objects.map(object => ({ ...object, kind: "object" }))];
147
+ const listing = options.fields ? { ...result, objects: projectEntries(entries, validated.fields) } : result;
148
+ await listingOutput(command, options, { ok: true, session: sessionMetadata(session), listing }, entries, `OSS 对象 ${result.objects.length} 个 · 目录 ${result.directories.length} 个${result.truncated ? "(还有更多)" : ""}${result.nextMarker ? `\nnextMarker: ${result.nextMarker}` : ""}`);
149
+ });
150
+ oss.command("search <grantId> [prefix]")
151
+ .description("在授权范围内分页扫描、筛选并排序 OSS 对象;不搜索文件内容")
152
+ .option("--contains <text>", "对象完整 key 或目录名包含指定文字")
153
+ .option("--suffix <text>", "对象 key 后缀,例如 .pdf")
154
+ .option("--ignore-case", "contains/suffix 忽略大小写")
155
+ .option("--delimiter <separator>", "设置 /,仅检索当前层级")
156
+ .option("--directories-only", "仅检索当前层级的子目录(隐式使用 /)")
157
+ .option("--min-size <bytes>", "最小文件大小(包含边界)", value => boundedInteger(value, "min-size", 0, Number.MAX_SAFE_INTEGER))
158
+ .option("--max-size <bytes>", "最大文件大小(包含边界)", value => boundedInteger(value, "max-size", 0, Number.MAX_SAFE_INTEGER))
159
+ .option("--modified-after <date>", "最后修改时间下界(包含),日期按 UTC,时间戳需含时区")
160
+ .option("--modified-before <date>", "最后修改时间上界(不包含)")
161
+ .option("--meta <key=value>", "自定义元数据精确匹配;可重复,条件为 AND;需要 HEAD", (value, previous) => [...previous, value], [])
162
+ .option("--content-type <type>", "Content-Type 精确匹配,需要 HEAD")
163
+ .option("--created-time-key <key>", "用指定自定义元数据表示创建时间,例如 created-at;需要 HEAD")
164
+ .option("--created-after <date>", "创建时间下界(包含),必须指定 created-time-key")
165
+ .option("--created-before <date>", "创建时间上界(不包含),必须指定 created-time-key")
166
+ .option("--sort <field>", "key、size、modified 或 created;仅对本次扫描结果排序", "key")
167
+ .option("--order <order>", "asc 或 desc;相同值按 key 升序", "asc")
168
+ .option("-l, --limit <number>", "最多输出多少条匹配结果,1-1000", limit, 100)
169
+ .option("--scan-limit <number>", "最多扫描对象和目录数量,1-100000;元数据可能逐个 HEAD", value => boundedInteger(value, "scan-limit", 1, 100000), 1000)
170
+ .option("--marker <key>", "从上次 nextMarker 继续扫描;不会补回上次输出 limit 省略的结果")
171
+ .option("--fields <fields>", "逗号分隔输出字段:key,kind,sizeBytes,lastModified,createdAt,contentType,etag,type,storageClass,metadata")
172
+ .option("--format <format>", "table、json、jsonl 或 keys;后两种的扫描摘要写入 stderr")
173
+ .option("--ttl <seconds>", "STS 有效期,900-3600 秒", ttl)
174
+ .action(async (grant, prefix, options, command) => {
175
+ const id = grantId(grant), parsedPrefix = prefix?.trim() ? key(prefix, "prefix") : undefined;
176
+ const validated = validateSearchOptions(options);
177
+ checkFormat(command, options);
178
+ const session = await client().issueSession(id, options.ttl);
179
+ const result = await searchObjects(transfer, session, parsedPrefix, options);
180
+ const summary = `扫描 ${result.scanned} 项 · 匹配 ${result.matched} 项 · 输出 ${result.returned} 项 · HEAD ${result.headRequests} 次 · ${result.complete ? "扫描完成" : "达到扫描上限,结果不完整"}${result.resultsLimited ? " · 匹配结果超过输出上限" : ""}\n排序范围:本次扫描${result.nextMarker ? `\nnextMarker: ${result.nextMarker}` : ""}`;
181
+ await listingOutput(command, options, { ok: true, session: sessionMetadata(session), search: { ...result, objects: projectEntries(result.objects, validated.fields) } }, result.objects, summary);
115
182
  });
116
183
  oss
117
184
  .command("stat <grantId> <key>")