Remove redundant comments throughout codebase
Keep only comments that explain non-obvious behavior or add context not apparent from reading the code. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,6 @@ async function authenticate(): Promise<void> {
|
||||
|
||||
const { state, saveCreds } = await useMultiFileAuthState(AUTH_DIR);
|
||||
|
||||
// Check if already authenticated
|
||||
if (state.creds.registered) {
|
||||
console.log('✓ Already authenticated with WhatsApp');
|
||||
console.log(' To re-authenticate, delete the store/auth folder and run again.');
|
||||
|
||||
@@ -2,18 +2,17 @@ import path from 'path';
|
||||
|
||||
export const ASSISTANT_NAME = process.env.ASSISTANT_NAME || 'Andy';
|
||||
export const POLL_INTERVAL = 2000;
|
||||
export const SCHEDULER_POLL_INTERVAL = 60000; // Check for due tasks every minute
|
||||
export const SCHEDULER_POLL_INTERVAL = 60000;
|
||||
|
||||
// Use absolute paths for container mounts
|
||||
// Absolute paths needed for container mounts
|
||||
const PROJECT_ROOT = process.cwd();
|
||||
export const STORE_DIR = path.resolve(PROJECT_ROOT, 'store');
|
||||
export const GROUPS_DIR = path.resolve(PROJECT_ROOT, 'groups');
|
||||
export const DATA_DIR = path.resolve(PROJECT_ROOT, 'data');
|
||||
export const MAIN_GROUP_FOLDER = 'main';
|
||||
|
||||
// Container configuration
|
||||
export const CONTAINER_IMAGE = process.env.CONTAINER_IMAGE || 'nanoclaw-agent:latest';
|
||||
export const CONTAINER_TIMEOUT = parseInt(process.env.CONTAINER_TIMEOUT || '300000', 10); // 5 minutes default
|
||||
export const IPC_POLL_INTERVAL = 1000; // Check IPC directories every second
|
||||
export const CONTAINER_TIMEOUT = parseInt(process.env.CONTAINER_TIMEOUT || '300000', 10);
|
||||
export const IPC_POLL_INTERVAL = 1000;
|
||||
|
||||
export const TRIGGER_PATTERN = new RegExp(`^@${ASSISTANT_NAME}\\b`, 'i');
|
||||
|
||||
@@ -90,7 +90,6 @@ function buildVolumeMounts(group: RegisteredGroup, isMain: boolean): VolumeMount
|
||||
});
|
||||
}
|
||||
|
||||
// Gmail MCP credentials
|
||||
const gmailDir = path.join(homeDir, '.gmail-mcp');
|
||||
if (fs.existsSync(gmailDir)) {
|
||||
mounts.push({
|
||||
@@ -100,7 +99,6 @@ function buildVolumeMounts(group: RegisteredGroup, isMain: boolean): VolumeMount
|
||||
});
|
||||
}
|
||||
|
||||
// IPC directory for messages and tasks
|
||||
const ipcDir = path.join(DATA_DIR, 'ipc');
|
||||
fs.mkdirSync(path.join(ipcDir, 'messages'), { recursive: true });
|
||||
fs.mkdirSync(path.join(ipcDir, 'tasks'), { recursive: true });
|
||||
@@ -115,7 +113,6 @@ function buildVolumeMounts(group: RegisteredGroup, isMain: boolean): VolumeMount
|
||||
fs.mkdirSync(envDir, { recursive: true });
|
||||
const envFile = path.join(projectRoot, '.env');
|
||||
if (fs.existsSync(envFile)) {
|
||||
// Copy .env to the env directory as a plain file called 'env'
|
||||
fs.copyFileSync(envFile, path.join(envDir, 'env'));
|
||||
mounts.push({
|
||||
hostPath: envDir,
|
||||
@@ -124,10 +121,8 @@ function buildVolumeMounts(group: RegisteredGroup, isMain: boolean): VolumeMount
|
||||
});
|
||||
}
|
||||
|
||||
// Additional mounts from group config
|
||||
if (group.containerConfig?.additionalMounts) {
|
||||
for (const mount of group.containerConfig.additionalMounts) {
|
||||
// Resolve home directory in path
|
||||
const hostPath = mount.hostPath.startsWith('~')
|
||||
? path.join(homeDir, mount.hostPath.slice(1))
|
||||
: mount.hostPath;
|
||||
@@ -150,8 +145,7 @@ function buildVolumeMounts(group: RegisteredGroup, isMain: boolean): VolumeMount
|
||||
function buildContainerArgs(mounts: VolumeMount[]): string[] {
|
||||
const args: string[] = ['run', '-i', '--rm'];
|
||||
|
||||
// Add volume mounts
|
||||
// Apple Container: use --mount for readonly, -v for read-write
|
||||
// Apple Container: --mount for readonly, -v for read-write
|
||||
for (const mount of mounts) {
|
||||
if (mount.readonly) {
|
||||
args.push('--mount', `type=bind,source=${mount.hostPath},target=${mount.containerPath},readonly`);
|
||||
@@ -160,7 +154,6 @@ function buildContainerArgs(mounts: VolumeMount[]): string[] {
|
||||
}
|
||||
}
|
||||
|
||||
// Add the image name
|
||||
args.push(CONTAINER_IMAGE);
|
||||
|
||||
return args;
|
||||
@@ -172,15 +165,12 @@ export async function runContainerAgent(
|
||||
): Promise<ContainerOutput> {
|
||||
const startTime = Date.now();
|
||||
|
||||
// Ensure group directory exists
|
||||
const groupDir = path.join(GROUPS_DIR, group.folder);
|
||||
fs.mkdirSync(groupDir, { recursive: true });
|
||||
|
||||
// Build volume mounts
|
||||
const mounts = buildVolumeMounts(group, input.isMain);
|
||||
const containerArgs = buildContainerArgs(mounts);
|
||||
|
||||
// Log detailed mount info at debug level
|
||||
logger.debug({
|
||||
group: group.name,
|
||||
mounts: mounts.map(m => `${m.hostPath} -> ${m.containerPath}${m.readonly ? ' (ro)' : ''}`),
|
||||
@@ -193,7 +183,6 @@ export async function runContainerAgent(
|
||||
isMain: input.isMain
|
||||
}, 'Spawning container agent');
|
||||
|
||||
// Create logs directory for this group
|
||||
const logsDir = path.join(GROUPS_DIR, group.folder, 'logs');
|
||||
fs.mkdirSync(logsDir, { recursive: true });
|
||||
|
||||
@@ -205,7 +194,6 @@ export async function runContainerAgent(
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
// Send input JSON to container stdin
|
||||
container.stdin.write(JSON.stringify(input));
|
||||
container.stdin.end();
|
||||
|
||||
@@ -215,14 +203,12 @@ export async function runContainerAgent(
|
||||
|
||||
container.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
// Log container stderr in real-time
|
||||
const lines = data.toString().trim().split('\n');
|
||||
for (const line of lines) {
|
||||
if (line) logger.debug({ container: group.folder }, line);
|
||||
}
|
||||
});
|
||||
|
||||
// Timeout handler
|
||||
const timeout = setTimeout(() => {
|
||||
logger.error({ group: group.name }, 'Container timeout, killing');
|
||||
container.kill('SIGKILL');
|
||||
@@ -237,12 +223,10 @@ export async function runContainerAgent(
|
||||
clearTimeout(timeout);
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
// Write container log file
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const logFile = path.join(logsDir, `container-${timestamp}.log`);
|
||||
const isVerbose = process.env.LOG_LEVEL === 'debug' || process.env.LOG_LEVEL === 'trace';
|
||||
|
||||
// Build log content - only include full input/output in verbose mode
|
||||
const logLines = [
|
||||
`=== Container Run Log ===`,
|
||||
`Timestamp: ${new Date().toISOString()}`,
|
||||
@@ -254,7 +238,6 @@ export async function runContainerAgent(
|
||||
];
|
||||
|
||||
if (isVerbose) {
|
||||
// Full content logging only in debug/trace mode
|
||||
logLines.push(
|
||||
`=== Input ===`,
|
||||
JSON.stringify(input, null, 2),
|
||||
@@ -272,7 +255,6 @@ export async function runContainerAgent(
|
||||
stdout
|
||||
);
|
||||
} else {
|
||||
// Minimal logging by default - no message content
|
||||
logLines.push(
|
||||
`=== Input Summary ===`,
|
||||
`Prompt length: ${input.prompt.length} chars`,
|
||||
@@ -283,7 +265,6 @@ export async function runContainerAgent(
|
||||
``
|
||||
);
|
||||
|
||||
// Only include stderr/stdout if there was an error
|
||||
if (code !== 0) {
|
||||
logLines.push(
|
||||
`=== Stderr (last 500 chars) ===`,
|
||||
@@ -313,9 +294,8 @@ export async function runContainerAgent(
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse JSON output from stdout
|
||||
try {
|
||||
// Find the JSON line (last non-empty line should be the output)
|
||||
// Last non-empty line is the JSON output
|
||||
const lines = stdout.trim().split('\n');
|
||||
const jsonLine = lines[lines.length - 1];
|
||||
const output: ContainerOutput = JSON.parse(jsonLine);
|
||||
@@ -355,7 +335,6 @@ export async function runContainerAgent(
|
||||
});
|
||||
}
|
||||
|
||||
// Export task snapshot for container IPC
|
||||
export function writeTasksSnapshot(tasks: Array<{
|
||||
id: string;
|
||||
groupFolder: string;
|
||||
|
||||
@@ -129,8 +129,6 @@ export function getMessagesSince(chatJid: string, sinceTimestamp: string): NewMe
|
||||
return db.prepare(sql).all(chatJid, sinceTimestamp) as NewMessage[];
|
||||
}
|
||||
|
||||
// Scheduled Tasks
|
||||
|
||||
export function createTask(task: Omit<ScheduledTask, 'last_run' | 'last_result'>): void {
|
||||
db.prepare(`
|
||||
INSERT INTO scheduled_tasks (id, group_folder, chat_jid, prompt, schedule_type, schedule_value, next_run, status, created_at)
|
||||
|
||||
11
src/index.ts
11
src/index.ts
@@ -81,11 +81,10 @@ async function processMessage(msg: NewMessage): Promise<void> {
|
||||
|
||||
if (!TRIGGER_PATTERN.test(content)) return;
|
||||
|
||||
// Get messages since last agent interaction to catch up the session
|
||||
// Get all messages since last agent interaction so the session has full context
|
||||
const sinceTimestamp = lastAgentTimestamp[msg.chat_jid] || '';
|
||||
const missedMessages = getMessagesSince(msg.chat_jid, sinceTimestamp);
|
||||
|
||||
// Build prompt with conversation history
|
||||
const lines = missedMessages.map(m => {
|
||||
const d = new Date(m.timestamp);
|
||||
const date = d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
@@ -102,7 +101,6 @@ async function processMessage(msg: NewMessage): Promise<void> {
|
||||
const response = await runAgent(group, prompt, msg.chat_jid);
|
||||
await setTyping(msg.chat_jid, false);
|
||||
|
||||
// Update last agent timestamp
|
||||
lastAgentTimestamp[msg.chat_jid] = msg.timestamp;
|
||||
|
||||
if (response) {
|
||||
@@ -135,7 +133,6 @@ async function runAgent(group: RegisteredGroup, prompt: string, chatJid: string)
|
||||
isMain
|
||||
});
|
||||
|
||||
// Update session if changed
|
||||
if (output.newSessionId) {
|
||||
sessions[group.folder] = output.newSessionId;
|
||||
saveJson(path.join(DATA_DIR, 'sessions.json'), sessions);
|
||||
@@ -162,7 +159,6 @@ async function sendMessage(jid: string, text: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// IPC watcher for container messages and tasks
|
||||
function startIpcWatcher(): void {
|
||||
const messagesDir = path.join(DATA_DIR, 'ipc', 'messages');
|
||||
const tasksDir = path.join(DATA_DIR, 'ipc', 'tasks');
|
||||
@@ -171,7 +167,6 @@ function startIpcWatcher(): void {
|
||||
fs.mkdirSync(tasksDir, { recursive: true });
|
||||
|
||||
const processIpcFiles = async () => {
|
||||
// Process pending messages
|
||||
try {
|
||||
const messageFiles = fs.readdirSync(messagesDir).filter(f => f.endsWith('.json'));
|
||||
for (const file of messageFiles) {
|
||||
@@ -195,7 +190,6 @@ function startIpcWatcher(): void {
|
||||
logger.error({ err }, 'Error reading IPC messages directory');
|
||||
}
|
||||
|
||||
// Process pending task operations
|
||||
try {
|
||||
const taskFiles = fs.readdirSync(tasksDir).filter(f => f.endsWith('.json'));
|
||||
for (const file of taskFiles) {
|
||||
@@ -241,7 +235,6 @@ async function processTaskIpc(data: {
|
||||
if (data.prompt && data.schedule_type && data.schedule_value && data.groupFolder && data.chatJid) {
|
||||
const scheduleType = data.schedule_type as 'cron' | 'interval' | 'once';
|
||||
|
||||
// Calculate next run time
|
||||
let nextRun: string | null = null;
|
||||
if (scheduleType === 'cron') {
|
||||
const interval = CronExpressionParser.parse(data.schedule_value);
|
||||
@@ -395,11 +388,9 @@ async function startMessageLoop(): Promise<void> {
|
||||
|
||||
function ensureContainerSystemRunning(): void {
|
||||
try {
|
||||
// Check if container system is already running
|
||||
execSync('container system status', { stdio: 'pipe' });
|
||||
logger.debug('Apple Container system already running');
|
||||
} catch {
|
||||
// Not running, try to start it
|
||||
logger.info('Starting Apple Container system...');
|
||||
try {
|
||||
execSync('container system start', { stdio: 'pipe', timeout: 30000 });
|
||||
|
||||
@@ -24,7 +24,6 @@ async function runTask(task: ScheduledTask, deps: SchedulerDependencies): Promis
|
||||
|
||||
logger.info({ taskId: task.id, group: task.group_folder }, 'Running scheduled task');
|
||||
|
||||
// Find the group config for this task
|
||||
const groups = deps.registeredGroups();
|
||||
const group = Object.values(groups).find(g => g.folder === task.group_folder);
|
||||
|
||||
@@ -78,7 +77,6 @@ async function runTask(task: ScheduledTask, deps: SchedulerDependencies): Promis
|
||||
|
||||
const durationMs = Date.now() - startTime;
|
||||
|
||||
// Log the run
|
||||
logTaskRun({
|
||||
task_id: task.id,
|
||||
run_at: new Date().toISOString(),
|
||||
@@ -88,7 +86,6 @@ async function runTask(task: ScheduledTask, deps: SchedulerDependencies): Promis
|
||||
error
|
||||
});
|
||||
|
||||
// Calculate next run
|
||||
let nextRun: string | null = null;
|
||||
if (task.schedule_type === 'cron') {
|
||||
const interval = CronExpressionParser.parse(task.schedule_value);
|
||||
@@ -97,9 +94,8 @@ async function runTask(task: ScheduledTask, deps: SchedulerDependencies): Promis
|
||||
const ms = parseInt(task.schedule_value, 10);
|
||||
nextRun = new Date(Date.now() + ms).toISOString();
|
||||
}
|
||||
// 'once' tasks don't have a next run
|
||||
// 'once' tasks have no next run
|
||||
|
||||
// Update task
|
||||
const resultSummary = error ? `Error: ${error}` : (result ? result.slice(0, 200) : 'Completed');
|
||||
updateTaskAfterRun(task.id, nextRun, resultSummary);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user