weapp-vite 6.13.4 → 6.14.1
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 +15 -1
- package/dist/auto-import-components/resolvers.d.mts +1 -1
- package/dist/auto-routes.d.mts +1 -1
- package/dist/auto-routes.mjs +1 -1
- package/dist/cli.mjs +920 -171
- package/dist/config-DJjSbpNX.mjs +6 -0
- package/dist/{config-CiuNcUYK.d.mts → config-GqcM7sq1.d.mts} +15 -3
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +2 -6
- package/dist/{createContext-Bb4OSo1-.mjs → createContext-DT4tLA1C.mjs} +11083 -10887
- package/dist/docs/README.md +15 -1
- package/dist/docs/mcp.md +88 -16
- package/dist/file-3AtJoiKx.mjs +2 -0
- package/dist/{file-Cf3pf5w7.mjs → file-BkvmW6_2.mjs} +2 -18
- package/dist/getInstance-ChnpuIME.mjs +2 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +4 -4
- package/dist/json-D0HkutE0.mjs +33 -0
- package/dist/json.d.mts +1 -1
- package/dist/json.mjs +1 -32
- package/dist/mcp-DRlj32v4.mjs +39 -0
- package/dist/mcp.d.mts +1 -1
- package/dist/mcp.mjs +1 -38
- package/dist/runtime.d.mts +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/types.d.mts +3 -3
- package/package.json +17 -14
- package/dist/rolldown-runtime-twds-ZHy.mjs +0 -14
- /package/dist/{index-CUFOUU8V.d.mts → index-hN5mdpQ_.d.mts} +0 -0
- /package/dist/{logger-gutcwWKE.mjs → logger-CgxdNjvb.mjs} +0 -0
- /package/dist/{routes-o20IHwXa.d.mts → routes-DiEBrMtj.d.mts} +0 -0
- /package/dist/{runtime-CpQutkhu.d.mts → runtime-58wbbRDf.d.mts} +0 -0
- /package/dist/{runtime-gyZnAGCZ.mjs → runtime-tVnzZwtl.mjs} +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
import { r as logger_default, t as colors } from "./logger-
|
|
3
|
-
import {
|
|
4
|
-
import { resolveWeappMcpConfig, startWeappViteMcpServer } from "./mcp.mjs";
|
|
1
|
+
import { S as isPathInside, _ as DEFAULT_MP_PLATFORM, b as resolveMiniPlatform, c as createSharedBuildConfig, d as resolveWeappConfigFile, f as checkRuntime, g as createCjsConfigLoadError, h as parseCommentJson, l as SHARED_CHUNK_VIRTUAL_PREFIX, m as loadViteConfigFile, n as syncProjectSupportFiles, p as getProjectConfigFileName, r as syncManagedTsconfigBootstrapFiles, s as formatBytes, t as createCompilerContext, v as getDefaultIdeProjectRoot, x as shouldPassPlatformArgToIdeOpen, y as normalizeMiniPlatform } from "./createContext-DT4tLA1C.mjs";
|
|
2
|
+
import { r as logger_default, t as colors } from "./logger-CgxdNjvb.mjs";
|
|
3
|
+
import { m as VERSION } from "./file-BkvmW6_2.mjs";
|
|
4
|
+
import { a as resolveWeappMcpConfig, o as startWeappViteMcpServer } from "./mcp-DRlj32v4.mjs";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { defu, fs } from "@weapp-core/shared";
|
|
7
7
|
import path, { posix } from "pathe";
|
|
8
8
|
import process from "node:process";
|
|
9
9
|
import fs$1 from "node:fs/promises";
|
|
10
10
|
import { build, createServer } from "vite";
|
|
11
|
+
import os from "node:os";
|
|
11
12
|
import { execFile } from "node:child_process";
|
|
12
13
|
import { Buffer } from "node:buffer";
|
|
13
14
|
import fs$2 from "node:fs";
|
|
14
15
|
import { cac } from "cac";
|
|
15
16
|
import { resolveCommand } from "package-manager-detector/commands";
|
|
16
17
|
import { promisify } from "node:util";
|
|
17
|
-
import { formatRetryHotkeyPrompt, formatWechatIdeLoginRequiredError, getConfig, isWeappIdeTopLevelCommand, isWechatIdeLoginRequiredError, parse, startForwardConsole, waitForRetryKeypress } from "weapp-ide-cli";
|
|
18
|
+
import { closeSharedMiniProgram, connectOpenedAutomator, formatRetryHotkeyPrompt, formatWechatIdeLoginRequiredError, getConfig, isWeappIdeTopLevelCommand, isWechatIdeLoginRequiredError, launchAutomator, parse, startForwardConsole, takeScreenshot, waitForRetryKeypress } from "weapp-ide-cli";
|
|
18
19
|
import { generateJs, generateJson, generateWxml, generateWxss } from "@weapp-core/schematics";
|
|
19
20
|
import { determineAgent } from "@vercel/detect-agent";
|
|
20
21
|
import { initConfig } from "@weapp-core/init";
|
|
22
|
+
import { createInterface } from "node:readline/promises";
|
|
23
|
+
import { clearTimeout, setTimeout as setTimeout$1 } from "node:timers";
|
|
24
|
+
import { emitKeypressEvents } from "node:readline";
|
|
21
25
|
//#region src/analyze/subpackages/classifier.ts
|
|
22
26
|
const VIRTUAL_MODULE_INDICATOR = "\0";
|
|
23
27
|
const VIRTUAL_PREFIX = `${SHARED_CHUNK_VIRTUAL_PREFIX}/`;
|
|
@@ -950,13 +954,27 @@ function logBuildPackageSizeReport(options) {
|
|
|
950
954
|
//#region src/cli/openIde.ts
|
|
951
955
|
const execFileAsync = promisify(execFile);
|
|
952
956
|
async function openWechatIdeByAutomator(projectPath) {
|
|
953
|
-
|
|
954
|
-
(await new Launcher().launch({
|
|
957
|
+
(await launchAutomator({
|
|
955
958
|
projectPath,
|
|
956
959
|
trustProject: true
|
|
957
960
|
})).disconnect();
|
|
958
961
|
}
|
|
959
962
|
/**
|
|
963
|
+
* @description 若当前项目已在微信开发者工具中打开且自动化可连通,则直接复用现有会话,避免重复拉起 IDE。
|
|
964
|
+
*/
|
|
965
|
+
async function tryReuseOpenedWechatIde(projectPath) {
|
|
966
|
+
try {
|
|
967
|
+
(await connectOpenedAutomator({
|
|
968
|
+
projectPath,
|
|
969
|
+
timeout: 3e3
|
|
970
|
+
})).disconnect();
|
|
971
|
+
logger_default.info("目标项目已在微信开发者工具中打开,跳过重复打开。");
|
|
972
|
+
return true;
|
|
973
|
+
} catch {
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
960
978
|
* @description 执行 IDE 打开流程,并在登录失效时允许按键重试。
|
|
961
979
|
*/
|
|
962
980
|
async function runWechatIdeOpenWithRetry(argv) {
|
|
@@ -1017,6 +1035,7 @@ function resolveIdeProjectRoot(mpDistRoot, cwd) {
|
|
|
1017
1035
|
}
|
|
1018
1036
|
async function openIde(platform, projectPath, options = {}) {
|
|
1019
1037
|
if (platform === "weapp" && projectPath && options.trustProject !== false) try {
|
|
1038
|
+
if (await tryReuseOpenedWechatIde(projectPath)) return;
|
|
1020
1039
|
await openWechatIdeByAutomator(projectPath);
|
|
1021
1040
|
return;
|
|
1022
1041
|
} catch (error) {
|
|
@@ -1117,7 +1136,9 @@ function registerBuildCommand(cli) {
|
|
|
1117
1136
|
configFile,
|
|
1118
1137
|
inlineConfig,
|
|
1119
1138
|
cliPlatform: targets.rawPlatform,
|
|
1120
|
-
projectConfigPath: options.projectConfig
|
|
1139
|
+
projectConfigPath: options.projectConfig,
|
|
1140
|
+
syncSupportFiles: false,
|
|
1141
|
+
preloadAppEntry: false
|
|
1121
1142
|
});
|
|
1122
1143
|
const { buildService, configService, webService } = ctx;
|
|
1123
1144
|
logRuntimeTarget(targets, { resolvedConfigPlatform: configService.platform });
|
|
@@ -1588,6 +1609,278 @@ function registerInitCommand(cli) {
|
|
|
1588
1609
|
});
|
|
1589
1610
|
}
|
|
1590
1611
|
//#endregion
|
|
1612
|
+
//#region src/cli/mcpClient.ts
|
|
1613
|
+
const CODEX_BLOCK_PREFIX = "# >>> weapp-vite mcp ";
|
|
1614
|
+
const CODEX_BLOCK_SUFFIX = " >>>";
|
|
1615
|
+
const CODEX_BLOCK_END_PREFIX = "# <<< weapp-vite mcp ";
|
|
1616
|
+
const CODEX_BLOCK_END_SUFFIX = " <<<";
|
|
1617
|
+
const DEFAULT_HTTP_TIMEOUT_MS = 1500;
|
|
1618
|
+
const WORKSPACE_FOLDER_TOKEN = "${workspaceFolder}";
|
|
1619
|
+
const REG_CodexUrl = /^\s*url\s*=\s*"([^"]+)"/m;
|
|
1620
|
+
const REG_CodexCommand = /^\s*command\s*=\s*"([^"]+)"/m;
|
|
1621
|
+
const REG_CodexArgs = /^\s*args\s*=\s*\[([^\]]*)\]/m;
|
|
1622
|
+
const REG_FirstTomlString = /"([^"]+)"/;
|
|
1623
|
+
function resolveTargetWorkspaceRoot(target) {
|
|
1624
|
+
if (target.client === "cursor") return path.dirname(path.dirname(target.configPath));
|
|
1625
|
+
if (target.client === "claude-code") return path.dirname(target.configPath);
|
|
1626
|
+
return process.cwd();
|
|
1627
|
+
}
|
|
1628
|
+
function escapeRegExp(value) {
|
|
1629
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1630
|
+
}
|
|
1631
|
+
function renderTomlString(value) {
|
|
1632
|
+
return JSON.stringify(value);
|
|
1633
|
+
}
|
|
1634
|
+
function sanitizeServerNameSegment(value) {
|
|
1635
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "");
|
|
1636
|
+
}
|
|
1637
|
+
function resolveServerName(workspaceRoot) {
|
|
1638
|
+
return `weapp-vite-${sanitizeServerNameSegment(path.basename(workspaceRoot)) || "project"}`;
|
|
1639
|
+
}
|
|
1640
|
+
function resolveNodeBinCommand() {
|
|
1641
|
+
return process.execPath || "node";
|
|
1642
|
+
}
|
|
1643
|
+
function resolveProjectCliBin(workspaceRoot) {
|
|
1644
|
+
return path.join(workspaceRoot, "node_modules", "weapp-vite", "bin", "weapp-vite.js");
|
|
1645
|
+
}
|
|
1646
|
+
function renderCommandArgs(args) {
|
|
1647
|
+
return args.map(renderTomlString).join(", ");
|
|
1648
|
+
}
|
|
1649
|
+
function renderCodexBlock(serverName, entry) {
|
|
1650
|
+
const startMarker = `${CODEX_BLOCK_PREFIX}${serverName}${CODEX_BLOCK_SUFFIX}`;
|
|
1651
|
+
const endMarker = `${CODEX_BLOCK_END_PREFIX}${serverName}${CODEX_BLOCK_END_SUFFIX}`;
|
|
1652
|
+
if (entry.url) return `${startMarker}
|
|
1653
|
+
[mcp_servers.${serverName}]
|
|
1654
|
+
url = ${renderTomlString(entry.url)}
|
|
1655
|
+
${endMarker}
|
|
1656
|
+
`;
|
|
1657
|
+
return `${startMarker}
|
|
1658
|
+
[mcp_servers.${serverName}]
|
|
1659
|
+
command = ${renderTomlString(entry.command ?? resolveNodeBinCommand())}
|
|
1660
|
+
args = [${renderCommandArgs(entry.args ?? [])}]
|
|
1661
|
+
${endMarker}
|
|
1662
|
+
`;
|
|
1663
|
+
}
|
|
1664
|
+
function renderJsonPreview(serverName, entry) {
|
|
1665
|
+
return `${JSON.stringify({ mcpServers: { [serverName]: entry } }, null, 2)}
|
|
1666
|
+
`;
|
|
1667
|
+
}
|
|
1668
|
+
function resolveCommandEntry(client, workspaceRoot) {
|
|
1669
|
+
const binPath = resolveProjectCliBin(workspaceRoot);
|
|
1670
|
+
if (client === "cursor") return {
|
|
1671
|
+
command: "node",
|
|
1672
|
+
args: [
|
|
1673
|
+
`${WORKSPACE_FOLDER_TOKEN}/node_modules/weapp-vite/bin/weapp-vite.js`,
|
|
1674
|
+
"mcp",
|
|
1675
|
+
"--workspace-root",
|
|
1676
|
+
WORKSPACE_FOLDER_TOKEN
|
|
1677
|
+
]
|
|
1678
|
+
};
|
|
1679
|
+
return {
|
|
1680
|
+
command: resolveNodeBinCommand(),
|
|
1681
|
+
args: [
|
|
1682
|
+
binPath,
|
|
1683
|
+
"mcp",
|
|
1684
|
+
"--workspace-root",
|
|
1685
|
+
workspaceRoot
|
|
1686
|
+
]
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
function resolveHttpEntry(client, url) {
|
|
1690
|
+
if (client === "claude-code") return {
|
|
1691
|
+
type: "http",
|
|
1692
|
+
url
|
|
1693
|
+
};
|
|
1694
|
+
return { url };
|
|
1695
|
+
}
|
|
1696
|
+
function resolveCodexBlockPattern(serverName) {
|
|
1697
|
+
const startMarker = `${CODEX_BLOCK_PREFIX}${serverName}${CODEX_BLOCK_SUFFIX}`;
|
|
1698
|
+
const endMarker = `${CODEX_BLOCK_END_PREFIX}${serverName}${CODEX_BLOCK_END_SUFFIX}`;
|
|
1699
|
+
return new RegExp(`${escapeRegExp(startMarker)}[\\s\\S]*?${escapeRegExp(endMarker)}\\n?`, "g");
|
|
1700
|
+
}
|
|
1701
|
+
function upsertCodexManagedBlock(content, serverName, block) {
|
|
1702
|
+
const withoutExisting = content.trimEnd().replace(resolveCodexBlockPattern(serverName), "").trimEnd();
|
|
1703
|
+
if (!withoutExisting) return block;
|
|
1704
|
+
return `${withoutExisting}\n\n${block}`;
|
|
1705
|
+
}
|
|
1706
|
+
function parseJsonConfig(content, configPath) {
|
|
1707
|
+
if (!content.trim()) return {};
|
|
1708
|
+
try {
|
|
1709
|
+
return JSON.parse(content);
|
|
1710
|
+
} catch (error) {
|
|
1711
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1712
|
+
throw new Error(`无法解析 MCP 配置文件 ${configPath}:${message}`);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
function resolveTarget(client, workspaceRoot) {
|
|
1716
|
+
const serverName = resolveServerName(workspaceRoot);
|
|
1717
|
+
if (client === "codex") return {
|
|
1718
|
+
client,
|
|
1719
|
+
configPath: path.join(os.homedir(), ".codex", "config.toml"),
|
|
1720
|
+
displayName: "Codex",
|
|
1721
|
+
serverName
|
|
1722
|
+
};
|
|
1723
|
+
if (client === "claude-code") return {
|
|
1724
|
+
client,
|
|
1725
|
+
configPath: path.join(workspaceRoot, ".mcp.json"),
|
|
1726
|
+
displayName: "Claude Code",
|
|
1727
|
+
serverName
|
|
1728
|
+
};
|
|
1729
|
+
return {
|
|
1730
|
+
client,
|
|
1731
|
+
configPath: path.join(workspaceRoot, ".cursor", "mcp.json"),
|
|
1732
|
+
displayName: "Cursor",
|
|
1733
|
+
serverName
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
function resolveSupportedMcpClient(input) {
|
|
1737
|
+
if (input === "codex" || input === "claude-code" || input === "cursor") return input;
|
|
1738
|
+
throw new Error(`不支持的 MCP 客户端:${input}。当前仅支持 codex、claude-code、cursor。`);
|
|
1739
|
+
}
|
|
1740
|
+
function buildMcpClientConfigPlan(options) {
|
|
1741
|
+
const workspaceRoot = path.resolve(options.workspaceRoot);
|
|
1742
|
+
const target = resolveTarget(options.client, workspaceRoot);
|
|
1743
|
+
const entry = options.transport === "http" ? resolveHttpEntry(options.client, options.url ?? "") : resolveCommandEntry(options.client, workspaceRoot);
|
|
1744
|
+
if (options.transport === "http" && !entry.url) throw new Error("HTTP 模式缺少 MCP 服务地址,请使用 --url 指定或在当前项目中检测到可用地址。");
|
|
1745
|
+
return {
|
|
1746
|
+
entry,
|
|
1747
|
+
preview: target.client === "codex" ? renderCodexBlock(target.serverName, entry) : renderJsonPreview(target.serverName, entry),
|
|
1748
|
+
target,
|
|
1749
|
+
transport: options.transport
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
async function writeMcpClientConfig(plan) {
|
|
1753
|
+
await fs.ensureDir(path.dirname(plan.target.configPath));
|
|
1754
|
+
const existing = await fs.readFile(plan.target.configPath, "utf8").catch(() => "");
|
|
1755
|
+
if (plan.target.client === "codex") {
|
|
1756
|
+
const nextContent = upsertCodexManagedBlock(existing, plan.target.serverName, plan.preview);
|
|
1757
|
+
await fs.writeFile(plan.target.configPath, nextContent, "utf8");
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
const parsed = parseJsonConfig(existing, plan.target.configPath);
|
|
1761
|
+
const nextConfig = {
|
|
1762
|
+
...parsed,
|
|
1763
|
+
mcpServers: {
|
|
1764
|
+
...parsed.mcpServers ?? {},
|
|
1765
|
+
[plan.target.serverName]: plan.entry
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1768
|
+
await fs.writeFile(plan.target.configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
|
|
1769
|
+
}
|
|
1770
|
+
async function probeHttpEndpoint(url) {
|
|
1771
|
+
const controller = new AbortController();
|
|
1772
|
+
const timer = setTimeout$1(() => {
|
|
1773
|
+
controller.abort();
|
|
1774
|
+
}, DEFAULT_HTTP_TIMEOUT_MS);
|
|
1775
|
+
try {
|
|
1776
|
+
return (await fetch(url, {
|
|
1777
|
+
method: "GET",
|
|
1778
|
+
signal: controller.signal
|
|
1779
|
+
})).status > 0;
|
|
1780
|
+
} catch {
|
|
1781
|
+
return false;
|
|
1782
|
+
} finally {
|
|
1783
|
+
clearTimeout(timer);
|
|
1784
|
+
controller.abort();
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
async function inspectJsonConfig(target) {
|
|
1788
|
+
if (!await fs.pathExists(target.configPath)) return {
|
|
1789
|
+
configExists: false,
|
|
1790
|
+
configPath: target.configPath,
|
|
1791
|
+
displayName: target.displayName,
|
|
1792
|
+
issues: [`未找到配置文件:${target.configPath}`],
|
|
1793
|
+
serverName: target.serverName
|
|
1794
|
+
};
|
|
1795
|
+
const entry = parseJsonConfig(await fs.readFile(target.configPath, "utf8"), target.configPath).mcpServers?.[target.serverName];
|
|
1796
|
+
if (!entry) return {
|
|
1797
|
+
configExists: true,
|
|
1798
|
+
configPath: target.configPath,
|
|
1799
|
+
displayName: target.displayName,
|
|
1800
|
+
issues: [`配置文件中未找到服务器 ${target.serverName}`],
|
|
1801
|
+
serverName: target.serverName
|
|
1802
|
+
};
|
|
1803
|
+
const transport = entry.url ? "http" : "command";
|
|
1804
|
+
const issues = [];
|
|
1805
|
+
let httpReachable;
|
|
1806
|
+
if (transport === "command") {
|
|
1807
|
+
const normalizedBin = (entry.args?.[0])?.replaceAll(WORKSPACE_FOLDER_TOKEN, resolveTargetWorkspaceRoot(target));
|
|
1808
|
+
if (!normalizedBin || !await fs.pathExists(normalizedBin)) issues.push(`未找到本地 CLI 入口:${normalizedBin ?? "缺少 args[0]"}`);
|
|
1809
|
+
} else if (entry.url) {
|
|
1810
|
+
httpReachable = await probeHttpEndpoint(entry.url);
|
|
1811
|
+
if (!httpReachable) issues.push(`HTTP MCP 地址不可达:${entry.url}`);
|
|
1812
|
+
}
|
|
1813
|
+
return {
|
|
1814
|
+
configExists: true,
|
|
1815
|
+
configPath: target.configPath,
|
|
1816
|
+
displayName: target.displayName,
|
|
1817
|
+
httpReachable,
|
|
1818
|
+
issues,
|
|
1819
|
+
serverName: target.serverName,
|
|
1820
|
+
transport
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
async function inspectCodexConfig(target) {
|
|
1824
|
+
if (!await fs.pathExists(target.configPath)) return {
|
|
1825
|
+
configExists: false,
|
|
1826
|
+
configPath: target.configPath,
|
|
1827
|
+
displayName: target.displayName,
|
|
1828
|
+
issues: [`未找到配置文件:${target.configPath}`],
|
|
1829
|
+
serverName: target.serverName
|
|
1830
|
+
};
|
|
1831
|
+
const blockMatch = (await fs.readFile(target.configPath, "utf8")).match(resolveCodexBlockPattern(target.serverName));
|
|
1832
|
+
if (!blockMatch?.[0]) return {
|
|
1833
|
+
configExists: true,
|
|
1834
|
+
configPath: target.configPath,
|
|
1835
|
+
displayName: target.displayName,
|
|
1836
|
+
issues: [`未找到 weapp-vite 生成的 Codex 配置区块:${target.serverName}`],
|
|
1837
|
+
serverName: target.serverName
|
|
1838
|
+
};
|
|
1839
|
+
const block = blockMatch[0];
|
|
1840
|
+
const issues = [];
|
|
1841
|
+
const urlMatch = block.match(REG_CodexUrl);
|
|
1842
|
+
const commandMatch = block.match(REG_CodexCommand);
|
|
1843
|
+
const argsMatch = block.match(REG_CodexArgs);
|
|
1844
|
+
if (urlMatch?.[1]) {
|
|
1845
|
+
const reachable = await probeHttpEndpoint(urlMatch[1]);
|
|
1846
|
+
if (!reachable) issues.push(`HTTP MCP 地址不可达:${urlMatch[1]}`);
|
|
1847
|
+
return {
|
|
1848
|
+
configExists: true,
|
|
1849
|
+
configPath: target.configPath,
|
|
1850
|
+
displayName: target.displayName,
|
|
1851
|
+
httpReachable: reachable,
|
|
1852
|
+
issues,
|
|
1853
|
+
serverName: target.serverName,
|
|
1854
|
+
transport: "http"
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
const binPath = (argsMatch?.[1]?.match(REG_FirstTomlString))?.[1];
|
|
1858
|
+
if (!commandMatch?.[1]) issues.push("Codex 配置区块缺少 command 字段");
|
|
1859
|
+
if (!binPath || !await fs.pathExists(binPath)) issues.push(`未找到本地 CLI 入口:${binPath ?? "缺少 args[0]"}`);
|
|
1860
|
+
return {
|
|
1861
|
+
configExists: true,
|
|
1862
|
+
configPath: target.configPath,
|
|
1863
|
+
displayName: target.displayName,
|
|
1864
|
+
issues,
|
|
1865
|
+
serverName: target.serverName,
|
|
1866
|
+
transport: "command"
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
async function inspectMcpClientConfig(options) {
|
|
1870
|
+
const target = resolveTarget(options.client, path.resolve(options.workspaceRoot));
|
|
1871
|
+
if (target.client === "codex") return await inspectCodexConfig(target);
|
|
1872
|
+
return await inspectJsonConfig(target);
|
|
1873
|
+
}
|
|
1874
|
+
function formatMcpQuickStart(options) {
|
|
1875
|
+
const suffix = options.transport === "http" && options.httpUrl ? ` --transport http --url ${options.httpUrl}` : "";
|
|
1876
|
+
return [
|
|
1877
|
+
"在 AI 工具中接入 weapp-vite MCP:",
|
|
1878
|
+
` - Codex: wv mcp init codex${suffix}`,
|
|
1879
|
+
` - Claude Code: wv mcp init claude-code${suffix}`,
|
|
1880
|
+
` - Cursor: wv mcp init cursor${suffix}`
|
|
1881
|
+
];
|
|
1882
|
+
}
|
|
1883
|
+
//#endregion
|
|
1591
1884
|
//#region src/cli/commands/mcp.ts
|
|
1592
1885
|
function resolvePort(port) {
|
|
1593
1886
|
if (typeof port === "number" && Number.isInteger(port)) return port;
|
|
@@ -1596,17 +1889,109 @@ function resolvePort(port) {
|
|
|
1596
1889
|
if (Number.isInteger(parsed)) return parsed;
|
|
1597
1890
|
}
|
|
1598
1891
|
}
|
|
1892
|
+
async function resolveHttpUrl(options) {
|
|
1893
|
+
if (options.url?.trim()) return options.url.trim();
|
|
1894
|
+
const workspaceRoot = options.workspaceRoot ? path.resolve(options.workspaceRoot) : process.cwd();
|
|
1895
|
+
const originalCwd = process.cwd();
|
|
1896
|
+
try {
|
|
1897
|
+
if (workspaceRoot !== originalCwd) process.chdir(workspaceRoot);
|
|
1898
|
+
const resolved = resolveWeappMcpConfig((await loadConfig(resolveConfigFile(options)))?.config?.weapp?.mcp);
|
|
1899
|
+
return `http://${resolved.host}:${resolved.port}${resolved.endpoint}`;
|
|
1900
|
+
} catch {
|
|
1901
|
+
const resolved = resolveWeappMcpConfig(void 0);
|
|
1902
|
+
return `http://${resolved.host}:${resolved.port}${resolved.endpoint}`;
|
|
1903
|
+
} finally {
|
|
1904
|
+
if (process.cwd() !== originalCwd) process.chdir(originalCwd);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
async function confirmWrite() {
|
|
1908
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
1909
|
+
const rl = createInterface({
|
|
1910
|
+
input: process.stdin,
|
|
1911
|
+
output: process.stdout
|
|
1912
|
+
});
|
|
1913
|
+
try {
|
|
1914
|
+
const normalized = (await rl.question("是否写入配置文件?(Y/n) ")).trim().toLowerCase();
|
|
1915
|
+
return normalized === "" || normalized === "y" || normalized === "yes";
|
|
1916
|
+
} finally {
|
|
1917
|
+
rl.close();
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
function resolveClientTransport(transport) {
|
|
1921
|
+
return transport === "http" ? "http" : "command";
|
|
1922
|
+
}
|
|
1923
|
+
async function handlePrint(clientName, options, write = false) {
|
|
1924
|
+
const client = resolveSupportedMcpClient(clientName);
|
|
1925
|
+
const workspaceRoot = options.workspaceRoot ?? process.cwd();
|
|
1926
|
+
const transport = resolveClientTransport(options.transport);
|
|
1927
|
+
const plan = buildMcpClientConfigPlan({
|
|
1928
|
+
client,
|
|
1929
|
+
transport,
|
|
1930
|
+
url: transport === "http" ? await resolveHttpUrl(options) : void 0,
|
|
1931
|
+
workspaceRoot
|
|
1932
|
+
});
|
|
1933
|
+
logger_default.info(`${plan.target.displayName} 配置文件:${plan.target.configPath}`);
|
|
1934
|
+
logger_default.info(`服务器名称:${plan.target.serverName}`);
|
|
1935
|
+
process.stdout.write(`${plan.preview}\n`);
|
|
1936
|
+
if (!write) return;
|
|
1937
|
+
if (options.yes || await confirmWrite()) {
|
|
1938
|
+
await writeMcpClientConfig(plan);
|
|
1939
|
+
logger_default.success(`已写入 ${plan.target.displayName} MCP 配置。`);
|
|
1940
|
+
logger_default.info(`请重启 ${plan.target.displayName},然后执行:wv mcp doctor ${client}`);
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
logger_default.info("已取消写入。");
|
|
1944
|
+
}
|
|
1945
|
+
async function handleDoctor(clientName, options) {
|
|
1946
|
+
const result = await inspectMcpClientConfig({
|
|
1947
|
+
client: resolveSupportedMcpClient(clientName),
|
|
1948
|
+
workspaceRoot: options.workspaceRoot ?? process.cwd()
|
|
1949
|
+
});
|
|
1950
|
+
logger_default.info(`${result.displayName} 配置文件:${result.configPath}`);
|
|
1951
|
+
logger_default.info(`服务器名称:${result.serverName}`);
|
|
1952
|
+
if (!result.configExists || result.issues.length > 0) {
|
|
1953
|
+
for (const issue of result.issues) logger_default.warn(issue);
|
|
1954
|
+
throw new Error("MCP 客户端配置检查未通过。");
|
|
1955
|
+
}
|
|
1956
|
+
if (result.transport) logger_default.info(`传输模式:${result.transport}`);
|
|
1957
|
+
if (result.httpReachable !== void 0) logger_default.info(`HTTP 服务可达:${result.httpReachable ? "是" : "否"}`);
|
|
1958
|
+
logger_default.success("MCP 客户端配置检查通过。");
|
|
1959
|
+
}
|
|
1960
|
+
async function handleServer(options) {
|
|
1961
|
+
const resolvedTransport = options.transport === "http" ? "streamable-http" : options.transport;
|
|
1962
|
+
await startWeappViteMcpServer({
|
|
1963
|
+
endpoint: options.endpoint,
|
|
1964
|
+
host: options.host,
|
|
1965
|
+
port: resolvePort(options.port),
|
|
1966
|
+
transport: resolvedTransport,
|
|
1967
|
+
unref: options.unref,
|
|
1968
|
+
workspaceRoot: options.workspaceRoot
|
|
1969
|
+
});
|
|
1970
|
+
for (const line of formatMcpQuickStart({
|
|
1971
|
+
httpUrl: resolvedTransport === "streamable-http" ? `http://${options.host ?? "127.0.0.1"}:${resolvePort(options.port) ?? 3088}${options.endpoint ?? "/mcp"}` : void 0,
|
|
1972
|
+
transport: resolvedTransport === "streamable-http" ? "http" : "command"
|
|
1973
|
+
})) logger_default.info(line);
|
|
1974
|
+
}
|
|
1599
1975
|
function registerMcpCommand(cli) {
|
|
1600
|
-
cli.command("mcp", "start weapp-vite MCP server").option("--transport <type>", "[string] stdio | streamable-http", { default: "stdio" }).option("--host <host>", "[string] streamable-http host").option("--port <port>", "[number] streamable-http port").option("--endpoint <path>", "[string] streamable-http endpoint path").option("--unref", "[boolean] unref HTTP server to not block process exit").option("--workspace-root <path>", "[string] workspace root path, defaults to
|
|
1601
|
-
const
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1976
|
+
cli.command("mcp [...args]", "start weapp-vite MCP server or manage MCP client onboarding").option("--transport <type>", "[string] stdio | streamable-http | command | http", { default: "stdio" }).option("--host <host>", "[string] streamable-http host").option("--port <port>", "[number] streamable-http port").option("--endpoint <path>", "[string] streamable-http endpoint path").option("--unref", "[boolean] unref HTTP server to not block process exit").option("--url <url>", "[string] explicit HTTP MCP url").option("--workspace-root <path>", "[string] workspace root path, defaults to cwd").option("-y, --yes", "[boolean] write config without prompt").action(async (args, options) => {
|
|
1977
|
+
const [subcommand, client] = args;
|
|
1978
|
+
if (subcommand === "init") {
|
|
1979
|
+
if (!client) throw new Error("缺少客户端名称,请使用:wv mcp init <codex|claude-code|cursor>");
|
|
1980
|
+
await handlePrint(client, options, true);
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
if (subcommand === "print") {
|
|
1984
|
+
if (!client) throw new Error("缺少客户端名称,请使用:wv mcp print <codex|claude-code|cursor>");
|
|
1985
|
+
await handlePrint(client, options, false);
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1988
|
+
if (subcommand === "doctor") {
|
|
1989
|
+
if (!client) throw new Error("缺少客户端名称,请使用:wv mcp doctor <codex|claude-code|cursor>");
|
|
1990
|
+
await handleDoctor(client, options);
|
|
1991
|
+
return;
|
|
1992
|
+
}
|
|
1993
|
+
if (subcommand) throw new Error(`未知的 mcp 子命令:${subcommand}`);
|
|
1994
|
+
await handleServer(options);
|
|
1610
1995
|
});
|
|
1611
1996
|
}
|
|
1612
1997
|
//#endregion
|
|
@@ -1665,6 +2050,343 @@ function registerPrepareCommand(cli) {
|
|
|
1665
2050
|
});
|
|
1666
2051
|
}
|
|
1667
2052
|
//#endregion
|
|
2053
|
+
//#region package.json
|
|
2054
|
+
var version = "6.14.1";
|
|
2055
|
+
//#endregion
|
|
2056
|
+
//#region src/cli/devHotkeys.ts
|
|
2057
|
+
const DEV_SCREENSHOT_DIR = ".tmp/weapp-vite-dev-screenshots";
|
|
2058
|
+
const DEFAULT_SCREENSHOT_TIMEOUT = 3e4;
|
|
2059
|
+
const REG_PENDING_PREFIX = /^正在/;
|
|
2060
|
+
const FULLWIDTH_ASCII_START = 65281;
|
|
2061
|
+
const FULLWIDTH_ASCII_END = 65374;
|
|
2062
|
+
const FULLWIDTH_ASCII_OFFSET = 65248;
|
|
2063
|
+
const HOTKEY_DEDUP_WINDOW_MS = 32;
|
|
2064
|
+
function formatMcpUrl(host, port, endpoint) {
|
|
2065
|
+
return `http://${host}:${port}${endpoint}`;
|
|
2066
|
+
}
|
|
2067
|
+
function forwardSigint() {
|
|
2068
|
+
process.kill(process.pid, "SIGINT");
|
|
2069
|
+
}
|
|
2070
|
+
function forwardSigtstp() {
|
|
2071
|
+
process.kill(process.pid, "SIGTSTP");
|
|
2072
|
+
}
|
|
2073
|
+
function formatProjectLabel(cwd) {
|
|
2074
|
+
return path.basename(cwd) || cwd;
|
|
2075
|
+
}
|
|
2076
|
+
function formatMcpStatus(state) {
|
|
2077
|
+
if (!state.mcpEnabled) return "已禁用";
|
|
2078
|
+
return state.mcpRunning ? "运行中" : "未启动";
|
|
2079
|
+
}
|
|
2080
|
+
function formatFooterLine(state) {
|
|
2081
|
+
if (state.currentAction) return `执行中 ${state.currentAction}`;
|
|
2082
|
+
return "就绪 等待操作...";
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* @description 生成带状态的开发态快捷键帮助文本。
|
|
2086
|
+
*/
|
|
2087
|
+
function formatDevHotkeyHelpWithState(state) {
|
|
2088
|
+
const key = (value) => colors.bold(colors.green(value));
|
|
2089
|
+
const actionRows = [{
|
|
2090
|
+
key: key("s"),
|
|
2091
|
+
description: "截图当前页面并保存到本地"
|
|
2092
|
+
}, {
|
|
2093
|
+
key: key("m"),
|
|
2094
|
+
description: "开关 MCP 服务"
|
|
2095
|
+
}];
|
|
2096
|
+
const processRows = [
|
|
2097
|
+
{
|
|
2098
|
+
key: key("q"),
|
|
2099
|
+
description: "退出当前 dev"
|
|
2100
|
+
},
|
|
2101
|
+
{
|
|
2102
|
+
key: key("Ctrl+C"),
|
|
2103
|
+
description: "强制中断当前 dev"
|
|
2104
|
+
},
|
|
2105
|
+
{
|
|
2106
|
+
key: key("Ctrl+Z"),
|
|
2107
|
+
description: "暂时挂起当前 dev,恢复终端控制"
|
|
2108
|
+
}
|
|
2109
|
+
];
|
|
2110
|
+
const helpRows = [{
|
|
2111
|
+
key: key("h"),
|
|
2112
|
+
description: "重新显示这份帮助"
|
|
2113
|
+
}];
|
|
2114
|
+
const keyColumnWidth = Math.max(...[
|
|
2115
|
+
...actionRows,
|
|
2116
|
+
...processRows,
|
|
2117
|
+
...helpRows
|
|
2118
|
+
].map((row) => row.key.length));
|
|
2119
|
+
const formatRows = (rows) => rows.map(({ key, description }) => `按 ${key.padEnd(keyColumnWidth)} ${description}`);
|
|
2120
|
+
return [
|
|
2121
|
+
`${colors.bold(colors.green("DEV"))} weapp-vite v${version} ${state.projectLabel ?? "weapp"}`,
|
|
2122
|
+
"",
|
|
2123
|
+
"快捷命令",
|
|
2124
|
+
...formatRows(actionRows),
|
|
2125
|
+
"",
|
|
2126
|
+
"进程控制",
|
|
2127
|
+
...formatRows(processRows),
|
|
2128
|
+
"",
|
|
2129
|
+
"帮助",
|
|
2130
|
+
...formatRows(helpRows),
|
|
2131
|
+
"",
|
|
2132
|
+
`当前状态:${state.currentAction ?? "等待操作"} / MCP ${formatMcpStatus(state)}`
|
|
2133
|
+
].join("\n");
|
|
2134
|
+
}
|
|
2135
|
+
/**
|
|
2136
|
+
* @description 生成带状态的开发态快捷键简短提示。
|
|
2137
|
+
*/
|
|
2138
|
+
function formatDevHotkeyHintWithState(state) {
|
|
2139
|
+
const key = (value) => colors.bold(colors.green(value));
|
|
2140
|
+
if (state.currentAction) return `${formatFooterLine(state)},按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
|
|
2141
|
+
return `开发快捷键已就绪,按 ${key("h")} 显示帮助,按 ${key("q")} 退出`;
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* @description 生成开发态截图输出路径。
|
|
2145
|
+
*/
|
|
2146
|
+
function resolveDevScreenshotOutputPath(cwd, now = /* @__PURE__ */ new Date()) {
|
|
2147
|
+
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
2148
|
+
return path.join(cwd, DEV_SCREENSHOT_DIR, `screenshot-${stamp}.png`);
|
|
2149
|
+
}
|
|
2150
|
+
function formatLogPath(cwd, targetPath) {
|
|
2151
|
+
const relativePath = path.relative(cwd, targetPath);
|
|
2152
|
+
if (!relativePath || relativePath.startsWith("..")) return targetPath;
|
|
2153
|
+
return relativePath;
|
|
2154
|
+
}
|
|
2155
|
+
function formatResolvedScreenshotPath(cwd, fallbackPath, result) {
|
|
2156
|
+
return formatLogPath(cwd, result.path ?? fallbackPath);
|
|
2157
|
+
}
|
|
2158
|
+
function normalizeInputChar(input) {
|
|
2159
|
+
if (input.length !== 1) return input;
|
|
2160
|
+
const codePoint = input.codePointAt(0);
|
|
2161
|
+
if (!codePoint) return input;
|
|
2162
|
+
if (codePoint >= FULLWIDTH_ASCII_START && codePoint <= FULLWIDTH_ASCII_END) return String.fromCodePoint(codePoint - FULLWIDTH_ASCII_OFFSET);
|
|
2163
|
+
return input;
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* @description 执行当前页面截图并输出结果日志。
|
|
2167
|
+
*/
|
|
2168
|
+
async function runScreenshotAction(options) {
|
|
2169
|
+
const outputPath = resolveDevScreenshotOutputPath(options.cwd);
|
|
2170
|
+
await fs$1.mkdir(path.dirname(outputPath), { recursive: true });
|
|
2171
|
+
logger_default.info(`[dev action] 正在截图当前页面,输出到 ${colors.cyan(formatLogPath(options.cwd, outputPath))}`);
|
|
2172
|
+
const result = await takeScreenshot({
|
|
2173
|
+
fullPage: true,
|
|
2174
|
+
projectPath: options.projectPath,
|
|
2175
|
+
sharedSession: true,
|
|
2176
|
+
outputPath,
|
|
2177
|
+
timeout: DEFAULT_SCREENSHOT_TIMEOUT
|
|
2178
|
+
});
|
|
2179
|
+
const resolvedPath = formatResolvedScreenshotPath(options.cwd, outputPath, result);
|
|
2180
|
+
logger_default.success(`[dev action] 当前页面截图完成:${colors.cyan(resolvedPath)}`);
|
|
2181
|
+
return resolvedPath;
|
|
2182
|
+
}
|
|
2183
|
+
/**
|
|
2184
|
+
* @description 在开发态启动终端快捷键,并将动作输出到本地日志。
|
|
2185
|
+
*/
|
|
2186
|
+
function startDevHotkeys(options) {
|
|
2187
|
+
if (options.platform !== "weapp" || !process.stdin.isTTY) return;
|
|
2188
|
+
emitKeypressEvents(process.stdin);
|
|
2189
|
+
const hasSetRawMode = typeof process.stdin.setRawMode === "function";
|
|
2190
|
+
if (hasSetRawMode) process.stdin.setRawMode(true);
|
|
2191
|
+
process.stdin.resume();
|
|
2192
|
+
let closed = false;
|
|
2193
|
+
let running = false;
|
|
2194
|
+
let mcpHandle;
|
|
2195
|
+
let onData;
|
|
2196
|
+
let onKeypress;
|
|
2197
|
+
let onSigcont;
|
|
2198
|
+
let currentAction;
|
|
2199
|
+
let lastAction;
|
|
2200
|
+
let lastRenderedPanel = "";
|
|
2201
|
+
const recentInputs = /* @__PURE__ */ new Map();
|
|
2202
|
+
const resolvedMcp = resolveWeappMcpConfig(options.mcpConfig);
|
|
2203
|
+
const getState = () => ({
|
|
2204
|
+
currentAction,
|
|
2205
|
+
lastAction,
|
|
2206
|
+
mcpEnabled: resolvedMcp.enabled,
|
|
2207
|
+
mcpRunning: Boolean(mcpHandle?.close),
|
|
2208
|
+
projectLabel: formatProjectLabel(options.cwd)
|
|
2209
|
+
});
|
|
2210
|
+
const detachTerminal = () => {
|
|
2211
|
+
if (hasSetRawMode) process.stdin.setRawMode(false);
|
|
2212
|
+
process.stdin.pause();
|
|
2213
|
+
};
|
|
2214
|
+
const attachTerminal = () => {
|
|
2215
|
+
if (closed) return;
|
|
2216
|
+
if (hasSetRawMode) process.stdin.setRawMode(true);
|
|
2217
|
+
process.stdin.resume();
|
|
2218
|
+
};
|
|
2219
|
+
const ensureTerminalActive = () => {
|
|
2220
|
+
if (closed) return;
|
|
2221
|
+
if (hasSetRawMode) process.stdin.setRawMode(true);
|
|
2222
|
+
process.stdin.resume();
|
|
2223
|
+
};
|
|
2224
|
+
const close = () => {
|
|
2225
|
+
if (closed) return;
|
|
2226
|
+
closed = true;
|
|
2227
|
+
if (onData) process.stdin.off("data", onData);
|
|
2228
|
+
if (onKeypress) process.stdin.off("keypress", onKeypress);
|
|
2229
|
+
if (onSigcont) process.off("SIGCONT", onSigcont);
|
|
2230
|
+
detachTerminal();
|
|
2231
|
+
if (mcpHandle?.close) mcpHandle.close().catch((error) => {
|
|
2232
|
+
logger_default.warn(`[dev action] MCP 服务关闭失败:${error instanceof Error ? error.message : String(error)}`);
|
|
2233
|
+
});
|
|
2234
|
+
closeSharedMiniProgram(options.projectPath).catch((error) => {
|
|
2235
|
+
logger_default.warn(`[dev action] DevTools 会话关闭失败:${error instanceof Error ? error.message : String(error)}`);
|
|
2236
|
+
});
|
|
2237
|
+
};
|
|
2238
|
+
const printPanel = (message, force = false) => {
|
|
2239
|
+
if (!force && message === lastRenderedPanel) return;
|
|
2240
|
+
lastRenderedPanel = message;
|
|
2241
|
+
logger_default.info(message);
|
|
2242
|
+
};
|
|
2243
|
+
const printHelp = () => {
|
|
2244
|
+
printPanel(formatDevHotkeyHelpWithState(getState()), true);
|
|
2245
|
+
ensureTerminalActive();
|
|
2246
|
+
};
|
|
2247
|
+
const printHint = () => {
|
|
2248
|
+
printPanel(formatDevHotkeyHintWithState(getState()));
|
|
2249
|
+
ensureTerminalActive();
|
|
2250
|
+
};
|
|
2251
|
+
const restore = () => {
|
|
2252
|
+
if (closed) return;
|
|
2253
|
+
if (onData) process.stdin.off("data", onData);
|
|
2254
|
+
if (onKeypress) process.stdin.off("keypress", onKeypress);
|
|
2255
|
+
attachTerminal();
|
|
2256
|
+
if (onData) process.stdin.on("data", onData);
|
|
2257
|
+
if (onKeypress) process.stdin.on("keypress", onKeypress);
|
|
2258
|
+
printHint();
|
|
2259
|
+
};
|
|
2260
|
+
const suspend = () => {
|
|
2261
|
+
lastRenderedPanel = "";
|
|
2262
|
+
detachTerminal();
|
|
2263
|
+
forwardSigtstp();
|
|
2264
|
+
};
|
|
2265
|
+
const toggleMcp = async () => {
|
|
2266
|
+
if (!resolvedMcp.enabled) {
|
|
2267
|
+
logger_default.warn("[dev action] MCP 已在配置中禁用,跳过切换。");
|
|
2268
|
+
return "MCP 已禁用";
|
|
2269
|
+
}
|
|
2270
|
+
if (mcpHandle?.close) {
|
|
2271
|
+
const url = formatMcpUrl(resolvedMcp.host, resolvedMcp.port, resolvedMcp.endpoint);
|
|
2272
|
+
logger_default.info(`[dev action] 正在关闭 MCP 服务:${colors.cyan(url)}`);
|
|
2273
|
+
await mcpHandle.close();
|
|
2274
|
+
mcpHandle = void 0;
|
|
2275
|
+
logger_default.success(`[dev action] MCP 服务已关闭:${colors.cyan(url)}`);
|
|
2276
|
+
return `MCP 已关闭 (${url})`;
|
|
2277
|
+
}
|
|
2278
|
+
const url = formatMcpUrl(resolvedMcp.host, resolvedMcp.port, resolvedMcp.endpoint);
|
|
2279
|
+
logger_default.info(`[dev action] 正在启动 MCP 服务:${colors.cyan(url)}`);
|
|
2280
|
+
mcpHandle = await startWeappViteMcpServer({
|
|
2281
|
+
endpoint: resolvedMcp.endpoint,
|
|
2282
|
+
host: resolvedMcp.host,
|
|
2283
|
+
port: resolvedMcp.port,
|
|
2284
|
+
transport: "streamable-http",
|
|
2285
|
+
unref: false,
|
|
2286
|
+
workspaceRoot: options.cwd
|
|
2287
|
+
});
|
|
2288
|
+
logger_default.success(`[dev action] MCP 服务已启动:${colors.cyan(url)}`);
|
|
2289
|
+
for (const line of formatMcpQuickStart({
|
|
2290
|
+
httpUrl: url,
|
|
2291
|
+
transport: "http"
|
|
2292
|
+
})) logger_default.info(line);
|
|
2293
|
+
return `MCP 已启动 (${url})`;
|
|
2294
|
+
};
|
|
2295
|
+
const runAction = (label, pendingLabel, action) => {
|
|
2296
|
+
if (running) {
|
|
2297
|
+
const current = currentAction ?? "已有命令";
|
|
2298
|
+
logger_default.warn(`[dev action] 当前正在${current.replace(REG_PENDING_PREFIX, "")},请稍后再试。`);
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
running = true;
|
|
2302
|
+
currentAction = pendingLabel;
|
|
2303
|
+
printHint();
|
|
2304
|
+
action().then((summary) => {
|
|
2305
|
+
if (summary) lastAction = summary;
|
|
2306
|
+
}).catch((error) => {
|
|
2307
|
+
logger_default.error(`[dev action] ${label}失败:${error instanceof Error ? error.message : String(error)}`);
|
|
2308
|
+
}).finally(() => {
|
|
2309
|
+
running = false;
|
|
2310
|
+
currentAction = void 0;
|
|
2311
|
+
if (!closed) printHint();
|
|
2312
|
+
});
|
|
2313
|
+
};
|
|
2314
|
+
const handleInput = (input) => {
|
|
2315
|
+
if (closed) return;
|
|
2316
|
+
const normalizedInput = normalizeInputChar(input);
|
|
2317
|
+
if (normalizedInput === "") {
|
|
2318
|
+
close();
|
|
2319
|
+
forwardSigint();
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
2322
|
+
if (normalizedInput === "") {
|
|
2323
|
+
suspend();
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
const normalized = normalizedInput.toLowerCase();
|
|
2327
|
+
if (normalized === "q") {
|
|
2328
|
+
close();
|
|
2329
|
+
forwardSigint();
|
|
2330
|
+
return;
|
|
2331
|
+
}
|
|
2332
|
+
if (normalized === "h") {
|
|
2333
|
+
printHelp();
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
if (normalized === "s") {
|
|
2337
|
+
runAction("截图", "正在截图当前页面", async () => {
|
|
2338
|
+
return `截图已保存到 ${await runScreenshotAction(options)}`;
|
|
2339
|
+
});
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
if (normalized === "m") runAction("MCP 切换", mcpHandle?.close ? "正在关闭 MCP 服务" : "正在启动 MCP 服务", async () => {
|
|
2343
|
+
return await toggleMcp();
|
|
2344
|
+
});
|
|
2345
|
+
};
|
|
2346
|
+
const handleInputOnce = (input, source) => {
|
|
2347
|
+
const normalizedInput = normalizeInputChar(input);
|
|
2348
|
+
const now = Date.now();
|
|
2349
|
+
for (const [token, timestamp] of recentInputs) if (now - timestamp > HOTKEY_DEDUP_WINDOW_MS) recentInputs.delete(token);
|
|
2350
|
+
const dedupKey = normalizedInput;
|
|
2351
|
+
const recentEntry = recentInputs.get(dedupKey);
|
|
2352
|
+
if (recentEntry !== void 0) {
|
|
2353
|
+
const [recentSource, recentTimestamp] = recentEntry.split(":");
|
|
2354
|
+
if (recentSource !== source && now - Number(recentTimestamp) <= HOTKEY_DEDUP_WINDOW_MS) return;
|
|
2355
|
+
}
|
|
2356
|
+
recentInputs.set(dedupKey, `${source}:${now}`);
|
|
2357
|
+
handleInput(normalizedInput);
|
|
2358
|
+
};
|
|
2359
|
+
onData = (chunk) => {
|
|
2360
|
+
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
2361
|
+
for (const char of text) handleInputOnce(char, "data");
|
|
2362
|
+
};
|
|
2363
|
+
process.stdin.on("data", onData);
|
|
2364
|
+
onKeypress = (str, key) => {
|
|
2365
|
+
if (key?.ctrl && key.name === "c") {
|
|
2366
|
+
handleInputOnce("", "keypress");
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
if (key?.ctrl && key.name === "z") {
|
|
2370
|
+
handleInputOnce("", "keypress");
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
if (typeof str === "string" && str) handleInputOnce(str, "keypress");
|
|
2374
|
+
};
|
|
2375
|
+
process.stdin.on("keypress", onKeypress);
|
|
2376
|
+
onSigcont = () => {
|
|
2377
|
+
restore();
|
|
2378
|
+
};
|
|
2379
|
+
process.on("SIGCONT", onSigcont);
|
|
2380
|
+
if (!options.silentStartupHint) printHint();
|
|
2381
|
+
if (resolvedMcp.enabled && resolvedMcp.autoStart) runAction("MCP 自动启动", "正在启动 MCP 服务", async () => {
|
|
2382
|
+
await toggleMcp();
|
|
2383
|
+
});
|
|
2384
|
+
return {
|
|
2385
|
+
close,
|
|
2386
|
+
restore
|
|
2387
|
+
};
|
|
2388
|
+
}
|
|
2389
|
+
//#endregion
|
|
1668
2390
|
//#region src/cli/commands/serve.ts
|
|
1669
2391
|
function emitDashboardEvents(handle, events) {
|
|
1670
2392
|
handle?.emitRuntimeEvents(events);
|
|
@@ -1680,6 +2402,17 @@ const REG_DIST_POSIX_SEP = /\\/g;
|
|
|
1680
2402
|
function hasAnalyzeData(result) {
|
|
1681
2403
|
return result.packages.length > 0 || result.modules.length > 0;
|
|
1682
2404
|
}
|
|
2405
|
+
function waitForServeShutdownSignal() {
|
|
2406
|
+
return new Promise((resolve) => {
|
|
2407
|
+
const onSignal = () => {
|
|
2408
|
+
process.off("SIGINT", onSignal);
|
|
2409
|
+
process.off("SIGTERM", onSignal);
|
|
2410
|
+
resolve();
|
|
2411
|
+
};
|
|
2412
|
+
process.on("SIGINT", onSignal);
|
|
2413
|
+
process.on("SIGTERM", onSignal);
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
1683
2416
|
async function collectOutputFiles(root) {
|
|
1684
2417
|
const entries = await fs$1.readdir(root, { withFileTypes: true });
|
|
1685
2418
|
const files = [];
|
|
@@ -1811,165 +2544,181 @@ function registerServeCommand(cli) {
|
|
|
1811
2544
|
const enableAnalyze = Boolean(isUiEnabled(options) && targets.runMini);
|
|
1812
2545
|
let analyzeHandle;
|
|
1813
2546
|
let analyzeRunId = 0;
|
|
1814
|
-
const
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
2547
|
+
const devHotkeysSession = targets.runMini ? startDevHotkeys({
|
|
2548
|
+
cwd: configService.cwd,
|
|
2549
|
+
mcpConfig: configService.weappViteConfig?.weapp?.mcp,
|
|
2550
|
+
platform: configService.platform,
|
|
2551
|
+
projectPath: resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd),
|
|
2552
|
+
silentStartupHint: true
|
|
2553
|
+
}) : void 0;
|
|
2554
|
+
try {
|
|
2555
|
+
const runAnalyze = async () => {
|
|
2556
|
+
const startedAt = Date.now();
|
|
2557
|
+
try {
|
|
2558
|
+
const result = await analyzeSubpackages(await createCompilerContext({
|
|
2559
|
+
key: `serve-ui-analyze:${process.pid}:${++analyzeRunId}`,
|
|
2560
|
+
cwd: configService.cwd,
|
|
2561
|
+
mode: configService.mode,
|
|
2562
|
+
isDev: false,
|
|
2563
|
+
configFile,
|
|
2564
|
+
inlineConfig: createInlineConfig(targets.mpPlatform),
|
|
2565
|
+
cliPlatform: targets.rawPlatform,
|
|
2566
|
+
projectConfigPath: options.projectConfig,
|
|
2567
|
+
syncSupportFiles: false
|
|
2568
|
+
}));
|
|
2569
|
+
if (hasAnalyzeData(result)) return {
|
|
2570
|
+
result,
|
|
2571
|
+
durationMs: Date.now() - startedAt,
|
|
2572
|
+
mode: "full"
|
|
2573
|
+
};
|
|
2574
|
+
} catch (error) {
|
|
2575
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2576
|
+
logger_default.warn(`[ui] 完整分析失败,已回退到 dist 文件扫描:${message}`);
|
|
2577
|
+
return {
|
|
2578
|
+
result: await analyzeUiFallback(ctx),
|
|
2579
|
+
durationMs: Date.now() - startedAt,
|
|
2580
|
+
mode: "fallback",
|
|
2581
|
+
fallbackReason: message
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
1836
2584
|
return {
|
|
1837
2585
|
result: await analyzeUiFallback(ctx),
|
|
1838
2586
|
durationMs: Date.now() - startedAt,
|
|
1839
2587
|
mode: "fallback",
|
|
1840
|
-
fallbackReason:
|
|
2588
|
+
fallbackReason: "完整分析结果为空,已回退到 dist 文件扫描。"
|
|
1841
2589
|
};
|
|
1842
|
-
}
|
|
1843
|
-
return {
|
|
1844
|
-
result: await analyzeUiFallback(ctx),
|
|
1845
|
-
durationMs: Date.now() - startedAt,
|
|
1846
|
-
mode: "fallback",
|
|
1847
|
-
fallbackReason: "完整分析结果为空,已回退到 dist 文件扫描。"
|
|
1848
2590
|
};
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
if (!analyzeHandle) return;
|
|
1852
|
-
emitDashboardEvents(analyzeHandle, [{
|
|
1853
|
-
kind: reason === "watch" ? "hmr" : "build",
|
|
1854
|
-
level: "info",
|
|
1855
|
-
title: reason === "watch" ? "analyze refresh started" : "initial analyze started",
|
|
1856
|
-
detail: reason === "watch" ? "检测到新的构建结束事件,开始刷新 analyze 面板。" : "开发态 UI 已启动,开始生成第一份 analyze 结果。",
|
|
1857
|
-
tags: reason === "watch" ? ["watch", "analyze"] : ["initial", "analyze"]
|
|
1858
|
-
}]);
|
|
1859
|
-
const next = await runAnalyze();
|
|
1860
|
-
if (next.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
|
|
1861
|
-
kind: "diagnostic",
|
|
1862
|
-
level: "warning",
|
|
1863
|
-
title: "analyze fallback enabled",
|
|
1864
|
-
detail: next.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
|
|
1865
|
-
durationMs: next.durationMs,
|
|
1866
|
-
tags: ["analyze", "fallback"]
|
|
1867
|
-
}]);
|
|
1868
|
-
await analyzeHandle.update(next.result);
|
|
1869
|
-
emitDashboardEvents(analyzeHandle, [{
|
|
1870
|
-
kind: next.mode === "fallback" ? "diagnostic" : "build",
|
|
1871
|
-
level: next.mode === "fallback" ? "warning" : "success",
|
|
1872
|
-
title: reason === "watch" ? "analyze refresh completed" : "initial analyze completed",
|
|
1873
|
-
detail: next.mode === "fallback" ? `analyze 已回退到 dist 扫描,当前包含 ${next.result.packages.length} 个包。` : `analyze 已刷新完成,当前包含 ${next.result.packages.length} 个包与 ${next.result.modules.length} 个模块。`,
|
|
1874
|
-
durationMs: next.durationMs,
|
|
1875
|
-
tags: next.mode === "fallback" ? ["analyze", "fallback"] : ["analyze", reason === "watch" ? "refresh" : "initial"]
|
|
1876
|
-
}]);
|
|
1877
|
-
};
|
|
1878
|
-
if (targets.runMini) {
|
|
1879
|
-
const miniBuildStartedAt = Date.now();
|
|
1880
|
-
const buildResult = await buildService.build(options);
|
|
1881
|
-
logger_default.success(`小程序初次构建完成,耗时:${formatDuration(Date.now() - miniBuildStartedAt)}`);
|
|
1882
|
-
if (enableAnalyze) {
|
|
1883
|
-
const initialAnalyze = await runAnalyze();
|
|
1884
|
-
analyzeHandle = await startAnalyzeDashboard(initialAnalyze.result, {
|
|
1885
|
-
watch: true,
|
|
1886
|
-
cwd: configService.cwd,
|
|
1887
|
-
packageManagerAgent: configService.packageManager.agent,
|
|
1888
|
-
silentStartupLog: true
|
|
1889
|
-
}) ?? void 0;
|
|
2591
|
+
const triggerAnalyzeUpdate = async (reason = "watch") => {
|
|
2592
|
+
if (!analyzeHandle) return;
|
|
1890
2593
|
emitDashboardEvents(analyzeHandle, [{
|
|
1891
|
-
kind: "
|
|
1892
|
-
level: "
|
|
1893
|
-
title: "
|
|
1894
|
-
detail:
|
|
1895
|
-
|
|
1896
|
-
tags: ["dev", "ui"]
|
|
2594
|
+
kind: reason === "watch" ? "hmr" : "build",
|
|
2595
|
+
level: "info",
|
|
2596
|
+
title: reason === "watch" ? "analyze refresh started" : "initial analyze started",
|
|
2597
|
+
detail: reason === "watch" ? "检测到新的构建结束事件,开始刷新 analyze 面板。" : "开发态 UI 已启动,开始生成第一份 analyze 结果。",
|
|
2598
|
+
tags: reason === "watch" ? ["watch", "analyze"] : ["initial", "analyze"]
|
|
1897
2599
|
}]);
|
|
1898
|
-
|
|
2600
|
+
const next = await runAnalyze();
|
|
2601
|
+
if (next.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
|
|
1899
2602
|
kind: "diagnostic",
|
|
1900
2603
|
level: "warning",
|
|
1901
|
-
title: "
|
|
1902
|
-
detail:
|
|
1903
|
-
durationMs:
|
|
1904
|
-
tags: [
|
|
1905
|
-
"analyze",
|
|
1906
|
-
"fallback",
|
|
1907
|
-
"initial"
|
|
1908
|
-
]
|
|
2604
|
+
title: "analyze fallback enabled",
|
|
2605
|
+
detail: next.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
|
|
2606
|
+
durationMs: next.durationMs,
|
|
2607
|
+
tags: ["analyze", "fallback"]
|
|
1909
2608
|
}]);
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
2609
|
+
await analyzeHandle.update(next.result);
|
|
2610
|
+
emitDashboardEvents(analyzeHandle, [{
|
|
2611
|
+
kind: next.mode === "fallback" ? "diagnostic" : "build",
|
|
2612
|
+
level: next.mode === "fallback" ? "warning" : "success",
|
|
2613
|
+
title: reason === "watch" ? "analyze refresh completed" : "initial analyze completed",
|
|
2614
|
+
detail: next.mode === "fallback" ? `analyze 已回退到 dist 扫描,当前包含 ${next.result.packages.length} 个包。` : `analyze 已刷新完成,当前包含 ${next.result.packages.length} 个包与 ${next.result.modules.length} 个模块。`,
|
|
2615
|
+
durationMs: next.durationMs,
|
|
2616
|
+
tags: next.mode === "fallback" ? ["analyze", "fallback"] : ["analyze", reason === "watch" ? "refresh" : "initial"]
|
|
2617
|
+
}]);
|
|
2618
|
+
};
|
|
2619
|
+
if (targets.runMini) {
|
|
2620
|
+
const miniBuildStartedAt = Date.now();
|
|
2621
|
+
const buildResult = await buildService.build(options);
|
|
2622
|
+
logger_default.success(`小程序初次构建完成,耗时:${formatDuration(Date.now() - miniBuildStartedAt)}`);
|
|
2623
|
+
if (enableAnalyze) {
|
|
2624
|
+
const initialAnalyze = await runAnalyze();
|
|
2625
|
+
analyzeHandle = await startAnalyzeDashboard(initialAnalyze.result, {
|
|
2626
|
+
watch: true,
|
|
2627
|
+
cwd: configService.cwd,
|
|
2628
|
+
packageManagerAgent: configService.packageManager.agent,
|
|
2629
|
+
silentStartupLog: true
|
|
2630
|
+
}) ?? void 0;
|
|
2631
|
+
emitDashboardEvents(analyzeHandle, [{
|
|
2632
|
+
kind: "command",
|
|
2633
|
+
level: "success",
|
|
2634
|
+
title: "dev ui session ready",
|
|
2635
|
+
detail: `开发态分析面板已启动,当前包含 ${initialAnalyze.result.packages.length} 个包。`,
|
|
2636
|
+
durationMs: initialAnalyze.durationMs,
|
|
2637
|
+
tags: ["dev", "ui"]
|
|
2638
|
+
}]);
|
|
2639
|
+
if (initialAnalyze.mode === "fallback") emitDashboardEvents(analyzeHandle, [{
|
|
2640
|
+
kind: "diagnostic",
|
|
2641
|
+
level: "warning",
|
|
2642
|
+
title: "initial analyze fallback enabled",
|
|
2643
|
+
detail: initialAnalyze.fallbackReason ?? "完整分析不可用,已回退到 dist 文件扫描。",
|
|
2644
|
+
durationMs: initialAnalyze.durationMs,
|
|
2645
|
+
tags: [
|
|
2646
|
+
"analyze",
|
|
2647
|
+
"fallback",
|
|
2648
|
+
"initial"
|
|
2649
|
+
]
|
|
2650
|
+
}]);
|
|
2651
|
+
let updating = false;
|
|
2652
|
+
if (analyzeHandle && buildResult && typeof buildResult.on === "function") buildResult.on("event", (event) => {
|
|
2653
|
+
if (event.code !== "END" || updating) return;
|
|
2654
|
+
updating = true;
|
|
2655
|
+
triggerAnalyzeUpdate("watch").finally(() => {
|
|
2656
|
+
updating = false;
|
|
2657
|
+
});
|
|
1916
2658
|
});
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
2659
|
+
if (analyzeHandle) {
|
|
2660
|
+
updating = true;
|
|
2661
|
+
await triggerAnalyzeUpdate("initial");
|
|
2662
|
+
updating = false;
|
|
2663
|
+
}
|
|
1922
2664
|
}
|
|
1923
2665
|
}
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
}
|
|
1939
|
-
|
|
2666
|
+
let webServer;
|
|
2667
|
+
if (targets.runWeb) {
|
|
2668
|
+
const webServerStartedAt = Date.now();
|
|
2669
|
+
try {
|
|
2670
|
+
webServer = await webService?.startDevServer();
|
|
2671
|
+
logger_default.success(`Web 开发服务启动完成,耗时:${formatDuration(Date.now() - webServerStartedAt)}`);
|
|
2672
|
+
emitDashboardEvents(analyzeHandle, [{
|
|
2673
|
+
kind: "system",
|
|
2674
|
+
level: "success",
|
|
2675
|
+
title: "web dev server started",
|
|
2676
|
+
detail: "Web 开发服务器已启动,可与小程序调试 UI 并行工作。",
|
|
2677
|
+
durationMs: Date.now() - webServerStartedAt,
|
|
2678
|
+
tags: ["dev", "web"]
|
|
2679
|
+
}]);
|
|
2680
|
+
} catch (error) {
|
|
2681
|
+
emitDashboardEvents(analyzeHandle, [{
|
|
2682
|
+
kind: "diagnostic",
|
|
2683
|
+
level: "error",
|
|
2684
|
+
title: "web dev server failed",
|
|
2685
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
2686
|
+
durationMs: Date.now() - webServerStartedAt,
|
|
2687
|
+
tags: ["dev", "web"]
|
|
2688
|
+
}]);
|
|
2689
|
+
logger_default.error(error);
|
|
2690
|
+
throw error;
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
if (targets.runMini) {
|
|
2694
|
+
logBuildAppFinish(configService, webServer, {
|
|
2695
|
+
skipWeb: !targets.runWeb,
|
|
2696
|
+
uiUrls: analyzeHandle?.urls
|
|
2697
|
+
});
|
|
2698
|
+
devHotkeysSession?.restore();
|
|
2699
|
+
} else if (targets.runWeb) logBuildAppFinish(configService, webServer, { skipMini: true });
|
|
2700
|
+
if (options.open && targets.runMini) {
|
|
1940
2701
|
emitDashboardEvents(analyzeHandle, [{
|
|
1941
|
-
kind: "
|
|
1942
|
-
level: "
|
|
1943
|
-
title: "
|
|
1944
|
-
detail:
|
|
1945
|
-
|
|
1946
|
-
tags: ["dev", "web"]
|
|
2702
|
+
kind: "command",
|
|
2703
|
+
level: "info",
|
|
2704
|
+
title: "opening ide",
|
|
2705
|
+
detail: "开发服务已就绪,准备打开 IDE 项目。",
|
|
2706
|
+
tags: ["ide", "open"]
|
|
1947
2707
|
}]);
|
|
1948
|
-
|
|
1949
|
-
|
|
2708
|
+
if (!await maybeStartForwardConsole({
|
|
2709
|
+
platform: configService.platform,
|
|
2710
|
+
mpDistRoot: configService.mpDistRoot,
|
|
2711
|
+
cwd: configService.cwd,
|
|
2712
|
+
weappViteConfig: configService.weappViteConfig
|
|
2713
|
+
})) await openIde(configService.platform, resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd), { trustProject: options.trustProject });
|
|
2714
|
+
devHotkeysSession?.restore();
|
|
1950
2715
|
}
|
|
2716
|
+
if (analyzeHandle) await analyzeHandle.waitForExit();
|
|
2717
|
+
else if (targets.runMini || targets.runWeb) await waitForServeShutdownSignal();
|
|
2718
|
+
} finally {
|
|
2719
|
+
devHotkeysSession?.close();
|
|
2720
|
+
ctx.watcherService?.closeAll();
|
|
1951
2721
|
}
|
|
1952
|
-
if (targets.runMini) logBuildAppFinish(configService, webServer, {
|
|
1953
|
-
skipWeb: !targets.runWeb,
|
|
1954
|
-
uiUrls: analyzeHandle?.urls
|
|
1955
|
-
});
|
|
1956
|
-
else if (targets.runWeb) logBuildAppFinish(configService, webServer, { skipMini: true });
|
|
1957
|
-
if (options.open && targets.runMini) {
|
|
1958
|
-
emitDashboardEvents(analyzeHandle, [{
|
|
1959
|
-
kind: "command",
|
|
1960
|
-
level: "info",
|
|
1961
|
-
title: "opening ide",
|
|
1962
|
-
detail: "开发服务已就绪,准备打开 IDE 项目。",
|
|
1963
|
-
tags: ["ide", "open"]
|
|
1964
|
-
}]);
|
|
1965
|
-
if (!await maybeStartForwardConsole({
|
|
1966
|
-
platform: configService.platform,
|
|
1967
|
-
mpDistRoot: configService.mpDistRoot,
|
|
1968
|
-
cwd: configService.cwd,
|
|
1969
|
-
weappViteConfig: configService.weappViteConfig
|
|
1970
|
-
})) await openIde(configService.platform, resolveIdeProjectRoot(configService.mpDistRoot, configService.cwd), { trustProject: options.trustProject });
|
|
1971
|
-
}
|
|
1972
|
-
if (analyzeHandle) await analyzeHandle.waitForExit();
|
|
1973
2722
|
});
|
|
1974
2723
|
}
|
|
1975
2724
|
//#endregion
|
|
@@ -2058,13 +2807,12 @@ const SKIP_COMMANDS = new Set([
|
|
|
2058
2807
|
"ide",
|
|
2059
2808
|
"mcp"
|
|
2060
2809
|
]);
|
|
2061
|
-
const
|
|
2810
|
+
const REG_EADDRINUSE = /EADDRINUSE/;
|
|
2062
2811
|
let started = false;
|
|
2063
2812
|
function shouldAutoStartMcp(argv) {
|
|
2064
2813
|
const command = argv[0];
|
|
2065
2814
|
if (!command || command.startsWith("-")) return true;
|
|
2066
2815
|
if (SKIP_COMMANDS.has(command)) return false;
|
|
2067
|
-
if (DEV_COMMANDS.has(command)) return true;
|
|
2068
2816
|
return command.includes("/") || command.includes("\\") || command.startsWith(".");
|
|
2069
2817
|
}
|
|
2070
2818
|
async function maybeAutoStartMcpServer(argv, cliOptions) {
|
|
@@ -2092,9 +2840,13 @@ async function maybeAutoStartMcpServer(argv, cliOptions) {
|
|
|
2092
2840
|
const mcpUrl = `http://${resolvedMcp.host}:${resolvedMcp.port}${resolvedMcp.endpoint}`;
|
|
2093
2841
|
logger_default.success("MCP 服务已自动启动:");
|
|
2094
2842
|
logger_default.info(` ➜ ${colors.cyan(mcpUrl)}`);
|
|
2843
|
+
for (const line of formatMcpQuickStart({
|
|
2844
|
+
httpUrl: mcpUrl,
|
|
2845
|
+
transport: "http"
|
|
2846
|
+
})) logger_default.info(line);
|
|
2095
2847
|
} catch (error) {
|
|
2096
2848
|
const message = error instanceof Error ? error.message : String(error);
|
|
2097
|
-
if (
|
|
2849
|
+
if (REG_EADDRINUSE.test(message)) {
|
|
2098
2850
|
logger_default.info(`[mcp] 端口 ${resolvedMcp.port} 已被占用,跳过自动启动。`);
|
|
2099
2851
|
started = true;
|
|
2100
2852
|
return;
|
|
@@ -2174,22 +2926,19 @@ function resolveManagedTsconfigBootstrapRoot(args) {
|
|
|
2174
2926
|
return path.resolve(firstArg);
|
|
2175
2927
|
}
|
|
2176
2928
|
try {
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
if (await tryRunIdeCommand(args)) return;
|
|
2929
|
+
const args = process.argv.slice(2);
|
|
2930
|
+
if (!await tryRunIdeCommand(args)) {
|
|
2180
2931
|
const managedTsconfigBootstrapRoot = resolveManagedTsconfigBootstrapRoot(args);
|
|
2181
2932
|
if (managedTsconfigBootstrapRoot) await syncManagedTsconfigBootstrapFiles(managedTsconfigBootstrapRoot);
|
|
2182
2933
|
cli.parse(process.argv, { run: false });
|
|
2183
2934
|
await maybeAutoStartMcpServer(args, cli.options);
|
|
2184
2935
|
await cli.runMatchedCommand();
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2936
|
+
}
|
|
2937
|
+
} catch (error) {
|
|
2938
|
+
if (!handlePrepareLifecycleError(process.argv.slice(2), error)) {
|
|
2187
2939
|
handleCLIError(error);
|
|
2188
2940
|
process.exitCode = 1;
|
|
2189
|
-
}
|
|
2190
|
-
} catch (error) {
|
|
2191
|
-
handleCLIError(error);
|
|
2192
|
-
process.exitCode = 1;
|
|
2941
|
+
}
|
|
2193
2942
|
}
|
|
2194
2943
|
//#endregion
|
|
2195
2944
|
export {};
|