@farazirfan/costar-server-executor 1.7.54 → 1.7.56
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/dist/cron/local-run-log.d.ts +35 -0
- package/dist/cron/local-run-log.d.ts.map +1 -0
- package/dist/cron/local-run-log.js +153 -0
- package/dist/cron/local-run-log.js.map +1 -0
- package/dist/cron/local-store.d.ts +86 -0
- package/dist/cron/local-store.d.ts.map +1 -0
- package/dist/cron/local-store.js +199 -0
- package/dist/cron/local-store.js.map +1 -0
- package/dist/db/local-database.d.ts +134 -0
- package/dist/db/local-database.d.ts.map +1 -0
- package/dist/db/local-database.js +419 -0
- package/dist/db/local-database.js.map +1 -0
- package/dist/middleware/auth.d.ts +13 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +67 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/routes/db-ops.d.ts +11 -0
- package/dist/routes/db-ops.d.ts.map +1 -0
- package/dist/routes/db-ops.js +266 -0
- package/dist/routes/db-ops.js.map +1 -0
- package/dist/routes/sandbox-ops.d.ts +15 -0
- package/dist/routes/sandbox-ops.d.ts.map +1 -0
- package/dist/routes/sandbox-ops.js +306 -0
- package/dist/routes/sandbox-ops.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +61 -1
- package/dist/server.js.map +1 -1
- package/dist/services/backup-service.d.ts +49 -0
- package/dist/services/backup-service.d.ts.map +1 -0
- package/dist/services/backup-service.js +113 -0
- package/dist/services/backup-service.js.map +1 -0
- package/dist/services/s3-file-watcher.d.ts +79 -0
- package/dist/services/s3-file-watcher.d.ts.map +1 -0
- package/dist/services/s3-file-watcher.js +191 -0
- package/dist/services/s3-file-watcher.js.map +1 -0
- package/dist/startup/migrate.d.ts +32 -0
- package/dist/startup/migrate.d.ts.map +1 -0
- package/dist/startup/migrate.js +180 -0
- package/dist/startup/migrate.js.map +1 -0
- package/dist/web-server.d.ts +7 -0
- package/dist/web-server.d.ts.map +1 -1
- package/dist/web-server.js +59 -18
- package/dist/web-server.js.map +1 -1
- package/package.json +2 -1
- package/public/index.html +607 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Cron Run Log — JSONL execution history (OpenClaw pattern)
|
|
3
|
+
* One file per job: ~/.costar/cron/runs/{jobId}.jsonl
|
|
4
|
+
* Part of Phase 5: Direct Client → Sandbox Architecture.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - One JSON line per execution
|
|
8
|
+
* - Pruned at 2MB / 2000 lines (configurable)
|
|
9
|
+
* - Sequential write queue to prevent corruption
|
|
10
|
+
*/
|
|
11
|
+
export type CronRunLogEntry = {
|
|
12
|
+
ts: string;
|
|
13
|
+
jobId: string;
|
|
14
|
+
jobName: string;
|
|
15
|
+
status: "ok" | "error" | "skipped";
|
|
16
|
+
error?: string;
|
|
17
|
+
summary?: string;
|
|
18
|
+
durationMs?: number;
|
|
19
|
+
nextRunAt?: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Append a run log entry (sequential queue per job).
|
|
23
|
+
*/
|
|
24
|
+
export declare function appendRunLog(entry: CronRunLogEntry): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Read recent run log entries for a job.
|
|
27
|
+
* Returns newest first.
|
|
28
|
+
*/
|
|
29
|
+
export declare function readRunLog(jobId: string, limit?: number): CronRunLogEntry[];
|
|
30
|
+
/**
|
|
31
|
+
* Read run logs across all jobs (for dashboard).
|
|
32
|
+
* Returns newest first.
|
|
33
|
+
*/
|
|
34
|
+
export declare function readAllRunLogs(limit?: number): CronRunLogEntry[];
|
|
35
|
+
//# sourceMappingURL=local-run-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-run-log.d.ts","sourceRoot":"","sources":["../../src/cron/local-run-log.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAuBF;;GAEG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CASxE;AAuBD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAY,GAAG,eAAe,EAAE,CA0BhF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,GAAE,MAAY,GAAG,eAAe,EAAE,CAoCrE"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Cron Run Log — JSONL execution history (OpenClaw pattern)
|
|
3
|
+
* One file per job: ~/.costar/cron/runs/{jobId}.jsonl
|
|
4
|
+
* Part of Phase 5: Direct Client → Sandbox Architecture.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - One JSON line per execution
|
|
8
|
+
* - Pruned at 2MB / 2000 lines (configurable)
|
|
9
|
+
* - Sequential write queue to prevent corruption
|
|
10
|
+
*/
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { resolveCoStarDir } from "../cli/paths.js";
|
|
14
|
+
// ═══════════════════════════════════════════════════════
|
|
15
|
+
// PATHS
|
|
16
|
+
// ═══════════════════════════════════════════════════════
|
|
17
|
+
function getRunsDir() {
|
|
18
|
+
return path.join(resolveCoStarDir(), "cron", "runs");
|
|
19
|
+
}
|
|
20
|
+
function getRunLogPath(jobId) {
|
|
21
|
+
// Sanitize jobId to prevent path traversal
|
|
22
|
+
const safe = jobId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
23
|
+
return path.join(getRunsDir(), `${safe}.jsonl`);
|
|
24
|
+
}
|
|
25
|
+
// ═══════════════════════════════════════════════════════
|
|
26
|
+
// WRITE OPERATIONS
|
|
27
|
+
// ═══════════════════════════════════════════════════════
|
|
28
|
+
/** Write queue to prevent concurrent writes */
|
|
29
|
+
const writeQueue = new Map();
|
|
30
|
+
/**
|
|
31
|
+
* Append a run log entry (sequential queue per job).
|
|
32
|
+
*/
|
|
33
|
+
export async function appendRunLog(entry) {
|
|
34
|
+
const logPath = getRunLogPath(entry.jobId);
|
|
35
|
+
// Chain writes sequentially per job
|
|
36
|
+
const existing = writeQueue.get(entry.jobId) ?? Promise.resolve();
|
|
37
|
+
const next = existing.then(() => doAppend(logPath, entry));
|
|
38
|
+
writeQueue.set(entry.jobId, next);
|
|
39
|
+
await next;
|
|
40
|
+
}
|
|
41
|
+
async function doAppend(logPath, entry) {
|
|
42
|
+
try {
|
|
43
|
+
const dir = path.dirname(logPath);
|
|
44
|
+
if (!fs.existsSync(dir)) {
|
|
45
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
const line = JSON.stringify(entry) + "\n";
|
|
48
|
+
fs.appendFileSync(logPath, line, "utf-8");
|
|
49
|
+
// Check if pruning needed
|
|
50
|
+
await pruneIfNeeded(logPath);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.error(`[CRON-LOG] Error appending to ${logPath}:`, error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ═══════════════════════════════════════════════════════
|
|
57
|
+
// READ OPERATIONS
|
|
58
|
+
// ═══════════════════════════════════════════════════════
|
|
59
|
+
/**
|
|
60
|
+
* Read recent run log entries for a job.
|
|
61
|
+
* Returns newest first.
|
|
62
|
+
*/
|
|
63
|
+
export function readRunLog(jobId, limit = 200) {
|
|
64
|
+
const logPath = getRunLogPath(jobId);
|
|
65
|
+
if (!fs.existsSync(logPath)) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const content = fs.readFileSync(logPath, "utf-8");
|
|
70
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
71
|
+
const entries = [];
|
|
72
|
+
for (const line of lines) {
|
|
73
|
+
try {
|
|
74
|
+
entries.push(JSON.parse(line));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Skip malformed lines
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Return newest first, limited
|
|
81
|
+
return entries.reverse().slice(0, limit);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error(`[CRON-LOG] Error reading ${logPath}:`, error);
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Read run logs across all jobs (for dashboard).
|
|
90
|
+
* Returns newest first.
|
|
91
|
+
*/
|
|
92
|
+
export function readAllRunLogs(limit = 100) {
|
|
93
|
+
const runsDir = getRunsDir();
|
|
94
|
+
if (!fs.existsSync(runsDir)) {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const allEntries = [];
|
|
98
|
+
try {
|
|
99
|
+
const files = fs.readdirSync(runsDir).filter((f) => f.endsWith(".jsonl"));
|
|
100
|
+
for (const file of files) {
|
|
101
|
+
const filePath = path.join(runsDir, file);
|
|
102
|
+
try {
|
|
103
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
104
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
105
|
+
for (const line of lines) {
|
|
106
|
+
try {
|
|
107
|
+
allEntries.push(JSON.parse(line));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Skip malformed
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Skip unreadable files
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
console.error("[CRON-LOG] Error reading all run logs:", error);
|
|
121
|
+
}
|
|
122
|
+
// Sort newest first, limit
|
|
123
|
+
allEntries.sort((a, b) => (b.ts || "").localeCompare(a.ts || ""));
|
|
124
|
+
return allEntries.slice(0, limit);
|
|
125
|
+
}
|
|
126
|
+
// ═══════════════════════════════════════════════════════
|
|
127
|
+
// PRUNING (OpenClaw pattern)
|
|
128
|
+
// ═══════════════════════════════════════════════════════
|
|
129
|
+
const MAX_BYTES = 2 * 1024 * 1024; // 2MB
|
|
130
|
+
const KEEP_LINES = 2000;
|
|
131
|
+
async function pruneIfNeeded(logPath) {
|
|
132
|
+
try {
|
|
133
|
+
const stat = fs.statSync(logPath);
|
|
134
|
+
if (stat.size <= MAX_BYTES)
|
|
135
|
+
return;
|
|
136
|
+
const content = fs.readFileSync(logPath, "utf-8");
|
|
137
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
138
|
+
if (lines.length <= KEEP_LINES)
|
|
139
|
+
return;
|
|
140
|
+
// Keep only the most recent KEEP_LINES
|
|
141
|
+
const kept = lines.slice(-KEEP_LINES);
|
|
142
|
+
const newContent = kept.join("\n") + "\n";
|
|
143
|
+
// Atomic write
|
|
144
|
+
const tmpPath = logPath + `.tmp.${process.pid}`;
|
|
145
|
+
fs.writeFileSync(tmpPath, newContent, "utf-8");
|
|
146
|
+
fs.renameSync(tmpPath, logPath);
|
|
147
|
+
console.log(`[CRON-LOG] Pruned ${logPath}: ${lines.length} → ${kept.length} entries`);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Ignore pruning errors
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=local-run-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-run-log.js","sourceRoot":"","sources":["../../src/cron/local-run-log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Cron Store — File-based JSON cron job storage (OpenClaw pattern)
|
|
3
|
+
* Replaces Supabase cron_jobs table. Jobs stored in ~/.costar/cron/jobs.json.
|
|
4
|
+
* Part of Phase 5: Direct Client → Sandbox Architecture.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Atomic writes (temp file → rename)
|
|
8
|
+
* - Auto .bak on every save
|
|
9
|
+
* - OpenClaw-compatible job format
|
|
10
|
+
*/
|
|
11
|
+
export type CronJobLocal = {
|
|
12
|
+
id: string;
|
|
13
|
+
user_id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
instruction: string;
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
schedule_type: "cron" | "every" | "at";
|
|
18
|
+
/** Cron expression (for schedule_type=cron) */
|
|
19
|
+
cron_expression?: string;
|
|
20
|
+
/** Interval in ms (for schedule_type=every) */
|
|
21
|
+
schedule_value?: number;
|
|
22
|
+
/** ISO datetime (for schedule_type=at) */
|
|
23
|
+
schedule_at?: string;
|
|
24
|
+
/** IANA timezone */
|
|
25
|
+
timezone?: string;
|
|
26
|
+
/** Delete job after successful run (for one-shot 'at' jobs) */
|
|
27
|
+
delete_after_run?: boolean;
|
|
28
|
+
/** Current status */
|
|
29
|
+
status: "active" | "paused" | "completed" | "error";
|
|
30
|
+
/** Next scheduled run time */
|
|
31
|
+
next_run_at?: string;
|
|
32
|
+
/** Retry tracking */
|
|
33
|
+
retry_count: number;
|
|
34
|
+
max_retries: number;
|
|
35
|
+
/** Metadata */
|
|
36
|
+
created_at: string;
|
|
37
|
+
updated_at: string;
|
|
38
|
+
};
|
|
39
|
+
export type CronStoreFile = {
|
|
40
|
+
version: 1;
|
|
41
|
+
jobs: CronJobLocal[];
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Load jobs from disk. Returns empty store if file doesn't exist.
|
|
45
|
+
*/
|
|
46
|
+
export declare function loadJobs(): CronStoreFile;
|
|
47
|
+
/**
|
|
48
|
+
* Save jobs to disk with atomic write + backup.
|
|
49
|
+
*/
|
|
50
|
+
export declare function saveJobs(store: CronStoreFile): void;
|
|
51
|
+
/**
|
|
52
|
+
* Get all jobs for a user.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getAllJobs(userId: string, enabledOnly?: boolean): CronJobLocal[];
|
|
55
|
+
/**
|
|
56
|
+
* Get a job by ID.
|
|
57
|
+
*/
|
|
58
|
+
export declare function getJobById(jobId: string): CronJobLocal | null;
|
|
59
|
+
/**
|
|
60
|
+
* Get jobs that are due to run (next_run_at <= now).
|
|
61
|
+
*/
|
|
62
|
+
export declare function getDueJobs(userId: string): CronJobLocal[];
|
|
63
|
+
/**
|
|
64
|
+
* Create a new cron job.
|
|
65
|
+
*/
|
|
66
|
+
export declare function addJob(params: {
|
|
67
|
+
userId: string;
|
|
68
|
+
name: string;
|
|
69
|
+
instruction: string;
|
|
70
|
+
scheduleType: "cron" | "every" | "at";
|
|
71
|
+
cronExpression?: string;
|
|
72
|
+
scheduleValue?: number;
|
|
73
|
+
scheduleAt?: string;
|
|
74
|
+
timezone?: string;
|
|
75
|
+
deleteAfterRun?: boolean;
|
|
76
|
+
maxRetries?: number;
|
|
77
|
+
}): CronJobLocal;
|
|
78
|
+
/**
|
|
79
|
+
* Update an existing job.
|
|
80
|
+
*/
|
|
81
|
+
export declare function updateJob(jobId: string, updates: Partial<CronJobLocal>): CronJobLocal | null;
|
|
82
|
+
/**
|
|
83
|
+
* Remove a job.
|
|
84
|
+
*/
|
|
85
|
+
export declare function removeJob(jobId: string): boolean;
|
|
86
|
+
//# sourceMappingURL=local-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-store.d.ts","sourceRoot":"","sources":["../../src/cron/local-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACvC,+CAA+C;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,qBAAqB;IACrB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAC;IACpD,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,YAAY,EAAE,CAAC;CACtB,CAAC;AAkBF;;GAEG;AACH,wBAAgB,QAAQ,IAAI,aAAa,CAoCxC;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAwBnD;AAiBD;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,YAAY,EAAE,CAOvF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAG7D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAWzD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,YAAY,CA4Bf;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,GAAG,IAAI,CAmB5F;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAYhD"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Cron Store — File-based JSON cron job storage (OpenClaw pattern)
|
|
3
|
+
* Replaces Supabase cron_jobs table. Jobs stored in ~/.costar/cron/jobs.json.
|
|
4
|
+
* Part of Phase 5: Direct Client → Sandbox Architecture.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Atomic writes (temp file → rename)
|
|
8
|
+
* - Auto .bak on every save
|
|
9
|
+
* - OpenClaw-compatible job format
|
|
10
|
+
*/
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { resolveCoStarDir } from "../cli/paths.js";
|
|
14
|
+
// ═══════════════════════════════════════════════════════
|
|
15
|
+
// PATHS
|
|
16
|
+
// ═══════════════════════════════════════════════════════
|
|
17
|
+
function getCronDir() {
|
|
18
|
+
return path.join(resolveCoStarDir(), "cron");
|
|
19
|
+
}
|
|
20
|
+
function getStorePath() {
|
|
21
|
+
return path.join(getCronDir(), "jobs.json");
|
|
22
|
+
}
|
|
23
|
+
// ═══════════════════════════════════════════════════════
|
|
24
|
+
// FILE I/O (OpenClaw atomic write pattern)
|
|
25
|
+
// ═══════════════════════════════════════════════════════
|
|
26
|
+
/**
|
|
27
|
+
* Load jobs from disk. Returns empty store if file doesn't exist.
|
|
28
|
+
*/
|
|
29
|
+
export function loadJobs() {
|
|
30
|
+
const storePath = getStorePath();
|
|
31
|
+
try {
|
|
32
|
+
if (!fs.existsSync(storePath)) {
|
|
33
|
+
return { version: 1, jobs: [] };
|
|
34
|
+
}
|
|
35
|
+
const raw = fs.readFileSync(storePath, "utf-8");
|
|
36
|
+
const parsed = JSON.parse(raw);
|
|
37
|
+
// Validate
|
|
38
|
+
if (!parsed.version || !Array.isArray(parsed.jobs)) {
|
|
39
|
+
console.warn("[CRON-STORE] Invalid store file format, returning empty");
|
|
40
|
+
return { version: 1, jobs: [] };
|
|
41
|
+
}
|
|
42
|
+
return parsed;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error("[CRON-STORE] Error loading store:", error);
|
|
46
|
+
// Try .bak file
|
|
47
|
+
const bakPath = storePath + ".bak";
|
|
48
|
+
if (fs.existsSync(bakPath)) {
|
|
49
|
+
try {
|
|
50
|
+
const raw = fs.readFileSync(bakPath, "utf-8");
|
|
51
|
+
const parsed = JSON.parse(raw);
|
|
52
|
+
console.log("[CRON-STORE] Recovered from backup file");
|
|
53
|
+
return parsed;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
console.error("[CRON-STORE] Backup file also corrupted");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { version: 1, jobs: [] };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Save jobs to disk with atomic write + backup.
|
|
64
|
+
*/
|
|
65
|
+
export function saveJobs(store) {
|
|
66
|
+
const storePath = getStorePath();
|
|
67
|
+
const dir = getCronDir();
|
|
68
|
+
// Ensure directory exists
|
|
69
|
+
if (!fs.existsSync(dir)) {
|
|
70
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
const json = JSON.stringify(store, null, 2);
|
|
73
|
+
// Create .bak of current file
|
|
74
|
+
if (fs.existsSync(storePath)) {
|
|
75
|
+
try {
|
|
76
|
+
fs.copyFileSync(storePath, storePath + ".bak");
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Ignore backup errors
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Atomic write: temp file → rename
|
|
83
|
+
const tmpPath = storePath + `.tmp.${process.pid}`;
|
|
84
|
+
fs.writeFileSync(tmpPath, json, "utf-8");
|
|
85
|
+
fs.renameSync(tmpPath, storePath);
|
|
86
|
+
}
|
|
87
|
+
// ═══════════════════════════════════════════════════════
|
|
88
|
+
// CRUD OPERATIONS
|
|
89
|
+
// ═══════════════════════════════════════════════════════
|
|
90
|
+
/**
|
|
91
|
+
* Generate a random ID.
|
|
92
|
+
*/
|
|
93
|
+
function generateId() {
|
|
94
|
+
const bytes = new Uint8Array(16);
|
|
95
|
+
crypto.getRandomValues(bytes);
|
|
96
|
+
return Array.from(bytes)
|
|
97
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
98
|
+
.join("");
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get all jobs for a user.
|
|
102
|
+
*/
|
|
103
|
+
export function getAllJobs(userId, enabledOnly = false) {
|
|
104
|
+
const store = loadJobs();
|
|
105
|
+
let jobs = store.jobs.filter((j) => j.user_id === userId);
|
|
106
|
+
if (enabledOnly) {
|
|
107
|
+
jobs = jobs.filter((j) => j.enabled);
|
|
108
|
+
}
|
|
109
|
+
return jobs;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get a job by ID.
|
|
113
|
+
*/
|
|
114
|
+
export function getJobById(jobId) {
|
|
115
|
+
const store = loadJobs();
|
|
116
|
+
return store.jobs.find((j) => j.id === jobId) ?? null;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get jobs that are due to run (next_run_at <= now).
|
|
120
|
+
*/
|
|
121
|
+
export function getDueJobs(userId) {
|
|
122
|
+
const store = loadJobs();
|
|
123
|
+
const now = new Date().toISOString();
|
|
124
|
+
return store.jobs.filter((j) => {
|
|
125
|
+
if (j.user_id !== userId)
|
|
126
|
+
return false;
|
|
127
|
+
if (!j.enabled)
|
|
128
|
+
return false;
|
|
129
|
+
if (j.status !== "active")
|
|
130
|
+
return false;
|
|
131
|
+
if (!j.next_run_at)
|
|
132
|
+
return false;
|
|
133
|
+
return j.next_run_at <= now;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Create a new cron job.
|
|
138
|
+
*/
|
|
139
|
+
export function addJob(params) {
|
|
140
|
+
const store = loadJobs();
|
|
141
|
+
const now = new Date().toISOString();
|
|
142
|
+
const job = {
|
|
143
|
+
id: generateId(),
|
|
144
|
+
user_id: params.userId,
|
|
145
|
+
name: params.name,
|
|
146
|
+
instruction: params.instruction,
|
|
147
|
+
enabled: true,
|
|
148
|
+
schedule_type: params.scheduleType,
|
|
149
|
+
cron_expression: params.cronExpression,
|
|
150
|
+
schedule_value: params.scheduleValue,
|
|
151
|
+
schedule_at: params.scheduleAt,
|
|
152
|
+
timezone: params.timezone,
|
|
153
|
+
delete_after_run: params.deleteAfterRun,
|
|
154
|
+
status: "active",
|
|
155
|
+
retry_count: 0,
|
|
156
|
+
max_retries: params.maxRetries ?? 3,
|
|
157
|
+
created_at: now,
|
|
158
|
+
updated_at: now,
|
|
159
|
+
};
|
|
160
|
+
store.jobs.push(job);
|
|
161
|
+
saveJobs(store);
|
|
162
|
+
console.log(`[CRON-STORE] Created job: ${job.name} (${job.id})`);
|
|
163
|
+
return job;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Update an existing job.
|
|
167
|
+
*/
|
|
168
|
+
export function updateJob(jobId, updates) {
|
|
169
|
+
const store = loadJobs();
|
|
170
|
+
const idx = store.jobs.findIndex((j) => j.id === jobId);
|
|
171
|
+
if (idx === -1)
|
|
172
|
+
return null;
|
|
173
|
+
const job = store.jobs[idx];
|
|
174
|
+
const updated = {
|
|
175
|
+
...job,
|
|
176
|
+
...updates,
|
|
177
|
+
id: job.id, // Never overwrite ID
|
|
178
|
+
user_id: job.user_id, // Never overwrite user
|
|
179
|
+
updated_at: new Date().toISOString(),
|
|
180
|
+
};
|
|
181
|
+
store.jobs[idx] = updated;
|
|
182
|
+
saveJobs(store);
|
|
183
|
+
return updated;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Remove a job.
|
|
187
|
+
*/
|
|
188
|
+
export function removeJob(jobId) {
|
|
189
|
+
const store = loadJobs();
|
|
190
|
+
const before = store.jobs.length;
|
|
191
|
+
store.jobs = store.jobs.filter((j) => j.id !== jobId);
|
|
192
|
+
if (store.jobs.length < before) {
|
|
193
|
+
saveJobs(store);
|
|
194
|
+
console.log(`[CRON-STORE] Removed job: ${jobId}`);
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=local-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-store.js","sourceRoot":"","sources":["../../src/cron/local-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local SQLite Database — Activity, Assets, Projects
|
|
3
|
+
* Replaces Supabase for structured data. Runs on each sandbox.
|
|
4
|
+
* Part of Phase 4: Direct Client → Sandbox Architecture.
|
|
5
|
+
*
|
|
6
|
+
* Uses better-sqlite3 for synchronous, fast queries.
|
|
7
|
+
* Vector search via sqlite-vec for embeddings.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Initialize the database. Creates tables if they don't exist.
|
|
11
|
+
*/
|
|
12
|
+
export declare function initDatabase(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Close the database connection.
|
|
15
|
+
*/
|
|
16
|
+
export declare function closeDatabase(): void;
|
|
17
|
+
export type ActivityRow = {
|
|
18
|
+
id: string;
|
|
19
|
+
user_id: string;
|
|
20
|
+
request: string | null;
|
|
21
|
+
response: string | null;
|
|
22
|
+
status: string;
|
|
23
|
+
executor: string;
|
|
24
|
+
source: string | null;
|
|
25
|
+
tool_calls: number;
|
|
26
|
+
duration: number | null;
|
|
27
|
+
error: string | null;
|
|
28
|
+
keywords: string | null;
|
|
29
|
+
categories: string | null;
|
|
30
|
+
sandbox_id: string | null;
|
|
31
|
+
created_at: string;
|
|
32
|
+
updated_at: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function createActivity(params: {
|
|
35
|
+
userId: string;
|
|
36
|
+
request: string;
|
|
37
|
+
executor: "client" | "server";
|
|
38
|
+
source?: string;
|
|
39
|
+
sandboxId?: string;
|
|
40
|
+
embedding?: number[];
|
|
41
|
+
}): ActivityRow;
|
|
42
|
+
export declare function completeActivity(params: {
|
|
43
|
+
activityId: string;
|
|
44
|
+
response: string;
|
|
45
|
+
request?: string;
|
|
46
|
+
toolCalls?: number;
|
|
47
|
+
duration?: number;
|
|
48
|
+
keywords?: string;
|
|
49
|
+
categories?: string[];
|
|
50
|
+
embedding?: number[];
|
|
51
|
+
}): ActivityRow;
|
|
52
|
+
export declare function failActivity(params: {
|
|
53
|
+
activityId: string;
|
|
54
|
+
error: string;
|
|
55
|
+
duration?: number;
|
|
56
|
+
}): ActivityRow;
|
|
57
|
+
export declare function getActivityById(activityId: string): ActivityRow | null;
|
|
58
|
+
export declare function getRecentActivity(userId: string, limit?: number): ActivityRow[];
|
|
59
|
+
export declare function getActivityByStatus(userId: string, status: string, limit?: number): ActivityRow[];
|
|
60
|
+
export declare function getNewClientActivitySince(userId: string, afterTimestamp?: string | null, limit?: number): ActivityRow[];
|
|
61
|
+
export declare function searchActivityByKeywords(userId: string, query: string, limit?: number): ActivityRow[];
|
|
62
|
+
/**
|
|
63
|
+
* Semantic vector search via sqlite-vec.
|
|
64
|
+
* Falls back to empty results if sqlite-vec not loaded.
|
|
65
|
+
*/
|
|
66
|
+
export declare function searchActivityByEmbedding(embedding: number[], userId: string, limit?: number, threshold?: number): Array<ActivityRow & {
|
|
67
|
+
similarity: number;
|
|
68
|
+
}>;
|
|
69
|
+
export declare function getActivityStats(userId: string): {
|
|
70
|
+
total: number;
|
|
71
|
+
pending: number;
|
|
72
|
+
completed: number;
|
|
73
|
+
failed: number;
|
|
74
|
+
avgDuration?: number;
|
|
75
|
+
avgToolCalls?: number;
|
|
76
|
+
};
|
|
77
|
+
export type AssetRow = {
|
|
78
|
+
id: string;
|
|
79
|
+
user_id: string;
|
|
80
|
+
name: string | null;
|
|
81
|
+
type: string | null;
|
|
82
|
+
local_file_path: string | null;
|
|
83
|
+
remote_url: string | null;
|
|
84
|
+
description: string | null;
|
|
85
|
+
sandbox_id: string | null;
|
|
86
|
+
created_at: string;
|
|
87
|
+
updated_at: string;
|
|
88
|
+
};
|
|
89
|
+
export declare function upsertAsset(params: {
|
|
90
|
+
userId: string;
|
|
91
|
+
name?: string;
|
|
92
|
+
type?: string;
|
|
93
|
+
localFilePath?: string;
|
|
94
|
+
remoteUrl?: string;
|
|
95
|
+
description?: string;
|
|
96
|
+
sandboxId?: string;
|
|
97
|
+
}): AssetRow;
|
|
98
|
+
export declare function getAssets(userId: string, limit?: number): AssetRow[];
|
|
99
|
+
export declare function getAssetById(assetId: string): AssetRow | null;
|
|
100
|
+
export declare function linkAssetToActivity(params: {
|
|
101
|
+
requestId: string;
|
|
102
|
+
assetId: string;
|
|
103
|
+
action?: string;
|
|
104
|
+
}): void;
|
|
105
|
+
export type ProjectRow = {
|
|
106
|
+
id: string;
|
|
107
|
+
user_id: string;
|
|
108
|
+
name: string | null;
|
|
109
|
+
description: string | null;
|
|
110
|
+
sandbox_id: string | null;
|
|
111
|
+
project_path: string | null;
|
|
112
|
+
convo_summary: string | null;
|
|
113
|
+
is_active: number;
|
|
114
|
+
created_at: string;
|
|
115
|
+
updated_at: string;
|
|
116
|
+
};
|
|
117
|
+
export declare function upsertProject(params: {
|
|
118
|
+
userId: string;
|
|
119
|
+
name?: string;
|
|
120
|
+
description?: string;
|
|
121
|
+
sandboxId?: string;
|
|
122
|
+
projectPath?: string;
|
|
123
|
+
convoSummary?: string;
|
|
124
|
+
isActive?: boolean;
|
|
125
|
+
id?: string;
|
|
126
|
+
}): ProjectRow;
|
|
127
|
+
export declare function getProjects(userId: string, limit?: number): ProjectRow[];
|
|
128
|
+
export declare function getProjectById(projectId: string): ProjectRow | null;
|
|
129
|
+
export declare function linkProjectToActivity(params: {
|
|
130
|
+
requestId: string;
|
|
131
|
+
projectId: string;
|
|
132
|
+
action?: string;
|
|
133
|
+
}): void;
|
|
134
|
+
//# sourceMappingURL=local-database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-database.d.ts","sourceRoot":"","sources":["../../src/db/local-database.ts"],"names":[],"mappings":"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"}
|