@lovelybunch/api 1.0.57 → 1.0.59

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 (35) hide show
  1. package/dist/lib/git.d.ts +13 -0
  2. package/dist/lib/git.js +137 -5
  3. package/dist/lib/jobs/global-job-scheduler.d.ts +2 -0
  4. package/dist/lib/jobs/global-job-scheduler.js +11 -0
  5. package/dist/lib/jobs/job-runner.d.ts +17 -0
  6. package/dist/lib/jobs/job-runner.js +167 -0
  7. package/dist/lib/jobs/job-scheduler.d.ts +39 -0
  8. package/dist/lib/jobs/job-scheduler.js +309 -0
  9. package/dist/lib/jobs/job-store.d.ts +16 -0
  10. package/dist/lib/jobs/job-store.js +211 -0
  11. package/dist/lib/storage/file-storage.js +7 -5
  12. package/dist/lib/terminal/terminal-manager.d.ts +2 -0
  13. package/dist/lib/terminal/terminal-manager.js +65 -0
  14. package/dist/routes/api/v1/ai/route.d.ts +1 -7
  15. package/dist/routes/api/v1/ai/route.js +25 -12
  16. package/dist/routes/api/v1/git/index.js +63 -1
  17. package/dist/routes/api/v1/jobs/[id]/route.d.ts +133 -0
  18. package/dist/routes/api/v1/jobs/[id]/route.js +135 -0
  19. package/dist/routes/api/v1/jobs/[id]/run/route.d.ts +31 -0
  20. package/dist/routes/api/v1/jobs/[id]/run/route.js +37 -0
  21. package/dist/routes/api/v1/jobs/index.d.ts +3 -0
  22. package/dist/routes/api/v1/jobs/index.js +14 -0
  23. package/dist/routes/api/v1/jobs/route.d.ts +108 -0
  24. package/dist/routes/api/v1/jobs/route.js +144 -0
  25. package/dist/routes/api/v1/jobs/status/route.d.ts +23 -0
  26. package/dist/routes/api/v1/jobs/status/route.js +21 -0
  27. package/dist/routes/api/v1/resources/[id]/route.d.ts +2 -44
  28. package/dist/server-with-static.js +5 -0
  29. package/dist/server.js +5 -0
  30. package/package.json +4 -4
  31. package/static/assets/index-CHq6mL1J.css +33 -0
  32. package/static/assets/index-QHnHUcsV.js +820 -0
  33. package/static/index.html +2 -2
  34. package/static/assets/index-CRg4lVi6.js +0 -779
  35. package/static/assets/index-VqhUTak4.css +0 -33
