Apply fixes from closed PRs: sentinel markers, JID lookup, schedule validation
- PR #10: Add sentinel markers for robust JSON parsing between container and host. Fallback to last-line parsing for backwards compatibility. - PR #5: Look up target JID from registeredGroups instead of trusting IPC payload, fixing cross-group scheduled tasks getting wrong chat_jid. - PR #8: Add lightweight schedule validation in container MCP that returns errors to agents (cron syntax, positive interval, valid ISO timestamp). Also defensive validation on host side. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -45,8 +45,13 @@ async function readStdin(): Promise<string> {
|
||||
});
|
||||
}
|
||||
|
||||
const OUTPUT_START_MARKER = '---NANOCLAW_OUTPUT_START---';
|
||||
const OUTPUT_END_MARKER = '---NANOCLAW_OUTPUT_END---';
|
||||
|
||||
function writeOutput(output: ContainerOutput): void {
|
||||
console.log(OUTPUT_START_MARKER);
|
||||
console.log(JSON.stringify(output));
|
||||
console.log(OUTPUT_END_MARKER);
|
||||
}
|
||||
|
||||
function log(message: string): void {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
|
||||
import { z } from 'zod';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { CronExpressionParser } from 'cron-parser';
|
||||
|
||||
const IPC_DIR = '/workspace/ipc';
|
||||
const MESSAGES_DIR = path.join(IPC_DIR, 'messages');
|
||||
@@ -80,6 +81,34 @@ IMPORTANT - schedule_value format depends on schedule_type:
|
||||
target_group: z.string().optional().describe('Target group folder (main only, defaults to current group)')
|
||||
},
|
||||
async (args) => {
|
||||
// Validate schedule_value before writing IPC
|
||||
if (args.schedule_type === 'cron') {
|
||||
try {
|
||||
CronExpressionParser.parse(args.schedule_value);
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Invalid cron: "${args.schedule_value}". Use format like "0 9 * * *" (daily 9am) or "*/5 * * * *" (every 5 min).` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
} else if (args.schedule_type === 'interval') {
|
||||
const ms = parseInt(args.schedule_value, 10);
|
||||
if (isNaN(ms) || ms <= 0) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Invalid interval: "${args.schedule_value}". Must be positive milliseconds (e.g., "300000" for 5 min).` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
} else if (args.schedule_type === 'once') {
|
||||
const date = new Date(args.schedule_value);
|
||||
if (isNaN(date.getTime())) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Invalid timestamp: "${args.schedule_value}". Use ISO 8601 format like "2026-02-01T15:30:00.000Z".` }],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Non-main groups can only schedule for themselves
|
||||
const targetGroup = isMain && args.target_group ? args.target_group : groupFolder;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user