crnd 0.0.1 → 0.0.3

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 (122) hide show
  1. package/bin/crnd +88 -0
  2. package/package.json +19 -43
  3. package/drizzle/0000_init.sql +0 -35
  4. package/drizzle/0001_add_runs.sql +0 -13
  5. package/drizzle/meta/_journal.json +0 -20
  6. package/src/cli/commands/createDeleteCommand.ts +0 -93
  7. package/src/cli/commands/createDoctorCommand.ts +0 -93
  8. package/src/cli/commands/createExportCommand.ts +0 -75
  9. package/src/cli/commands/createImportCommand.ts +0 -80
  10. package/src/cli/commands/createKillCommand.ts +0 -89
  11. package/src/cli/commands/createListCommand.ts +0 -67
  12. package/src/cli/commands/createLogsCommand.ts +0 -125
  13. package/src/cli/commands/createPauseCommand.ts +0 -78
  14. package/src/cli/commands/createResetCommand.ts +0 -78
  15. package/src/cli/commands/createResumeCommand.ts +0 -78
  16. package/src/cli/commands/createRootCommand.ts +0 -50
  17. package/src/cli/commands/createRunOnceCommand.ts +0 -77
  18. package/src/cli/commands/createRunsCommand.ts +0 -106
  19. package/src/cli/commands/createScheduleCommand.ts +0 -184
  20. package/src/cli/commands/createShowCommand.ts +0 -92
  21. package/src/cli/commands/createStatusCommand.ts +0 -145
  22. package/src/cli/commands/createStopCommand.ts +0 -89
  23. package/src/cli/commands/createUpdateCommand.ts +0 -13
  24. package/src/cli/commands/daemon/createDaemonCommand.ts +0 -24
  25. package/src/cli/commands/daemon/createDaemonInstallCommand.ts +0 -144
  26. package/src/cli/commands/daemon/createDaemonServeCommand.ts +0 -14
  27. package/src/cli/commands/daemon/createDaemonStartCommand.ts +0 -73
  28. package/src/cli/commands/daemon/createDaemonStatusCommand.ts +0 -5
  29. package/src/cli/commands/daemon/createDaemonStopCommand.ts +0 -59
  30. package/src/cli/commands/daemon/createDaemonUninstallCommand.ts +0 -99
  31. package/src/cli/commands/daemon/createLaunchdPlist.ts +0 -34
  32. package/src/cli/commands/daemon/createSystemdService.ts +0 -24
  33. package/src/cli/commands/daemon/escapeXml.ts +0 -8
  34. package/src/cli/commands/daemon/getDaemonServiceArgs.ts +0 -10
  35. package/src/cli/commands/daemon/quoteWindowsArg.ts +0 -4
  36. package/src/cli/commands/getCommandArgs.ts +0 -8
  37. package/src/cli/commands/parseEnvArgs.ts +0 -28
  38. package/src/cli/getDaemonSpawnArgs.ts +0 -9
  39. package/src/cli/main.ts +0 -8
  40. package/src/daemon/autostart/ensureAutostart.ts +0 -30
  41. package/src/daemon/autostart/getAutostartPath.ts +0 -29
  42. package/src/daemon/autostart/getDaemonInstallArgs.ts +0 -10
  43. package/src/daemon/createLogger.ts +0 -13
  44. package/src/daemon/createShutdownHandler.ts +0 -29
  45. package/src/daemon/jobs/createJobsFileSync.ts +0 -113
  46. package/src/daemon/jobs/deleteJobByName.ts +0 -18
  47. package/src/daemon/jobs/upsertJob.ts +0 -85
  48. package/src/daemon/main.ts +0 -64
  49. package/src/daemon/runner/createRunOutputFds.ts +0 -18
  50. package/src/daemon/runner/getRunStatus.ts +0 -14
  51. package/src/daemon/runner/recordSkippedRun.ts +0 -27
  52. package/src/daemon/runner/recoverRunningRuns.ts +0 -36
  53. package/src/daemon/runner/runJob.ts +0 -94
  54. package/src/daemon/scheduler/createScheduler.ts +0 -45
  55. package/src/daemon/scheduler/createSchedulerState.ts +0 -8
  56. package/src/daemon/scheduler/loadJobs.ts +0 -10
  57. package/src/daemon/scheduler/runJobWithTracking.ts +0 -48
  58. package/src/daemon/scheduler/scheduleJob.ts +0 -32
  59. package/src/daemon/scheduler/unscheduleJob.ts +0 -11
  60. package/src/daemon/scheduler/updateNextRunAt.ts +0 -14
  61. package/src/daemon/server/createApp.ts +0 -76
  62. package/src/daemon/server/createAuthMiddleware.ts +0 -16
  63. package/src/daemon/server/routes/registerExportRoute.ts +0 -21
  64. package/src/daemon/server/routes/registerHealthRoute.ts +0 -16
  65. package/src/daemon/server/routes/registerImportRoute.ts +0 -22
  66. package/src/daemon/server/routes/registerJobRunsRoute.ts +0 -46
  67. package/src/daemon/server/routes/registerJobsDeleteRoute.ts +0 -37
  68. package/src/daemon/server/routes/registerJobsGetRoute.ts +0 -29
  69. package/src/daemon/server/routes/registerJobsKillRoute.ts +0 -45
  70. package/src/daemon/server/routes/registerJobsListRoute.ts +0 -13
  71. package/src/daemon/server/routes/registerJobsPauseRoute.ts +0 -53
  72. package/src/daemon/server/routes/registerJobsResetRoute.ts +0 -54
  73. package/src/daemon/server/routes/registerJobsResumeRoute.ts +0 -53
  74. package/src/daemon/server/routes/registerJobsRunRoute.ts +0 -37
  75. package/src/daemon/server/routes/registerJobsStopRoute.ts +0 -45
  76. package/src/daemon/server/routes/registerJobsUpsertRoute.ts +0 -30
  77. package/src/daemon/server/routes/registerRunGetRoute.ts +0 -25
  78. package/src/daemon/server/routes/registerRunLogsRoute.ts +0 -32
  79. package/src/daemon/server/routes/registerShutdownRoute.ts +0 -9
  80. package/src/daemon/server/startServer.ts +0 -23
  81. package/src/db/getMigrationsDir.ts +0 -5
  82. package/src/db/migrateDatabase.ts +0 -21
  83. package/src/db/openDatabase.ts +0 -12
  84. package/src/db/schema/jobs.ts +0 -26
  85. package/src/db/schema/jobsSchemas.ts +0 -10
  86. package/src/db/schema/runs.ts +0 -23
  87. package/src/db/schema/runsSchemas.ts +0 -10
  88. package/src/db/schema.ts +0 -5
  89. package/src/shared/auth/createToken.ts +0 -5
  90. package/src/shared/events/appendEvent.ts +0 -16
  91. package/src/shared/jobs/createCommandSchema.ts +0 -5
  92. package/src/shared/jobs/createEnvSchema.ts +0 -5
  93. package/src/shared/jobs/createJobInputSchema.ts +0 -31
  94. package/src/shared/jobs/createTomlJobSchema.ts +0 -31
  95. package/src/shared/jobs/formatJobRow.ts +0 -14
  96. package/src/shared/jobs/isOverlapPolicy.ts +0 -5
  97. package/src/shared/jobs/parseCommand.ts +0 -6
  98. package/src/shared/jobs/parseEnv.ts +0 -10
  99. package/src/shared/jobs/parseJobsToml.ts +0 -36
  100. package/src/shared/jobs/readJobsToml.ts +0 -17
  101. package/src/shared/jobs/serializeCommand.ts +0 -6
  102. package/src/shared/jobs/serializeEnv.ts +0 -6
  103. package/src/shared/jobs/serializeJobsToml.ts +0 -45
  104. package/src/shared/jobs/writeJobsToml.ts +0 -12
  105. package/src/shared/paths/ensureDir.ts +0 -6
  106. package/src/shared/paths/getConfigDir.ts +0 -6
  107. package/src/shared/paths/getEventsPath.ts +0 -6
  108. package/src/shared/paths/getJobRunsDir.ts +0 -7
  109. package/src/shared/paths/getJobsTomlPath.ts +0 -6
  110. package/src/shared/paths/getPaths.ts +0 -16
  111. package/src/shared/paths/getRunOutputPaths.ts +0 -10
  112. package/src/shared/paths/getRunsDir.ts +0 -7
  113. package/src/shared/paths/getStateDir.ts +0 -8
  114. package/src/shared/rpc/createRpcClient.ts +0 -20
  115. package/src/shared/runs/formatRunRow.ts +0 -6
  116. package/src/shared/state/daemonStateSchema.ts +0 -13
  117. package/src/shared/state/getDaemonStatePath.ts +0 -6
  118. package/src/shared/state/readDaemonState.ts +0 -14
  119. package/src/shared/state/removeDaemonState.ts +0 -9
  120. package/src/shared/state/writeDaemonState.ts +0 -8
  121. package/src/shared/utils/isRecord.ts +0 -5
  122. package/src/shared/version.ts +0 -5
