@rivet-dev/agentos-core 0.0.0-agentos-dylib-plugin.473700e

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.
Files changed (75) hide show
  1. package/README.md +204 -0
  2. package/dist/agent-os.d.ts +560 -0
  3. package/dist/agent-os.js +3624 -0
  4. package/dist/agent-session-types.d.ts +67 -0
  5. package/dist/agent-session-types.js +1 -0
  6. package/dist/agents.d.ts +63 -0
  7. package/dist/agents.js +42 -0
  8. package/dist/base-filesystem.d.ts +34 -0
  9. package/dist/base-filesystem.js +173 -0
  10. package/dist/cron/cron-manager.d.ts +22 -0
  11. package/dist/cron/cron-manager.js +160 -0
  12. package/dist/cron/index.d.ts +5 -0
  13. package/dist/cron/index.js +3 -0
  14. package/dist/cron/parse-schedule.d.ts +25 -0
  15. package/dist/cron/parse-schedule.js +66 -0
  16. package/dist/cron/schedule-driver.d.ts +19 -0
  17. package/dist/cron/schedule-driver.js +1 -0
  18. package/dist/cron/timer-driver.d.ts +17 -0
  19. package/dist/cron/timer-driver.js +61 -0
  20. package/dist/cron/types.d.ts +55 -0
  21. package/dist/cron/types.js +1 -0
  22. package/dist/filesystem-snapshot.d.ts +14 -0
  23. package/dist/filesystem-snapshot.js +105 -0
  24. package/dist/host-dir-mount.d.ts +38 -0
  25. package/dist/host-dir-mount.js +34 -0
  26. package/dist/host-tools-zod.d.ts +7 -0
  27. package/dist/host-tools-zod.js +243 -0
  28. package/dist/host-tools.d.ts +45 -0
  29. package/dist/host-tools.js +26 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +10 -0
  32. package/dist/js-bridge.d.ts +3 -0
  33. package/dist/js-bridge.js +2 -0
  34. package/dist/json-rpc.d.ts +50 -0
  35. package/dist/json-rpc.js +22 -0
  36. package/dist/layers.d.ts +51 -0
  37. package/dist/layers.js +185 -0
  38. package/dist/overlay-filesystem.d.ts +19 -0
  39. package/dist/overlay-filesystem.js +645 -0
  40. package/dist/packages.d.ts +127 -0
  41. package/dist/packages.js +335 -0
  42. package/dist/runtime-compat.d.ts +471 -0
  43. package/dist/runtime-compat.js +2009 -0
  44. package/dist/runtime.d.ts +336 -0
  45. package/dist/runtime.js +1 -0
  46. package/dist/sidecar/agent-os-protocol.d.ts +219 -0
  47. package/dist/sidecar/agent-os-protocol.js +594 -0
  48. package/dist/sidecar/binary.d.ts +9 -0
  49. package/dist/sidecar/binary.js +30 -0
  50. package/dist/sidecar/cargo.d.ts +2 -0
  51. package/dist/sidecar/cargo.js +142 -0
  52. package/dist/sidecar/native-process-client.d.ts +2 -0
  53. package/dist/sidecar/native-process-client.js +1 -0
  54. package/dist/sidecar/permissions.d.ts +3 -0
  55. package/dist/sidecar/permissions.js +54 -0
  56. package/dist/sidecar/process.d.ts +6 -0
  57. package/dist/sidecar/process.js +8 -0
  58. package/dist/sidecar/rpc-client.d.ts +244 -0
  59. package/dist/sidecar/rpc-client.js +2028 -0
  60. package/dist/test/docker.d.ts +133 -0
  61. package/dist/test/docker.js +336 -0
  62. package/dist/test/file-system.d.ts +39 -0
  63. package/dist/test/file-system.js +562 -0
  64. package/dist/test/mock-s3.d.ts +42 -0
  65. package/dist/test/mock-s3.js +292 -0
  66. package/dist/test/runtime.d.ts +11 -0
  67. package/dist/test/runtime.js +9 -0
  68. package/dist/test/sandbox-agent.d.ts +9 -0
  69. package/dist/test/sandbox-agent.js +395 -0
  70. package/dist/test/terminal-harness.d.ts +27 -0
  71. package/dist/test/terminal-harness.js +122 -0
  72. package/dist/types.d.ts +10 -0
  73. package/dist/types.js +1 -0
  74. package/fixtures/AGENTOS_SYSTEM_PROMPT.md +21 -0
  75. package/package.json +111 -0