@@ -0,0 +1,108 @@
1
+ import { Context } from 'hono';
2
+ import { DayOfWeek, ScheduledJobSchedule, ScheduledJobStatus } from '@lovelybunch/types';
3
+ export declare function normalizeSchedule(schedule?: Partial<ScheduledJobSchedule>): ScheduledJobSchedule;
4
+ export declare function normalizeStatus(status?: ScheduledJobStatus): ScheduledJobStatus;
5
+ export declare function GET(c: Context): Promise<(Response & import("hono").TypedResponse<{
6
+ success: true;
7
+ data: {
8
+ id: string;
9
+ name: string;
10
+ description?: string;
11
+ prompt: string;
12
+ model: string;
13
+ status: ScheduledJobStatus;
14
+ schedule: {
15
+ type: "cron";
16
+ expression: string;
17
+ timezone?: string;
18
+ description?: string;
19
+ } | {
20
+ type: "interval";
21
+ hours: number;
22
+ daysOfWeek: DayOfWeek[];
23
+ timezone?: string;
24
+ anchorHour?: number;
25
+ };
26
+ metadata: {
27
+ createdAt: string;
28
+ updatedAt: string;
29
+ lastRunAt?: string;
30
+ nextRunAt?: string;
31
+ };
32
+ runs: {
33
+ id: string;
34
+ jobId: string;
35
+ trigger: import("@lovelybunch/types").ScheduledJobTrigger;
36
+ status: import("@lovelybunch/types").ScheduledJobRunStatus;
37
+ startedAt: string;
38
+ finishedAt?: string;
39
+ outputPath?: string;
40
+ summary?: string;
41
+ error?: string;
42
+ cliCommand?: string;
43
+ }[];
44
+ tags?: string[];
45
+ contextPaths?: string[];
46
+ }[];
47
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
48
+ success: false;
49
+ error: {
50
+ code: string;
51
+ message: any;
52
+ };
53
+ }, 500, "json">)>;
54
+ export declare function POST(c: Context): Promise<(Response & import("hono").TypedResponse<{
55
+ success: false;
56
+ error: {
57
+ code: string;
58
+ message: any;
59
+ };
60
+ }, 400, "json">) | (Response & import("hono").TypedResponse<{
61
+ success: true;
62
+ data: {
63
+ id: string;
64
+ name: string;
65
+ description?: string;
66
+ prompt: string;
67
+ model: string;
68
+ status: ScheduledJobStatus;
69
+ schedule: {
70
+ type: "cron";
71
+ expression: string;
72
+ timezone?: string;
73
+ description?: string;
74
+ } | {
75
+ type: "interval";
76
+ hours: number;
77
+ daysOfWeek: DayOfWeek[];
78
+ timezone?: string;
79
+ anchorHour?: number;
80
+ };
81
+ metadata: {
82
+ createdAt: string;
83
+ updatedAt: string;
84
+ lastRunAt?: string;
85
+ nextRunAt?: string;
86
+ };
87
+ runs: {
88
+ id: string;
89
+ jobId: string;
90
+ trigger: import("@lovelybunch/types").ScheduledJobTrigger;
91
+ status: import("@lovelybunch/types").ScheduledJobRunStatus;
92
+ startedAt: string;
93
+ finishedAt?: string;
94
+ outputPath?: string;
95
+ summary?: string;
96
+ error?: string;
97
+ cliCommand?: string;
98
+ }[];
99
+ tags?: string[];
100
+ contextPaths?: string[];
101
+ };
102
+ }, 201, "json">) | (Response & import("hono").TypedResponse<{
103
+ success: false;
104
+ error: {
105
+ code: string;
106
+ message: any;
107
+ };
108
+ }, 500, "json">)>;
@@ -0,0 +1,144 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { JobStore } from '../../../../lib/jobs/job-store.js';
3
+ import { getGlobalJobScheduler } from '../../../../lib/jobs/global-job-scheduler.js';
4
+ const store = new JobStore();
5
+ const scheduler = getGlobalJobScheduler();
6
+ const DAY_ORDER = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
7
+ function normalizeDays(days) {
8
+ if (!Array.isArray(days) || days.length === 0) {
9
+ return ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'];
10
+ }
11
+ const unique = Array.from(new Set(days.map((day) => day.toLowerCase())));
12
+ return unique
13
+ .filter((day) => DAY_ORDER.includes(day))
14
+ .sort((a, b) => DAY_ORDER.indexOf(a) - DAY_ORDER.indexOf(b));
15
+ }
16
+ function validateIntervalSchedule(schedule) {
17
+ const hours = Number(schedule?.hours ?? 6);
18
+ if (!Number.isFinite(hours) || hours < 1) {
19
+ throw new Error('Interval hours must be a positive number of hours (>= 1).');
20
+ }
21
+ if (hours > 24 * 30) {
22
+ throw new Error('Interval hours must be less than or equal to 720 (30 days).');
23
+ }
24
+ const days = normalizeDays(schedule?.daysOfWeek);
25
+ if (days.length === 0) {
26
+ throw new Error('At least one day of the week must be selected.');
27
+ }
28
+ let anchorHour;
29
+ if (schedule?.anchorHour !== undefined) {
30
+ const parsed = Number(schedule.anchorHour);
31
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > 23) {
32
+ throw new Error('Anchor hour must be between 0 and 23.');
33
+ }
34
+ anchorHour = parsed;
35
+ }
36
+ return {
37
+ type: 'interval',
38
+ hours,
39
+ daysOfWeek: days,
40
+ anchorHour
41
+ };
42
+ }
43
+ export function normalizeSchedule(schedule) {
44
+ if (!schedule || schedule.type !== 'cron') {
45
+ return validateIntervalSchedule(schedule);
46
+ }
47
+ const expression = schedule.expression?.trim();
48
+ if (!expression) {
49
+ throw new Error('Cron expression is required when using cron schedules.');
50
+ }
51
+ return {
52
+ type: 'cron',
53
+ expression,
54
+ timezone: schedule.timezone,
55
+ description: schedule.description,
56
+ };
57
+ }
58
+ export function normalizeStatus(status) {
59
+ return status === 'active' ? 'active' : 'paused';
60
+ }
61
+ export async function GET(c) {
62
+ try {
63
+ const jobs = await store.listJobs();
64
+ return c.json({
65
+ success: true,
66
+ data: jobs
67
+ });
68
+ }
69
+ catch (error) {
70
+ console.error('Failed to list scheduled jobs:', error);
71
+ return c.json({
72
+ success: false,
73
+ error: {
74
+ code: 'LIST_JOBS_ERROR',
75
+ message: error?.message ?? 'Unknown error listing jobs'
76
+ }
77
+ }, 500);
78
+ }
79
+ }
80
+ export async function POST(c) {
81
+ try {
82
+ const body = await c.req.json();
83
+ if (!body?.prompt || typeof body.prompt !== 'string') {
84
+ return c.json({
85
+ success: false,
86
+ error: {
87
+ code: 'MISSING_PROMPT',
88
+ message: 'A prompt is required to create a scheduled job.'
89
+ }
90
+ }, 400);
91
+ }
92
+ const now = new Date();
93
+ const id = body.id || `job-${randomUUID()}`;
94
+ let schedule;
95
+ try {
96
+ schedule = normalizeSchedule(body.schedule);
97
+ }
98
+ catch (validationError) {
99
+ return c.json({
100
+ success: false,
101
+ error: {
102
+ code: 'INVALID_SCHEDULE',
103
+ message: validationError?.message || 'Invalid schedule definition.'
104
+ }
105
+ }, 400);
106
+ }
107
+ const status = normalizeStatus(body.status);
108
+ const job = {
109
+ id,
110
+ name: body.name || id,
111
+ description: body.description,
112
+ prompt: body.prompt,
113
+ model: (body.model || 'claude').toString(),
114
+ status,
115
+ schedule,
116
+ metadata: {
117
+ createdAt: now,
118
+ updatedAt: now,
119
+ lastRunAt: undefined,
120
+ nextRunAt: undefined,
121
+ },
122
+ runs: [],
123
+ tags: Array.isArray(body.tags) ? body.tags : [],
124
+ contextPaths: Array.isArray(body.contextPaths) ? body.contextPaths : [],
125
+ };
126
+ await store.saveJob(job);
127
+ await scheduler.refresh(job.id);
128
+ const created = await store.getJob(job.id);
129
+ return c.json({
130
+ success: true,
131
+ data: created ?? job
132
+ }, 201);
133
+ }
134
+ catch (error) {
135
+ console.error('Failed to create scheduled job:', error);
136
+ return c.json({
137
+ success: false,
138
+ error: {
139
+ code: 'CREATE_JOB_ERROR',
140
+ message: error?.message ?? 'Unknown error creating job'
141
+ }
142
+ }, 500);
143
+ }
144
+ }
@@ -0,0 +1,23 @@
1
+ import { Context } from 'hono';
2
+ export declare function GET(c: Context): Promise<(Response & import("hono").TypedResponse<{
3
+ success: true;
4
+ data: {
5
+ initialized: boolean;
6
+ jobCount: number;
7
+ runningCount: number;
8
+ jobs: {
9
+ id: string;
10
+ status: import("@lovelybunch/types").ScheduledJobStatus;
11
+ nextRunAt?: string;
12
+ lastRunAt?: string;
13
+ timerActive: boolean;
14
+ running: boolean;
15
+ }[];
16
+ };
17
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
18
+ success: false;
19
+ error: {
20
+ code: string;
21
+ message: any;
22
+ };
23
+ }, 500, "json">)>;
@@ -0,0 +1,21 @@
1
+ import { getGlobalJobScheduler } from '../../../../../lib/jobs/global-job-scheduler.js';
2
+ const scheduler = getGlobalJobScheduler();
3
+ export async function GET(c) {
4
+ try {
5
+ const status = scheduler.getStatus();
6
+ return c.json({
7
+ success: true,
8
+ data: status
9
+ });
10
+ }
11
+ catch (error) {
12
+ console.error('Failed to fetch scheduler status:', error);
13
+ return c.json({
14
+ success: false,
15
+ error: {
16
+ code: 'SCHEDULER_STATUS_ERROR',
17
+ message: error?.message ?? 'Unknown error retrieving scheduler status'
18
+ }
19
+ }, 500);
20
+ }
21
+ }
@@ -1,46 +1,4 @@
1
1
  import { Context } from 'hono';