@@ -1,5 +0,0 @@
1
- import { ulid } from "ulid";
2
-
3
- export default function createToken() {
4
- return ulid();
5
- }
@@ -1,16 +0,0 @@
1
- import { appendFileSync } from "node:fs";
2
- import getEventsPath from "../paths/getEventsPath";
3
-
4
- export default function appendEvent(
5
- type: string,
6
- payload?: Record<string, unknown>,
7
- ) {
8
- const record = {
9
- type,
10
- timestamp: new Date().toISOString(),
11
- payload,
12
- };
13
-
14
- const line = `${JSON.stringify(record)}\n`;
15
- appendFileSync(getEventsPath(), line, "utf-8");
16
- }
@@ -1,5 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export default function createCommandSchema() {
4
- return z.array(z.string().min(1)).min(1);
5
- }
@@ -1,5 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export default function createEnvSchema() {
4
- return z.record(z.string(), z.string());
5
- }
@@ -1,31 +0,0 @@
1
- import { z } from "zod";
2
- import createCommandSchema from "./createCommandSchema";
3
- import createEnvSchema from "./createEnvSchema";
4
-
5
- export default function createJobInputSchema() {
6
- return z
7
- .object({
8
- name: z.string().min(1),
9
- description: z.string().min(1).optional(),
10
- command: createCommandSchema(),
11
- cwd: z.string().min(1).optional(),
12
- env: createEnvSchema().optional(),
13
- schedule: z.string().min(1).optional(),
14
- runAt: z.string().datetime().optional(),
15
- timezone: z.string().min(1).optional(),
16
- timeoutMs: z.number().int().positive().optional(),
17
- paused: z.boolean().optional(),
18
- overlapPolicy: z.enum(["skip", "allow"]).optional(),
19
- })
20
- .superRefine((value, ctx) => {
21
- const hasSchedule = Boolean(value.schedule);
22
- const hasRunAt = Boolean(value.runAt);
23
- if (hasSchedule === hasRunAt) {
24
- ctx.addIssue({
25
- code: z.ZodIssueCode.custom,
26
- message: "Provide schedule or runAt",
27
- path: ["schedule"],
28
- });
29
- }
30
- });
31
- }
@@ -1,31 +0,0 @@
1
- import { z } from "zod";
2
- import createCommandSchema from "./createCommandSchema";
3
- import createEnvSchema from "./createEnvSchema";
4
-
5
- export default function createTomlJobSchema() {
6
- return z
7
- .object({
8
- id: z.string().min(1).optional(),
9
- command: createCommandSchema(),
10
- cwd: z.string().min(1).optional(),
11
- env: createEnvSchema().optional(),
12
- schedule: z.string().min(1).optional(),
13
- run_at: z.string().datetime().optional(),
14
- timezone: z.string().min(1).optional(),
15
- timeout_ms: z.number().int().positive().optional(),
16
- paused: z.boolean().optional(),
17
- overlap_policy: z.enum(["skip", "allow"]).optional(),
18
- description: z.string().min(1).optional(),
19
- })
20
- .superRefine((value, ctx) => {
21
- const hasSchedule = Boolean(value.schedule);
22
- const hasRunAt = Boolean(value.run_at);
23
- if (hasSchedule === hasRunAt) {
24
- ctx.addIssue({
25
- code: z.ZodIssueCode.custom,
26
- message: "Provide schedule or run_at",
27
- path: ["schedule"],
28
- });
29
- }
30
- });
31
- }
@@ -1,14 +0,0 @@
1
- import createJobsSchemas from "../../db/schema/jobsSchemas";
2
- import parseCommand from "./parseCommand";
3
- import parseEnv from "./parseEnv";
4
-
5
- export default function formatJobRow(row: unknown) {
6
- const schema = createJobsSchemas().select;
7
- const parsed = schema.parse(row);
8
-
9
- return {
10
- ...parsed,
11
- command: parseCommand(parsed.command),
12
- env: parseEnv(parsed.env),
13
- };
14
- }
@@ -1,5 +0,0 @@
1
- export default function isOverlapPolicy(
2
- value: string,
3
- ): value is "skip" | "allow" {
4
- return value === "skip" || value === "allow";
5
- }
@@ -1,6 +0,0 @@
1
- import createCommandSchema from "./createCommandSchema";
2
-
3
- export default function parseCommand(raw: string) {
4
- const parsed = JSON.parse(raw);
5
- return createCommandSchema().parse(parsed);
6
- }
@@ -1,10 +0,0 @@
1
- import createEnvSchema from "./createEnvSchema";
2
-
3
- export default function parseEnv(raw: string | null) {
4
- if (!raw) {
5
- return null;
6
- }
7
-
8
- const parsed = JSON.parse(raw);
9
- return createEnvSchema().parse(parsed);
10
- }
@@ -1,36 +0,0 @@
1
- import toml from "@iarna/toml";
2
- import { z } from "zod";
3
- import isRecord from "../utils/isRecord";
4
- import createTomlJobSchema from "./createTomlJobSchema";
5
-
6
- export default function parseJobsToml(content: string) {
7
- const parsed = toml.parse(content);
8
- if (!isRecord(parsed)) {
9
- throw new Error("Invalid jobs.toml");
10
- }
11
-
12
- const jobsValue = parsed.jobs;
13
- if (jobsValue === undefined) {
14
- return [];
15
- }
16
- if (!isRecord(jobsValue)) {
17
- throw new Error("Invalid jobs section");
18
- }
19
-
20
- const namedSchema = createTomlJobSchema().and(
21
- z.object({
22
- name: z.string().min(1),
23
- }),
24
- );
25
- const result: Array<z.infer<typeof namedSchema>> = [];
26
-
27
- for (const [name, value] of Object.entries(jobsValue)) {
28
- if (!isRecord(value)) {
29
- throw new Error(`Invalid job entry: ${name}`);
30
- }
31
- const job = namedSchema.parse({ name, ...value });
32
- result.push(job);
33
- }
34
-
35
- return result;
36
- }
@@ -1,17 +0,0 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import getJobsTomlPath from "../paths/getJobsTomlPath";
3
- import parseJobsToml from "./parseJobsToml";
4
-
5
- export default function readJobsToml() {
6
- const path = getJobsTomlPath();
7
- if (!existsSync(path)) {
8
- return [];
9
- }
10
-
11
- const content = readFileSync(path, "utf-8");
12
- if (!content.trim()) {
13
- return [];
14
- }
15
-
16
- return parseJobsToml(content);
17
- }
@@ -1,6 +0,0 @@
1
- import createCommandSchema from "./createCommandSchema";
2
-
3
- export default function serializeCommand(command: string[]) {
4
- const parsed = createCommandSchema().parse(command);
5
- return JSON.stringify(parsed);
6
- }
@@ -1,6 +0,0 @@
1
- import createEnvSchema from "./createEnvSchema";
2
-
3
- export default function serializeEnv(env: Record<string, string>) {
4
- const parsed = createEnvSchema().parse(env);
5
- return JSON.stringify(parsed);
6
- }
@@ -1,45 +0,0 @@
1
- import type { JsonMap } from "@iarna/toml";
2
- import toml from "@iarna/toml";
3
- import type formatJobRow from "./formatJobRow";
4
-
5
- export default function serializeJobsToml(
6
- jobs: Array<ReturnType<typeof formatJobRow>>,
7
- ) {
8
- const jobsSection: JsonMap = {};
9
- const doc: JsonMap = { jobs: jobsSection };
10
-
11
- for (const job of jobs) {
12
- const entry: JsonMap = {
13
- id: job.id,
14
- command: job.command,
15
- paused: job.paused,
16
- overlap_policy: job.overlapPolicy,
17
- };
18
-
19
- if (job.description) {
20
- entry.description = job.description;
21
- }
22
- if (job.cwd) {
23
- entry.cwd = job.cwd;
24
- }
25
- if (job.env) {
26
- entry.env = job.env;
27
- }
28
- if (job.scheduleType === "cron" && job.cron) {
29
- entry.schedule = job.cron;
30
- }
31
- if (job.scheduleType === "once" && job.runAt) {
32
- entry.run_at = job.runAt;
33
- }
34
- if (job.timezone) {
35
- entry.timezone = job.timezone;
36
- }
37
- if (job.timeoutMs) {
38
- entry.timeout_ms = job.timeoutMs;
39
- }
40
-
41
- jobsSection[job.name] = entry;
42
- }
43
-
44
- return toml.stringify(doc);
45
- }
@@ -1,12 +0,0 @@
1
- import { writeFileSync } from "node:fs";
2
- import getJobsTomlPath from "../paths/getJobsTomlPath";
3
- import type formatJobRow from "./formatJobRow";
4
- import serializeJobsToml from "./serializeJobsToml";
5
-
6
- export default function writeJobsToml(
7
- jobs: Array<ReturnType<typeof formatJobRow>>,
8
- ) {
9
- const path = getJobsTomlPath();
10
- const content = serializeJobsToml(jobs);
11
- writeFileSync(path, content, "utf-8");
12
- }
@@ -1,6 +0,0 @@
1
- import { mkdirSync } from "node:fs";
2
-
3
- export default function ensureDir(path: string) {
4
- mkdirSync(path, { recursive: true });
5
- return path;
6
- }
@@ -1,6 +0,0 @@
1
- import ensureDir from "./ensureDir";
2
- import getPaths from "./getPaths";
3
-
4
- export default function getConfigDir() {
5
- return ensureDir(getPaths().config);
6
- }
@@ -1,6 +0,0 @@
1
- import path from "node:path";
2
- import getStateDir from "./getStateDir";
3
-
4
- export default function getEventsPath() {
5
- return path.join(getStateDir(), "events.jsonl");
6
- }
@@ -1,7 +0,0 @@
1
- import path from "node:path";
2
- import ensureDir from "./ensureDir";
3
- import getRunsDir from "./getRunsDir";
4
-
5
- export default function getJobRunsDir(jobId: string) {
6
- return ensureDir(path.join(getRunsDir(), jobId));
7
- }
@@ -1,6 +0,0 @@
1
- import path from "node:path";
2
- import getConfigDir from "./getConfigDir";
3
-
4
- export default function getJobsTomlPath() {
5
- return path.join(getConfigDir(), "jobs.toml");
6
- }
@@ -1,16 +0,0 @@
1
- import envPaths from "env-paths";
2
-
3
- export default function getPaths() {
4
- const root = process.env.CRND_PATHS_ROOT;
5
- if (root) {
6
- return {
7
- data: root,
8
- config: root,
9
- cache: root,
10
- log: root,
11
- temp: root,
12
- };
13
- }
14
-
15
- return envPaths("crnd");
16
- }
@@ -1,10 +0,0 @@
1
- import path from "node:path";
2
- import getJobRunsDir from "./getJobRunsDir";
3
-
4
- export default function getRunOutputPaths(jobId: string, runId: string) {
5
- const dir = getJobRunsDir(jobId);
6
- return {
7
- stdoutPath: path.join(dir, `${runId}.out`),
8
- stderrPath: path.join(dir, `${runId}.err`),
9
- };
10
- }
@@ -1,7 +0,0 @@
1
- import path from "node:path";
2
- import ensureDir from "./ensureDir";
3
- import getStateDir from "./getStateDir";
4
-
5
- export default function getRunsDir() {
6
- return ensureDir(path.join(getStateDir(), "runs"));
7
- }
@@ -1,8 +0,0 @@
1
- import path from "node:path";
2
- import ensureDir from "./ensureDir";
3
- import getPaths from "./getPaths";
4
-
5
- export default function getStateDir() {
6
- const dir = path.join(getPaths().data, "state");
7
- return ensureDir(dir);
8
- }
@@ -1,20 +0,0 @@
1
- import { hc } from "hono/client";
2
- import type { AppType } from "../../daemon/server/createApp";
3
- import readDaemonState from "../state/readDaemonState";
4
-
5
- export default function createRpcClient() {
6
- try {
7
- const state = readDaemonState();
8
- if (!state) {
9
- return null;
10
- }
11
-
12
- return hc<AppType>(`http://127.0.0.1:${state.port}`, {
13
- headers: {
14
- Authorization: `Bearer ${state.token}`,
15
- },
16
- });
17
- } catch {
18
- return null;
19
- }
20
- }
@@ -1,6 +0,0 @@
1
- import createRunsSchemas from "../../db/schema/runsSchemas";
2
-
3
- export default function formatRunRow(row: unknown) {
4
- const schema = createRunsSchemas().select;
5
- return schema.parse(row);
6
- }
@@ -1,13 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export default function createDaemonStateSchema() {
4
- return z.object({
5
- port: z.number().int().positive(),
6
- token: z.string().min(1),
7
- pid: z.number().int().positive(),
8
- startedAt: z.string().datetime(),
9
- version: z.string().min(1),
10
- });
11
- }
12
-
13
- export type DaemonState = z.infer<ReturnType<typeof createDaemonStateSchema>>;
@@ -1,6 +0,0 @@
1
- import path from "node:path";
2
- import getStateDir from "../paths/getStateDir";
3
-
4
- export default function getDaemonStatePath() {
5
- return path.join(getStateDir(), "daemon.json");
6
- }
@@ -1,14 +0,0 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import createDaemonStateSchema from "./daemonStateSchema";
3
- import getDaemonStatePath from "./getDaemonStatePath";
4
-
5
- export default function readDaemonState() {
6
- const path = getDaemonStatePath();
7
- if (!existsSync(path)) {
8
- return null;
9
- }
10
-
11
- const raw = readFileSync(path, "utf-8");
12
- const parsed = JSON.parse(raw);
13
- return createDaemonStateSchema().parse(parsed);
14
- }
@@ -1,9 +0,0 @@
1
- import { existsSync, unlinkSync } from "node:fs";
2
- import getDaemonStatePath from "./getDaemonStatePath";
3
-
4
- export default function removeDaemonState() {
5
- const path = getDaemonStatePath();
6
- if (existsSync(path)) {
7
- unlinkSync(path);
8
- }
9
- }
@@ -1,8 +0,0 @@
1
- import { writeFileSync } from "node:fs";
2
- import type { DaemonState } from "./daemonStateSchema";
3
- import getDaemonStatePath from "./getDaemonStatePath";
4
-
5
- export default function writeDaemonState(state: DaemonState) {
6
- const path = getDaemonStatePath();
7
- writeFileSync(path, `${JSON.stringify(state, null, 2)}\n`, "utf-8");
8
- }
@@ -1,5 +0,0 @@
1
- export default function isRecord(
2
- value: unknown,
3
- ): value is Record<string, unknown> {
4
- return typeof value === "object" && value !== null && !Array.isArray(value);
5
- }
@@ -1,5 +0,0 @@
1
- import pkg from "../../package.json";
2
-
3
- export default function getVersion() {
4
- return pkg.version;
5
- }