@@ -0,0 +1,67 @@
1
+ import type { JsonRpcNotification } from "./json-rpc.js";
2
+ export type SessionEventHandler = (event: JsonRpcNotification) => void;
3
+ export interface PermissionRequest {
4
+ permissionId: string;
5
+ description?: string;
6
+ params: Record<string, unknown>;
7
+ }
8
+ export type PermissionReply = "once" | "always" | "reject";
9
+ export type PermissionRequestHandler = (request: PermissionRequest) => void;
10
+ export interface SessionMode {
11
+ id: string;
12
+ name?: string;
13
+ label?: string;
14
+ description?: string;
15
+ [key: string]: unknown;
16
+ }
17
+ export interface SessionModeState {
18
+ currentModeId: string;
19
+ availableModes: SessionMode[];
20
+ }
21
+ export interface SessionConfigOption {
22
+ id: string;
23
+ category?: string;
24
+ label?: string;
25
+ description?: string;
26
+ currentValue?: string;
27
+ allowedValues?: Array<{
28
+ id: string;
29
+ label?: string;
30
+ }>;
31
+ readOnly?: boolean;
32
+ }
33
+ export interface PromptCapabilities {
34
+ audio?: boolean;
35
+ embeddedContext?: boolean;
36
+ image?: boolean;
37
+ [key: string]: unknown;
38
+ }
39
+ export interface AgentCapabilities {
40
+ permissions?: boolean;
41
+ plan_mode?: boolean;
42
+ questions?: boolean;
43
+ tool_calls?: boolean;
44
+ text_messages?: boolean;
45
+ images?: boolean;
46
+ file_attachments?: boolean;
47
+ session_lifecycle?: boolean;
48
+ error_events?: boolean;
49
+ reasoning?: boolean;
50
+ status?: boolean;
51
+ streaming_deltas?: boolean;
52
+ mcp_tools?: boolean;
53
+ promptCapabilities?: PromptCapabilities;
54
+ [key: string]: unknown;
55
+ }
56
+ export interface AgentInfo {
57
+ name: string;
58
+ title?: string;
59
+ version?: string;
60
+ [key: string]: unknown;
61
+ }
62
+ export interface SessionInitData {
63
+ modes?: SessionModeState;
64
+ configOptions?: SessionConfigOption[];
65
+ capabilities?: AgentCapabilities;
66
+ agentInfo?: AgentInfo;
67
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,63 @@
1
+ export interface AgentConfig {
2
+ /** npm package name for the ACP adapter (spawned inside the VM) */
3
+ acpAdapter: string;
4
+ /** npm package name for the underlying agent */
5
+ agentPackage: string;
6
+ /**
7
+ * Absolute host path to the software package directory that registered this
8
+ * agent config. Package-provided agent adapters should resolve their nested
9
+ * dependencies relative to this directory before falling back to the host dir
10
+ * behind the caller-supplied `/root/node_modules` mount.
11
+ */
12
+ declaringPackageDir?: string;
13
+ /** Additional CLI args prepended when launching the ACP adapter. */
14
+ launchArgs?: string[];
15
+ /**
16
+ * Default env vars to pass when spawning the adapter. These are merged
17
+ * UNDER user env (lowest priority).
18
+ * Typically set by package descriptors for computed paths (e.g. PI_ACP_PI_COMMAND).
19
+ */
20
+ defaultEnv?: Record<string, string>;
21
+ }
22
+ export declare const AGENT_CONFIGS: {
23
+ pi: {
24
+ acpAdapter: string;
25
+ agentPackage: string;
26
+ };
27
+ "pi-cli": {
28
+ acpAdapter: string;
29
+ agentPackage: string;
30
+ };
31
+ opencode: {
32
+ acpAdapter: string;
33
+ agentPackage: string;
34
+ defaultEnv: {
35
+ OPENCODE_DISABLE_CONFIG_DEP_INSTALL: string;
36
+ OPENCODE_DISABLE_EMBEDDED_WEB_UI: string;
37
+ };
38
+ };
39
+ claude: {
40
+ acpAdapter: string;
41
+ agentPackage: string;
42
+ defaultEnv: {
43
+ CLAUDE_AGENT_SDK_CLIENT_APP: string;
44
+ CLAUDE_CODE_SIMPLE: string;
45
+ CLAUDE_CODE_FORCE_AGENT_OS_RIPGREP: string;
46
+ CLAUDE_CODE_DEFER_GROWTHBOOK_INIT: string;
47
+ CLAUDE_CODE_DISABLE_CWD_PERSIST: string;
48
+ CLAUDE_CODE_DISABLE_DEV_NULL_REDIRECT: string;
49
+ CLAUDE_CODE_NODE_SHELL_WRAPPER: string;
50
+ CLAUDE_CODE_DISABLE_STREAM_JSON_HOOK_EVENTS: string;
51
+ CLAUDE_CODE_SHELL: string;
52
+ CLAUDE_CODE_SKIP_INITIAL_MESSAGES: string;
53
+ CLAUDE_CODE_SKIP_SANDBOX_INIT: string;
54
+ CLAUDE_CODE_SIMPLE_SHELL_EXEC: string;
55
+ CLAUDE_CODE_SWAP_STDIO: string;
56
+ CLAUDE_CODE_USE_PIPE_OUTPUT: string;
57
+ DISABLE_TELEMETRY: string;
58
+ SHELL: string;
59
+ USE_BUILTIN_RIPGREP: string;
60
+ };
61
+ };
62
+ };
63
+ export type AgentType = keyof typeof AGENT_CONFIGS;
package/dist/agents.js ADDED
@@ -0,0 +1,42 @@
1
+ // Agent configurations for ACP-compatible coding agents
2
+ export const AGENT_CONFIGS = {
3
+ pi: {
4
+ acpAdapter: "@rivet-dev/agentos-pi",
5
+ agentPackage: "@mariozechner/pi-coding-agent",
6
+ },
7
+ "pi-cli": {
8
+ acpAdapter: "pi-acp",
9
+ agentPackage: "@mariozechner/pi-coding-agent",
10
+ },
11
+ opencode: {
12
+ acpAdapter: "@rivet-dev/agentos-opencode",
13
+ agentPackage: "@rivet-dev/agentos-opencode",
14
+ defaultEnv: {
15
+ OPENCODE_DISABLE_CONFIG_DEP_INSTALL: "1",
16
+ OPENCODE_DISABLE_EMBEDDED_WEB_UI: "1",
17
+ },
18
+ },
19
+ claude: {
20
+ acpAdapter: "@rivet-dev/agentos-claude",
21
+ agentPackage: "@anthropic-ai/claude-agent-sdk",
22
+ defaultEnv: {
23
+ CLAUDE_AGENT_SDK_CLIENT_APP: "@rivet-dev/agent-os",
24
+ CLAUDE_CODE_SIMPLE: "1",
25
+ CLAUDE_CODE_FORCE_AGENT_OS_RIPGREP: "1",
26
+ CLAUDE_CODE_DEFER_GROWTHBOOK_INIT: "1",
27
+ CLAUDE_CODE_DISABLE_CWD_PERSIST: "1",
28
+ CLAUDE_CODE_DISABLE_DEV_NULL_REDIRECT: "1",
29
+ CLAUDE_CODE_NODE_SHELL_WRAPPER: "1",
30
+ CLAUDE_CODE_DISABLE_STREAM_JSON_HOOK_EVENTS: "1",
31
+ CLAUDE_CODE_SHELL: "/bin/sh",
32
+ CLAUDE_CODE_SKIP_INITIAL_MESSAGES: "1",
33
+ CLAUDE_CODE_SKIP_SANDBOX_INIT: "1",
34
+ CLAUDE_CODE_SIMPLE_SHELL_EXEC: "1",
35
+ CLAUDE_CODE_SWAP_STDIO: "0",
36
+ CLAUDE_CODE_USE_PIPE_OUTPUT: "1",
37
+ DISABLE_TELEMETRY: "1",
38
+ SHELL: "/bin/sh",
39
+ USE_BUILTIN_RIPGREP: "0",
40
+ },
41
+ },
42
+ };
@@ -0,0 +1,34 @@
1
+ import { type FilesystemEntry } from "./filesystem-snapshot.js";
2
+ import { type VirtualFileSystem } from "./runtime-compat.js";
3
+ export interface BaseFilesystemEnvironment {
4
+ env: Record<string, string>;
5
+ prompt: string;
6
+ }
7
+ export type BaseFilesystemEntry = FilesystemEntry;
8
+ export interface BaseFilesystemSnapshot {
9
+ source?: {
10
+ snapshotPath?: string;
11
+ image?: string;
12
+ snapshotCreatedAt?: string;
13
+ builtAt?: string;
14
+ transforms?: string[];
15
+ };
16
+ environment: BaseFilesystemEnvironment;
17
+ filesystem: {
18
+ entries: BaseFilesystemEntry[];
19
+ };
20
+ }
21
+ export declare function createBaseLowerFilesystem(): Promise<VirtualFileSystem>;
22
+ export declare function createBootstrapAwareFilesystem(filesystem: VirtualFileSystem, existingRoot: VirtualFileSystem, options?: {
23
+ readOnlyAfterBootstrap?: boolean;
24
+ }): {
25
+ filesystem: VirtualFileSystem;
26
+ finishKernelBootstrap: () => void;
27
+ };
28
+ export declare function getBaseFilesystemSnapshot(): BaseFilesystemSnapshot;
29
+ export declare function getBaseEnvironment(): Record<string, string>;
30
+ export declare function getBaseFilesystemEntries(): BaseFilesystemEntry[];
31
+ export declare function createBaseRootFilesystem(): Promise<{
32
+ filesystem: VirtualFileSystem;
33
+ finishKernelBootstrap: () => void;
34
+ }>;
@@ -0,0 +1,173 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import * as posixPath from "node:path/posix";
4
+ import { createFilesystemFromEntries, } from "./filesystem-snapshot.js";
5
+ import { createOverlayBackend } from "./overlay-filesystem.js";
6
+ import { KernelError } from "./runtime-compat.js";
7
+ const require = createRequire(import.meta.url);
8
+ const SNAPSHOT_PATH = require.resolve("@secure-exec/core/fixtures/base-filesystem.json");
9
+ const SUPPRESSED_KERNEL_BOOTSTRAP_DIRS = new Set([
10
+ "/boot",
11
+ "/usr/games",
12
+ "/usr/include",
13
+ "/usr/libexec",
14
+ "/usr/man",
15
+ ]);
16
+ const SUPPRESSED_KERNEL_BOOTSTRAP_FILES = new Set(["/usr/bin/env"]);
17
+ let snapshotCache = null;
18
+ function loadSnapshot() {
19
+ if (snapshotCache) {
20
+ return snapshotCache;
21
+ }
22
+ snapshotCache = JSON.parse(readFileSync(SNAPSHOT_PATH, "utf-8"));
23
+ return snapshotCache;
24
+ }
25
+ function normalizePath(path) {
26
+ const normalized = posixPath.normalize(path);
27
+ return normalized === "." ? "/" : normalized;
28
+ }
29
+ export async function createBaseLowerFilesystem() {
30
+ return createFilesystemFromEntries(getBaseFilesystemEntries());
31
+ }
32
+ export function createBootstrapAwareFilesystem(filesystem, existingRoot, options) {
33
+ let bootstrapActive = true;
34
+ let writesLocked = false;
35
+ function throwReadOnly() {
36
+ throw new KernelError("EROFS", "read-only file system");
37
+ }
38
+ async function rootHasPath(path) {
39
+ try {
40
+ return await existingRoot.exists(path);
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ }
46
+ const wrapped = {
47
+ ...filesystem,
48
+ async createDir(path) {
49
+ if (writesLocked) {
50
+ throwReadOnly();
51
+ }
52
+ const normalized = normalizePath(path);
53
+ if (bootstrapActive &&
54
+ SUPPRESSED_KERNEL_BOOTSTRAP_DIRS.has(normalized) &&
55
+ !(await rootHasPath(normalized))) {
56
+ return;
57
+ }
58
+ return filesystem.createDir(path);
59
+ },
60
+ async mkdir(path, options) {
61
+ if (writesLocked) {
62
+ if (options?.recursive && (await rootHasPath(path))) {
63
+ return;
64
+ }
65
+ throwReadOnly();
66
+ }
67
+ const normalized = normalizePath(path);
68
+ if (bootstrapActive &&
69
+ options?.recursive &&
70
+ SUPPRESSED_KERNEL_BOOTSTRAP_DIRS.has(normalized) &&
71
+ !(await rootHasPath(normalized))) {
72
+ return;
73
+ }
74
+ return filesystem.mkdir(path, options);
75
+ },
76
+ async writeFile(path, content) {
77
+ if (writesLocked) {
78
+ throwReadOnly();
79
+ }
80
+ const normalized = normalizePath(path);
81
+ if (bootstrapActive &&
82
+ SUPPRESSED_KERNEL_BOOTSTRAP_FILES.has(normalized)) {
83
+ return;
84
+ }
85
+ return filesystem.writeFile(path, content);
86
+ },
87
+ async removeFile(path) {
88
+ if (writesLocked) {
89
+ throwReadOnly();
90
+ }
91
+ return filesystem.removeFile(path);
92
+ },
93
+ async removeDir(path) {
94
+ if (writesLocked) {
95
+ throwReadOnly();
96
+ }
97
+ return filesystem.removeDir(path);
98
+ },
99
+ async rename(oldPath, newPath) {
100
+ if (writesLocked) {
101
+ throwReadOnly();
102
+ }
103
+ return filesystem.rename(oldPath, newPath);
104
+ },
105
+ async symlink(target, linkPath) {
106
+ if (writesLocked) {
107
+ throwReadOnly();
108
+ }
109
+ return filesystem.symlink(target, linkPath);
110
+ },
111
+ async link(oldPath, newPath) {
112
+ if (writesLocked) {
113
+ throwReadOnly();
114
+ }
115
+ return filesystem.link(oldPath, newPath);
116
+ },
117
+ async chmod(path, mode) {
118
+ if (writesLocked) {
119
+ throwReadOnly();
120
+ }
121
+ return filesystem.chmod(path, mode);
122
+ },
123
+ async chown(path, uid, gid) {
124
+ if (writesLocked) {
125
+ throwReadOnly();
126
+ }
127
+ return filesystem.chown(path, uid, gid);
128
+ },
129
+ async utimes(path, atime, mtime) {
130
+ if (writesLocked) {
131
+ throwReadOnly();
132
+ }
133
+ return filesystem.utimes(path, atime, mtime);
134
+ },
135
+ async truncate(path, length) {
136
+ if (writesLocked) {
137
+ throwReadOnly();
138
+ }
139
+ return filesystem.truncate(path, length);
140
+ },
141
+ async pwrite(path, offset, data) {
142
+ if (writesLocked) {
143
+ throwReadOnly();
144
+ }
145
+ return filesystem.pwrite(path, offset, data);
146
+ },
147
+ };
148
+ return {
149
+ filesystem: wrapped,
150
+ finishKernelBootstrap() {
151
+ bootstrapActive = false;
152
+ writesLocked = options?.readOnlyAfterBootstrap ?? false;
153
+ },
154
+ };
155
+ }
156
+ export function getBaseFilesystemSnapshot() {
157
+ return loadSnapshot();
158
+ }
159
+ export function getBaseEnvironment() {
160
+ const snapshot = loadSnapshot();
161
+ return {
162
+ ...snapshot.environment.env,
163
+ PS1: snapshot.environment.prompt,
164
+ };
165
+ }
166
+ export function getBaseFilesystemEntries() {
167
+ return loadSnapshot().filesystem.entries;
168
+ }
169
+ export async function createBaseRootFilesystem() {
170
+ const lower = await createBaseLowerFilesystem();
171
+ const overlay = createOverlayBackend({ lower });
172
+ return createBootstrapAwareFilesystem(overlay, lower);
173
+ }
@@ -0,0 +1,22 @@
1
+ import type { AgentOs } from "../agent-os.js";
2
+ import type { ScheduleDriver } from "./schedule-driver.js";
3
+ import type { CronEventHandler, CronJob, CronJobInfo, CronJobOptions } from "./types.js";
4
+ /**
5
+ * Internal class that bridges ScheduleDriver and AgentOs. Owns the job
6
+ * registry, executes actions, and emits lifecycle events.
7
+ */
8
+ export declare class CronManager {
9
+ private jobs;
10
+ private driver;
11
+ private vm;
12
+ private listeners;
13
+ constructor(vm: AgentOs, driver: ScheduleDriver);
14
+ schedule(options: CronJobOptions): CronJob;
15
+ cancel(id: string): void;
16
+ list(): CronJobInfo[];
17
+ onEvent(handler: CronEventHandler): void;
18
+ dispose(): void;
19
+ private emit;
20
+ private executeJob;
21
+ private runAction;
22
+ }
@@ -0,0 +1,160 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { resolveSchedule, validateScheduleForRegistration, } from "./parse-schedule.js";
3
+ /**
4
+ * Compute the next fire time for a schedule string. Returns undefined if
5
+ * the schedule is a one-shot ISO timestamp in the past or if croner
6
+ * cannot determine a next run.
7
+ */
8
+ function computeNextTime(schedule) {
9
+ return resolveSchedule(schedule).nextRun;
10
+ }
11
+ /**
12
+ * Internal class that bridges ScheduleDriver and AgentOs. Owns the job
13
+ * registry, executes actions, and emits lifecycle events.
14
+ */
15
+ export class CronManager {
16
+ jobs = new Map();
17
+ driver;
18
+ vm;
19
+ listeners = [];
20
+ constructor(vm, driver) {
21
+ this.vm = vm;
22
+ this.driver = driver;
23
+ }
24
+ schedule(options) {
25
+ const id = options.id ?? randomUUID();
26
+ const overlap = options.overlap ?? "allow";
27
+ const resolved = validateScheduleForRegistration(options.schedule);
28
+ const handle = this.driver.schedule({
29
+ id,
30
+ schedule: options.schedule,
31
+ callback: () => this.executeJob(id),
32
+ });
33
+ const state = {
34
+ id,
35
+ schedule: options.schedule,
36
+ action: options.action,
37
+ overlap,
38
+ handle,
39
+ lastRun: undefined,
40
+ nextRun: resolved.nextRun,
41
+ runCount: 0,
42
+ running: false,
43
+ queued: false,
44
+ };
45
+ this.jobs.set(id, state);
46
+ return { id, cancel: () => this.cancel(id) };
47
+ }
48
+ cancel(id) {
49
+ const state = this.jobs.get(id);
50
+ if (!state)
51
+ return;
52
+ this.driver.cancel(state.handle);
53
+ this.jobs.delete(id);
54
+ }
55
+ list() {
56
+ const result = [];
57
+ for (const state of this.jobs.values()) {
58
+ result.push({
59
+ id: state.id,
60
+ schedule: state.schedule,
61
+ action: state.action,
62
+ overlap: state.overlap,
63
+ lastRun: state.lastRun,
64
+ nextRun: state.nextRun,
65
+ runCount: state.runCount,
66
+ running: state.running,
67
+ });
68
+ }
69
+ return result;
70
+ }
71
+ onEvent(handler) {
72
+ this.listeners.push(handler);
73
+ }
74
+ dispose() {
75
+ for (const state of this.jobs.values()) {
76
+ this.driver.cancel(state.handle);
77
+ }
78
+ this.jobs.clear();
79
+ this.driver.dispose();
80
+ }
81
+ emit(event) {
82
+ for (const handler of this.listeners) {
83
+ try {
84
+ handler(event);
85
+ }
86
+ catch {
87
+ // Event handler errors must not crash the manager.
88
+ }
89
+ }
90
+ }
91
+ async executeJob(id) {
92
+ const state = this.jobs.get(id);
93
+ if (!state)
94
+ return;
95
+ // Overlap policy.
96
+ if (state.running && state.overlap === "skip") {
97
+ return;
98
+ }
99
+ if (state.running && state.overlap === "queue") {
100
+ state.queued = true;
101
+ return;
102
+ }
103
+ state.running = true;
104
+ state.lastRun = new Date();
105
+ state.runCount++;
106
+ this.emit({ type: "cron:fire", jobId: state.id, time: new Date() });
107
+ const startTime = Date.now();
108
+ try {
109
+ await this.runAction(state.action);
110
+ this.emit({
111
+ type: "cron:complete",
112
+ jobId: state.id,
113
+ time: new Date(),
114
+ durationMs: Date.now() - startTime,
115
+ });
116
+ }
117
+ catch (error) {
118
+ this.emit({
119
+ type: "cron:error",
120
+ jobId: state.id,
121
+ time: new Date(),
122
+ error: error,
123
+ });
124
+ }
125
+ finally {
126
+ state.running = false;
127
+ state.nextRun = computeNextTime(state.schedule);
128
+ // Process queued execution.
129
+ if (state.queued) {
130
+ state.queued = false;
131
+ void this.executeJob(id);
132
+ }
133
+ }
134
+ }
135
+ async runAction(action) {
136
+ switch (action.type) {
137
+ case "session": {
138
+ const { sessionId } = await this.vm.createSession(action.agentType, action.options);
139
+ try {
140
+ await this.vm.prompt(sessionId, action.prompt);
141
+ }
142
+ finally {
143
+ this.vm.closeSession(sessionId);
144
+ }
145
+ break;
146
+ }
147
+ case "exec": {
148
+ const cmd = action.args?.length
149
+ ? `${action.command} ${action.args.join(" ")}`
150
+ : action.command;
151
+ await this.vm.exec(cmd);
152
+ break;
153
+ }
154
+ case "callback": {
155
+ await action.fn();
156
+ break;
157
+ }
158
+ }
159
+ }
160
+ }
@@ -0,0 +1,5 @@
1
+ export { CronManager } from "./cron-manager.js";
2
+ export { InvalidScheduleError, PastScheduleError, } from "./parse-schedule.js";
3
+ export type { ScheduleDriver, ScheduleEntry, ScheduleHandle, } from "./schedule-driver.js";
4
+ export { TimerScheduleDriver } from "./timer-driver.js";
5
+ export type { CronAction, CronEvent, CronEventHandler, CronJob, CronJobInfo, CronJobOptions, } from "./types.js";
@@ -0,0 +1,3 @@
1
+ export { CronManager } from "./cron-manager.js";
2
+ export { InvalidScheduleError, PastScheduleError, } from "./parse-schedule.js";
3
+ export { TimerScheduleDriver } from "./timer-driver.js";
@@ -0,0 +1,25 @@
1
+ import { Cron } from "croner";
2
+ export type ParsedSchedule = {
3
+ kind: "date";
4
+ date: Date;
5
+ } | {
6
+ kind: "cron";
7
+ cron: Cron;
8
+ };
9
+ export declare class InvalidScheduleError extends Error {
10
+ readonly schedule: string;
11
+ constructor(schedule: string);
12
+ }
13
+ export declare class PastScheduleError extends Error {
14
+ readonly schedule: string;
15
+ constructor(schedule: string);
16
+ }
17
+ export declare function parseSchedule(schedule: string): ParsedSchedule;
18
+ export declare function resolveSchedule(schedule: string, now?: Date): {
19
+ parsed: ParsedSchedule;
20
+ nextRun?: Date;
21
+ };
22
+ export declare function validateScheduleForRegistration(schedule: string, now?: Date): {
23
+ parsed: ParsedSchedule;
24
+ nextRun?: Date;
25
+ };
@@ -0,0 +1,66 @@
1
+ import { Cron } from "croner";
2
+ const ONE_SHOT_SCHEDULE_PATTERN = /^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,3})?)?(?:Z|[+-]\d{2}:\d{2})?)?$/;
3
+ const DATE_TIME_WITHOUT_ZONE_PATTERN = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,3})?)?$/;
4
+ export class InvalidScheduleError extends Error {
5
+ schedule;
6
+ constructor(schedule) {
7
+ super(`Invalid schedule "${schedule}". Expected a cron expression or an ISO-like one-shot timestamp.`);
8
+ this.name = "InvalidScheduleError";
9
+ this.schedule = schedule;
10
+ }
11
+ }
12
+ export class PastScheduleError extends Error {
13
+ schedule;
14
+ constructor(schedule) {
15
+ super(`One-shot schedule "${schedule}" is already in the past.`);
16
+ this.name = "PastScheduleError";
17
+ this.schedule = schedule;
18
+ }
19
+ }
20
+ function looksLikeOneShotSchedule(schedule) {
21
+ return ONE_SHOT_SCHEDULE_PATTERN.test(schedule);
22
+ }
23
+ function normalizeOneShotScheduleForDateParse(schedule) {
24
+ const dateParseSchedule = schedule.replace(" ", "T");
25
+ return DATE_TIME_WITHOUT_ZONE_PATTERN.test(schedule)
26
+ ? `${dateParseSchedule}Z`
27
+ : dateParseSchedule;
28
+ }
29
+ export function parseSchedule(schedule) {
30
+ const normalizedSchedule = schedule.trim();
31
+ if (looksLikeOneShotSchedule(normalizedSchedule)) {
32
+ const parsedTime = Date.parse(normalizeOneShotScheduleForDateParse(normalizedSchedule));
33
+ if (!Number.isFinite(parsedTime)) {
34
+ throw new InvalidScheduleError(schedule);
35
+ }
36
+ return {
37
+ kind: "date",
38
+ date: new Date(parsedTime),
39
+ };
40
+ }
41
+ try {
42
+ return {
43
+ kind: "cron",
44
+ cron: new Cron(normalizedSchedule),
45
+ };
46
+ }
47
+ catch {
48
+ throw new InvalidScheduleError(schedule);
49
+ }
50
+ }
51
+ export function resolveSchedule(schedule, now = new Date()) {
52
+ const parsed = parseSchedule(schedule);
53
+ const nextRun = parsed.kind === "cron"
54
+ ? (parsed.cron.nextRun() ?? undefined)
55
+ : parsed.date.getTime() > now.getTime()
56
+ ? parsed.date
57
+ : undefined;
58
+ return { parsed, nextRun };
59
+ }
60
+ export function validateScheduleForRegistration(schedule, now = new Date()) {
61
+ const resolved = resolveSchedule(schedule, now);
62
+ if (resolved.parsed.kind === "date" && !resolved.nextRun) {
63
+ throw new PastScheduleError(schedule);
64
+ }
65
+ return resolved;
66
+ }
@@ -0,0 +1,19 @@
1
+ export interface ScheduleEntry {
2
+ /** Unique ID for this job. */
3
+ id: string;
4
+ /** Standard 5-field cron expression or ISO 8601 timestamp for one-shot. */
5
+ schedule: string;
6
+ /** Called when the schedule fires. */
7
+ callback: () => void | Promise<void>;
8
+ }
9
+ export interface ScheduleHandle {
10
+ id: string;
11
+ }
12
+ export interface ScheduleDriver {
13
+ /** Schedule a callback to fire on a cron expression or at a specific time. */
14
+ schedule(entry: ScheduleEntry): ScheduleHandle;
15
+ /** Cancel a previously scheduled entry. */
16
+ cancel(handle: ScheduleHandle): void;
17
+ /** Tear down all scheduled work. */
18
+ dispose(): void;
19
+ }
@@ -0,0 +1 @@
1
+ export {};