2
2
  export declare function GET(c: Context): Promise<Response>;
3
- export declare function PUT(c: Context): Promise<(Response & import("hono").TypedResponse<{
4
- success: false;
5
- error: {
6
- code: string;
7
- message: string;
8
- };
9
- }, 404, "json">) | (Response & import("hono").TypedResponse<{
10
- success: true;
11
- data: {
12
- id: string;
13
- name: string;
14
- type: string;
15
- size: number;
16
- uploadedAt: string;
17
- metadata: {
18
- tags?: string[];
19
- description?: string;
20
- };
21
- path: string;
22
- thumbnailPath?: string;
23
- };
24
- }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
25
- success: false;
26
- error: {
27
- code: string;
28
- message: any;
29
- };
30
- }, 500, "json">)>;
31
- export declare function DELETE(c: Context): Promise<(Response & import("hono").TypedResponse<{
32
- success: false;
33
- error: {
34
- code: string;
35
- message: string;
36
- };
37
- }, 404, "json">) | (Response & import("hono").TypedResponse<{
38
- success: true;
39
- message: string;
40
- }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
41
- success: false;
42
- error: {
43
- code: string;
44
- message: any;
45
- };
46
- }, 500, "json">)>;
3
+ export declare function PUT(c: Context): Promise<Response>;
4
+ export declare function DELETE(c: Context): Promise<Response>;
@@ -8,6 +8,7 @@ import { trimTrailingSlash } from 'hono/trailing-slash';
8
8
  import path from 'path';
