agent-office 0.0.1 → 0.0.2

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 CHANGED
@@ -4,11 +4,11 @@ import { Command } from "commander";
4
4
  const program = new Command();
5
5
  program
6
6
  .name("agent-office")
7
- .description("Manage OpenCode sessions with named aliases")
7
+ .description("Manage OpenCode sessions with named aliases.\n\n HUMAN OPERATORS: use 'serve' and 'manage'.\n AGENTS: use 'worker' subcommands only.")
8
8
  .version("0.1.0");
9
9
  program
10
10
  .command("serve")
11
- .description("Start the agent-office HTTP server")
11
+ .description("[HUMAN ONLY] Start the agent-office HTTP server")
12
12
  .option("--database-url <url>", "PostgreSQL connection string", process.env.DATABASE_URL)
13
13
  .option("--opencode-url <url>", "OpenCode server URL", process.env.OPENCODE_URL ?? "http://localhost:4096")
14
14
  .option("--host <host>", "Host to bind to", "127.0.0.1")
@@ -20,7 +20,7 @@ program
20
20
  });
21
21
  program
22
22
  .command("manage")
23
- .description("Launch the interactive TUI to manage sessions")
23
+ .description("[HUMAN ONLY] Launch the interactive TUI to manage sessions")
24
24
  .argument("<url>", "URL of the agent-office server (e.g. http://localhost:7654)")
25
25
  .option("--password <password>", "REQUIRED. API password", process.env.AGENT_OFFICE_PASSWORD)
26
26
  .action(async (url, options) => {
@@ -56,4 +56,85 @@ workerCmd
56
56
  const { sendMessage } = await import("./commands/worker.js");
57
57
  await sendMessage(token, options.name, options.body);
58
58
  });
59
+ // ── Worker Cron Commands (nested) ────────────────────────────────────────────────
60
+ const cronCmd = workerCmd
61
+ .command("cron")
62
+ .description("Manage cron jobs");
63
+ cronCmd
64
+ .command("list")
65
+ .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
66
+ .description("List all your cron jobs")
67
+ .action(async (token) => {
68
+ const { listCrons } = await import("./commands/worker.js");
69
+ await listCrons(token);
70
+ });
71
+ cronCmd
72
+ .command("create")
73
+ .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
74
+ .description("Create a new cron job")
75
+ .requiredOption("--name <name>", "Cron job name")
76
+ .requiredOption("--schedule <schedule>", "Cron expression (e.g., '0 9 * * *' for daily at 9am)")
77
+ .requiredOption("--message <message>", "Message to inject when job fires")
78
+ .option("--timezone <timezone>", "IANA timezone (e.g., 'America/New_York')")
79
+ .action(async (token, options) => {
80
+ const { createCron } = await import("./commands/worker.js");
81
+ await createCron(token, options);
82
+ });
83
+ cronCmd
84
+ .command("delete")
85
+ .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
86
+ .argument("<id>", "Cron job ID")
87
+ .description("Delete a cron job")
88
+ .action(async (token, id) => {
89
+ const cronId = parseInt(id, 10);
90
+ if (isNaN(cronId)) {
91
+ console.error("Error: Invalid cron job ID");
92
+ process.exit(1);
93
+ }
94
+ const { deleteCron } = await import("./commands/worker.js");
95
+ await deleteCron(token, cronId);
96
+ });
97
+ cronCmd
98
+ .command("enable")
99
+ .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
100
+ .argument("<id>", "Cron job ID")
101
+ .description("Enable a cron job")
102
+ .action(async (token, id) => {
103
+ const cronId = parseInt(id, 10);
104
+ if (isNaN(cronId)) {
105
+ console.error("Error: Invalid cron job ID");
106
+ process.exit(1);
107
+ }
108
+ const { enableCron } = await import("./commands/worker.js");
109
+ await enableCron(token, cronId);
110
+ });
111
+ cronCmd
112
+ .command("disable")
113
+ .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
114
+ .argument("<id>", "Cron job ID")
115
+ .description("Disable a cron job")
116
+ .action(async (token, id) => {
117
+ const cronId = parseInt(id, 10);
118
+ if (isNaN(cronId)) {
119
+ console.error("Error: Invalid cron job ID");
120
+ process.exit(1);
121
+ }
122
+ const { disableCron } = await import("./commands/worker.js");
123
+ await disableCron(token, cronId);
124
+ });
125
+ cronCmd
126
+ .command("history")
127
+ .argument("<token>", "Agent token in the format <agent_code>@<server-url>")
128
+ .argument("<id>", "Cron job ID")
129
+ .description("View execution history for a cron job")
130
+ .option("--limit <limit>", "Maximum history entries (default 10)", "10")
131
+ .action(async (token, id, options) => {
132
+ const cronId = parseInt(id, 10);
133
+ if (isNaN(cronId)) {
134
+ console.error("Error: Invalid cron job ID");
135
+ process.exit(1);
136
+ }
137
+ const { cronHistory } = await import("./commands/worker.js");
138
+ await cronHistory(token, cronId);
139
+ });
59
140
  program.parse();
