Files
memind/server/portal-runtime-routes.mjs
john 286069449b
Memind CI / Test, build, and release guards (push) Failing after 2m14s
feat: add guarded portal canary release
2026-07-26 19:51:44 +08:00

123 lines
4.1 KiB
JavaScript

import { resolvePortalRuntimeRole } from './portal-runtime-role.mjs';
function runtimeEnvFlag(value, fallback = false) {
const raw = String(value ?? '').trim().toLowerCase();
if (!raw) return fallback;
return ['1', 'true', 'yes', 'on'].includes(raw);
}
function runtimeCsvList(value) {
return String(value ?? '')
.split(',')
.map((item) => item.trim())
.filter(Boolean);
}
function runtimeCodeRunPolicyStatus(env) {
return {
source: 'env',
enabled: runtimeEnvFlag(env.MEMIND_AGENT_CODE_RUNS_ENABLED),
clientEnabled: runtimeEnvFlag(env.VITE_AGENT_CODE_RUNS_ENABLED),
userAllowlist: runtimeCsvList(env.MEMIND_AGENT_CODE_RUNS_USER_IDS),
taskTypeAllowlist: runtimeCsvList(env.MEMIND_AGENT_CODE_RUN_TASK_TYPES),
requireValidation: runtimeEnvFlag(env.MEMIND_AGENT_CODE_RUNS_REQUIRE_VALIDATION),
pageDataDevAutodetect: runtimeEnvFlag(env.VITE_AGENT_PAGE_DATA_DEV_AUTODETECT),
generalAutodetect: runtimeEnvFlag(env.VITE_AGENT_CODE_RUNS_AUTODETECT),
envOverrideActive: String(env.MEMIND_CODE_RUN_POLICY_SOURCE ?? '').trim().toLowerCase() === 'env',
updatedAt: null,
updatedBy: null,
};
}
async function resolveRuntimeCodeRunPolicyStatus(env, getCodeRunPolicyService) {
const service = typeof getCodeRunPolicyService === 'function'
? getCodeRunPolicyService()
: null;
if (service?.getRuntimeStatusSummary) {
return service.getRuntimeStatusSummary();
}
return runtimeCodeRunPolicyStatus(env);
}
export function attachPortalRuntimeRoutes(
api,
{
waitForUserAuthReady = async () => {},
getUserAuth = () => null,
getTkmindProxy = () => null,
getEpisodicMemoryService = () => null,
getAgentRunGateway = () => null,
getCodeRunPolicyService = () => null,
env = process.env,
} = {},
) {
if (!api || typeof api.get !== 'function') {
throw new Error('attachPortalRuntimeRoutes requires an Express-compatible router');
}
api.get('/status', async (_req, res, next) => {
res.setHeader?.('X-Memind-Runtime-Role', resolvePortalRuntimeRole(env));
await waitForUserAuthReady();
const userAuth = getUserAuth();
const tkmindProxy = getTkmindProxy();
if (userAuth && tkmindProxy) {
try {
const upstream = await tkmindProxy.apiFetch('/status', { method: 'GET' });
const text = await upstream.text();
return res.status(upstream.status).send(text);
} catch (err) {
return res.status(502).json({ message: err instanceof Error ? err.message : '代理失败' });
}
}
return next();
});
api.get('/runtime/status', async (_req, res) => {
await waitForUserAuthReady();
const tkmindProxy = getTkmindProxy();
if (!tkmindProxy?.getRuntimeStatus) {
return res.status(503).json({ ok: false, message: 'runtime router unavailable' });
}
try {
const status = await tkmindProxy.getRuntimeStatus();
const episodicMemoryService = getEpisodicMemoryService();
if (episodicMemoryService?.getStatus) {
status.memory = {
...(status.memory ?? {}),
episodic: await episodicMemoryService.getStatus().catch((err) => ({
configuredEnabled: false,
mode: 'off',
error: err instanceof Error ? err.message : String(err),
})),
};
}
const agentRunGateway = getAgentRunGateway();
const toolQueue = agentRunGateway?.getQueueStatus
? await agentRunGateway.getQueueStatus().catch((err) => ({
error: err instanceof Error ? err.message : String(err),
}))
: null;
if (toolQueue) {
status.toolRuntime = {
...(status.toolRuntime ?? {}),
codeRunPolicy: await resolveRuntimeCodeRunPolicyStatus(
env,
getCodeRunPolicyService,
),
queue: toolQueue,
};
}
return res.json({
ok: true,
timestamp: new Date().toISOString(),
...status,
});
} catch (err) {
return res.status(502).json({
ok: false,
message: err instanceof Error ? err.message : 'runtime status failed',
});
}
});
}