Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 486e25f86b | |||
| f4bc716789 | |||
| 3cafadba5a | |||
| 122e478894 | |||
| 7b0f269180 | |||
| c7e4a063bf |
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
1. 构建只发生在本机 Mac:`node scripts/build-portal-runtime.mjs`。
|
1. 构建只发生在本机 Mac:`node scripts/build-portal-runtime.mjs`。
|
||||||
2. 103 只接收 `.runtime/portal/` 打出来的 artifact,不直接覆盖源码树。
|
2. 103 只接收 `.runtime/portal/` 打出来的 artifact,不直接覆盖源码树。
|
||||||
|
- 所有由 goosed 以 stdio 启动的 Portal MCP 入口都必须打进 artifact,并同时加入发布脚本的必需文件和容器可见性检查;当前包括 `mindspace-sandbox-mcp.mjs` 与 `tkmind-search-mcp.mjs`。
|
||||||
3. 103 在切换前必须做 `Memind` 全量备份,并单独备份持久目录。
|
3. 103 在切换前必须做 `Memind` 全量备份,并单独备份持久目录。
|
||||||
4. 103 **禁止** `npm install`、`npm run build`、在线改源码后继续运行。
|
4. 103 **禁止** `npm install`、`npm run build`、在线改源码后继续运行。
|
||||||
5. 切换后必须通过 Portal 健康检查;失败立即回滚。
|
5. 切换后必须通过 Portal 健康检查;失败立即回滚。
|
||||||
|
|||||||
Generated
+10
-10
@@ -24,8 +24,8 @@ importers:
|
|||||||
specifier: ^12.42.0
|
specifier: ^12.42.0
|
||||||
version: 12.42.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
version: 12.42.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||||
http-proxy-middleware:
|
http-proxy-middleware:
|
||||||
specifier: ^3.0.3
|
specifier: ^3.0.7
|
||||||
version: 3.0.6
|
version: 3.0.7
|
||||||
jsonrepair:
|
jsonrepair:
|
||||||
specifier: ^3.14.0
|
specifier: ^3.14.0
|
||||||
version: 3.14.0
|
version: 3.14.0
|
||||||
@@ -60,8 +60,8 @@ importers:
|
|||||||
specifier: ^0.35.2
|
specifier: ^0.35.2
|
||||||
version: 0.35.2
|
version: 0.35.2
|
||||||
undici:
|
undici:
|
||||||
specifier: ^6.26.0
|
specifier: ^6.27.0
|
||||||
version: 6.26.0
|
version: 6.27.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^19.0.10
|
specifier: ^19.0.10
|
||||||
@@ -1201,8 +1201,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
|
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
http-proxy-middleware@3.0.6:
|
http-proxy-middleware@3.0.7:
|
||||||
resolution: {integrity: sha512-jhO3QfahaHWfQjEnyGW0vpYIYaXcnA6FEfehrBthOokGppvmI6zcV+1yb6TWn3vyeh8yQUoEqH51DNHOCjivxg==}
|
resolution: {integrity: sha512-iwbQltVlx8bCrqePUM8C+hllHvdawVhQJaLrj1X7qllkvFQdXFsr16pW/mo9+JDVjN+QO2XUx9jd8SmoFkE5qw==}
|
||||||
engines: {node: ^14.18.0 || ^16.10.0 || >=18.0.0}
|
engines: {node: ^14.18.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
http-proxy@1.18.1:
|
http-proxy@1.18.1:
|
||||||
@@ -1635,8 +1635,8 @@ packages:
|
|||||||
undici-types@7.24.6:
|
undici-types@7.24.6:
|
||||||
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
|
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
|
||||||
|
|
||||||
undici@6.26.0:
|
undici@6.27.0:
|
||||||
resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==}
|
resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==}
|
||||||
engines: {node: '>=18.17'}
|
engines: {node: '>=18.17'}
|
||||||
|
|
||||||
unpipe@1.0.0:
|
unpipe@1.0.0:
|
||||||
@@ -2678,7 +2678,7 @@ snapshots:
|
|||||||
statuses: 2.0.2
|
statuses: 2.0.2
|
||||||
toidentifier: 1.0.1
|
toidentifier: 1.0.1
|
||||||
|
|
||||||
http-proxy-middleware@3.0.6:
|
http-proxy-middleware@3.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/http-proxy': 1.17.17
|
'@types/http-proxy': 1.17.17
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
@@ -3125,7 +3125,7 @@ snapshots:
|
|||||||
|
|
||||||
undici-types@7.24.6: {}
|
undici-types@7.24.6: {}
|
||||||
|
|
||||||
undici@6.26.0: {}
|
undici@6.27.0: {}
|
||||||
|
|
||||||
unpipe@1.0.0: {}
|
unpipe@1.0.0: {}
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,26 @@ async function bundleSandboxMcp() {
|
|||||||
await run(esbuildBin, args);
|
await run(esbuildBin, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function bundleMindSearchMcp() {
|
||||||
|
if (!(await exists(esbuildBin))) {
|
||||||
|
throw new Error(`未找到 esbuild: ${esbuildBin}`);
|
||||||
|
}
|
||||||
|
console.log('==> 打包 MindSearch MCP 为单文件 runtime');
|
||||||
|
const args = [
|
||||||
|
'tkmind-search-mcp.mjs',
|
||||||
|
'--bundle',
|
||||||
|
'--platform=node',
|
||||||
|
'--format=esm',
|
||||||
|
`--target=${runtimeNodeTarget}`,
|
||||||
|
'--outfile=.runtime/portal/tkmind-search-mcp.mjs',
|
||||||
|
'--banner:js=import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);',
|
||||||
|
];
|
||||||
|
for (const pkg of externalPackages) {
|
||||||
|
args.push(`--external:${pkg}`);
|
||||||
|
}
|
||||||
|
await run(esbuildBin, args);
|
||||||
|
}
|
||||||
|
|
||||||
async function bundleAgentRunWorker() {
|
async function bundleAgentRunWorker() {
|
||||||
if (!(await exists(esbuildBin))) {
|
if (!(await exists(esbuildBin))) {
|
||||||
throw new Error(`未找到 esbuild: ${esbuildBin}`);
|
throw new Error(`未找到 esbuild: ${esbuildBin}`);
|
||||||
@@ -414,6 +434,7 @@ async function writeMetadata() {
|
|||||||
'',
|
'',
|
||||||
'Bundled alongside server.mjs (required for sandbox-fs MCP):',
|
'Bundled alongside server.mjs (required for sandbox-fs MCP):',
|
||||||
' mindspace-sandbox-mcp.mjs (esbuild bundle; includes schedule-service deps)',
|
' mindspace-sandbox-mcp.mjs (esbuild bundle; includes schedule-service deps)',
|
||||||
|
' tkmind-search-mcp.mjs (esbuild bundle; required when MindSearch is enabled)',
|
||||||
' wechat-mp.bundle.mjs (esbuild bundle; hot-swappable WeChat MP module)',
|
' wechat-mp.bundle.mjs (esbuild bundle; hot-swappable WeChat MP module)',
|
||||||
'',
|
'',
|
||||||
'Post-deploy validation (scripts/check-mindspace-public-links.mjs):',
|
'Post-deploy validation (scripts/check-mindspace-public-links.mjs):',
|
||||||
@@ -486,6 +507,7 @@ async function main() {
|
|||||||
await bundleServer();
|
await bundleServer();
|
||||||
await bundleWechatMp();
|
await bundleWechatMp();
|
||||||
await bundleSandboxMcp();
|
await bundleSandboxMcp();
|
||||||
|
await bundleMindSearchMcp();
|
||||||
await bundleAgentRunWorker();
|
await bundleAgentRunWorker();
|
||||||
if (runtimeInstallMode === 'bundle-node-modules' && !skipNodeModules) {
|
if (runtimeInstallMode === 'bundle-node-modules' && !skipNodeModules) {
|
||||||
await copyNodeModules();
|
await copyNodeModules();
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ fi
|
|||||||
|
|
||||||
verify_runtime_artifact() {
|
verify_runtime_artifact() {
|
||||||
local missing=0
|
local missing=0
|
||||||
for required in server.mjs wechat-mp.bundle.mjs mindspace-sandbox-mcp.mjs mindspace-public-links.mjs dist package.json scripts/run-memind-portal-prod.sh scripts/check-mindspace-public-links.mjs scripts/load-env.mjs scripts/wechat-mp-menu.mjs scripts/memind-portal-tunnel.sh; do
|
for required in server.mjs wechat-mp.bundle.mjs mindspace-sandbox-mcp.mjs tkmind-search-mcp.mjs mindspace-public-links.mjs dist package.json scripts/run-memind-portal-prod.sh scripts/check-mindspace-public-links.mjs scripts/load-env.mjs scripts/wechat-mp-menu.mjs scripts/memind-portal-tunnel.sh; do
|
||||||
if [[ ! -e "${RUNTIME_ROOT}/${required}" ]]; then
|
if [[ ! -e "${RUNTIME_ROOT}/${required}" ]]; then
|
||||||
echo "runtime 产物缺失: ${RUNTIME_ROOT}/${required}" >&2
|
echo "runtime 产物缺失: ${RUNTIME_ROOT}/${required}" >&2
|
||||||
missing=1
|
missing=1
|
||||||
@@ -214,21 +214,25 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
docker_bin="/opt/homebrew/bin/docker"
|
docker_bin="/opt/homebrew/bin/docker"
|
||||||
if [[ -x "${docker_bin}" ]] && "${docker_bin}" ps --format '{{.Names}}' 2>/dev/null | grep -q '^goosed-prod-1$'; then
|
goosed_containers=()
|
||||||
goosed_indexes=()
|
goosed_indexes=()
|
||||||
while IFS= read -r name; do
|
if [[ -x "${docker_bin}" ]]; then
|
||||||
if [[ "${name}" =~ ^goosed-prod-([0-9]+)$ ]]; then
|
while IFS='|' read -r name service; do
|
||||||
|
if [[ "${service}" =~ ^goosed-([0-9]+)$ ]]; then
|
||||||
|
goosed_containers+=("${name}")
|
||||||
goosed_indexes+=("${BASH_REMATCH[1]}")
|
goosed_indexes+=("${BASH_REMATCH[1]}")
|
||||||
fi
|
fi
|
||||||
done < <("${docker_bin}" ps --format '{{.Names}}' | sort -V)
|
done < <("${docker_bin}" ps \
|
||||||
if ((${#goosed_indexes[@]} == 0)); then
|
--filter 'label=com.docker.compose.project=goosed-prod' \
|
||||||
echo "goosed dependency check failed: no goosed-prod-* containers running" >&2
|
--format '{{.Names}}|{{.Label "com.docker.compose.service"}}')
|
||||||
missing=1
|
fi
|
||||||
fi
|
if ((${#goosed_containers[@]} > 0)); then
|
||||||
for index in "${goosed_indexes[@]}"; do
|
for position in "${!goosed_containers[@]}"; do
|
||||||
container="goosed-prod-${index}"
|
container="${goosed_containers[$position]}"
|
||||||
|
index="${goosed_indexes[$position]}"
|
||||||
host_port=$((18005 + index))
|
host_port=$((18005 + index))
|
||||||
if ! "${docker_bin}" ps --format '{{.Names}} {{.Ports}} {{.Status}}' | grep -q "^${container} .*0.0.0.0:${host_port}->18006/tcp.*healthy"; then
|
health="$("${docker_bin}" inspect "${container}" --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' 2>/dev/null || true)"
|
||||||
|
if [[ "${health}" != "healthy" ]] || ! "${docker_bin}" port "${container}" 18006/tcp 2>/dev/null | grep -q "0.0.0.0:${host_port}$"; then
|
||||||
echo "goosed dependency check failed: ${container} is not healthy on host port ${host_port}" >&2
|
echo "goosed dependency check failed: ${container} is not healthy on host port ${host_port}" >&2
|
||||||
missing=1
|
missing=1
|
||||||
fi
|
fi
|
||||||
@@ -244,8 +248,7 @@ if [[ -x "${docker_bin}" ]] && "${docker_bin}" ps --format '{{.Names}}' 2>/dev/n
|
|||||||
echo "goosed dependency check failed: Portal .env must set GOOSED_MCP_NODE_PATH and GOOSED_MCP_SERVER_PATH for Docker goosed" >&2
|
echo "goosed dependency check failed: Portal .env must set GOOSED_MCP_NODE_PATH and GOOSED_MCP_SERVER_PATH for Docker goosed" >&2
|
||||||
missing=1
|
missing=1
|
||||||
else
|
else
|
||||||
for index in "${goosed_indexes[@]}"; do
|
for container in "${goosed_containers[@]}"; do
|
||||||
container="goosed-prod-${index}"
|
|
||||||
if ! "${docker_bin}" exec "${container}" sh -lc "test -x '${portal_mcp_node}' && test -f '${portal_mcp_server}'" >/dev/null 2>&1; then
|
if ! "${docker_bin}" exec "${container}" sh -lc "test -x '${portal_mcp_node}' && test -f '${portal_mcp_server}'" >/dev/null 2>&1; then
|
||||||
echo "goosed dependency check failed: ${container} cannot resolve Portal .env MCP paths '${portal_mcp_node}' and '${portal_mcp_server}'" >&2
|
echo "goosed dependency check failed: ${container} cannot resolve Portal .env MCP paths '${portal_mcp_node}' and '${portal_mcp_server}'" >&2
|
||||||
missing=1
|
missing=1
|
||||||
@@ -627,6 +630,7 @@ say "检查 live 目录中不再保留源码树"
|
|||||||
allowed_live_mjs=(
|
allowed_live_mjs=(
|
||||||
"${APP_DIR}/server.mjs"
|
"${APP_DIR}/server.mjs"
|
||||||
"${APP_DIR}/mindspace-sandbox-mcp.mjs"
|
"${APP_DIR}/mindspace-sandbox-mcp.mjs"
|
||||||
|
"${APP_DIR}/tkmind-search-mcp.mjs"
|
||||||
"${APP_DIR}/mindspace-public-links.mjs"
|
"${APP_DIR}/mindspace-public-links.mjs"
|
||||||
)
|
)
|
||||||
extra_files=""
|
extra_files=""
|
||||||
|
|||||||
Reference in New Issue
Block a user