feat: PLANNING体系首版(00路线图/02任务总表/03执行协议/I-03/I-04/registry42口径)+M1止血交付

This commit is contained in:
2026-09-12 14:25:25 +08:00
commit 8242e730b4
482 changed files with 37029 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
// Direct tool-call test: list antd components via antd-mcp stdio.
import { Client } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js";
import { StdioClientTransport } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js";
const transport = new StdioClientTransport({
command: "C:\\Program Files\\nodejs\\node.exe",
args: ["C:\\Users\\12914\\AppData\\Roaming\\TRAE SOLO CN\\ModularData\\ai-agent\\vm\\tools\\node\\node_modules\\antd-mcp\\dist\\cli.js"]
});
const client = new Client({ name: "call-test", version: "0.0.1" });
await client.connect(transport);
const r = await client.callTool({ name: "list-components", arguments: {} });
const text = r.content?.[0]?.text ?? JSON.stringify(r).slice(0, 300);
console.log("result length:", text.length, "| head:", text.slice(0, 160).replace(/\n/g, " | "));
await client.close();
process.exit(0);
+14
View File
@@ -0,0 +1,14 @@
# Opt-in overlay: mount the dsh-mcp-client bridge with a local demo MCP
# stdio server. Apply with:
# dsh web --patch <this file>
# or paste the insert list into a profile's cordis.patch.yml.
# Tools appear as mcp__demo__echo once the harness boots.
- insert:
- id: mcp-demo
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: demo
transport: stdio
command: node
args: ['C:/Users/12914/Desktop/vscode/mcp-demo/server.mjs']
failOnStartupError: false
+22
View File
@@ -0,0 +1,22 @@
// Stdio handshake smoke test for gitea-mcp.exe: spawn it over stdio pointing
// at gitea.mymoyu.top, list tools, call the read-only get_me tool. Because the
// dsh sandbox denies spawning processes with piped stdio (documented EPERM),
// this script must be run by the harness outside that boundary — the running
// dsh web process — or skipped in favor of the bridge's own live verification.
import { Client } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js";
import { StdioClientTransport } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js";
const exe = "C:\\Users\\12914\\.dsh\\bin\\gitea-mcp\\gitea-mcp.exe";
const transport = new StdioClientTransport({
command: exe,
args: ["-t", "stdio", "-H", "https://gitea.mymoyu.top"]
});
const client = new Client({ name: "smoke", version: "0.0.1" });
await client.connect(transport);
const tools = await client.listTools();
const names = tools.tools.map((t) => t.name);
console.log(JSON.stringify({ toolCount: names.length, sample: names.slice(0, 8) }));
await client.close();
process.exit(0);
+1
View File
@@ -0,0 +1 @@
iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACHSURBVHhe7dAhAQAADITA719681QAcQbJbjuzMdg0gMGmAQw2DWCwaQCDTQMYbBrAYNMABpsGMNg0gMGmAQw2DWCwaQCDTQMYbBrAYNMABpsGMNg0gMGmAQw2DWCwaQCDTQMYbBrAYNMABpsGMNg0gMGmAQw2DWCwaQCDTQMYbBrAYNMABpsHQ4jh0hEeUY0AAAAASUVORK5CYII=
+21
View File
@@ -0,0 +1,21 @@
// Stdio handshake smoke test for arbitrary MCP stdio servers.
// Usage: node mcp-smoke.mjs <command> [args...]
// Lists tools and prints the first few names.
import { Client } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js";
import { StdioClientTransport } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js";
const [command, ...args] = process.argv.slice(2);
if (!command) {
console.error("usage: node mcp-smoke.mjs <command> [args...]");
process.exit(1);
}
const transport = new StdioClientTransport({ command, args, env: { ...process.env } });
const client = new Client({ name: "smoke", version: "0.0.1" });
await client.connect(transport);
const tools = await client.listTools();
console.log(JSON.stringify({ toolCount: tools.tools.length, sample: tools.tools.slice(0, 10).map((t) => t.name) }));
await client.close();
process.exit(0);
+122
View File
@@ -0,0 +1,122 @@
// Capability probe for the user's configured third-party models.
// For each model: (1) vision probe — send a tiny PNG via image_url; if the
// API accepts image content the call succeeds. (2) thinking probes — send
// three wire-dialect hints (reasoning_effort, enable_thinking, thinking.type)
// and report which are accepted and whether reasoning_content comes back.
import { readFileSync } from "node:fs";
const cred = readFileSync("C:/Users/12914/.dsh/.credentials.yaml", "utf8");
function keyFor(name) {
const m = cred.match(new RegExp(`${name}:\\s*([^\\s]+)`));
return m ? m[1] : "";
}
// refs section: " B_API_KEY: sk-..."
function refKey(name) {
const m = cred.match(new RegExp(`\\s${name}: ([^\\s]+)`));
return m ? m[1] : "";
}
const TINY_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFklEQVR42mP8z8AAw0BowY0wxx1jGQDFmA1jJ0nK1AAAAABJRU5ErkJggg==";
const providers = [
{
name: "b",
base: "http://156.225.30.43:65423/bai/v1",
key: refKey("B_API_KEY"),
models: ["glm-5.3-flash", "qwen3.8-flash", "hy3", "mimo-v2.5"]
},
{
name: "command-code",
base: "https://api.commandcode.ai/provider/v1",
key: refKey("COMMAND_CODE_API_KEY"),
models: [
"deepseek/deepseek-v4-flash",
"z-ai/glm-5.3-flash",
"xiaomi/mimo-v2.5",
"meituan/LongCat-2.0:free",
"poolside/laguna-s-2.1-free"
]
}
];
async function chat(base, key, body, timeoutMs = 45000) {
const ctl = new AbortController();
const t = setTimeout(() => ctl.abort(), timeoutMs);
try {
const r = await fetch(`${base}/chat/completions`, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
body: JSON.stringify(body),
signal: ctl.signal
});
const text = await r.text();
let json = null;
try { json = JSON.parse(text); } catch {}
return { status: r.status, json, text: text.slice(0, 300) };
} catch (e) {
return { status: 0, json: null, text: `${e}` };
} finally { clearTimeout(t); }
}
async function probeVision(base, key, model) {
const r = await chat(base, key, {
model,
messages: [{ role: "user", content: [
{ type: "text", text: "Reply with one word: what color dominates this image?" },
{ type: "image_url", image_url: { url: `data:image/png;base64,${TINY_PNG}` } }
] }],
max_tokens: 32
});
if (r.status === 200 && r.json?.choices?.length) {
return { vision: "YES", note: (r.json.choices[0].message?.content ?? "").slice(0, 40) };
}
const err = (r.json?.error?.message ?? r.text ?? "").slice(0, 90);
return { vision: r.status === 0 ? "TIMEOUT" : "no", note: err };
}
async function probeThinking(base, key, model, variant) {
const extra = variant === "effort" ? { reasoning_effort: "low" }
: variant === "enable" ? { enable_thinking: true }
: { thinking: { type: "enabled" } };
const r = await chat(base, key, {
model,
messages: [{ role: "user", content: "1+1=? Answer with just the number." }],
max_tokens: 512,
...extra
});
if (r.status === 0) return { verdict: "TIMEOUT", reasoning: false, note: "" };
if (r.status !== 200) {
const err = (r.json?.error?.message ?? r.text ?? "").slice(0, 80);
return { verdict: "rejected", reasoning: false, note: err };
}
const msg = r.json?.choices?.[0]?.message ?? {};
const hasReasoning = typeof msg.reasoning_content === "string" && msg.reasoning_content.length > 0;
const usage = r.json?.usage ?? {};
const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens
?? usage.reasoning_tokens ?? null;
return {
verdict: "accepted",
reasoning: hasReasoning || (typeof reasoningTokens === "number" && reasoningTokens > 0),
note: `rt=${reasoningTokens ?? "-"}`
};
}
for (const p of providers) {
for (const model of p.models) {
const v = await probeVision(p.base, p.key, model);
const t1 = await probeThinking(p.base, p.key, model, "effort");
const t2 = await probeThinking(p.base, p.key, model, "enable");
const t3 = await probeThinking(p.base, p.key, model, "zai");
console.log(JSON.stringify({
provider: p.name, model,
vision: v.vision, visionNote: v.note,
thinking: {
reasoning_effort: `${t1.verdict}${t1.reasoning ? "+reasoning" : ""}`,
enable_thinking: `${t2.verdict}${t2.reasoning ? "+reasoning" : ""}`,
thinking_zai: `${t3.verdict}${t3.reasoning ? "+reasoning" : ""}`
},
hints: [t1.note, t2.note, t3.note].filter(n => n && n !== "rt=-").slice(0, 2)
}));
}
}
console.log("PROBE_DONE");
+48
View File
@@ -0,0 +1,48 @@
// Re-probe vision on models that errored or returned empty, with a proper
// 64x64 PNG. Also probes the clearly-vision-named models the user has NOT
// configured, to show what they are missing.
import { readFileSync } from "node:fs";
const cred = readFileSync("C:/Users/12914/.dsh/.credentials.yaml", "utf8");
function refKey(name) { const m = cred.match(new RegExp(`\\s${name}: ([^\\s]+)`)); return m ? m[1] : ""; }
const IMG = readFileSync("C:/Users/12914/Desktop/vscode/mcp-demo/img64.b64", "utf8").trim();
const targets = [
{ name: "b", base: "http://156.225.30.43:65423/bai/v1", key: refKey("B_API_KEY"),
models: ["glm-5.3-flash", "hy3", "mimo-v2.5", "deepseek-v4-flash-vision-exp", "gemini-3.8-flash"] },
{ name: "command-code", base: "https://api.commandcode.ai/provider/v1", key: refKey("COMMAND_CODE_API_KEY"),
models: ["z-ai/glm-5.3-flash", "xiaomi/mimo-v2.5", "meituan/LongCat-2.0:free", "deepseek/deepseek-v4-flash-vision-exp", "google/gemini-3.8-flash"] }
];
for (const p of targets) {
for (const model of p.models) {
const ctl = new AbortController();
const t = setTimeout(() => ctl.abort(), 45000);
try {
const r = await fetch(`${p.base}/chat/completions`, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${p.key}` },
body: JSON.stringify({
model,
messages: [{ role: "user", content: [
{ type: "text", text: "What color is this image? One word." },
{ type: "image_url", image_url: { url: `data:image/png;base64,${IMG}` } }
] }],
max_tokens: 32
}),
signal: ctl.signal
});
const text = await r.text();
let j = null; try { j = JSON.parse(text); } catch {}
const ok = r.status === 200 && j?.choices?.length;
console.log(JSON.stringify({
provider: p.name, model,
vision: ok ? "YES" : (r.status === 0 ? "TIMEOUT" : "no"),
note: ok ? (j.choices[0].message?.content ?? "").slice(0, 40) : (j?.error?.message ?? text.slice(0, 80))
}));
} catch (e) {
console.log(JSON.stringify({ provider: p.name, model, vision: "TIMEOUT", note: `${e}`.slice(0, 60) }));
} finally { clearTimeout(t); }
}
}
console.log("DONE");
+34
View File
@@ -0,0 +1,34 @@
// Reproduce the 1214 role error: same endpoint, glm-5.3-flash, with a
// realistic harness-shaped conversation (system + tool_calls + tool result),
// once WITH the zai thinking param and once WITHOUT it.
import { readFileSync } from "node:fs";
const cred = readFileSync("C:/Users/12914/.dsh/.credentials.yaml", "utf8");
const key = cred.match(/\sB_API_KEY: ([^\s]+)/)[1];
const base = "http://156.225.30.43:65423/bai/v1";
const convo = [
{ role: "system", content: "You are a helpful coding agent." },
{ role: "user", content: "List the files in the project." },
{ role: "assistant", content: null, tool_calls: [
{ id: "call_1", type: "function", function: { name: "ls", arguments: "{}" } }
] },
{ role: "tool", tool_call_id: "call_1", content: "src/\npackage.json" }
];
async function call(label, extra) {
const r = await fetch(`${base}/chat/completions`, {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
body: JSON.stringify({ model: "glm-5.3-flash", messages: convo, max_tokens: 64, ...extra })
});
const text = await r.text();
let j = null; try { j = JSON.parse(text); } catch {}
const code = j?.code ?? j?.error?.code ?? r.status;
console.log(`${label}: status=${r.status} code=${JSON.stringify(code)} msg=${(j?.message ?? j?.error?.message ?? (j?.choices?.[0]?.message?.content ?? "")).slice(0, 60)}`);
}
await call("with-thinking-enabled ", { thinking: { type: "enabled", clear_thinking: false } });
await call("with-thinking-disabled ", { thinking: { type: "disabled" } });
await call("without-thinking-param ", {});
await call("with-enable-thinking ", { enable_thinking: true });
console.log("REPRO_DONE");
+41
View File
@@ -0,0 +1,41 @@
// Minimal MCP stdio server for verifying the dsh-mcp-client bridge.
// Exposes one tool `echo` (returns its `message` argument) using the
// @modelcontextprotocol/sdk vendored with the local dsh installation.
// Imports use absolute file URLs so the script resolves the SDK from
// the dsh profiles junction farm; the SDK's own `zod` import resolves
// from the same farm.
//
// Run directly (`node server.mjs`) it serves MCP over stdio for the
// dsh-mcp-client bridge. Imported as a module it exports createServer()
// so tests can drive the real handlers over InMemoryTransport.
import { McpServer } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js";
import { StdioServerTransport } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js";
import { z } from "file:///C:/Users/12914/.dsh/profiles/node_modules/zod/index.js";
import { pathToFileURL } from "node:url";
export function createServer() {
const server = new McpServer({
name: "dsh-demo",
version: "0.1.0"
});
server.registerTool("echo", {
title: "Echo",
description: "Returns the message it was given, prefixed with 'echo:'.",
inputSchema: {
message: z.string().describe("Text to echo back.")
}
}, async ({ message }) => ({
content: [{ type: "text", text: `echo: ${message}` }]
}));
return server;
}
const isMain = process.argv[1] !== undefined
&& import.meta.url === pathToFileURL(process.argv[1]).href;
if (isMain) {
const server = createServer();
await server.connect(new StdioServerTransport());
}
+29
View File
@@ -0,0 +1,29 @@
// In-process round-trip test for server.mjs: import the real createServer()
// and drive its actual tool handlers over InMemoryTransport (no process
// spawn, no stdio pipes — the sandbox denies those).
import { Client } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js";
import { InMemoryTransport } from "file:///C:/Users/12914/.dsh/profiles/node_modules/@modelcontextprotocol/sdk/dist/esm/inMemory.js";
import { createServer } from "./server.mjs";
const server = createServer();
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
const client = new Client({ name: "smoke", version: "0.0.1" });
await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
const tools = await client.listTools();
const names = tools.tools.map((t) => t.name).sort();
if (JSON.stringify(names) !== JSON.stringify(["echo"])) {
console.error("FAIL: unexpected tool list", names);
process.exit(1);
}
const result = await client.callTool({ name: "echo", arguments: { message: "ping" } });
const text = result.content?.[0]?.text;
if (text !== "echo: ping") {
console.error("FAIL: unexpected echo result", JSON.stringify(result));
process.exit(1);
}
console.log(JSON.stringify({ tools: names, echo: text }));
await client.close();
process.exit(0);
View File