agent-office 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.
- package/dist/cli.js +104 -3
- package/dist/commands/serve.js +7 -1
- package/dist/commands/worker.d.ts +12 -0
- package/dist/commands/worker.js +51 -0
- package/dist/db/index.d.ts +20 -0
- package/dist/db/migrate.js +43 -0
- package/dist/manage/app.js +42 -43
- package/dist/manage/components/CronList.d.ts +9 -0
- package/dist/manage/components/CronList.js +310 -0
- package/dist/manage/components/ItemSelector.d.ts +7 -0
- package/dist/manage/components/ItemSelector.js +20 -0
- package/dist/manage/components/MenuSelect.d.ts +13 -0
- package/dist/manage/components/MenuSelect.js +22 -0
- package/dist/manage/components/MyMail.d.ts +2 -1
- package/dist/manage/components/MyMail.js +107 -34
- package/dist/manage/components/ReadMail.js +3 -3
- package/dist/manage/components/SendMessage.d.ts +2 -1
- package/dist/manage/components/SendMessage.js +9 -6
- package/dist/manage/components/SessionList.js +472 -31
- package/dist/manage/components/SessionSidebar.js +7 -1
- package/dist/manage/components/TailMessages.js +54 -5
- package/dist/manage/hooks/useApi.d.ts +54 -3
- package/dist/manage/hooks/useApi.js +38 -2
- package/dist/server/cron.d.ts +24 -0
- package/dist/server/cron.js +121 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +2 -2
- package/dist/server/routes.d.ts +2 -1
- package/dist/server/routes.js +916 -42
- package/package.json +3 -1
|
@@ -61,12 +61,61 @@ export function TailMessages({ serverUrl, password, onBack, contentHeight }) {
|
|
|
61
61
|
const messageLines = [];
|
|
62
62
|
for (const msg of messages) {
|
|
63
63
|
for (const part of msg.parts) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
if (part.type === "text" && part.text) {
|
|
65
|
+
// Split multiline text into individual lines for rendering
|
|
66
|
+
const lines = part.text.split("\n");
|
|
67
|
+
for (let i = 0; i < lines.length; i++) {
|
|
68
|
+
messageLines.push({ role: msg.role, text: i === 0 ? lines[i] : ` ${lines[i]}` });
|
|
69
|
+
}
|
|
70
|
+
messageLines.push({ role: msg.role, text: "" }); // blank line between parts
|
|
71
|
+
}
|
|
72
|
+
else if (part.type === "tool") {
|
|
73
|
+
// Tool part has: tool (string), input, output
|
|
74
|
+
const toolName = typeof part.tool === "string" ? part.tool : String(part.tool ?? "unknown");
|
|
75
|
+
messageLines.push({ role: msg.role, text: "" }); // Blank line before tool
|
|
76
|
+
messageLines.push({ role: msg.role, text: `▶ Tool: ${toolName}` });
|
|
77
|
+
if (part.input !== undefined) {
|
|
78
|
+
// Try to pretty-print input if it's an object
|
|
79
|
+
let inputStr;
|
|
80
|
+
if (typeof part.input === "object" && part.input !== null) {
|
|
81
|
+
inputStr = JSON.stringify(part.input, null, 2);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
inputStr = String(part.input);
|
|
85
|
+
}
|
|
86
|
+
const lines = inputStr.split("\n");
|
|
87
|
+
for (let i = 0; i < Math.min(lines.length, 15); i++) {
|
|
88
|
+
messageLines.push({ role: msg.role, text: ` ${lines[i]}` });
|
|
89
|
+
}
|
|
90
|
+
if (lines.length > 15) {
|
|
91
|
+
messageLines.push({ role: msg.role, text: ` [...] (${lines.length - 15} more lines)` });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (part.output !== undefined) {
|
|
95
|
+
// Show output type and preview
|
|
96
|
+
let outputPreview;
|
|
97
|
+
if (typeof part.output === "object" && part.output !== null) {
|
|
98
|
+
outputPreview = JSON.stringify(part.output, null, 2).slice(0, 200);
|
|
99
|
+
}
|
|
100
|
+
else if (typeof part.output === "string") {
|
|
101
|
+
outputPreview = part.output.slice(0, 200);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
outputPreview = String(part.output).slice(0, 200);
|
|
105
|
+
}
|
|
106
|
+
const outputType = typeof part.output === "object" ? "object" : typeof part.output;
|
|
107
|
+
messageLines.push({ role: msg.role, text: ` ${"—".repeat(40)}` });
|
|
108
|
+
messageLines.push({ role: msg.role, text: ` Result (${outputType}):` });
|
|
109
|
+
const lines = outputPreview.split("\n");
|
|
110
|
+
for (let i = 0; i < Math.min(lines.length, 5); i++) {
|
|
111
|
+
messageLines.push({ role: msg.role, text: ` ${lines[i]}` });
|
|
112
|
+
}
|
|
113
|
+
if (lines.length > 5 || outputPreview.length >= 200) {
|
|
114
|
+
messageLines.push({ role: msg.role, text: " [...]" });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
messageLines.push({ role: msg.role, text: "" }); // Blank line after tool
|
|
68
118
|
}
|
|
69
|
-
messageLines.push({ role: msg.role, text: "" }); // blank line between parts
|
|
70
119
|
}
|
|
71
120
|
}
|
|
72
121
|
const viewHeight = contentHeight - 3; // header + separator
|
|
@@ -3,11 +3,22 @@ export interface Session {
|
|
|
3
3
|
name: string;
|
|
4
4
|
session_id: string;
|
|
5
5
|
agent_code: string;
|
|
6
|
+
mode: string | null;
|
|
7
|
+
status: string | null;
|
|
6
8
|
created_at: string;
|
|
7
9
|
}
|
|
10
|
+
export interface AppMode {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
model: string;
|
|
14
|
+
}
|
|
8
15
|
export interface MessagePart {
|
|
9
|
-
type: "text";
|
|
10
|
-
text
|
|
16
|
+
type: "text" | "tool" | "tool-output";
|
|
17
|
+
text?: string;
|
|
18
|
+
tool?: string;
|
|
19
|
+
input?: unknown;
|
|
20
|
+
output?: unknown;
|
|
21
|
+
data?: unknown;
|
|
11
22
|
}
|
|
12
23
|
export interface SessionMessage {
|
|
13
24
|
role: "user" | "assistant";
|
|
@@ -25,9 +36,28 @@ export interface MailMessage {
|
|
|
25
36
|
export interface Config {
|
|
26
37
|
[key: string]: string;
|
|
27
38
|
}
|
|
39
|
+
export interface CronJob {
|
|
40
|
+
id: number;
|
|
41
|
+
name: string;
|
|
42
|
+
session_name: string;
|
|
43
|
+
schedule: string;
|
|
44
|
+
timezone: string | null;
|
|
45
|
+
message: string;
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
created_at: string;
|
|
48
|
+
last_run: string | null;
|
|
49
|
+
next_run: string | null;
|
|
50
|
+
}
|
|
51
|
+
export interface CronHistoryEntry {
|
|
52
|
+
id: number;
|
|
53
|
+
cron_job_id: number;
|
|
54
|
+
executed_at: string;
|
|
55
|
+
success: boolean;
|
|
56
|
+
error_message: string | null;
|
|
57
|
+
}
|
|
28
58
|
export declare function useApi(serverUrl: string, password: string): {
|
|
29
59
|
listSessions: () => Promise<Session[]>;
|
|
30
|
-
createSession: (name: string) => Promise<Session>;
|
|
60
|
+
createSession: (name: string, mode?: string) => Promise<Session>;
|
|
31
61
|
deleteSession: (name: string) => Promise<void>;
|
|
32
62
|
checkHealth: () => Promise<boolean>;
|
|
33
63
|
getMessages: (name: string, limit?: number) => Promise<SessionMessage[]>;
|
|
@@ -35,6 +65,10 @@ export declare function useApi(serverUrl: string, password: string): {
|
|
|
35
65
|
ok: boolean;
|
|
36
66
|
messageID: string;
|
|
37
67
|
}>;
|
|
68
|
+
revertToStart: (name: string) => Promise<{
|
|
69
|
+
ok: boolean;
|
|
70
|
+
messageID: string;
|
|
71
|
+
}>;
|
|
38
72
|
regenerateCode: (name: string) => Promise<Session>;
|
|
39
73
|
getConfig: () => Promise<Config>;
|
|
40
74
|
setConfig: (key: string, value: string) => Promise<{
|
|
@@ -55,6 +89,23 @@ export declare function useApi(serverUrl: string, password: string): {
|
|
|
55
89
|
}>;
|
|
56
90
|
}>;
|
|
57
91
|
markMessageRead: (id: number) => Promise<MailMessage>;
|
|
92
|
+
getModes: () => Promise<AppMode[]>;
|
|
93
|
+
listCrons: (sessionName?: string) => Promise<CronJob[]>;
|
|
94
|
+
createCron: (data: {
|
|
95
|
+
name: string;
|
|
96
|
+
session_name: string;
|
|
97
|
+
schedule: string;
|
|
98
|
+
message: string;
|
|
99
|
+
timezone?: string;
|
|
100
|
+
}) => Promise<CronJob>;
|
|
101
|
+
deleteCron: (id: number) => Promise<{
|
|
102
|
+
deleted: boolean;
|
|
103
|
+
id: number;
|
|
104
|
+
name: string;
|
|
105
|
+
}>;
|
|
106
|
+
enableCron: (id: number) => Promise<CronJob>;
|
|
107
|
+
disableCron: (id: number) => Promise<CronJob>;
|
|
108
|
+
getCronHistory: (id: number, limit?: number) => Promise<CronHistoryEntry[]>;
|
|
58
109
|
};
|
|
59
110
|
export declare function useAsyncState<T>(): {
|
|
60
111
|
run: (fn: () => Promise<T>) => Promise<T | null>;
|
|
@@ -28,12 +28,15 @@ export function useApi(serverUrl, password) {
|
|
|
28
28
|
const listSessions = useCallback(async () => {
|
|
29
29
|
return apiFetch(`${base}/sessions`, password);
|
|
30
30
|
}, [base, password]);
|
|
31
|
-
const createSession = useCallback(async (name) => {
|
|
31
|
+
const createSession = useCallback(async (name, mode) => {
|
|
32
32
|
return apiFetch(`${base}/sessions`, password, {
|
|
33
33
|
method: "POST",
|
|
34
|
-
body: JSON.stringify({ name }),
|
|
34
|
+
body: JSON.stringify({ name, ...(mode ? { mode } : {}) }),
|
|
35
35
|
});
|
|
36
36
|
}, [base, password]);
|
|
37
|
+
const getModes = useCallback(async () => {
|
|
38
|
+
return apiFetch(`${base}/modes`, password);
|
|
39
|
+
}, [base, password]);
|
|
37
40
|
const deleteSession = useCallback(async (name) => {
|
|
38
41
|
await apiFetch(`${base}/sessions/${encodeURIComponent(name)}`, password, {
|
|
39
42
|
method: "DELETE",
|
|
@@ -54,6 +57,9 @@ export function useApi(serverUrl, password) {
|
|
|
54
57
|
const injectText = useCallback(async (name, text) => {
|
|
55
58
|
return apiFetch(`${base}/sessions/${encodeURIComponent(name)}/inject`, password, { method: "POST", body: JSON.stringify({ text }) });
|
|
56
59
|
}, [base, password]);
|
|
60
|
+
const revertToStart = useCallback(async (name) => {
|
|
61
|
+
return apiFetch(`${base}/sessions/${encodeURIComponent(name)}/revert-to-start`, password, { method: "POST" });
|
|
62
|
+
}, [base, password]);
|
|
57
63
|
const regenerateCode = useCallback(async (name) => {
|
|
58
64
|
return apiFetch(`${base}/sessions/${encodeURIComponent(name)}/regenerate-code`, password, { method: "POST" });
|
|
59
65
|
}, [base, password]);
|
|
@@ -78,6 +84,28 @@ export function useApi(serverUrl, password) {
|
|
|
78
84
|
const markMessageRead = useCallback(async (id) => {
|
|
79
85
|
return apiFetch(`${base}/messages/${id}/read`, password, { method: "POST" });
|
|
80
86
|
}, [base, password]);
|
|
87
|
+
const listCrons = useCallback(async (sessionName) => {
|
|
88
|
+
const params = sessionName ? `?session_name=${encodeURIComponent(sessionName)}` : "";
|
|
89
|
+
return apiFetch(`${base}/crons${params}`, password);
|
|
90
|
+
}, [base, password]);
|
|
91
|
+
const createCron = useCallback(async (data) => {
|
|
92
|
+
return apiFetch(`${base}/crons`, password, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
body: JSON.stringify(data),
|
|
95
|
+
});
|
|
96
|
+
}, [base, password]);
|
|
97
|
+
const deleteCron = useCallback(async (id) => {
|
|
98
|
+
return apiFetch(`${base}/crons/${id}`, password, { method: "DELETE" });
|
|
99
|
+
}, [base, password]);
|
|
100
|
+
const enableCron = useCallback(async (id) => {
|
|
101
|
+
return apiFetch(`${base}/crons/${id}/enable`, password, { method: "POST" });
|
|
102
|
+
}, [base, password]);
|
|
103
|
+
const disableCron = useCallback(async (id) => {
|
|
104
|
+
return apiFetch(`${base}/crons/${id}/disable`, password, { method: "POST" });
|
|
105
|
+
}, [base, password]);
|
|
106
|
+
const getCronHistory = useCallback(async (id, limit = 10) => {
|
|
107
|
+
return apiFetch(`${base}/crons/${id}/history?limit=${limit}`, password);
|
|
108
|
+
}, [base, password]);
|
|
81
109
|
return {
|
|
82
110
|
listSessions,
|
|
83
111
|
createSession,
|
|
@@ -85,12 +113,20 @@ export function useApi(serverUrl, password) {
|
|
|
85
113
|
checkHealth,
|
|
86
114
|
getMessages,
|
|
87
115
|
injectText,
|
|
116
|
+
revertToStart,
|
|
88
117
|
regenerateCode,
|
|
89
118
|
getConfig,
|
|
90
119
|
setConfig,
|
|
91
120
|
getMailMessages,
|
|
92
121
|
sendMailMessage,
|
|
93
122
|
markMessageRead,
|
|
123
|
+
getModes,
|
|
124
|
+
listCrons,
|
|
125
|
+
createCron,
|
|
126
|
+
deleteCron,
|
|
127
|
+
enableCron,
|
|
128
|
+
disableCron,
|
|
129
|
+
getCronHistory,
|
|
94
130
|
};
|
|
95
131
|
}
|
|
96
132
|
export function useAsyncState() {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Sql } from "../db/index.js";
|
|
2
|
+
import type { OpencodeClient } from "../lib/opencode.js";
|
|
3
|
+
import type { CronJobRow } from "../db/index.js";
|
|
4
|
+
interface CronSchedulerOptions {
|
|
5
|
+
onJobExecuted?: (jobId: number, success: boolean, error?: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare class CronScheduler {
|
|
8
|
+
private options;
|
|
9
|
+
private activeJobs;
|
|
10
|
+
private sql;
|
|
11
|
+
private opencode;
|
|
12
|
+
private started;
|
|
13
|
+
constructor(options?: CronSchedulerOptions);
|
|
14
|
+
start(sql: Sql, opencode: OpencodeClient): Promise<void>;
|
|
15
|
+
stop(): void;
|
|
16
|
+
private addJob;
|
|
17
|
+
private executeJob;
|
|
18
|
+
addCronJob(job: CronJobRow): void;
|
|
19
|
+
removeCronJob(jobId: number): void;
|
|
20
|
+
enableCronJob(job: CronJobRow): void;
|
|
21
|
+
disableCronJob(jobId: number): void;
|
|
22
|
+
isTracking(jobId: number): boolean;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Cron } from "croner";
|
|
2
|
+
const CRON_INJECTION_BLURB = [
|
|
3
|
+
``,
|
|
4
|
+
`---`,
|
|
5
|
+
`You have a scheduled cron job trigger. Please review the injected message above`,
|
|
6
|
+
`and take appropriate action.`,
|
|
7
|
+
].join("\n");
|
|
8
|
+
export class CronScheduler {
|
|
9
|
+
options;
|
|
10
|
+
activeJobs = new Map();
|
|
11
|
+
sql = null;
|
|
12
|
+
opencode = null;
|
|
13
|
+
started = false;
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
async start(sql, opencode) {
|
|
18
|
+
if (this.started)
|
|
19
|
+
return;
|
|
20
|
+
this.sql = sql;
|
|
21
|
+
this.opencode = opencode;
|
|
22
|
+
const rows = await sql `
|
|
23
|
+
SELECT id, name, schedule, timezone, message, session_name
|
|
24
|
+
FROM cron_jobs
|
|
25
|
+
WHERE enabled = TRUE
|
|
26
|
+
`;
|
|
27
|
+
for (const job of rows) {
|
|
28
|
+
this.addJob(job);
|
|
29
|
+
}
|
|
30
|
+
this.started = true;
|
|
31
|
+
console.log(`Cron scheduler started: tracking ${rows.length} active jobs`);
|
|
32
|
+
}
|
|
33
|
+
stop() {
|
|
34
|
+
for (const cronInst of this.activeJobs.values()) {
|
|
35
|
+
cronInst.cron.stop();
|
|
36
|
+
}
|
|
37
|
+
this.activeJobs.clear();
|
|
38
|
+
this.started = false;
|
|
39
|
+
console.log("Cron scheduler stopped");
|
|
40
|
+
}
|
|
41
|
+
addJob(job) {
|
|
42
|
+
if (!this.opencode || !this.sql)
|
|
43
|
+
return;
|
|
44
|
+
const options = {
|
|
45
|
+
protect: true,
|
|
46
|
+
name: job.name,
|
|
47
|
+
};
|
|
48
|
+
if (job.timezone) {
|
|
49
|
+
options.timezone = job.timezone;
|
|
50
|
+
}
|
|
51
|
+
const cron = new Cron(job.schedule, options, async () => {
|
|
52
|
+
await this.executeJob(job);
|
|
53
|
+
});
|
|
54
|
+
this.activeJobs.set(job.id, { cron, job });
|
|
55
|
+
}
|
|
56
|
+
async executeJob(job) {
|
|
57
|
+
if (!this.sql || !this.opencode)
|
|
58
|
+
return;
|
|
59
|
+
const executedAt = new Date();
|
|
60
|
+
try {
|
|
61
|
+
const [session] = await this.sql `
|
|
62
|
+
SELECT session_id FROM sessions WHERE name = ${job.session_name}
|
|
63
|
+
`;
|
|
64
|
+
if (!session) {
|
|
65
|
+
throw new Error(`Session "${job.session_name}" not found`);
|
|
66
|
+
}
|
|
67
|
+
const providers = await this.opencode.app.providers();
|
|
68
|
+
const defaultEntry = Object.entries(providers.default)[0];
|
|
69
|
+
if (!defaultEntry) {
|
|
70
|
+
throw new Error("No default model configured");
|
|
71
|
+
}
|
|
72
|
+
const injectText = `[Cron Job "${job.name}" — ${executedAt.toISOString()}]\n${job.message}${CRON_INJECTION_BLURB}`;
|
|
73
|
+
await this.opencode.session.chat(session.session_id, {
|
|
74
|
+
modelID: defaultEntry[0],
|
|
75
|
+
providerID: defaultEntry[1],
|
|
76
|
+
parts: [{ type: "text", text: injectText }],
|
|
77
|
+
});
|
|
78
|
+
await this.sql `
|
|
79
|
+
UPDATE cron_jobs SET last_run = ${executedAt} WHERE id = ${job.id}
|
|
80
|
+
`;
|
|
81
|
+
await this.sql `
|
|
82
|
+
INSERT INTO cron_history (cron_job_id, executed_at, success)
|
|
83
|
+
VALUES (${job.id}, ${executedAt}, TRUE)
|
|
84
|
+
`;
|
|
85
|
+
this.options.onJobExecuted?.(job.id, true);
|
|
86
|
+
console.log(`Cron job "${job.name}" executed successfully`);
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
90
|
+
await this.sql `
|
|
91
|
+
INSERT INTO cron_history (cron_job_id, executed_at, success, error_message)
|
|
92
|
+
VALUES (${job.id}, ${executedAt}, FALSE, ${errorMessage})
|
|
93
|
+
`;
|
|
94
|
+
this.options.onJobExecuted?.(job.id, false, errorMessage);
|
|
95
|
+
console.error(`Cron job "${job.name}" failed:`, errorMessage);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
addCronJob(job) {
|
|
99
|
+
if (job.enabled) {
|
|
100
|
+
this.addJob(job);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
removeCronJob(jobId) {
|
|
104
|
+
const cronInst = this.activeJobs.get(jobId);
|
|
105
|
+
if (cronInst) {
|
|
106
|
+
cronInst.cron.stop();
|
|
107
|
+
this.activeJobs.delete(jobId);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
enableCronJob(job) {
|
|
111
|
+
if (!this.activeJobs.has(job.id) && job.enabled) {
|
|
112
|
+
this.addJob(job);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
disableCronJob(jobId) {
|
|
116
|
+
this.removeCronJob(jobId);
|
|
117
|
+
}
|
|
118
|
+
isTracking(jobId) {
|
|
119
|
+
return this.activeJobs.has(jobId);
|
|
120
|
+
}
|
|
121
|
+
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { Sql } from "../db/index.js";
|
|
2
2
|
import type { OpencodeClient } from "../lib/opencode.js";
|
|
3
|
-
|
|
3
|
+
import { CronScheduler } from "./cron.js";
|
|
4
|
+
export declare function createApp(sql: Sql, opencode: OpencodeClient, password: string, serverUrl: string, cronScheduler: CronScheduler): import("express-serve-static-core").Express;
|
package/dist/server/index.js
CHANGED
|
@@ -10,13 +10,13 @@ function authMiddleware(password) {
|
|
|
10
10
|
next();
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
export function createApp(sql, opencode, password, serverUrl) {
|
|
13
|
+
export function createApp(sql, opencode, password, serverUrl, cronScheduler) {
|
|
14
14
|
const app = express();
|
|
15
15
|
app.use(express.json());
|
|
16
16
|
// Worker routes are unauthenticated — mounted before auth middleware
|
|
17
17
|
app.use("/", createWorkerRouter(sql, opencode, serverUrl));
|
|
18
18
|
// Everything else requires Bearer auth
|
|
19
19
|
app.use(authMiddleware(password));
|
|
20
|
-
app.use("/", createRouter(sql, opencode, serverUrl));
|
|
20
|
+
app.use("/", createRouter(sql, opencode, serverUrl, cronScheduler));
|
|
21
21
|
return app;
|
|
22
22
|
}
|
package/dist/server/routes.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Router } from "express";
|
|
2
2
|
import type { Sql } from "../db/index.js";
|
|
3
3
|
import type { OpencodeClient } from "../lib/opencode.js";
|
|
4
|
-
|
|
4
|
+
import { CronScheduler } from "./cron.js";
|
|
5
|
+
export declare function createRouter(sql: Sql, opencode: OpencodeClient, serverUrl: string, scheduler: CronScheduler): Router;
|
|
5
6
|
export declare function createWorkerRouter(sql: Sql, opencode: OpencodeClient, serverUrl: string): Router;
|