agent-office 0.4.5 → 0.4.7

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.
@@ -361,6 +361,208 @@ export class AgentOfficeSqliteStorage extends AgentOfficeStorageBase {
361
361
  `);
362
362
  stmt.run(cronJobId, executedAt.toISOString(), success ? 1 : 0, errorMessage ?? null);
363
363
  }
364
+ // Cron Requests
365
+ async listCronRequests(filters) {
366
+ let query = `
367
+ SELECT id, name, session_name, schedule, timezone, message, status, requested_at, reviewed_at, reviewed_by, reviewer_notes
368
+ FROM cron_requests
369
+ WHERE 1=1
370
+ `;
371
+ const params = [];
372
+ if (filters?.status) {
373
+ query += ` AND status = ?`;
374
+ params.push(filters.status);
375
+ }
376
+ if (filters?.sessionName) {
377
+ query += ` AND session_name = ?`;
378
+ params.push(filters.sessionName);
379
+ }
380
+ query += ` ORDER BY requested_at DESC`;
381
+ const stmt = this.db.prepare(query);
382
+ const rows = stmt.all(...params);
383
+ return rows.map(row => ({
384
+ ...row,
385
+ requested_at: new Date(row.requested_at),
386
+ reviewed_at: row.reviewed_at ? new Date(row.reviewed_at) : null,
387
+ }));
388
+ }
389
+ async getCronRequestById(id) {
390
+ const stmt = this.db.prepare(`
391
+ SELECT id, name, session_name, schedule, timezone, message, status, requested_at, reviewed_at, reviewed_by, reviewer_notes
392
+ FROM cron_requests WHERE id = ?
393
+ `);
394
+ const row = stmt.get(id);
395
+ if (!row)
396
+ return null;
397
+ return {
398
+ ...row,
399
+ requested_at: new Date(row.requested_at),
400
+ reviewed_at: row.reviewed_at ? new Date(row.reviewed_at) : null,
401
+ };
402
+ }
403
+ async createCronRequest(name, sessionName, schedule, timezone, message) {
404
+ const stmt = this.db.prepare(`
405
+ INSERT INTO cron_requests (name, session_name, schedule, timezone, message)
406
+ VALUES (?, ?, ?, ?, ?)
407
+ RETURNING id, name, session_name, schedule, timezone, message, status, requested_at, reviewed_at, reviewed_by, reviewer_notes
408
+ `);
409
+ const row = stmt.get(name, sessionName, schedule, timezone, message);
410
+ return {
411
+ ...row,
412
+ requested_at: new Date(row.requested_at),
413
+ reviewed_at: row.reviewed_at ? new Date(row.reviewed_at) : null,
414
+ };
415
+ }
416
+ async updateCronRequestStatus(id, status, reviewedBy, reviewerNotes) {
417
+ const stmt = this.db.prepare(`
418
+ UPDATE cron_requests
419
+ SET status = ?, reviewed_at = ?, reviewed_by = ?, reviewer_notes = ?
420
+ WHERE id = ?
421
+ RETURNING id, name, session_name, schedule, timezone, message, status, requested_at, reviewed_at, reviewed_by, reviewer_notes
422
+ `);
423
+ const row = stmt.get(status, new Date().toISOString(), reviewedBy, reviewerNotes ?? null, id);
424
+ if (!row)
425
+ return null;
426
+ return {
427
+ ...row,
428
+ requested_at: new Date(row.requested_at),
429
+ reviewed_at: row.reviewed_at ? new Date(row.reviewed_at) : null,
430
+ };
431
+ }
432
+ // Tasks
433
+ async listTasks() {
434
+ const stmt = this.db.prepare(`
435
+ SELECT id, title, description, assignee, column_name, dependencies, created_at, updated_at
436
+ FROM tasks
437
+ ORDER BY created_at DESC
438
+ `);
439
+ const rows = stmt.all();
440
+ return rows.map(row => ({
441
+ id: row.id,
442
+ title: row.title,
443
+ description: row.description,
444
+ assignee: row.assignee,
445
+ column: row.column_name,
446
+ dependencies: JSON.parse(row.dependencies),
447
+ created_at: new Date(row.created_at),
448
+ updated_at: new Date(row.updated_at),
449
+ }));
450
+ }
451
+ async getTaskById(id) {
452
+ const stmt = this.db.prepare(`
453
+ SELECT id, title, description, assignee, column_name, dependencies, created_at, updated_at
454
+ FROM tasks WHERE id = ?
455
+ `);
456
+ const row = stmt.get(id);
457
+ if (!row)
458
+ return null;
459
+ return {
460
+ id: row.id,
461
+ title: row.title,
462
+ description: row.description,
463
+ assignee: row.assignee,
464
+ column: row.column_name,
465
+ dependencies: JSON.parse(row.dependencies),
466
+ created_at: new Date(row.created_at),
467
+ updated_at: new Date(row.updated_at),
468
+ };
469
+ }
470
+ async createTask(title, description, assignee, column, dependencies) {
471
+ const now = new Date().toISOString();
472
+ const stmt = this.db.prepare(`
473
+ INSERT INTO tasks (title, description, assignee, column_name, dependencies, created_at, updated_at)
474
+ VALUES (?, ?, ?, ?, ?, ?, ?)
475
+ RETURNING id, title, description, assignee, column_name, dependencies, created_at, updated_at
476
+ `);
477
+ const row = stmt.get(title, description, assignee, column, JSON.stringify(dependencies), now, now);
478
+ return {
479
+ id: row.id,
480
+ title: row.title,
481
+ description: row.description,
482
+ assignee: row.assignee,
483
+ column: row.column_name,
484
+ dependencies: JSON.parse(row.dependencies),
485
+ created_at: new Date(row.created_at),
486
+ updated_at: new Date(row.updated_at),
487
+ };
488
+ }
489
+ async updateTask(id, updates) {
490
+ const setParts = [];
491
+ const values = [];
492
+ if (updates.title !== undefined) {
493
+ setParts.push('title = ?');
494
+ values.push(updates.title);
495
+ }
496
+ if (updates.description !== undefined) {
497
+ setParts.push('description = ?');
498
+ values.push(updates.description);
499
+ }
500
+ if (updates.assignee !== undefined) {
501
+ setParts.push('assignee = ?');
502
+ values.push(updates.assignee);
503
+ }
504
+ if (updates.column !== undefined) {
505
+ setParts.push('column_name = ?');
506
+ values.push(updates.column);
507
+ }
508
+ if (updates.dependencies !== undefined) {
509
+ setParts.push('dependencies = ?');
510
+ values.push(JSON.stringify(updates.dependencies));
511
+ }
512
+ if (setParts.length === 0)
513
+ return this.getTaskById(id);
514
+ setParts.push('updated_at = ?');
515
+ values.push(new Date().toISOString());
516
+ const sql = `UPDATE tasks SET ${setParts.join(', ')} WHERE id = ? RETURNING id, title, description, assignee, column_name, dependencies, created_at, updated_at`;
517
+ values.push(id);
518
+ const stmt = this.db.prepare(sql);
519
+ const row = stmt.get(...values);
520
+ if (!row)
521
+ return null;
522
+ return {
523
+ id: row.id,
524
+ title: row.title,
525
+ description: row.description,
526
+ assignee: row.assignee,
527
+ column: row.column_name,
528
+ dependencies: JSON.parse(row.dependencies),
529
+ created_at: new Date(row.created_at),
530
+ updated_at: new Date(row.updated_at),
531
+ };
532
+ }
533
+ async deleteTask(id) {
534
+ const stmt = this.db.prepare(`DELETE FROM tasks WHERE id = ?`);
535
+ stmt.run(id);
536
+ }
537
+ async searchTasks(query, filters) {
538
+ let sql = `
539
+ SELECT id, title, description, assignee, column_name, dependencies, created_at, updated_at
540
+ FROM tasks
541
+ WHERE (title LIKE ? OR description LIKE ?)
542
+ `;
543
+ const params = [`${query}%`, `${query}%`];
544
+ if (filters?.assignee) {
545
+ sql += ` AND assignee = ?`;
546
+ params.push(filters.assignee);
547
+ }
548
+ if (filters?.column) {
549
+ sql += ` AND column_name = ?`;
550
+ params.push(filters.column);
551
+ }
552
+ sql += ` ORDER BY created_at DESC`;
553
+ const stmt = this.db.prepare(sql);
554
+ const rows = stmt.all(...params);
555
+ return rows.map(row => ({
556
+ id: row.id,
557
+ title: row.title,
558
+ description: row.description,
559
+ assignee: row.assignee,
560
+ column: row.column_name,
561
+ dependencies: JSON.parse(row.dependencies),
562
+ created_at: new Date(row.created_at),
563
+ updated_at: new Date(row.updated_at),
564
+ }));
565
+ }
364
566
  // Migrations
365
567
  async runMigrations() {
366
568
  const MIGRATIONS = [
@@ -488,6 +690,45 @@ export class AgentOfficeSqliteStorage extends AgentOfficeStorageBase {
488
690
  name: "add_notified_to_messages",
489
691
  sql: `
490
692
  ALTER TABLE messages ADD COLUMN notified INTEGER NOT NULL DEFAULT 0;
693
+ `,
694
+ },
695
+ {
696
+ version: 10,
697
+ name: "create_tasks_table",
698
+ sql: `
699
+ CREATE TABLE IF NOT EXISTS tasks (
700
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
701
+ title TEXT NOT NULL,
702
+ description TEXT NOT NULL,
703
+ assignee TEXT,
704
+ column_name TEXT NOT NULL,
705
+ dependencies TEXT NOT NULL DEFAULT '[]',
706
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
707
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
708
+ );
709
+ CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee);
710
+ CREATE INDEX IF NOT EXISTS idx_tasks_column ON tasks(column_name);
711
+ `,
712
+ },
713
+ {
714
+ version: 11,
715
+ name: "create_cron_requests_table",
716
+ sql: `
717
+ CREATE TABLE IF NOT EXISTS cron_requests (
718
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
719
+ name TEXT NOT NULL,
720
+ session_name TEXT NOT NULL REFERENCES sessions(name) ON DELETE CASCADE,
721
+ schedule TEXT NOT NULL,
722
+ timezone TEXT,
723
+ message TEXT NOT NULL,
724
+ status TEXT NOT NULL DEFAULT 'pending',
725
+ requested_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
726
+ reviewed_at DATETIME,
727
+ reviewed_by TEXT,
728
+ reviewer_notes TEXT
729
+ );
730
+ CREATE INDEX IF NOT EXISTS idx_cron_requests_session_name ON cron_requests(session_name);
731
+ CREATE INDEX IF NOT EXISTS idx_cron_requests_status ON cron_requests(status);
491
732
  `,
492
733
  },
493
734
  ];
@@ -1,4 +1,4 @@
1
- import type { AgentOfficeStorage, SessionRow, ConfigRow, MessageRow, CronJobRow, CronHistoryRow } from "./index.js";
1
+ import type { AgentOfficeStorage, SessionRow, ConfigRow, MessageRow, CronJobRow, CronHistoryRow, CronRequestRow, TaskRow } from "./index.js";
2
2
  import type { WatchListener, WatchState, SenderInfo } from "./storage.js";
3
3
  export { WatchListener, WatchState, SenderInfo };
4
4
  export declare abstract class AgentOfficeStorageBase implements AgentOfficeStorage {
@@ -44,6 +44,22 @@ export declare abstract class AgentOfficeStorageBase implements AgentOfficeStora
44
44
  abstract cronJobExistsForSession(name: string, sessionName: string): Promise<boolean>;
45
45
  abstract listCronHistory(cronJobId: number, limit: number): Promise<CronHistoryRow[]>;
46
46
  abstract createCronHistory(cronJobId: number, executedAt: Date, success: boolean, errorMessage?: string): Promise<void>;
47
+ abstract listCronRequests(filters?: {
48
+ status?: string;
49
+ sessionName?: string;
50
+ }): Promise<CronRequestRow[]>;
51
+ abstract getCronRequestById(id: number): Promise<CronRequestRow | null>;
52
+ abstract createCronRequest(name: string, sessionName: string, schedule: string, timezone: string | null, message: string): Promise<CronRequestRow>;
53
+ abstract updateCronRequestStatus(id: number, status: "approved" | "rejected", reviewedBy: string, reviewerNotes?: string): Promise<CronRequestRow | null>;
54
+ abstract listTasks(): Promise<TaskRow[]>;
55
+ abstract getTaskById(id: number): Promise<TaskRow | null>;
56
+ abstract createTask(title: string, description: string, assignee: string | null, column: string, dependencies: number[]): Promise<TaskRow>;
57
+ abstract updateTask(id: number, updates: Partial<Pick<TaskRow, 'title' | 'description' | 'assignee' | 'column' | 'dependencies'>>): Promise<TaskRow | null>;
58
+ abstract deleteTask(id: number): Promise<void>;
59
+ abstract searchTasks(query: string, filters?: {
60
+ assignee?: string;
61
+ column?: string;
62
+ }): Promise<TaskRow[]>;
47
63
  abstract runMigrations(): Promise<void>;
48
64
  abstract createMessageImpl(from: string, to: string, body: string): Promise<MessageRow>;
49
65
  /**
@@ -1,4 +1,4 @@
1
- import type { SessionRow, ConfigRow, MessageRow, CronJobRow, CronHistoryRow } from "./index.js";
1
+ import type { SessionRow, ConfigRow, MessageRow, CronJobRow, CronHistoryRow, CronRequestRow } from "./index.js";
2
2
  export interface SenderInfo {
3
3
  lastSent: string;
4
4
  }
@@ -54,5 +54,21 @@ export interface AgentOfficeStorage {
54
54
  cronJobExistsForSession(name: string, sessionName: string): Promise<boolean>;
55
55
  listCronHistory(cronJobId: number, limit: number): Promise<CronHistoryRow[]>;
56
56
  createCronHistory(cronJobId: number, executedAt: Date, success: boolean, errorMessage?: string): Promise<void>;
57
+ listCronRequests(filters?: {
58
+ status?: string;
59
+ sessionName?: string;
60
+ }): Promise<CronRequestRow[]>;
61
+ getCronRequestById(id: number): Promise<CronRequestRow | null>;
62
+ createCronRequest(name: string, sessionName: string, schedule: string, timezone: string | null, message: string): Promise<CronRequestRow>;
63
+ updateCronRequestStatus(id: number, status: "approved" | "rejected", reviewedBy: string, reviewerNotes?: string): Promise<CronRequestRow | null>;
64
+ listTasks(): Promise<import("./index.js").TaskRow[]>;
65
+ getTaskById(id: number): Promise<import("./index.js").TaskRow | null>;
66
+ createTask(title: string, description: string, assignee: string | null, column: string, dependencies: number[]): Promise<import("./index.js").TaskRow>;
67
+ updateTask(id: number, updates: Partial<Pick<import("./index.js").TaskRow, 'title' | 'description' | 'assignee' | 'column' | 'dependencies'>>): Promise<import("./index.js").TaskRow | null>;
68
+ deleteTask(id: number): Promise<void>;
69
+ searchTasks(query: string, filters?: {
70
+ assignee?: string;
71
+ column?: string;
72
+ }): Promise<import("./index.js").TaskRow[]>;
57
73
  runMigrations(): Promise<void>;
58
74
  }
@@ -0,0 +1,20 @@
1
+ import type { AgenticCodingServer, SessionMessage, AgentMode } from "./agentic-coding-server.js";
2
+ export declare class PiCodingServer implements AgenticCodingServer {
3
+ private sessions;
4
+ private authStorage;
5
+ private modelRegistry;
6
+ private selectedModel;
7
+ private vendor;
8
+ private modelName;
9
+ constructor(vendor: string, model: string, apiKey: string);
10
+ /** Shared creation logic (no duplication) */
11
+ private createSessionInternal;
12
+ /** Fallback helper: returns cached session OR creates new one with the exact provided ID */
13
+ private ensureSession;
14
+ createSession(_title: string): Promise<string>;
15
+ deleteSession(sessionID: string): Promise<void>;
16
+ sendMessage(sessionID: string, text: string, agent: string, system: string): Promise<void>;
17
+ getMessages(sessionID: string, limit?: number): Promise<SessionMessage[]>;
18
+ revertSession(sessionID: string, messageID: string): Promise<void>;
19
+ getAgentModes(): Promise<AgentMode[]>;
20
+ }
@@ -0,0 +1,162 @@
1
+ import { createAgentSession, SessionManager, AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
2
+ import { getModel } from "@mariozechner/pi-ai";
3
+ import crypto from "crypto";
4
+ import path from "path";
5
+ import fs from "fs/promises";
6
+ export class PiCodingServer {
7
+ sessions = new Map();
8
+ authStorage;
9
+ modelRegistry;
10
+ selectedModel;
11
+ vendor;
12
+ modelName;
13
+ constructor(vendor, model, apiKey) {
14
+ if (!vendor || !model || !apiKey?.trim()) {
15
+ throw new Error("vendor, model, and apiKey are all required and must not be empty");
16
+ }
17
+ this.vendor = vendor.trim().toLowerCase();
18
+ this.modelName = model.trim();
19
+ this.authStorage = AuthStorage.create();
20
+ this.authStorage.setRuntimeApiKey(this.vendor, apiKey);
21
+ this.modelRegistry = new ModelRegistry(this.authStorage);
22
+ this.selectedModel = getModel(this.vendor, this.modelName);
23
+ if (!this.selectedModel) {
24
+ throw new Error(`Model '${this.modelName}' not found for provider '${this.vendor}'. ` +
25
+ `Check spelling or run 'pi /model' in a terminal.`);
26
+ }
27
+ }
28
+ /** Shared creation logic (no duplication) */
29
+ async createSessionInternal(sessionID) {
30
+ const workspace = path.join(process.cwd(), "workspaces", sessionID);
31
+ await fs.mkdir(workspace, { recursive: true });
32
+ const sessionManager = SessionManager.create(workspace);
33
+ const { session } = await createAgentSession({
34
+ cwd: workspace,
35
+ model: this.selectedModel,
36
+ thinkingLevel: "medium",
37
+ sessionManager,
38
+ authStorage: this.authStorage,
39
+ modelRegistry: this.modelRegistry,
40
+ });
41
+ return session;
42
+ }
43
+ /** Fallback helper: returns cached session OR creates new one with the exact provided ID */
44
+ async ensureSession(sessionID) {
45
+ let session = this.sessions.get(sessionID);
46
+ if (!session) {
47
+ session = await this.createSessionInternal(sessionID);
48
+ this.sessions.set(sessionID, session);
49
+ }
50
+ return session;
51
+ }
52
+ async createSession(_title) {
53
+ const sessionID = crypto.randomUUID();
54
+ const session = await this.createSessionInternal(sessionID);
55
+ this.sessions.set(sessionID, session);
56
+ return sessionID;
57
+ }
58
+ async deleteSession(sessionID) {
59
+ const session = this.sessions.get(sessionID);
60
+ if (session) {
61
+ session.dispose?.();
62
+ this.sessions.delete(sessionID);
63
+ }
64
+ }
65
+ async sendMessage(sessionID, text, agent, system) {
66
+ const session = await this.ensureSession(sessionID);
67
+ const prefix = agent ? `[Message from coworker ${agent}]: ` : "";
68
+ const message = `${prefix}${text}`;
69
+ // Set the system prompt if provided
70
+ if (system && system.trim()) {
71
+ session.agent.setSystemPrompt(system);
72
+ }
73
+ // Create a promise that resolves when the agent finishes processing
74
+ const completionPromise = new Promise((resolve) => {
75
+ const unsubscribe = session.subscribe((event) => {
76
+ if (event.type === "agent_end") {
77
+ unsubscribe();
78
+ resolve();
79
+ }
80
+ });
81
+ // Timeout after 5 minutes in case agent never finishes
82
+ setTimeout(() => {
83
+ unsubscribe();
84
+ resolve();
85
+ }, 5 * 60 * 1000);
86
+ });
87
+ // Send the prompt
88
+ await session.prompt(message);
89
+ // Wait for processing to complete
90
+ await completionPromise;
91
+ }
92
+ async getMessages(sessionID, limit = 100) {
93
+ const session = await this.ensureSession(sessionID);
94
+ const msgs = session.messages || [];
95
+ return msgs.slice(-limit).map((m) => {
96
+ let role = "assistant";
97
+ let parts = [];
98
+ if (m.role === "user") {
99
+ role = "user";
100
+ // User message content can be string or array
101
+ if (typeof m.content === "string") {
102
+ parts = [{ type: "text", text: m.content }];
103
+ }
104
+ else if (Array.isArray(m.content)) {
105
+ parts = m.content.map((c) => {
106
+ if (c.type === "text")
107
+ return { type: "text", text: c.text };
108
+ return { type: c.type || "text", text: JSON.stringify(c) };
109
+ });
110
+ }
111
+ else {
112
+ parts = [{ type: "text", text: JSON.stringify(m.content) }];
113
+ }
114
+ }
115
+ else if (m.role === "assistant") {
116
+ role = "assistant";
117
+ // Assistant message content is always an array of TextContent | ThinkingContent | ToolCall
118
+ if (Array.isArray(m.content)) {
119
+ parts = m.content.map((c) => {
120
+ if (c.type === "text")
121
+ return { type: "text", text: c.text };
122
+ if (c.type === "thinking")
123
+ return { type: "thinking", text: c.thinking };
124
+ if (c.type === "toolCall")
125
+ return { type: "toolCall", text: `[Tool: ${c.name}]` };
126
+ return { type: c.type || "text", text: JSON.stringify(c) };
127
+ });
128
+ }
129
+ else if (typeof m.content === "string") {
130
+ parts = [{ type: "text", text: m.content }];
131
+ }
132
+ }
133
+ else {
134
+ // toolResult or other custom types
135
+ parts = [{ type: "text", text: JSON.stringify(m.content) }];
136
+ }
137
+ return {
138
+ id: m.id || m.entryId || crypto.randomUUID(),
139
+ role,
140
+ parts,
141
+ };
142
+ });
143
+ }
144
+ async revertSession(sessionID, messageID) {
145
+ const session = await this.ensureSession(sessionID);
146
+ try {
147
+ await session.navigateTree(messageID, { summarize: true });
148
+ }
149
+ catch (err) {
150
+ // Graceful: new/empty sessions can't revert → still succeed the call
151
+ }
152
+ }
153
+ async getAgentModes() {
154
+ return [
155
+ {
156
+ name: "coworker",
157
+ description: `${this.vendor.toUpperCase()} ${this.modelName} – fast agentic coding coworker`,
158
+ model: this.modelName,
159
+ },
160
+ ];
161
+ }
162
+ }
@@ -10,15 +10,17 @@ import { MyMail } from "./components/MyMail.js";
10
10
  import { SessionSidebar } from "./components/SessionSidebar.js";
11
11
  import { MenuSelect } from "./components/MenuSelect.js";
12
12
  import { CronList } from "./components/CronList.js";
13
+ import { CronRequests } from "./components/CronRequests.js";
13
14
  const MENU_OPTIONS = [
14
15
  { label: "Send message", value: "send-message" },
15
16
  { label: "My mail", value: "my-mail" },
16
17
  { label: "Coworkers", value: "list" },
17
18
  { label: "Cron jobs", value: "cron" },
19
+ { label: "Cron requests", value: "cron-requests" },
18
20
  { label: "My profile", value: "profile" },
19
21
  { label: "Quit", value: "quit" },
20
22
  ];
21
- const SUB_SCREENS = ["list", "send-message", "my-mail", "profile", "cron"];
23
+ const SUB_SCREENS = ["list", "send-message", "my-mail", "profile", "cron", "cron-requests"];
22
24
  const FOOTER_HINTS = {
23
25
  connecting: "",
24
26
  "auth-error": "",
@@ -28,6 +30,7 @@ const FOOTER_HINTS = {
28
30
  "my-mail": "↑↓ select message · r reply · m mark read · a mark all read · s sent tab · Esc back",
29
31
  "profile": "Enter submit · Esc back to menu",
30
32
  cron: "↑↓ navigate · c create · d delete · e enable/disable · h history · Esc back",
33
+ "cron-requests": "↑↓ navigate · a approve · r reject · v view · f filter · Esc back",
31
34
  };
32
35
  function Header({ serverUrl, unreadCount }) {
33
36
  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 })] })] }));
@@ -117,6 +120,8 @@ export function App({ serverUrl, password }) {
117
120
  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"); } }) }));
118
121
  case "cron":
119
122
  return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(CronList, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
123
+ case "cron-requests":
124
+ return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(CronRequests, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
120
125
  }
121
126
  };
122
127
  return (_jsxs(Box, { flexDirection: "column", width: termWidth, children: [_jsx(Header, { serverUrl: serverUrl, unreadCount: unreadCount }), renderContent(), screen !== "connecting" && (_jsx(Footer, { hint: FOOTER_HINTS[screen] }))] }));
@@ -0,0 +1,8 @@
1
+ interface CronRequestsProps {
2
+ serverUrl: string;
3
+ password: string;
4
+ onBack: () => void;
5
+ contentHeight: number;
6
+ }
7
+ export declare function CronRequests({ serverUrl, password, onBack, contentHeight }: CronRequestsProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};