158 lines
5.1 KiB
JavaScript
158 lines
5.1 KiB
JavaScript
import { backfillConversationPackageArtifacts } from './mindspace-conversation-package-backfill.mjs';
|
|
import { createConversationPackagePublicHtmlHydrator } from './mindspace-conversation-package-public-html.mjs';
|
|
import { createMindSpaceLocalRuntimeServices } from './mindspace-local-runtime-services.mjs';
|
|
|
|
export function createMindSpaceLocalServerAdapter({
|
|
pool,
|
|
h5Root,
|
|
env = process.env,
|
|
maxFileBytes,
|
|
publicPageLimit,
|
|
resolveUserIdForAgentSession,
|
|
resolveSessionSnapshot,
|
|
registerPublicHtmlArtifactsForConversation,
|
|
logger = console,
|
|
setIntervalFn = setInterval,
|
|
} = {}) {
|
|
const services = createMindSpaceLocalRuntimeServices({
|
|
pool,
|
|
h5Root,
|
|
env,
|
|
maxFileBytes,
|
|
publicPageLimit,
|
|
resolveUserIdForAgentSession,
|
|
conversationPackageBackfill: ({ user, sessionId, storageRoot, registry }) =>
|
|
backfillConversationPackageArtifacts({
|
|
pool,
|
|
registry,
|
|
storageRoot,
|
|
h5Root,
|
|
user,
|
|
sessionId,
|
|
}),
|
|
conversationPackagePublicHtmlHydrator: createConversationPackagePublicHtmlHydrator({
|
|
h5Root,
|
|
resolveSessionSnapshot,
|
|
registerPublicHtmlArtifactsForConversation,
|
|
}),
|
|
logger,
|
|
});
|
|
|
|
return {
|
|
...services,
|
|
kind: 'local',
|
|
implementationStatus: 'ready',
|
|
assertReady() {
|
|
return true;
|
|
},
|
|
startPublicationCleanup(intervalMs = 60 * 1000) {
|
|
if (!services.publicationService || typeof setIntervalFn !== 'function') return null;
|
|
const timer = setIntervalFn(async () => {
|
|
try {
|
|
const result = await services.publicationService.cleanupExpiredUnconfirmedPublications();
|
|
if (result.cleaned > 0) {
|
|
logger.log?.(
|
|
`[Publication Cleanup] Auto-privatized ${result.cleaned} expired unconfirmed publications`,
|
|
);
|
|
}
|
|
} catch (error) {
|
|
logger.error?.('[Publication Cleanup Error]', error instanceof Error ? error.message : error);
|
|
}
|
|
}, intervalMs);
|
|
timer?.unref?.();
|
|
return timer;
|
|
},
|
|
startAgentWorker({
|
|
enabled = false,
|
|
concurrency = 1,
|
|
pollMs = 1000,
|
|
staleMs = 5 * 60 * 1000,
|
|
runner,
|
|
} = {}) {
|
|
if (!enabled || !services.agentJobService || !runner || typeof setIntervalFn !== 'function') return null;
|
|
let inFlight = 0;
|
|
let draining = false;
|
|
const drainQueue = async () => {
|
|
if (draining) return;
|
|
draining = true;
|
|
try {
|
|
while (inFlight < concurrency) {
|
|
const claim = await services.agentJobService.claimNextJob();
|
|
if (!claim) break;
|
|
inFlight += 1;
|
|
void runner
|
|
.runJob(claim.jobId, claim)
|
|
.catch((error) => {
|
|
logger.error?.('Agent worker job failed:', error);
|
|
})
|
|
.finally(() => {
|
|
inFlight -= 1;
|
|
});
|
|
}
|
|
} catch (error) {
|
|
logger.error?.('Agent worker drain failed:', error);
|
|
} finally {
|
|
draining = false;
|
|
}
|
|
};
|
|
const workerTimer = setIntervalFn(() => {
|
|
void drainQueue();
|
|
}, pollMs);
|
|
const reaperTimer = setIntervalFn(() => {
|
|
void services.agentJobService
|
|
.reapStaleJobs(staleMs)
|
|
.then((reaped) => {
|
|
if (reaped > 0) {
|
|
logger.warn?.(`Agent worker reaped ${reaped} stale running job(s)`);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
logger.error?.('Agent worker reaper failed:', error);
|
|
});
|
|
}, Math.min(staleMs, 60_000));
|
|
workerTimer?.unref?.();
|
|
reaperTimer?.unref?.();
|
|
logger.log?.(`Agent job worker enabled (concurrency=${concurrency}, poll=${pollMs}ms)`);
|
|
return { workerTimer, reaperTimer };
|
|
},
|
|
startBackgroundJobs({
|
|
publicationCleanupIntervalMs = 60 * 1000,
|
|
agentWorker,
|
|
agentRunner,
|
|
workspaceMaintenanceEnabled = false,
|
|
startWorkspaceThumbnailWatcher,
|
|
startWorkspaceAssetSyncWatcher,
|
|
publishRoot,
|
|
syncUserWorkspaceByDirKey,
|
|
expireStaleUploadsIntervalMs = 5 * 60 * 1000,
|
|
} = {}) {
|
|
const backgroundJobs = {
|
|
publicationCleanup: this.startPublicationCleanup(publicationCleanupIntervalMs),
|
|
agentWorker: this.startAgentWorker({
|
|
...agentWorker,
|
|
runner: agentRunner,
|
|
}),
|
|
workspaceMaintenance: null,
|
|
};
|
|
if (!workspaceMaintenanceEnabled) {
|
|
logger.log?.('Workspace maintenance daemons disabled (MEMIND_WORKSPACE_MAINTENANCE=0)');
|
|
return backgroundJobs;
|
|
}
|
|
startWorkspaceThumbnailWatcher?.(publishRoot);
|
|
startWorkspaceAssetSyncWatcher?.({
|
|
publishRoot,
|
|
syncUserWorkspaceByDirKey,
|
|
});
|
|
void services.assetService?.expireStaleUploads?.().catch(() => {});
|
|
backgroundJobs.workspaceMaintenance = {
|
|
expireStaleUploadsTimer:
|
|
setIntervalFn(() => {
|
|
void services.assetService?.expireStaleUploads?.().catch(() => {});
|
|
}, expireStaleUploadsIntervalMs),
|
|
};
|
|
backgroundJobs.workspaceMaintenance.expireStaleUploadsTimer?.unref?.();
|
|
return backgroundJobs;
|
|
},
|
|
};
|
|
}
|