9
9
  import fs from 'fs';
10
10
  import { getGlobalTerminalManager } from './lib/terminal/global-manager.js';
11
+ import { getGlobalJobScheduler } from './lib/jobs/global-job-scheduler.js';
11
12
  import { fileURLToPath } from 'url';
12
13
  const __filename = fileURLToPath(import.meta.url);
13
14
  const __dirname = path.dirname(__filename);
@@ -99,6 +100,7 @@ import agents from './routes/api/v1/agents/index.js';
99
100
  import agentsById from './routes/api/v1/agents/[id]/index.js';
100
101
  import git from './routes/api/v1/git/index.js';
101
102
  import mcp from './routes/api/v1/mcp/index.js';
103
+ import jobs from './routes/api/v1/jobs/index.js';
102
104
  // Register API routes FIRST
103
105
  console.log('🔗 Registering API routes...');
104
106
  app.route('/api/v1/auth', auth);
@@ -122,7 +124,10 @@ app.route('/api/v1/agents', agents);
122
124
  app.route('/api/v1/agents/:id', agentsById);
123
125
  app.route('/api/v1/git', git);
124
126
  app.route('/api/v1/mcp', mcp);
127
+ app.route('/api/v1/jobs', jobs);
125
128
  console.log('✅ API routes registered');
129
+ // Initialize background services
130
+ getGlobalJobScheduler();
126
131
  // Health check endpoint
127
132
  app.get('/api/health', (c) => {
128
133
  return c.json({ status: 'ok', timestamp: new Date().toISOString() });
package/dist/server.js CHANGED
@@ -5,6 +5,7 @@ import { Hono } from 'hono';
5
5
  import { cors } from 'hono/cors';
6
6
  import { trimTrailingSlash } from 'hono/trailing-slash';
7
7
  import { getGlobalTerminalManager } from './lib/terminal/global-manager.js';
8
+ import { getGlobalJobScheduler } from './lib/jobs/global-job-scheduler.js';
8
9
  import path from 'path';
9
10
  import { fileURLToPath } from 'url';
10
11
  const __filename = fileURLToPath(import.meta.url);
@@ -98,6 +99,7 @@ import agentsById from './routes/api/v1/agents/[id]/index.js';
98
99
  import git from './routes/api/v1/git/index.js';
99
100
  import mcp from './routes/api/v1/mcp/index.js';
100
101
  import symlinks from './routes/api/v1/symlinks/index.js';
102
+ import jobs from './routes/api/v1/jobs/index.js';
101
103
  // Register API routes
102
104
  app.route('/api/v1/auth', auth);
103
105
  app.route('/api/v1/auth-settings', authSettings);
@@ -121,6 +123,7 @@ app.route('/api/v1/agents/:id', agentsById);
121
123
  app.route('/api/v1/git', git);
122
124
  app.route('/api/v1/mcp', mcp);
123
125
  app.route('/api/v1/symlinks', symlinks);
126
+ app.route('/api/v1/jobs', jobs);
124
127
  // Health check endpoint
125
128
  app.get('/health', (c) => {
126
129
  return c.json({ status: 'ok', timestamp: new Date().toISOString() });
@@ -137,3 +140,5 @@ injectWebSocket(server);
137
140
  console.log(`🚀 Server running at http://localhost:${port}`);
138
141
  console.log(`🔌 WebSocket available at ws://localhost:${port}/ws/terminal/:sessionId`);
139
142
  console.log(`🔎 Preview WebSocket available at ws://localhost:${port}/ws/terminal-preview/:sessionId`);
143
+ // Initialize background services
144
+ getGlobalJobScheduler();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lovelybunch/api",
3
- "version": "1.0.57",
3
+ "version": "1.0.59",
4
4
  "type": "module",
5
5
  "main": "dist/server-with-static.js",
6
6
  "exports": {
@@ -32,9 +32,9 @@
32
32
  "dependencies": {
33
33
  "@hono/node-server": "^1.13.7",
34
34
  "@hono/node-ws": "^1.0.6",
35
- "@lovelybunch/core": "^1.0.57",
36
- "@lovelybunch/mcp": "^1.0.57",
37
- "@lovelybunch/types": "^1.0.57",
35
+ "@lovelybunch/core": "^1.0.59",
36
+ "@lovelybunch/mcp": "^1.0.59",
37
+ "@lovelybunch/types": "^1.0.59",
38
38
  "arctic": "^1.9.2",
39
39
  "bcrypt": "^5.1.1",
40
40
  "cookie": "^0.6.0",