Files
john 8ccf2db05c
Memind CI / Test, build, and release guards (push) Failing after 3m18s
feat(page-data): add validation suggestions, repair path, and thinking fixes
Map Page Data failure codes to Chinese remediation hints, trigger one-shot
goosed repair for remediable cases, and recover poisoned thinking sessions.
Route local DeepSeek through the no-think proxy via host.docker.internal so
tool rounds no longer hit reasoning_content 400; document gate and case-study
scenarios for event registration repair.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 23:01:55 +08:00

123 lines
4.0 KiB
JavaScript

#!/usr/bin/env node
import os from 'node:os';
import { pathToFileURL } from 'node:url';
import {
createAiderExecutorAdapter,
createGoosedExecutorAdapter,
createOpenHandsExecutorAdapter,
} from './executor-adapters.mjs';
import {
createExecutorWorkerClient,
createExecutorWorkerRuntime,
createWorkerAdapterRegistry,
} from './executor-worker-runtime.mjs';
function envFlag(value, fallback = false) {
const normalized = String(value ?? '').trim().toLowerCase();
if (!normalized) return fallback;
return ['1', 'true', 'yes', 'on'].includes(normalized);
}
function positiveInteger(value, fallback) {
const number = Number(value);
return Number.isFinite(number) && number > 0 ? Math.floor(number) : fallback;
}
function jsonObject(value, fallback = {}) {
try {
const parsed = JSON.parse(String(value ?? ''));
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : fallback;
} catch {
return fallback;
}
}
export function createConfiguredWorker({ env = process.env, fetchImpl = null } = {}) {
const registry = createWorkerAdapterRegistry();
const workspaceAliases = jsonObject(env.MEMIND_EXECUTOR_WORKSPACE_ALIASES_JSON);
const allowedRoot = String(env.MEMIND_EXECUTOR_WORKSPACE_ROOT ?? '').trim();
const goosedUrl = String(env.MEMIND_EXECUTOR_GOOSED_URL ?? '').trim();
if (goosedUrl) {
registry.register(createGoosedExecutorAdapter({
baseUrl: goosedUrl,
secret: env.MEMIND_EXECUTOR_GOOSED_TOKEN,
...(fetchImpl ? { fetchImpl } : {}),
allowRemote: envFlag(env.MEMIND_EXECUTOR_ALLOW_REMOTE_GOOSED, false),
tlsInsecure: envFlag(env.MEMIND_EXECUTOR_GOOSED_TLS_INSECURE, false),
provider: env.MEMIND_EXECUTOR_GOOSED_PROVIDER,
model: env.MEMIND_EXECUTOR_GOOSED_MODEL,
workspaceResolver: async (reference) => workspaceAliases[reference?.id] ?? null,
}));
}
if (env.MEMIND_EXECUTOR_AIDER_COMMAND && allowedRoot) {
registry.register(createAiderExecutorAdapter({
command: env.MEMIND_EXECUTOR_AIDER_COMMAND,
allowedRoot,
workspaceAliases,
}));
}
if (env.MEMIND_EXECUTOR_OPENHANDS_COMMAND && allowedRoot) {
registry.register(createOpenHandsExecutorAdapter({
command: env.MEMIND_EXECUTOR_OPENHANDS_COMMAND,
allowedRoot,
workspaceAliases,
}));
}
if (!registry.ids().length) {
throw new Error('Executor worker has no configured adapters');
}
const client = createExecutorWorkerClient({
baseUrl: env.MEMIND_ORCHESTRATOR_URL ?? 'http://127.0.0.1:8093',
serviceToken: env.MEMIND_ORCHESTRATOR_SERVICE_TOKEN,
workerToken: env.MEMIND_ORCHESTRATOR_WORKER_TOKEN,
fetchImpl: fetchImpl ?? globalThis.fetch,
});
return createExecutorWorkerRuntime({
client,
registry,
workerId: env.MEMIND_EXECUTOR_WORKER_ID ?? `${os.hostname()}-${process.pid}`,
heartbeatMs: positiveInteger(env.MEMIND_EXECUTOR_HEARTBEAT_MS, 10_000),
leaseDurationMs: positiveInteger(env.MEMIND_EXECUTOR_LEASE_MS, 30_000),
});
}
async function main() {
const worker = createConfiguredWorker();
const once = process.argv.includes('--once');
const recover = process.argv.includes('--recover-expired');
if (recover) {
console.log(JSON.stringify(await worker.recoverExpired(), null, 2));
return;
}
if (once) {
console.log(JSON.stringify(await worker.runOnce(), null, 2));
return;
}
let stopping = false;
const stop = async () => {
if (stopping) return;
stopping = true;
await worker.stop();
};
process.once('SIGINT', () => void stop());
process.once('SIGTERM', () => void stop());
const pollMs = positiveInteger(process.env.MEMIND_EXECUTOR_POLL_MS, 1000);
console.log(JSON.stringify({ ok: true, ...worker.status() }));
while (!stopping) {
await worker.runOnce();
await new Promise((resolve) => setTimeout(resolve, pollMs));
}
}
const isEntrypoint = process.argv[1]
&& import.meta.url === pathToFileURL(process.argv[1]).href;
if (isEntrypoint) {
await main();
}
export const executorWorkerEntrypointInternals = {
envFlag,
jsonObject,
positiveInteger,
};