@@ -2,6 +2,7 @@ import { createDb } from "../db/index.js";
2
2
  import { runMigrations } from "../db/migrate.js";
3
3
  import { createOpencodeClient } from "../lib/opencode.js";
4
4
  import { createApp } from "../server/index.js";
5
+ import { CronScheduler } from "../server/cron.js";
5
6
  export async function serve(options) {
6
7
  const password = options.password;
7
8
  if (!password) {
@@ -35,8 +36,12 @@ export async function serve(options) {
35
36
  // Init OpenCode client
36
37
  const opencode = createOpencodeClient(options.opencodeUrl);
37
38
  const serverUrl = `http://${options.host}:${port}`;
39
+ // Create cron scheduler
40
+ const cronScheduler = new CronScheduler();
38
41
  // Create Express app
39
- const app = createApp(sql, opencode, password, serverUrl);
42
+ const app = createApp(sql, opencode, password, serverUrl, cronScheduler);
43
+ // Start cron scheduler
44
+ await cronScheduler.start(sql, opencode);
40
45
  // Start server
41
46
  const server = app.listen(port, options.host, () => {
42
47
  console.log(`agent-office server listening on ${serverUrl}`);
@@ -45,6 +50,7 @@ export async function serve(options) {
45
50
  const shutdown = async () => {
46
51
  console.log("\nShutting down...");
47
52
  server.close(async () => {
53
+ cronScheduler.stop();
48
54
  await sql.end();
49
55
  console.log("Goodbye.");
50
56
  process.exit(0);
@@ -1,3 +1,14 @@
1
1
  export declare function clockIn(token: string): Promise<void>;
2
2
  export declare function listCoworkers(token: string): Promise<void>;
3
3
  export declare function sendMessage(token: string, recipients: string[], body: string): Promise<void>;
4
+ export declare function listCrons(token: string): Promise<void>;
5
+ export declare function createCron(token: string, options: {
6
+ name: string;
7
+ schedule: string;
8
+ message: string;
9
+ timezone?: string;
10
+ }): Promise<void>;
11
+ export declare function deleteCron(token: string, cronId: number): Promise<void>;
12
+ export declare function enableCron(token: string, cronId: number): Promise<void>;
13
+ export declare function disableCron(token: string, cronId: number): Promise<void>;
14
+ export declare function cronHistory(token: string, cronId: number): Promise<void>;
@@ -116,3 +116,50 @@ export async function sendMessage(token, recipients, body) {
116
116
  const result = await postWorker(token, "/worker/send-message", { to: recipients, body });
117
117
  console.log(JSON.stringify(result, null, 2));
118
118
  }
119
+ export async function listCrons(token) {
120
+ const crons = await fetchWorker(token, "/worker/crons");
121
+ console.log(JSON.stringify(crons, null, 2));
122
+ }
123
+ export async function createCron(token, options) {
124
+ const cron = await postWorker(token, "/worker/crons", options);
125
+ console.log(JSON.stringify(cron, null, 2));
126
+ }
127
+ export async function deleteCron(token, cronId) {
128
+ const { agentCode, serverUrl } = parseToken(token);
129
+ const url = `${serverUrl}/worker/crons/${cronId}?code=${encodeURIComponent(agentCode)}`;
130
+ let res;
131
+ try {
132
+ res = await fetch(url, { method: "DELETE" });
133
+ }
134
+ catch (err) {
135
+ console.error(`Error: could not reach ${serverUrl}`);
136
+ console.error(err instanceof Error ? err.message : String(err));
137
+ process.exit(1);
138
+ }
139
+ let body;
140
+ try {
141
+ body = await res.json();
142
+ }
143
+ catch {
144
+ console.error(`Error: invalid response from server`);
145
+ process.exit(1);
146
+ }
147
+ if (!res.ok) {
148
+ const msg = body.error ?? `HTTP ${res.status}`;
149
+ console.error(`Error: ${msg}`);
150
+ process.exit(1);
151
+ }
152
+ console.log(JSON.stringify(body, null, 2));
153
+ }
154
+ export async function enableCron(token, cronId) {
155
+ const result = await fetchWorker(token, `/worker/crons/${cronId}/enable`);
156
+ console.log(JSON.stringify(result, null, 2));
157
+ }
158
+ export async function disableCron(token, cronId) {
159
+ const result = await fetchWorker(token, `/worker/crons/${cronId}/disable`);
160
+ console.log(JSON.stringify(result, null, 2));
161
+ }
162
+ export async function cronHistory(token, cronId) {
163
+ const history = await fetchWorker(token, `/worker/crons/${cronId}/history`);
164
+ console.log(JSON.stringify(history, null, 2));
165
+ }
@@ -6,6 +6,7 @@ export interface SessionRow {
6
6
  name: string;
7
7
  session_id: string;
8
8
  agent_code: string;
9
+ mode: string | null;
9
10
  created_at: Date;
10
11
  }
11
12
  export interface ConfigRow {
@@ -21,3 +22,21 @@ export interface MessageRow {
21
22
  injected: boolean;
22
23
  created_at: Date;
23
24
  }
25
+ export interface CronJobRow {
26
+ id: number;
27
+ name: string;
28
+ session_name: string;
29
+ schedule: string;
30
+ timezone: string | null;
31
+ message: string;
32
+ enabled: boolean;
33
+ created_at: Date;
34
+ last_run: Date | null;
35
+ }
36
+ export interface CronHistoryRow {
37
+ id: number;
38
+ cron_job_id: number;
39
+ executed_at: Date;
40
+ success: boolean;
41
+ error_message: string | null;
42
+ }
@@ -32,6 +32,13 @@ const MIGRATIONS = [
32
32
  INSERT INTO config (key, value) VALUES ('human_description', '') ON CONFLICT DO NOTHING;
33
33
  `,
34
34
  },
35
+ {
36
+ version: 5,
37
+ name: "add_mode_to_sessions",
38
+ sql: `
39
+ ALTER TABLE sessions ADD COLUMN IF NOT EXISTS mode VARCHAR(255) NULL;
40
+ `,
41
+ },
35
42
  {
36
43
  version: 4,
37
44
  name: "create_messages_table",
@@ -50,6 +57,35 @@ const MIGRATIONS = [
50
57
  CREATE INDEX IF NOT EXISTS idx_messages_read ON messages(read);
51
58
  `,
52
59
  },
60
+ {
61
+ version: 6,
62
+ name: "create_cron_tables",
63
+ sql: `
64
+ CREATE TABLE IF NOT EXISTS cron_jobs (
65
+ id SERIAL PRIMARY KEY,
66
+ name VARCHAR(255) NOT NULL,
67
+ session_name VARCHAR(255) NOT NULL REFERENCES sessions(name) ON DELETE CASCADE,
68
+ schedule TEXT NOT NULL,
69
+ timezone VARCHAR(100),
70
+ message TEXT NOT NULL,
71
+ enabled BOOLEAN NOT NULL DEFAULT TRUE,
72
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
73
+ last_run TIMESTAMPTZ
74
+ );
75
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_cron_jobs_name_session ON cron_jobs(name, session_name);
76
+ CREATE INDEX IF NOT EXISTS idx_cron_jobs_session_name ON cron_jobs(session_name);
77
+ CREATE INDEX IF NOT EXISTS idx_cron_jobs_enabled ON cron_jobs(enabled);
78
+
79
+ CREATE TABLE IF NOT EXISTS cron_history (
80
+ id SERIAL PRIMARY KEY,
81
+ cron_job_id INTEGER NOT NULL REFERENCES cron_jobs(id) ON DELETE CASCADE,
82
+ executed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
83
+ success BOOLEAN NOT NULL DEFAULT TRUE,
84
+ error_message TEXT
85
+ );
86
+ CREATE INDEX IF NOT EXISTS idx_cron_history_job_id ON cron_history(cron_job_id);
87
+ `,
88
+ },
53
89
  ];
54
90
  export async function runMigrations(sql) {
55
91
  await sql `
@@ -1,50 +1,36 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
2
+ import { useState, useEffect, useRef } from "react";
3
3
  import { Box, Text, useApp, useStdout, useInput } from "ink";
4
- import { Select, Spinner } from "@inkjs/ui";
4
+ import { Spinner } from "@inkjs/ui";
5
5
  import { useApi } from "./hooks/useApi.js";
6
6
  import { SessionList } from "./components/SessionList.js";
7
- import { CreateSession } from "./components/CreateSession.js";
8
- import { DeleteSession } from "./components/DeleteSession.js";
9
- import { TailMessages } from "./components/TailMessages.js";
10
- import { InjectText } from "./components/InjectText.js";
11
- import { AgentCode } from "./components/AgentCode.js";
12
- import { ReadMail } from "./components/ReadMail.js";
13
7
  import { SendMessage } from "./components/SendMessage.js";
14
8
  import { Profile } from "./components/Profile.js";
15
9
  import { MyMail } from "./components/MyMail.js";
16
10
  import { SessionSidebar } from "./components/SessionSidebar.js";
11
+ import { MenuSelect } from "./components/MenuSelect.js";
12
+ import { CronList } from "./components/CronList.js";
17
13
  const MENU_OPTIONS = [
18
14
  { label: "Send message", value: "send-message" },
19
15
  { label: "My mail", value: "my-mail" },
20
- { label: "List sessions", value: "list" },
21
- { label: "Create session", value: "create" },
22
- { label: "Delete session", value: "delete" },
23
- { label: "Tail messages", value: "tail" },
24
- { label: "Inject text", value: "inject" },
25
- { label: "Agent code", value: "agent-code" },
26
- { label: "Read mail", value: "read-mail" },
16
+ { label: "Coworkers", value: "list" },
17
+ { label: "Cron jobs", value: "cron" },
27
18
  { label: "My profile", value: "profile" },
28
19
  { label: "Quit", value: "quit" },
29
20
  ];
30
- const SUB_SCREENS = ["list", "create", "delete", "tail", "inject", "agent-code", "read-mail", "send-message", "my-mail", "profile"];
21
+ const SUB_SCREENS = ["list", "send-message", "my-mail", "profile", "cron"];
31
22
  const FOOTER_HINTS = {
32
23
  connecting: "",
33
24
  "auth-error": "",
34
25
  menu: "↑↓ navigate · Enter select · q quit",
35
- list: "↑↓ navigate · r reveal/hide agent code · Esc back to menu",
36
- create: "Enter submit · Esc back to menu",
37
- delete: "↑↓ navigate · Enter select · Esc back to menu",
38
- tail: "↑↓ scroll · Esc back to menu",
39
- inject: "Enter submit · Esc back to menu",
40
- "agent-code": "r reveal/hide · g regenerate · Esc back to menu",
41
- "read-mail": "r received · s sent · Esc back to menu",
26
+ list: "↑↓ navigate · c create · d delete · r reveal code · g regen · t tail · i inject · m mail · Esc back",
42
27
  "send-message": "Enter submit · Esc back to menu",
43
- "my-mail": "r received · s sent · Esc back to menu",
28
+ "my-mail": "↑↓ select message · r reply · m mark read · a mark all read · s sent tab · Esc back",
44
29
  "profile": "Enter submit · Esc back to menu",
30
+ cron: "↑↓ navigate · c create · d delete · e enable/disable · h history · Esc back",
45
31
  };
46
- function Header({ serverUrl }) {
47
- return (_jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, justifyContent: "space-between", children: [_jsx(Text, { bold: true, color: "cyan", children: "agent-office" }), _jsx(Text, { dimColor: true, children: serverUrl })] }));
32
+ function Header({ serverUrl, unreadCount }) {
33
+ return (_jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, justifyContent: "space-between", children: [_jsx(Text, { bold: true, color: "cyan", children: "agent-office" }), _jsxs(Box, { gap: 2, children: [unreadCount > 0 && (_jsxs(Text, { color: "yellow", bold: true, children: ["\u2709 ", unreadCount, " unread"] })), _jsx(Text, { dimColor: true, children: serverUrl })] })] }));
48
34
  }
49
35
  function Footer({ hint }) {
50
36
  return (_jsx(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, children: _jsx(Text, { dimColor: true, children: hint }) }));
@@ -52,10 +38,13 @@ function Footer({ hint }) {
52
38
  export function App({ serverUrl, password }) {
53
39
  const { exit } = useApp();
54
40
  const { stdout } = useStdout();
55
- const { checkHealth } = useApi(serverUrl, password);
41
+ const { checkHealth, getConfig, getMailMessages } = useApi(serverUrl, password);
56
42
  const [screen, setScreen] = useState("connecting");
57
43
  const [termHeight, setTermHeight] = useState(stdout?.rows ?? 24);
58
44
  const [termWidth, setTermWidth] = useState(stdout?.columns ?? 80);
45
+ const [unreadCount, setUnreadCount] = useState(0);
46
+ const [replyTo, setReplyTo] = useState(null);
47
+ const humanNameRef = useRef("Human");
59
48
  // Track terminal size
60
49
  useEffect(() => {
61
50
  const update = () => {
@@ -71,6 +60,24 @@ export function App({ serverUrl, password }) {
71
60
  setScreen(ok ? "menu" : "auth-error");
72
61
  });
73
62
  }, []);
63
+ // Resolve human name once, then poll unread mail every 15s
64
+ useEffect(() => {
65
+ const fetchUnread = async () => {
66
+ try {
67
+ const msgs = await getMailMessages(humanNameRef.current, { unreadOnly: true });
68
+ setUnreadCount(msgs.length);
69
+ }
70
+ catch {
71
+ // ignore poll errors silently
72
+ }
73
+ };
74
+ getConfig().then((cfg) => {
75
+ humanNameRef.current = cfg.human_name ?? "Human";
76
+ fetchUnread();
77
+ }).catch(() => { });
78
+ const timer = setInterval(fetchUnread, 15_000);
79
+ return () => clearInterval(timer);
80
+ }, []);
74
81
  useInput((input, key) => {
75
82
  if (key.escape && SUB_SCREENS.includes(screen)) {
76
83
  setScreen("menu");
@@ -85,6 +92,8 @@ export function App({ serverUrl, password }) {
85
92
  exit();
86
93
  return;
87
94
  }
95
+ if (value === "my-mail")
96
+ setUnreadCount(0);
88
97
  setScreen(value);
89
98
  };
90
99
  // content area = terminal height minus header (3 rows) and footer (3 rows)
@@ -96,28 +105,18 @@ export function App({ serverUrl, password }) {
96
105
  case "auth-error":
97
106
  return (_jsxs(Box, { height: contentHeight, flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 1, children: [_jsx(Text, { color: "red", bold: true, children: "Connection failed" }), _jsxs(Text, { children: ["Could not reach ", _jsx(Text, { color: "cyan", children: serverUrl })] }), _jsx(Text, { dimColor: true, children: "Check that agent-office serve is running and your --password is correct." })] }));
98
107
  case "menu":
99
- return (_jsxs(Box, { height: contentHeight, flexDirection: "row", flexGrow: 1, children: [_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, flexGrow: 1, children: [_jsx(Text, { bold: true, children: "Main Menu" }), _jsx(Select, { options: MENU_OPTIONS, onChange: handleMenuSelect })] }), _jsx(SessionSidebar, { serverUrl: serverUrl, password: password })] }));
108
+ return (_jsxs(Box, { height: contentHeight, flexDirection: "row", flexGrow: 1, children: [_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, flexGrow: 1, children: [_jsx(Text, { bold: true, children: "Main Menu" }), _jsx(MenuSelect, { options: MENU_OPTIONS, onChange: handleMenuSelect, badges: unreadCount > 0 ? { "my-mail": `(${unreadCount})` } : {} })] }), _jsx(SessionSidebar, { serverUrl: serverUrl, password: password })] }));
100
109
  case "list":
101
110
  return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(SessionList, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
102
- case "create":
103
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(CreateSession, { serverUrl: serverUrl, password: password, onBack: goBack }) }));
104
- case "delete":
105
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(DeleteSession, { serverUrl: serverUrl, password: password, onBack: goBack }) }));
106
- case "tail":
107
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(TailMessages, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
108
- case "inject":
109
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(InjectText, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
110
- case "agent-code":
111
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(AgentCode, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
112
- case "read-mail":
113
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(ReadMail, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
114
111
  case "send-message":
115
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(SendMessage, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
112
+ return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(SendMessage, { serverUrl: serverUrl, password: password, onBack: () => { setReplyTo(null); goBack(); }, contentHeight: contentHeight - 2, initialRecipient: replyTo ?? undefined }) }));
116
113
  case "profile":
117
114
  return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(Profile, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
118
115
  case "my-mail":
119
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(MyMail, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
116
+ return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(MyMail, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2, onReply: (name) => { setReplyTo(name); setScreen("send-message"); } }) }));
117
+ case "cron":
118
+ return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(CronList, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
120
119
  }
121
120
  };
122
- return (_jsxs(Box, { flexDirection: "column", width: termWidth, children: [_jsx(Header, { serverUrl: serverUrl }), renderContent(), screen !== "connecting" && (_jsx(Footer, { hint: FOOTER_HINTS[screen] }))] }));
121
+ return (_jsxs(Box, { flexDirection: "column", width: termWidth, children: [_jsx(Header, { serverUrl: serverUrl, unreadCount: unreadCount }), renderContent(), screen !== "connecting" && (_jsx(Footer, { hint: FOOTER_HINTS[screen] }))] }));
123
122
  }
@@ -0,0 +1,9 @@
1
+ interface CronListProps {
2
+ serverUrl: string;
3
+ password: string;
4
+ onBack: () => void;
5
+ contentHeight: number;
6
+ sessionName?: string | null;
7
+ }
8
+ export declare function CronList({ serverUrl, password, onBack, contentHeight, sessionName: initialSessionName }: CronListProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};