@mono-agent/cron-adapter 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +157 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/scheduler.d.ts +67 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +171 -0
- package/dist/scheduler.js.map +1 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @mono-agent/cron-adapter
|
|
2
|
+
|
|
3
|
+
## Category
|
|
4
|
+
|
|
5
|
+
Category: `communication`
|
|
6
|
+
|
|
7
|
+
## Responsibility
|
|
8
|
+
|
|
9
|
+
Cron-based scheduled invocation adapter for agent hosts. It parses configured cron jobs, schedules future ticks, invokes a structural `AgentResponder`, and reports explicit succeeded, failed, cancelled, or skipped results.
|
|
10
|
+
|
|
11
|
+
## Install / Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm --filter @mono-agent/cron-adapter run build
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { startCronAdapter } from "@mono-agent/cron-adapter";
|
|
19
|
+
|
|
20
|
+
const cron = startCronAdapter({
|
|
21
|
+
responder,
|
|
22
|
+
jobs: [
|
|
23
|
+
{
|
|
24
|
+
id: "daily-check",
|
|
25
|
+
expression: "0 9 * * *",
|
|
26
|
+
timezone: "UTC",
|
|
27
|
+
prompt: "Run the daily check.",
|
|
28
|
+
conversationId: "cron:daily-check",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Only future ticks after startup are scheduled. Overlapping runs for the same job are skipped, not queued or run concurrently.
|
|
35
|
+
|
|
36
|
+
## Public API
|
|
37
|
+
|
|
38
|
+
- `startCronAdapter`
|
|
39
|
+
- `CronAdapterError`
|
|
40
|
+
- `loadCronAdapterConfig`
|
|
41
|
+
- `redactCronAdapterConfig`
|
|
42
|
+
- `cronFieldGroup`
|
|
43
|
+
- Cron adapter, job, result, metadata, config, and logger types
|
|
44
|
+
|
|
45
|
+
## Dependency Boundary
|
|
46
|
+
|
|
47
|
+
This adapter depends on `cron-parser` plus shared contracts/settings primitives. It must not depend on the agent harness, runtime adapter, operator surfaces, memory, observability, other communication adapters, or host/demo code. Hosts compose it with a structural responder.
|
|
48
|
+
|
|
49
|
+
## What This Package Does Not Own
|
|
50
|
+
|
|
51
|
+
It does not build prompts, run models, persist missed runs, catch up after restart, queue overlapping jobs, expose UI, or define core core agent settings. Durable scheduling state can be added later by a host-level persistence package.
|
|
52
|
+
|
|
53
|
+
## Verification
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm --filter @mono-agent/cron-adapter run build
|
|
57
|
+
pnpm --filter @mono-agent/cron-adapter run typecheck
|
|
58
|
+
pnpm --filter @mono-agent/cron-adapter run test
|
|
59
|
+
```
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { FieldGroup, SettingsJson } from "@mono-agent/settings";
|
|
2
|
+
import { type CronJob } from "./scheduler.js";
|
|
3
|
+
export interface CronJobConfig {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly enabled: boolean;
|
|
6
|
+
readonly expression: string;
|
|
7
|
+
readonly timezone: string;
|
|
8
|
+
readonly prompt: string;
|
|
9
|
+
readonly conversationId?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CronAdapterConfig {
|
|
12
|
+
readonly jobs: readonly CronJobConfig[];
|
|
13
|
+
}
|
|
14
|
+
export type RedactedCronAdapterConfig = CronAdapterConfig;
|
|
15
|
+
export interface LoadCronAdapterConfigInput {
|
|
16
|
+
readonly env: Record<string, string | undefined>;
|
|
17
|
+
readonly json?: SettingsJson;
|
|
18
|
+
readonly jsonPath?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare const cronFieldGroup: FieldGroup;
|
|
21
|
+
export declare function loadCronAdapterConfig(input: LoadCronAdapterConfigInput): Promise<CronAdapterConfig>;
|
|
22
|
+
/**
|
|
23
|
+
* Project the loaded config down to the runtime {@link CronJob} shape consumed
|
|
24
|
+
* by {@link import("./scheduler.js").startCronAdapter}, dropping disabled jobs.
|
|
25
|
+
* Hosts must route config jobs through this rather than spreading them directly,
|
|
26
|
+
* otherwise the `enabled` flag is silently ignored and disabled jobs would run.
|
|
27
|
+
*/
|
|
28
|
+
export declare function toCronJobs(config: CronAdapterConfig): CronJob[];
|
|
29
|
+
export declare function redactCronAdapterConfig(config: CronAdapterConfig): RedactedCronAdapterConfig;
|
|
30
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,EAAoB,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEhE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;CACzC;AAED,MAAM,MAAM,yBAAyB,GAAG,iBAAiB,CAAC;AAE1D,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACjD,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAQD,eAAO,MAAM,cAAc,EAAE,UA2C3B,CAAC;AAEH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA8BzG;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,EAAE,CAU/D;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,yBAAyB,CAM5F"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { defineFieldGroup, layerJsonOntoEnv, normalizeOptionalString, readBoolean, readJsonSection, readSettingsJson, } from "@mono-agent/settings";
|
|
2
|
+
import { CronAdapterError } from "./scheduler.js";
|
|
3
|
+
const DEFAULT_JOB_ID = "default";
|
|
4
|
+
const DEFAULT_TIMEZONE = "UTC";
|
|
5
|
+
const invalidConfig = (message, details) => new CronAdapterError("invalid_config", message, details);
|
|
6
|
+
export const cronFieldGroup = defineFieldGroup({
|
|
7
|
+
id: "cron",
|
|
8
|
+
label: "Cron",
|
|
9
|
+
description: "Optional single-job cron invocation configuration. Use JSON for multiple jobs.",
|
|
10
|
+
fields: [
|
|
11
|
+
{
|
|
12
|
+
id: "cron.enabled",
|
|
13
|
+
label: "Enable cron",
|
|
14
|
+
description: "Enable the default scheduled agent job.",
|
|
15
|
+
kind: "switch",
|
|
16
|
+
path: ["cron", "enabled"],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "cron.expression",
|
|
20
|
+
label: "Cron expression",
|
|
21
|
+
description: "Five-field cron expression for the default job.",
|
|
22
|
+
kind: "string",
|
|
23
|
+
placeholder: "0 * * * *",
|
|
24
|
+
path: ["cron", "expression"],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "cron.timezone",
|
|
28
|
+
label: "Timezone",
|
|
29
|
+
description: "IANA timezone used to calculate scheduled runs.",
|
|
30
|
+
kind: "string",
|
|
31
|
+
placeholder: "UTC",
|
|
32
|
+
path: ["cron", "timezone"],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "cron.prompt",
|
|
36
|
+
label: "Prompt",
|
|
37
|
+
description: "Prompt sent to the agent for the default scheduled job.",
|
|
38
|
+
kind: "string",
|
|
39
|
+
path: ["cron", "prompt"],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "cron.conversationId",
|
|
43
|
+
label: "Conversation id",
|
|
44
|
+
description: "Optional conversation id for memory/history continuity.",
|
|
45
|
+
kind: "string",
|
|
46
|
+
path: ["cron", "conversationId"],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
export async function loadCronAdapterConfig(input) {
|
|
51
|
+
const json = input.json ?? (input.jsonPath === undefined ? {} : (await readSettingsJson(input.jsonPath)).json);
|
|
52
|
+
const jobsJson = normalizeOptionalString(input.env.MONO_AGENT_CRON_JOBS_JSON);
|
|
53
|
+
if (jobsJson !== undefined) {
|
|
54
|
+
return { jobs: readJobsJson(jobsJson) };
|
|
55
|
+
}
|
|
56
|
+
const env = layerCronJsonOntoEnv(json, input.env);
|
|
57
|
+
const enabled = readBoolean(env.MONO_AGENT_CRON_ENABLED, "MONO_AGENT_CRON_ENABLED", false, invalidConfig);
|
|
58
|
+
const expression = normalizeOptionalString(env.MONO_AGENT_CRON_EXPRESSION);
|
|
59
|
+
const prompt = normalizeOptionalString(env.MONO_AGENT_CRON_PROMPT);
|
|
60
|
+
if (!enabled && expression === undefined && prompt === undefined) {
|
|
61
|
+
return { jobs: [] };
|
|
62
|
+
}
|
|
63
|
+
if (expression === undefined) {
|
|
64
|
+
throw invalidConfig("Cron expression is required when cron is configured.");
|
|
65
|
+
}
|
|
66
|
+
if (prompt === undefined) {
|
|
67
|
+
throw invalidConfig("Cron prompt is required when cron is configured.");
|
|
68
|
+
}
|
|
69
|
+
const conversationId = normalizeOptionalString(env.MONO_AGENT_CRON_CONVERSATION_ID);
|
|
70
|
+
return {
|
|
71
|
+
jobs: [{
|
|
72
|
+
id: DEFAULT_JOB_ID,
|
|
73
|
+
enabled,
|
|
74
|
+
expression,
|
|
75
|
+
timezone: normalizeOptionalString(env.MONO_AGENT_CRON_TIMEZONE) ?? DEFAULT_TIMEZONE,
|
|
76
|
+
prompt,
|
|
77
|
+
...(conversationId === undefined ? {} : { conversationId }),
|
|
78
|
+
}],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Project the loaded config down to the runtime {@link CronJob} shape consumed
|
|
83
|
+
* by {@link import("./scheduler.js").startCronAdapter}, dropping disabled jobs.
|
|
84
|
+
* Hosts must route config jobs through this rather than spreading them directly,
|
|
85
|
+
* otherwise the `enabled` flag is silently ignored and disabled jobs would run.
|
|
86
|
+
*/
|
|
87
|
+
export function toCronJobs(config) {
|
|
88
|
+
return config.jobs
|
|
89
|
+
.filter((job) => job.enabled)
|
|
90
|
+
.map((job) => ({
|
|
91
|
+
id: job.id,
|
|
92
|
+
expression: job.expression,
|
|
93
|
+
timezone: job.timezone,
|
|
94
|
+
prompt: job.prompt,
|
|
95
|
+
...(job.conversationId === undefined ? {} : { conversationId: job.conversationId }),
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
export function redactCronAdapterConfig(config) {
|
|
99
|
+
// Cron config holds no secrets (the prompt is not treated as one), so
|
|
100
|
+
// redaction is the identity transform; we only clone to preserve immutability.
|
|
101
|
+
return {
|
|
102
|
+
jobs: config.jobs.map((job) => ({ ...job })),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function readJobsJson(value) {
|
|
106
|
+
let parsed;
|
|
107
|
+
try {
|
|
108
|
+
parsed = JSON.parse(value);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
throw invalidConfig("MONO_AGENT_CRON_JOBS_JSON must contain valid JSON.", {
|
|
112
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (!Array.isArray(parsed)) {
|
|
116
|
+
throw invalidConfig("MONO_AGENT_CRON_JOBS_JSON must be an array.");
|
|
117
|
+
}
|
|
118
|
+
return parsed.map((entry, index) => normalizeJobConfig(entry, index));
|
|
119
|
+
}
|
|
120
|
+
function normalizeJobConfig(entry, index) {
|
|
121
|
+
if (!isRecord(entry)) {
|
|
122
|
+
throw invalidConfig("Cron job entries must be objects.", { index });
|
|
123
|
+
}
|
|
124
|
+
const id = asOptionalString(entry.id);
|
|
125
|
+
const expression = asOptionalString(entry.expression);
|
|
126
|
+
const prompt = asOptionalString(entry.prompt);
|
|
127
|
+
if (id === undefined || expression === undefined || prompt === undefined) {
|
|
128
|
+
throw invalidConfig("Cron jobs require id, expression, and prompt.", { index });
|
|
129
|
+
}
|
|
130
|
+
const conversationId = asOptionalString(entry.conversationId);
|
|
131
|
+
return {
|
|
132
|
+
id,
|
|
133
|
+
enabled: typeof entry.enabled === "boolean" ? entry.enabled : true,
|
|
134
|
+
expression,
|
|
135
|
+
timezone: asOptionalString(entry.timezone) ?? DEFAULT_TIMEZONE,
|
|
136
|
+
prompt,
|
|
137
|
+
...(conversationId === undefined ? {} : { conversationId }),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function layerCronJsonOntoEnv(json, env) {
|
|
141
|
+
const section = readJsonSection(json, "cron");
|
|
142
|
+
return layerJsonOntoEnv(env, [
|
|
143
|
+
{ env: "MONO_AGENT_CRON_ENABLED", value: section.enabled, kind: "boolean" },
|
|
144
|
+
{ env: "MONO_AGENT_CRON_EXPRESSION", value: section.expression },
|
|
145
|
+
{ env: "MONO_AGENT_CRON_TIMEZONE", value: section.timezone },
|
|
146
|
+
{ env: "MONO_AGENT_CRON_PROMPT", value: section.prompt },
|
|
147
|
+
{ env: "MONO_AGENT_CRON_CONVERSATION_ID", value: section.conversationId },
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
/** Trim a JSON value to a non-empty string, treating non-strings as absent. */
|
|
151
|
+
function asOptionalString(value) {
|
|
152
|
+
return typeof value === "string" ? normalizeOptionalString(value) : undefined;
|
|
153
|
+
}
|
|
154
|
+
function isRecord(value) {
|
|
155
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { CronAdapterError, startCronAdapter, } from "./scheduler.js";
|
|
2
|
+
export type { CronAdapterErrorCode, CronAdapterErrorDetails, CronAdapterLogger, CronAdapterOptions, CronAdapterStartResult, CronJob, CronJobResult, CronRequestMetadata, } from "./scheduler.js";
|
|
3
|
+
export { cronFieldGroup, loadCronAdapterConfig, redactCronAdapterConfig, toCronJobs, } from "./config.js";
|
|
4
|
+
export type { CronAdapterConfig, CronJobConfig, LoadCronAdapterConfigInput, RedactedCronAdapterConfig, } from "./config.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,OAAO,EACP,aAAa,EACb,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAYxB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,UAAU,GACX,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type AgentMessageStream, type AgentRequestBase, type AgentResponder, type AgentResponse } from "@mono-agent/agent-contracts";
|
|
2
|
+
export interface CronRequestMetadata {
|
|
3
|
+
readonly jobId: string;
|
|
4
|
+
readonly expression: string;
|
|
5
|
+
readonly timezone: string;
|
|
6
|
+
readonly scheduledAt: string;
|
|
7
|
+
readonly startedAt: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CronJob {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly expression: string;
|
|
12
|
+
readonly timezone?: string;
|
|
13
|
+
readonly prompt: string;
|
|
14
|
+
readonly conversationId?: string;
|
|
15
|
+
}
|
|
16
|
+
export type CronJobResult = {
|
|
17
|
+
readonly kind: "succeeded";
|
|
18
|
+
readonly jobId: string;
|
|
19
|
+
readonly scheduledAt: string;
|
|
20
|
+
readonly startedAt: string;
|
|
21
|
+
readonly completedAt: string;
|
|
22
|
+
readonly text?: string;
|
|
23
|
+
readonly metadata?: Record<string, unknown>;
|
|
24
|
+
} | {
|
|
25
|
+
readonly kind: "failed" | "cancelled";
|
|
26
|
+
readonly jobId: string;
|
|
27
|
+
readonly scheduledAt: string;
|
|
28
|
+
readonly startedAt: string;
|
|
29
|
+
readonly completedAt: string;
|
|
30
|
+
readonly error: string;
|
|
31
|
+
} | {
|
|
32
|
+
readonly kind: "skipped";
|
|
33
|
+
readonly jobId: string;
|
|
34
|
+
readonly scheduledAt: string;
|
|
35
|
+
readonly reason: "overlap";
|
|
36
|
+
};
|
|
37
|
+
export interface CronAdapterLogger {
|
|
38
|
+
debug?(message: string, metadata?: Record<string, unknown>): void;
|
|
39
|
+
info?(message: string, metadata?: Record<string, unknown>): void;
|
|
40
|
+
warn?(message: string, metadata?: Record<string, unknown>): void;
|
|
41
|
+
error?(message: string, metadata?: Record<string, unknown>): void;
|
|
42
|
+
}
|
|
43
|
+
export interface CronAdapterOptions {
|
|
44
|
+
readonly responder: AgentResponder<AgentRequestBase, AgentMessageStream, AgentResponse>;
|
|
45
|
+
readonly jobs: readonly CronJob[];
|
|
46
|
+
readonly now?: () => Date;
|
|
47
|
+
readonly onResult?: (result: CronJobResult) => void | Promise<void>;
|
|
48
|
+
readonly logger?: CronAdapterLogger;
|
|
49
|
+
}
|
|
50
|
+
export interface CronAdapterStartResult {
|
|
51
|
+
readonly jobs: readonly CronJob[];
|
|
52
|
+
readonly activeJobCount: number;
|
|
53
|
+
stop(): void;
|
|
54
|
+
}
|
|
55
|
+
export type CronAdapterErrorCode = "invalid_config" | "stream_closed";
|
|
56
|
+
export interface CronAdapterErrorDetails {
|
|
57
|
+
readonly code?: CronAdapterErrorCode;
|
|
58
|
+
readonly reason?: string;
|
|
59
|
+
readonly [key: string]: unknown;
|
|
60
|
+
}
|
|
61
|
+
export declare class CronAdapterError extends Error {
|
|
62
|
+
readonly code: CronAdapterErrorCode;
|
|
63
|
+
readonly details: CronAdapterErrorDetails;
|
|
64
|
+
constructor(code: CronAdapterErrorCode, message: string, details?: CronAdapterErrorDetails);
|
|
65
|
+
}
|
|
66
|
+
export declare function startCronAdapter(options: CronAdapterOptions): CronAdapterStartResult;
|
|
67
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { CronExpressionParser } from "cron-parser";
|
|
2
|
+
import { BufferedMessageStream, isAgentResponseCancelledError, } from "@mono-agent/agent-contracts";
|
|
3
|
+
import { normalizeOptionalString } from "@mono-agent/settings";
|
|
4
|
+
export class CronAdapterError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
details;
|
|
7
|
+
constructor(code, message, details = {}) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "CronAdapterError";
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.details = { ...details, code };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const DEFAULT_TIMEZONE = "UTC";
|
|
15
|
+
const MAX_TIMEOUT_MS = 2_147_483_647;
|
|
16
|
+
export function startCronAdapter(options) {
|
|
17
|
+
validateOptions(options);
|
|
18
|
+
const activeJobs = new Map();
|
|
19
|
+
const scheduled = options.jobs.map((job) => ({ job, timer: undefined }));
|
|
20
|
+
for (const entry of scheduled) {
|
|
21
|
+
scheduleNext(entry, options, activeJobs);
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
jobs: options.jobs.slice(),
|
|
25
|
+
get activeJobCount() {
|
|
26
|
+
return activeJobs.size;
|
|
27
|
+
},
|
|
28
|
+
stop() {
|
|
29
|
+
for (const entry of scheduled) {
|
|
30
|
+
if (entry.timer !== undefined) {
|
|
31
|
+
clearTimeout(entry.timer);
|
|
32
|
+
entry.timer = undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const running of activeJobs.values()) {
|
|
36
|
+
running.controller.abort(new Error("Cron adapter stopped."));
|
|
37
|
+
}
|
|
38
|
+
activeJobs.clear();
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function scheduleNext(entry, options, activeJobs) {
|
|
43
|
+
const now = options.now?.() ?? new Date();
|
|
44
|
+
const scheduledAt = nextDateFor(entry.job, now);
|
|
45
|
+
const delayMs = Math.max(0, scheduledAt.getTime() - now.getTime());
|
|
46
|
+
entry.timer = setTimeout(() => {
|
|
47
|
+
entry.timer = undefined;
|
|
48
|
+
if (delayMs > MAX_TIMEOUT_MS) {
|
|
49
|
+
scheduleNext(entry, options, activeJobs);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
handleTick(entry.job, scheduledAt, options, activeJobs);
|
|
53
|
+
scheduleNext(entry, options, activeJobs);
|
|
54
|
+
}, Math.min(delayMs, MAX_TIMEOUT_MS));
|
|
55
|
+
}
|
|
56
|
+
function handleTick(job, scheduledAtDate, options, activeJobs) {
|
|
57
|
+
const scheduledAt = scheduledAtDate.toISOString();
|
|
58
|
+
if (activeJobs.has(job.id)) {
|
|
59
|
+
const result = { kind: "skipped", jobId: job.id, scheduledAt, reason: "overlap" };
|
|
60
|
+
options.logger?.warn?.("Cron job skipped because a prior run is still active.", { jobId: job.id, scheduledAt });
|
|
61
|
+
void emitResult(options, result);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
activeJobs.set(job.id, { controller });
|
|
66
|
+
const startedAt = (options.now?.() ?? new Date()).toISOString();
|
|
67
|
+
const stream = new BufferedMessageStream({
|
|
68
|
+
onClosed: () => new CronAdapterError("stream_closed", "Cannot write to a finished cron stream."),
|
|
69
|
+
});
|
|
70
|
+
const request = {
|
|
71
|
+
conversationId: job.conversationId ?? `cron:${job.id}`,
|
|
72
|
+
text: job.prompt,
|
|
73
|
+
abortSignal: controller.signal,
|
|
74
|
+
metadata: {
|
|
75
|
+
cron: {
|
|
76
|
+
jobId: job.id,
|
|
77
|
+
expression: job.expression,
|
|
78
|
+
timezone: job.timezone ?? DEFAULT_TIMEZONE,
|
|
79
|
+
scheduledAt,
|
|
80
|
+
startedAt,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
void options.responder.respond(request, stream)
|
|
85
|
+
.then(async (response) => {
|
|
86
|
+
await stream.finish(response.text);
|
|
87
|
+
const result = {
|
|
88
|
+
kind: "succeeded",
|
|
89
|
+
jobId: job.id,
|
|
90
|
+
scheduledAt,
|
|
91
|
+
startedAt,
|
|
92
|
+
completedAt: (options.now?.() ?? new Date()).toISOString(),
|
|
93
|
+
...(stream.text.length === 0 ? {} : { text: stream.text }),
|
|
94
|
+
...(response.metadata === undefined ? {} : { metadata: response.metadata }),
|
|
95
|
+
};
|
|
96
|
+
await emitResult(options, result);
|
|
97
|
+
})
|
|
98
|
+
.catch(async (error) => {
|
|
99
|
+
const cancelled = controller.signal.aborted || isAgentResponseCancelledError(error);
|
|
100
|
+
const result = {
|
|
101
|
+
kind: cancelled ? "cancelled" : "failed",
|
|
102
|
+
jobId: job.id,
|
|
103
|
+
scheduledAt,
|
|
104
|
+
startedAt,
|
|
105
|
+
completedAt: (options.now?.() ?? new Date()).toISOString(),
|
|
106
|
+
error: errorToMessage(error),
|
|
107
|
+
};
|
|
108
|
+
options.logger?.[cancelled ? "warn" : "error"]?.("Cron job responder failed.", {
|
|
109
|
+
jobId: job.id,
|
|
110
|
+
error: result.error,
|
|
111
|
+
});
|
|
112
|
+
await emitResult(options, result);
|
|
113
|
+
})
|
|
114
|
+
.finally(() => {
|
|
115
|
+
activeJobs.delete(job.id);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async function emitResult(options, result) {
|
|
119
|
+
await options.onResult?.(result);
|
|
120
|
+
}
|
|
121
|
+
function nextDateFor(job, currentDate) {
|
|
122
|
+
try {
|
|
123
|
+
return CronExpressionParser.parse(job.expression, {
|
|
124
|
+
currentDate,
|
|
125
|
+
tz: job.timezone ?? DEFAULT_TIMEZONE,
|
|
126
|
+
}).next().toDate();
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
throw new CronAdapterError("invalid_config", "Cron job expression is invalid.", {
|
|
130
|
+
jobId: job.id,
|
|
131
|
+
reason: errorToMessage(error),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function validateOptions(options) {
|
|
136
|
+
if (typeof options.responder?.respond !== "function") {
|
|
137
|
+
throw new CronAdapterError("invalid_config", "Cron adapter requires a responder.");
|
|
138
|
+
}
|
|
139
|
+
const seen = new Set();
|
|
140
|
+
for (const job of options.jobs) {
|
|
141
|
+
if (normalizeOptionalString(job.id) === undefined) {
|
|
142
|
+
throw new CronAdapterError("invalid_config", "Cron job id is required.");
|
|
143
|
+
}
|
|
144
|
+
if (seen.has(job.id)) {
|
|
145
|
+
throw new CronAdapterError("invalid_config", "Cron job ids must be unique.", { jobId: job.id });
|
|
146
|
+
}
|
|
147
|
+
seen.add(job.id);
|
|
148
|
+
if (normalizeOptionalString(job.prompt) === undefined) {
|
|
149
|
+
throw new CronAdapterError("invalid_config", "Cron job prompt is required.", { jobId: job.id });
|
|
150
|
+
}
|
|
151
|
+
assertFiveFieldExpression(job);
|
|
152
|
+
nextDateFor(job, options.now?.() ?? new Date());
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function assertFiveFieldExpression(job) {
|
|
156
|
+
const expression = normalizeOptionalString(job.expression);
|
|
157
|
+
if (expression === undefined) {
|
|
158
|
+
throw new CronAdapterError("invalid_config", "Cron job expression is required.", { jobId: job.id });
|
|
159
|
+
}
|
|
160
|
+
const fields = expression.split(/\s+/u);
|
|
161
|
+
if (fields.length !== 5) {
|
|
162
|
+
throw new CronAdapterError("invalid_config", "Cron job expression must use exactly five fields.", {
|
|
163
|
+
jobId: job.id,
|
|
164
|
+
fieldCount: fields.length,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function errorToMessage(error) {
|
|
169
|
+
return error instanceof Error ? error.message : String(error);
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mono-agent/cron-adapter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cron-based scheduled invocation adapter for agent responders.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"private": false,
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@mono-agent/agent-contracts": "0.1.0",
|
|
22
|
+
"@mono-agent/settings": "0.1.0",
|
|
23
|
+
"cron-parser": "^5.5.0"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.build.json",
|
|
30
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
31
|
+
"test": "vitest run --passWithNoTests"
|
|
32
|
+
}
|
|
33
|
+
}
|