feat: remove container layer, run agents as direct host processes
- Rewrite container-runner.ts to spawn node processes directly instead of Docker/Apple Container - Runner paths configurable via env vars (NANOCLAW_GROUP_DIR, etc.) with container-path defaults for backwards compat - Pass real credentials directly (no credential proxy needed) - Remove ensureContainerSystemRunning() and startCredentialProxy() from startup - Add build:runners script for building agent-runner and codex-runner - Fix TS strict mode errors in agent-runner ipc-mcp-stdio.ts
This commit is contained in:
57
src/index.ts
57
src/index.ts
@@ -3,13 +3,11 @@ import path from 'path';
|
||||
|
||||
import {
|
||||
ASSISTANT_NAME,
|
||||
CREDENTIAL_PROXY_PORT,
|
||||
IDLE_TIMEOUT,
|
||||
POLL_INTERVAL,
|
||||
TIMEZONE,
|
||||
TRIGGER_PATTERN,
|
||||
} from './config.js';
|
||||
import { startCredentialProxy } from './credential-proxy.js';
|
||||
import './channels/index.js';
|
||||
import {
|
||||
getChannelFactory,
|
||||
@@ -21,11 +19,6 @@ import {
|
||||
writeGroupsSnapshot,
|
||||
writeTasksSnapshot,
|
||||
} from './container-runner.js';
|
||||
import {
|
||||
cleanupOrphans,
|
||||
ensureContainerRuntimeRunning,
|
||||
PROXY_BIND_HOST,
|
||||
} from './container-runtime.js';
|
||||
import {
|
||||
getAllChats,
|
||||
getAllRegisteredGroups,
|
||||
@@ -53,7 +46,11 @@ import {
|
||||
loadSenderAllowlist,
|
||||
shouldDropMessage,
|
||||
} from './sender-allowlist.js';
|
||||
import { extractSessionCommand, handleSessionCommand, isSessionCommandAllowed } from './session-commands.js';
|
||||
import {
|
||||
extractSessionCommand,
|
||||
handleSessionCommand,
|
||||
isSessionCommandAllowed,
|
||||
} from './session-commands.js';
|
||||
import { startSchedulerLoop } from './task-scheduler.js';
|
||||
import { Channel, NewMessage, RegisteredGroup } from './types.js';
|
||||
import { logger } from './logger.js';
|
||||
@@ -175,18 +172,26 @@ async function processGroupMessages(chatJid: string): Promise<boolean> {
|
||||
timezone: TIMEZONE,
|
||||
deps: {
|
||||
sendMessage: (text) => channel.sendMessage(chatJid, text),
|
||||
setTyping: (typing) => channel.setTyping?.(chatJid, typing) ?? Promise.resolve(),
|
||||
runAgent: (prompt, onOutput) => runAgent(group, prompt, chatJid, onOutput),
|
||||
setTyping: (typing) =>
|
||||
channel.setTyping?.(chatJid, typing) ?? Promise.resolve(),
|
||||
runAgent: (prompt, onOutput) =>
|
||||
runAgent(group, prompt, chatJid, onOutput),
|
||||
closeStdin: () => queue.closeStdin(chatJid),
|
||||
advanceCursor: (ts) => { lastAgentTimestamp[chatJid] = ts; saveState(); },
|
||||
advanceCursor: (ts) => {
|
||||
lastAgentTimestamp[chatJid] = ts;
|
||||
saveState();
|
||||
},
|
||||
formatMessages,
|
||||
canSenderInteract: (msg) => {
|
||||
const hasTrigger = TRIGGER_PATTERN.test(msg.content.trim());
|
||||
const reqTrigger = !isMainGroup && group.requiresTrigger !== false;
|
||||
return isMainGroup || !reqTrigger || (hasTrigger && (
|
||||
msg.is_from_me ||
|
||||
isTriggerAllowed(chatJid, msg.sender, loadSenderAllowlist())
|
||||
));
|
||||
return (
|
||||
isMainGroup ||
|
||||
!reqTrigger ||
|
||||
(hasTrigger &&
|
||||
(msg.is_from_me ||
|
||||
isTriggerAllowed(chatJid, msg.sender, loadSenderAllowlist())))
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -431,8 +436,7 @@ async function startMessageLoop(): Promise<void> {
|
||||
const lastHuman = getLastHumanMessageTimestamp(chatJid);
|
||||
if (
|
||||
!lastHuman ||
|
||||
Date.now() - new Date(lastHuman).getTime() >
|
||||
BOT_COLLAB_TIMEOUT_MS
|
||||
Date.now() - new Date(lastHuman).getTime() > BOT_COLLAB_TIMEOUT_MS
|
||||
) {
|
||||
logger.info(
|
||||
{ chatJid, lastHuman },
|
||||
@@ -453,7 +457,12 @@ async function startMessageLoop(): Promise<void> {
|
||||
// Only close active container if the sender is authorized — otherwise an
|
||||
// untrusted user could kill in-flight work by sending /compact (DoS).
|
||||
// closeStdin no-ops internally when no container is active.
|
||||
if (isSessionCommandAllowed(isMainGroup, loopCmdMsg.is_from_me === true)) {
|
||||
if (
|
||||
isSessionCommandAllowed(
|
||||
isMainGroup,
|
||||
loopCmdMsg.is_from_me === true,
|
||||
)
|
||||
) {
|
||||
queue.closeStdin(chatJid);
|
||||
}
|
||||
// Enqueue so processGroupMessages handles auth + cursor advancement.
|
||||
@@ -536,23 +545,11 @@ function recoverPendingMessages(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureContainerSystemRunning(): void {
|
||||
ensureContainerRuntimeRunning();
|
||||
cleanupOrphans();
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
ensureContainerSystemRunning();
|
||||
initDatabase();
|
||||
logger.info('Database initialized');
|
||||
loadState();
|
||||
|
||||
// Start credential proxy (containers route API calls through this)
|
||||
await startCredentialProxy(
|
||||
CREDENTIAL_PROXY_PORT,
|
||||
PROXY_BIND_HOST,
|
||||
);
|
||||
|
||||
// Graceful shutdown handlers
|
||||
const shutdown = async (signal: string) => {
|
||||
logger.info({ signal }, 'Shutdown signal received');
|
||||
|
||||
Reference in New Issue
Block a user