集成 OpenHub 与 WeKnora Page AI
This commit is contained in:
@@ -9,6 +9,15 @@
|
||||
* - ENABLE_OPENCODE:设为 "1" or "true" 时启用 opencode serve
|
||||
* - OPENCODE_CMD:覆盖 opencode 启动命令;设置后即视为显式启用 opencode
|
||||
* - SKIP_OPENCODE:设为 "1" or "true" 可强制跳过 opencode
|
||||
* - ENABLE_OPENHUB:设为 "1" or "true" 时启用 OpenHub FastAPI
|
||||
* - OPENHUB_CMD / OPENHUB_BACKEND_CMD:覆盖 OpenHub FastAPI 启动命令;设置后即视为显式启用 OpenHub
|
||||
* - OPENHUB_PORT / OPENHUB_BACKEND_PORT:OpenHub FastAPI 端口,默认 18080
|
||||
* - SKIP_OPENHUB:设为 "1" or "true" 可强制跳过 OpenHub
|
||||
* - OPENHUB_REDIS_URL / OPENHUB_REDIS_DB:记录 OpenHub Redis 位置;OPENHUB_REDIS_HEALTH_URL 可选做 HTTP health 检查
|
||||
* - OPENHUB_REDIS_HEALTH_URL:可选 Redis health URL;dev-hot 只检查,不释放或结束 OpenHub 相关端口
|
||||
* - OPENHUB_OPENCODE_BASE_URL:OpenHub 侧 opencode serve base URL;默认复用 MNOTE_OPENCODE_BASE_URL
|
||||
* - SKIP_OPENHUB_HEALTH:设为 "1" or "true" 可跳过 OpenHub/Redis/opencode health 预检
|
||||
* - MNOTE_OPENHUB_DISABLE_GIT_SNAPSHOT_RESTORE:默认 "1",禁用 OpenHub Git snapshot/restore/revert 写链
|
||||
* - PYTHON_BIN:只在 BACKEND_CMD 未覆盖时,设置 Python 可执行文件,默认 "python"
|
||||
*/
|
||||
|
||||
@@ -49,6 +58,7 @@ function resolveBackendExecutable(envName, fallbackName) {
|
||||
const pythonBin = resolveBackendExecutable("PYTHON_BIN", "python");
|
||||
const backendPortFromEnv = Number(process.env.BACKEND_PORT || 8000);
|
||||
const opencodePortFromEnv = Number(process.env.OPENCODE_PORT || 4096);
|
||||
const openhubPortFromEnv = Number(process.env.OPENHUB_BACKEND_PORT || process.env.OPENHUB_PORT || 18080);
|
||||
|
||||
function hasCommand(command) {
|
||||
try {
|
||||
@@ -76,6 +86,11 @@ function buildDefaultOpencodeCommand(port) {
|
||||
return `while true; do script -qfec "opencode serve --hostname=127.0.0.1 --port ${port} --print-logs" /dev/null; sleep 1; done`;
|
||||
}
|
||||
|
||||
function buildDefaultOpenHubCommand(port) {
|
||||
const openhubBackendDir = process.env.OPENHUB_BACKEND_DIR || "/tmp/mnote-openhub-research/OpenHub/smart-query-backend";
|
||||
return `cd ${JSON.stringify(openhubBackendDir)} && uvicorn app.main:app --host 127.0.0.1 --port ${port}`;
|
||||
}
|
||||
|
||||
function isEnabledEnv(value) {
|
||||
const normalized = String(value || "").toLowerCase();
|
||||
return normalized === "1" || normalized === "true";
|
||||
@@ -93,6 +108,54 @@ function shouldStartOpencode(env = process.env) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function shouldStartOpenHub(env = process.env) {
|
||||
if (isEnabledEnv(env.SKIP_OPENHUB)) return false;
|
||||
if (String(env.OPENHUB_BACKEND_CMD || env.OPENHUB_CMD || "").trim()) return true;
|
||||
return isEnabledEnv(env.ENABLE_OPENHUB);
|
||||
}
|
||||
|
||||
function shouldCheckOpenHubHealth(env = process.env) {
|
||||
if (isEnabledEnv(env.SKIP_OPENHUB_HEALTH)) return false;
|
||||
return shouldStartOpenHub(env) || isEnabledEnv(env.CHECK_OPENHUB_HEALTH);
|
||||
}
|
||||
|
||||
function resolveOpenHubHealthPlan(env = process.env) {
|
||||
const openhubPort = Number(env.OPENHUB_BACKEND_PORT || env.OPENHUB_PORT || openhubPortFromEnv);
|
||||
const opencodePort = Number(env.OPENCODE_PORT || opencodePortFromEnv);
|
||||
const baseUrl = String(env.MNOTE_OPENHUB_BASE_URL || env.OPENHUB_BASE_URL || `http://127.0.0.1:${openhubPort}`).replace(/\/+$/, "");
|
||||
const redisHealthUrl = String(env.OPENHUB_REDIS_HEALTH_URL || "").trim();
|
||||
const redisUrl = String(env.OPENHUB_REDIS_URL || "").trim();
|
||||
const redisDb = String(env.OPENHUB_REDIS_DB || "").trim();
|
||||
const opencodeBaseUrl = String(env.OPENHUB_OPENCODE_BASE_URL || env.MNOTE_OPENCODE_BASE_URL || `http://127.0.0.1:${opencodePort}`).replace(/\/+$/, "");
|
||||
const requireHealth = isEnabledEnv(env.REQUIRE_OPENHUB_HEALTH);
|
||||
return {
|
||||
enabled: shouldCheckOpenHubHealth(env),
|
||||
openhub: {
|
||||
label: "OpenHub FastAPI",
|
||||
url: env.OPENHUB_HEALTH_URL || `${baseUrl}/health`,
|
||||
required: requireHealth,
|
||||
},
|
||||
redis: {
|
||||
label: "OpenHub Redis",
|
||||
url: redisHealthUrl,
|
||||
redisUrl,
|
||||
redisDb,
|
||||
required: isEnabledEnv(env.REQUIRE_OPENHUB_REDIS_HEALTH),
|
||||
skipped: !redisHealthUrl,
|
||||
},
|
||||
opencode: {
|
||||
label: "opencode",
|
||||
url: env.OPENCODE_HEALTH_URL || `${opencodeBaseUrl}/global/health`,
|
||||
required: requireHealth,
|
||||
},
|
||||
gitSnapshotRestore: {
|
||||
env: "MNOTE_OPENHUB_DISABLE_GIT_SNAPSHOT_RESTORE",
|
||||
value: String(env.MNOTE_OPENHUB_DISABLE_GIT_SNAPSHOT_RESTORE || "1"),
|
||||
defaultDisabled: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function resolveRuntimePlan(env = process.env) {
|
||||
const frontendPort = Number(env.FRONTEND_PORT || 3000);
|
||||
const skipGateway = false;
|
||||
@@ -107,6 +170,8 @@ function resolveRuntimePlan(env = process.env) {
|
||||
MNOTE_WEB_BIND: env.MNOTE_WEB_BIND || `0.0.0.0:${publicPort}`,
|
||||
MNOTE_WEB_PUBLIC_BIND: env.MNOTE_WEB_PUBLIC_BIND || `127.0.0.1:${publicPort}`,
|
||||
MNOTE_OPENCODE_BASE_URL: env.MNOTE_OPENCODE_BASE_URL || `http://127.0.0.1:${opencodePortFromEnv}`,
|
||||
MNOTE_OPENHUB_BASE_URL: env.MNOTE_OPENHUB_BASE_URL || `http://127.0.0.1:${openhubPortFromEnv}`,
|
||||
MNOTE_OPENHUB_DISABLE_GIT_SNAPSHOT_RESTORE: env.MNOTE_OPENHUB_DISABLE_GIT_SNAPSHOT_RESTORE || "1",
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -148,6 +213,18 @@ const tasks = [
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(shouldStartOpenHub(process.env)
|
||||
? [
|
||||
{
|
||||
name: "openhub",
|
||||
command:
|
||||
process.env.OPENHUB_BACKEND_CMD ||
|
||||
process.env.OPENHUB_CMD ||
|
||||
buildDefaultOpenHubCommand(openhubPortFromEnv),
|
||||
cwd: rootDir,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
function findTask(name) {
|
||||
@@ -363,6 +440,50 @@ async function ensurePortFree(port, nameForLog) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async function checkHttpHealth(url, label, required) {
|
||||
if (!url) {
|
||||
logPrefix("openhub-health", `${label} health 未配置,已跳过。`);
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), 1200);
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: { accept: "application/json,text/plain,*/*" },
|
||||
signal: controller.signal,
|
||||
});
|
||||
clearTimeout(timer);
|
||||
if (response.ok) {
|
||||
logPrefix("openhub-health", `${label} 可达:${url}`);
|
||||
return true;
|
||||
}
|
||||
logPrefix("openhub-health", `${label} 返回 HTTP ${response.status}:${url}`);
|
||||
return !required;
|
||||
} catch (error) {
|
||||
logPrefix("openhub-health", `${label} 不可达:${url} (${error.message})`);
|
||||
return !required;
|
||||
}
|
||||
}
|
||||
|
||||
async function checkOpenHubHealth(plan = resolveOpenHubHealthPlan(process.env)) {
|
||||
if (!plan.enabled) {
|
||||
return true;
|
||||
}
|
||||
logPrefix(
|
||||
"openhub-health",
|
||||
`Git snapshot/restore 默认关闭:${plan.gitSnapshotRestore.env}=${plan.gitSnapshotRestore.value || "1"}`,
|
||||
);
|
||||
const checks = [
|
||||
await checkHttpHealth(plan.openhub.url, plan.openhub.label, plan.openhub.required),
|
||||
plan.redis.skipped
|
||||
? (logPrefix("openhub-health", "OpenHub Redis health 未配置,已跳过非破坏性检查。"), true)
|
||||
: await checkHttpHealth(plan.redis.url, plan.redis.label, plan.redis.required),
|
||||
await checkHttpHealth(plan.opencode.url, plan.opencode.label, plan.opencode.required),
|
||||
];
|
||||
return checks.every(Boolean);
|
||||
}
|
||||
|
||||
function loadEnvFile(filePath) {
|
||||
if (!fs.existsSync(filePath)) return {};
|
||||
const content = fs.readFileSync(filePath, "utf8");
|
||||
@@ -519,6 +640,22 @@ async function main() {
|
||||
|
||||
}
|
||||
|
||||
if (shouldStartOpenHub(process.env) && !process.env.OPENHUB_BACKEND_CMD && !process.env.OPENHUB_CMD) {
|
||||
const openhubTask = findTask("openhub");
|
||||
if (!openhubTask) {
|
||||
throw new Error("缺少 OpenHub 任务配置");
|
||||
}
|
||||
openhubTask.command = buildDefaultOpenHubCommand(openhubPortFromEnv);
|
||||
} else if (isEnabledEnv(process.env.SKIP_OPENHUB)) {
|
||||
logPrefix("openhub", "已跳过 OpenHub FastAPI(SKIP_OPENHUB=1)。");
|
||||
}
|
||||
|
||||
const healthOk = await checkOpenHubHealth(resolveOpenHubHealthPlan(process.env));
|
||||
if (!healthOk) {
|
||||
console.error("OpenHub health 预检失败,已中止启动。");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (tasks.length === 0) {
|
||||
console.error("未配置任何可运行的任务,检查环境变量设置。");
|
||||
process.exit(1);
|
||||
@@ -537,12 +674,16 @@ if (require.main === module) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildDefaultOpenHubCommand,
|
||||
checkOpenHubHealth,
|
||||
ensurePortFree,
|
||||
getListeningPidsByPort,
|
||||
getProcessNameByPid,
|
||||
isPortFree,
|
||||
resolveOpenHubHealthPlan,
|
||||
resolveRuntimePlan,
|
||||
resolveBackendExecutable,
|
||||
shouldStartBackend,
|
||||
shouldStartOpenHub,
|
||||
terminatePid,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user