Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ if (config.schedules.syncOnInit !== false) {
const jobResult = Cron.add({
name: "reflection-daily",
cron: "0 2 * * *",
command: `cd ${cwd} && node index.js --chat "/reflection"`,
command: `cd ${cwd} && node index.js --message run /reflection`,
});
if (jobResult.added || !jobResult.error) {
try {
Expand All @@ -70,7 +70,7 @@ if (config.schedules.syncOnInit !== false) {
const jobData = {
name: "reflection-daily",
cron: "0 2 * * *",
command: `cd ${cwd} && node index.js --chat "/reflection"`,
command: `cd ${cwd} && node index.js --message run /reflection`,
enabled: true,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
Expand Down
9 changes: 5 additions & 4 deletions src/scheduler/autoSchedule.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { Cron } from "./cron.js";
import { logger } from "../logger.js";
import { loadConfig } from "../config/loader.js";

const cwd = loadConfig().cwd;
const config = loadConfig();
const cwd = config.cwd;

const JOB_CRON = "0 2 * * *";

const SCHEDULES_DIR = "memory/schedules/";
const SCHEDULES_DIR = config.memory?.schedulesDir || "memory/schedules/";

/**
* Job definition shape for the daily reflection cron job.
Expand All @@ -27,7 +28,7 @@ function createJobDefinition(cwd) {
return {
name: "reflection-daily",
cron: JOB_CRON,
command: `cd ${cwd} && timeout 300 node index.js "run /reflection"`,
command: `cd ${cwd} && timeout 300 node src/index.js --message run /reflection`,
};
}

Expand Down Expand Up @@ -58,7 +59,7 @@ function persistJobFile(jobName, job, cwd) {
const jobData = Object.freeze({
name: job.name,
cron: job.cron,
command: `cd ${cwd} && timeout 300 node index.js "run /reflection"`,
command: `cd ${cwd} && timeout 300 node src/index.js --message run /reflection`,
enabled: true,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
Expand Down
8 changes: 2 additions & 6 deletions src/scheduler/cron.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { execSync } from "node:child_process";
import { readdir, readFile } from "node:fs/promises";
import { join } from "node:path";
import { getLogDirectory } from "../logger.js";

// Block delimiters for madz-managed crontab entries
const BLOCK_START = "# --- BEGIN madz-schedules ---";
Expand Down Expand Up @@ -29,11 +28,8 @@ export function sanitizeCrontabCommand(command) {
* @param {string} [logPath] - Optional log file path for output redirection
* @returns {string} The prepared command
*/
export function prepareCrontabCommand(command, logPath) {
const sanitized = sanitizeCrontabCommand(command);
const withAbsolutePath = sanitized.replace(/\bnode\b/g, "/usr/local/bin/node");
const logFile = logPath || join(getLogDirectory(), "madz_cron.log");
return `${withAbsolutePath} >> ${logFile} 2>&1`;
export function prepareCrontabCommand(command) {
return sanitizeCrontabCommand(command);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/autoSchedule.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ describe("setupAutoSchedule", () => {
assert.strictEqual(capturedJob.cron, "0 2 * * *");
assert.ok(typeof capturedJob.command === "string");
assert.ok(
capturedJob.command.includes('node index.js "run /reflection"'),
"Command should include the run /reflection command",
capturedJob.command.includes("node src/index.js --message run /reflection"),
"Command should include the --message /reflection command",
);
});

Expand Down Expand Up @@ -99,7 +99,7 @@ describe("setupAutoSchedule", () => {
assert.strictEqual(content.cron, "0 2 * * *");
assert.ok(
content.command.startsWith("cd ") &&
content.command.includes('node index.js "run /reflection"'),
content.command.includes("node src/index.js --message run /reflection"),
);
assert.strictEqual(content.enabled, true);
assert.ok(content.createdAt);
Expand Down
25 changes: 7 additions & 18 deletions tests/unit/scheduler/cron.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { describe, it } from "node:test";
import assert from "node:assert";
import { join } from "node:path";
import {
Cron,
sanitizeCrontabCommand,
prepareCrontabCommand,
} from "../../../src/scheduler/cron.js";
import { getLogDirectory } from "../../../src/logger.js";

describe("cron - sanitizeCrontabCommand", () => {
it("strips carriage returns", () => {
Expand All @@ -31,42 +29,33 @@ describe("cron - sanitizeCrontabCommand", () => {
});

describe("cron - prepareCrontabCommand", () => {
it("replaces bare node with absolute path", () => {
it("returns the sanitized command as-is", () => {
const result = prepareCrontabCommand("node index.js", "/tmp/test.log");
assert.strictEqual(result, "/usr/local/bin/node index.js >> /tmp/test.log 2>&1");
assert.strictEqual(result, "node index.js");
});

it("uses provided log path", () => {
it("ignores logPath parameter", () => {
const result = prepareCrontabCommand("node test.js", "/custom/path.log");
assert.strictEqual(result, "/usr/local/bin/node test.js >> /custom/path.log 2>&1");
});

it("defaults to logger directory when no log path provided", () => {
const logDir = getLogDirectory();
const result = prepareCrontabCommand("node index.js");
assert.strictEqual(
result,
`/usr/local/bin/node index.js >> ${join(logDir, "madz_cron.log")} 2>&1`,
);
assert.strictEqual(result, "node test.js");
});

it("sanitizes newlines in command", () => {
const result = prepareCrontabCommand("node index.js\r\n", "/tmp/test.log");
assert.strictEqual(result, "/usr/local/bin/node index.js >> /tmp/test.log 2>&1");
assert.strictEqual(result, "node index.js");
});
});

describe("cron - Cron.setLogPath", () => {
it("sets the log path on the Cron instance", () => {
Cron.setLogPath("/custom/log/path.log");
const result = prepareCrontabCommand("node test.js", "/custom/log/path.log");
assert.strictEqual(result, "/usr/local/bin/node test.js >> /custom/log/path.log 2>&1");
assert.strictEqual(result, "node test.js");
});

it("is idempotent — subsequent calls overwrite", () => {
Cron.setLogPath("/first/path.log");
Cron.setLogPath("/second/path.log");
const result = prepareCrontabCommand("node test.js", "/second/path.log");
assert.strictEqual(result, "/usr/local/bin/node test.js >> /second/path.log 2>&1");
assert.strictEqual(result, "node test.js");
});
});