agent-office 0.4.9 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +259 -228
  3. package/dist/commands/cron-requests.d.ts +7 -0
  4. package/dist/commands/cron-requests.d.ts.map +1 -0
  5. package/dist/commands/cron-requests.js +31 -0
  6. package/dist/commands/cron-requests.js.map +1 -0
  7. package/dist/commands/crons.d.ts +10 -0
  8. package/dist/commands/crons.d.ts.map +1 -0
  9. package/dist/commands/crons.js +45 -0
  10. package/dist/commands/crons.js.map +1 -0
  11. package/dist/commands/hello.d.ts +5 -0
  12. package/dist/commands/hello.d.ts.map +1 -0
  13. package/dist/commands/hello.js +4 -0
  14. package/dist/commands/hello.js.map +1 -0
  15. package/dist/commands/messages.d.ts +5 -0
  16. package/dist/commands/messages.d.ts.map +1 -0
  17. package/dist/commands/messages.js +18 -0
  18. package/dist/commands/messages.js.map +1 -0
  19. package/dist/commands/sessions.d.ts +13 -0
  20. package/dist/commands/sessions.d.ts.map +1 -0
  21. package/dist/commands/sessions.js +58 -0
  22. package/dist/commands/sessions.js.map +1 -0
  23. package/dist/commands/task-columns.d.ts +2 -0
  24. package/dist/commands/task-columns.d.ts.map +1 -0
  25. package/dist/commands/task-columns.js +13 -0
  26. package/dist/commands/task-columns.js.map +1 -0
  27. package/dist/commands/tasks.d.ts +11 -0
  28. package/dist/commands/tasks.d.ts.map +1 -0
  29. package/dist/commands/tasks.js +75 -0
  30. package/dist/commands/tasks.js.map +1 -0
  31. package/dist/config.test.d.ts +2 -0
  32. package/dist/config.test.d.ts.map +1 -0
  33. package/dist/config.test.js +50 -0
  34. package/dist/config.test.js.map +1 -0
  35. package/dist/db/index.d.ts +6 -70
  36. package/dist/db/index.d.ts.map +1 -0
  37. package/dist/db/index.js +4 -11
  38. package/dist/db/index.js.map +1 -0
  39. package/dist/db/mock-storage.d.ts +79 -0
  40. package/dist/db/mock-storage.d.ts.map +1 -0
  41. package/dist/db/mock-storage.js +381 -0
  42. package/dist/db/mock-storage.js.map +1 -0
  43. package/dist/db/mock-storage.test.d.ts +2 -0
  44. package/dist/db/mock-storage.test.d.ts.map +1 -0
  45. package/dist/db/mock-storage.test.js +234 -0
  46. package/dist/db/mock-storage.test.js.map +1 -0
  47. package/dist/db/postgresql-storage.d.ts +10 -8
  48. package/dist/db/postgresql-storage.d.ts.map +1 -0
  49. package/dist/db/postgresql-storage.js +76 -42
  50. package/dist/db/postgresql-storage.js.map +1 -0
  51. package/dist/db/sqlite-storage.d.ts +9 -8
  52. package/dist/db/sqlite-storage.d.ts.map +1 -0
  53. package/dist/db/sqlite-storage.js +75 -41
  54. package/dist/db/sqlite-storage.js.map +1 -0
  55. package/dist/db/storage-base.d.ts +7 -8
  56. package/dist/db/storage-base.d.ts.map +1 -0
  57. package/dist/db/storage-base.js +3 -2
  58. package/dist/db/storage-base.js.map +1 -0
  59. package/dist/db/storage.d.ts +12 -12
  60. package/dist/db/storage.d.ts.map +1 -0
  61. package/dist/db/storage.js +1 -0
  62. package/dist/db/storage.js.map +1 -0
  63. package/dist/db/types.d.ts +67 -0
  64. package/dist/db/types.d.ts.map +1 -0
  65. package/dist/db/types.js +2 -0
  66. package/dist/db/types.js.map +1 -0
  67. package/dist/index.d.ts +2 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +397 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/index.test.d.ts +2 -0
  72. package/dist/index.test.d.ts.map +1 -0
  73. package/dist/index.test.js +49 -0
  74. package/dist/index.test.js.map +1 -0
  75. package/dist/lib/output.d.ts +2 -0
  76. package/dist/lib/output.d.ts.map +1 -0
  77. package/dist/lib/output.js +8 -0
  78. package/dist/lib/output.js.map +1 -0
  79. package/dist/services/cron-service.constraints.test.d.ts +2 -0
  80. package/dist/services/cron-service.constraints.test.d.ts.map +1 -0
  81. package/dist/services/cron-service.constraints.test.js +90 -0
  82. package/dist/services/cron-service.constraints.test.js.map +1 -0
  83. package/dist/services/cron-service.d.ts +45 -0
  84. package/dist/services/cron-service.d.ts.map +1 -0
  85. package/dist/services/cron-service.js +157 -0
  86. package/dist/services/cron-service.js.map +1 -0
  87. package/dist/services/cron-service.test.d.ts +2 -0
  88. package/dist/services/cron-service.test.d.ts.map +1 -0
  89. package/dist/services/cron-service.test.js +280 -0
  90. package/dist/services/cron-service.test.js.map +1 -0
  91. package/dist/services/index.d.ts +5 -0
  92. package/dist/services/index.d.ts.map +1 -0
  93. package/dist/services/index.js +5 -0
  94. package/dist/services/index.js.map +1 -0
  95. package/dist/services/message-service.d.ts +16 -0
  96. package/dist/services/message-service.d.ts.map +1 -0
  97. package/dist/services/message-service.js +39 -0
  98. package/dist/services/message-service.js.map +1 -0
  99. package/dist/services/message-service.test.d.ts +2 -0
  100. package/dist/services/message-service.test.d.ts.map +1 -0
  101. package/dist/services/message-service.test.js +145 -0
  102. package/dist/services/message-service.test.js.map +1 -0
  103. package/dist/services/session-service.constraints.test.d.ts +2 -0
  104. package/dist/services/session-service.constraints.test.d.ts.map +1 -0
  105. package/dist/services/session-service.constraints.test.js +34 -0
  106. package/dist/services/session-service.constraints.test.js.map +1 -0
  107. package/dist/services/session-service.d.ts +27 -0
  108. package/dist/services/session-service.d.ts.map +1 -0
  109. package/dist/services/session-service.js +55 -0
  110. package/dist/services/session-service.js.map +1 -0
  111. package/dist/services/session-service.test.d.ts +2 -0
  112. package/dist/services/session-service.test.d.ts.map +1 -0
  113. package/dist/services/session-service.test.js +87 -0
  114. package/dist/services/session-service.test.js.map +1 -0
  115. package/dist/services/task-service.d.ts +25 -0
  116. package/dist/services/task-service.d.ts.map +1 -0
  117. package/dist/services/task-service.js +87 -0
  118. package/dist/services/task-service.js.map +1 -0
  119. package/dist/services/task-service.test.d.ts +2 -0
  120. package/dist/services/task-service.test.d.ts.map +1 -0
  121. package/dist/services/task-service.test.js +180 -0
  122. package/dist/services/task-service.test.js.map +1 -0
  123. package/package.json +40 -42
  124. package/dist/cli.d.ts +0 -2
  125. package/dist/cli.js +0 -317
  126. package/dist/commands/communicator.d.ts +0 -9
  127. package/dist/commands/communicator.js +0 -2231
  128. package/dist/commands/manage.d.ts +0 -5
  129. package/dist/commands/manage.js +0 -20
  130. package/dist/commands/notifier.d.ts +0 -11
  131. package/dist/commands/notifier.js +0 -100
  132. package/dist/commands/screensaver.d.ts +0 -8
  133. package/dist/commands/screensaver.js +0 -1280
  134. package/dist/commands/serve.d.ts +0 -13
  135. package/dist/commands/serve.js +0 -95
  136. package/dist/commands/task-board.d.ts +0 -29
  137. package/dist/commands/task-board.js +0 -251
  138. package/dist/commands/worker.d.ts +0 -16
  139. package/dist/commands/worker.js +0 -145
  140. package/dist/db/migrate.d.ts +0 -2
  141. package/dist/db/migrate.js +0 -3
  142. package/dist/lib/agentic-coding-server.d.ts +0 -66
  143. package/dist/lib/agentic-coding-server.js +0 -7
  144. package/dist/lib/notifier.d.ts +0 -18
  145. package/dist/lib/notifier.js +0 -15
  146. package/dist/lib/opencode-coding-server.d.ts +0 -11
  147. package/dist/lib/opencode-coding-server.js +0 -66
  148. package/dist/lib/pi-coding-server.d.ts +0 -20
  149. package/dist/lib/pi-coding-server.js +0 -162
  150. package/dist/manage/app.d.ts +0 -6
  151. package/dist/manage/app.js +0 -128
  152. package/dist/manage/components/AgentCode.d.ts +0 -8
  153. package/dist/manage/components/AgentCode.js +0 -73
  154. package/dist/manage/components/CreateSession.d.ts +0 -8
  155. package/dist/manage/components/CreateSession.js +0 -37
  156. package/dist/manage/components/CronList.d.ts +0 -9
  157. package/dist/manage/components/CronList.js +0 -321
  158. package/dist/manage/components/CronRequests.d.ts +0 -8
  159. package/dist/manage/components/CronRequests.js +0 -181
  160. package/dist/manage/components/DeleteSession.d.ts +0 -7
  161. package/dist/manage/components/DeleteSession.js +0 -55
  162. package/dist/manage/components/InjectText.d.ts +0 -8
  163. package/dist/manage/components/InjectText.js +0 -51
  164. package/dist/manage/components/ItemSelector.d.ts +0 -7
  165. package/dist/manage/components/ItemSelector.js +0 -20
  166. package/dist/manage/components/MenuSelect.d.ts +0 -13
  167. package/dist/manage/components/MenuSelect.js +0 -22
  168. package/dist/manage/components/MyMail.d.ts +0 -9
  169. package/dist/manage/components/MyMail.js +0 -143
  170. package/dist/manage/components/Profile.d.ts +0 -8
  171. package/dist/manage/components/Profile.js +0 -60
  172. package/dist/manage/components/ReadMail.d.ts +0 -8
  173. package/dist/manage/components/ReadMail.js +0 -110
  174. package/dist/manage/components/SendMessage.d.ts +0 -9
  175. package/dist/manage/components/SendMessage.js +0 -79
  176. package/dist/manage/components/SessionList.d.ts +0 -9
  177. package/dist/manage/components/SessionList.js +0 -608
  178. package/dist/manage/components/SessionSidebar.d.ts +0 -6
  179. package/dist/manage/components/SessionSidebar.js +0 -24
  180. package/dist/manage/components/TailMessages.d.ts +0 -8
  181. package/dist/manage/components/TailMessages.js +0 -126
  182. package/dist/manage/hooks/useApi.d.ts +0 -147
  183. package/dist/manage/hooks/useApi.js +0 -181
  184. package/dist/server/cron.d.ts +0 -25
  185. package/dist/server/cron.js +0 -107
  186. package/dist/server/index.d.ts +0 -4
  187. package/dist/server/index.js +0 -22
  188. package/dist/server/routes.d.ts +0 -13
  189. package/dist/server/routes.js +0 -1396
@@ -1,66 +0,0 @@
1
- /**
2
- * Concrete AgenticCodingServer backed by an OpenCode server
3
- * via the @opencode-ai/sdk.
4
- */
5
- import { createOpencodeClient } from "@opencode-ai/sdk";
6
- export class OpenCodeCodingServer {
7
- client;
8
- baseURL;
9
- constructor(opencodeUrl) {
10
- this.baseURL = opencodeUrl;
11
- this.client = createOpencodeClient({
12
- baseUrl: opencodeUrl,
13
- });
14
- }
15
- async createSession(title) {
16
- if (!this.client)
17
- throw new Error("Internal error: OpenCode client not initialized");
18
- const { data: session } = await this.client.session.create({ title });
19
- if (!session)
20
- throw new Error("OpenCode returned no session");
21
- return session.id;
22
- }
23
- async deleteSession(sessionID) {
24
- if (!this.client)
25
- throw new Error("Internal error: OpenCode client not initialized");
26
- await this.client.session.delete({ path: { id: sessionID } });
27
- }
28
- async sendMessage(sessionID, text, agent, system) {
29
- if (!this.client)
30
- throw new Error("Internal error: OpenCode client not initialized");
31
- await this.client.session.promptAsync({
32
- path: { id: sessionID },
33
- body: {
34
- parts: [{ type: "text", text }],
35
- agent,
36
- system,
37
- },
38
- });
39
- }
40
- async getMessages(sessionID, limit) {
41
- if (!this.client)
42
- throw new Error("Internal error: OpenCode client not initialized");
43
- const { data: messages } = await this.client.session.messages({
44
- path: { id: sessionID },
45
- ...(limit != null ? { query: { limit } } : {}),
46
- });
47
- return (messages ?? []).map((m) => ({
48
- id: m.info.id,
49
- role: m.info.role,
50
- parts: m.parts,
51
- }));
52
- }
53
- async getAgentModes() {
54
- if (!this.client)
55
- throw new Error("Internal error: OpenCode client not initialized");
56
- const { data: config } = await this.client.config.get();
57
- const agent = config?.agent ?? {};
58
- return Object.entries(agent)
59
- .filter(([, val]) => val != null)
60
- .map(([name, val]) => ({
61
- name,
62
- description: val.description ?? "",
63
- model: val.model ?? "",
64
- }));
65
- }
66
- }
@@ -1,20 +0,0 @@
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
- }
@@ -1,162 +0,0 @@
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
- }
@@ -1,6 +0,0 @@
1
- interface AppProps {
2
- serverUrl: string;
3
- password: string;
4
- }
5
- export declare function App({ serverUrl, password }: AppProps): import("react/jsx-runtime").JSX.Element;
6
- export {};
@@ -1,128 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect, useRef } from "react";
3
- import { Box, Text, useApp, useStdout, useInput } from "ink";
4
- import { Spinner } from "@inkjs/ui";
5
- import { useApi } from "./hooks/useApi.js";
6
- import { SessionList } from "./components/SessionList.js";
7
- import { SendMessage } from "./components/SendMessage.js";
8
- import { Profile } from "./components/Profile.js";
9
- import { MyMail } from "./components/MyMail.js";
10
- import { SessionSidebar } from "./components/SessionSidebar.js";
11
- import { MenuSelect } from "./components/MenuSelect.js";
12
- import { CronList } from "./components/CronList.js";
13
- import { CronRequests } from "./components/CronRequests.js";
14
- const MENU_OPTIONS = [
15
- { label: "Send message", value: "send-message" },
16
- { label: "My mail", value: "my-mail" },
17
- { label: "Coworkers", value: "list" },
18
- { label: "Cron jobs", value: "cron" },
19
- { label: "Cron requests", value: "cron-requests" },
20
- { label: "My profile", value: "profile" },
21
- { label: "Quit", value: "quit" },
22
- ];
23
- const SUB_SCREENS = ["list", "send-message", "my-mail", "profile", "cron", "cron-requests"];
24
- const FOOTER_HINTS = {
25
- connecting: "",
26
- "auth-error": "",
27
- menu: "↑↓ navigate · Enter select · q quit",
28
- list: "↑↓ navigate · c create · d delete · r reveal code · g regen · x revert · X revert-all · t tail · i inject · m mail · M memories · Esc back",
29
- "send-message": "Enter submit · Esc back to menu",
30
- "my-mail": "↑↓ select message · r reply · m mark read · a mark all read · s sent tab · Esc back",
31
- "profile": "Enter submit · Esc back to menu",
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",
34
- };
35
- function Header({ serverUrl, unreadCount }) {
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 })] })] }));
37
- }
38
- function Footer({ hint }) {
39
- return (_jsx(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, children: _jsx(Text, { dimColor: true, children: hint }) }));
40
- }
41
- export function App({ serverUrl, password }) {
42
- const { exit } = useApp();
43
- const { stdout } = useStdout();
44
- const { checkHealth, getConfig, getMailMessages } = useApi(serverUrl, password);
45
- const [screen, setScreen] = useState("connecting");
46
- const [termHeight, setTermHeight] = useState(stdout?.rows ?? 24);
47
- const [termWidth, setTermWidth] = useState(stdout?.columns ?? 80);
48
- const [unreadCount, setUnreadCount] = useState(0);
49
- const [replyTo, setReplyTo] = useState(null);
50
- const [subViewActive, setSubViewActive] = useState(false);
51
- const humanNameRef = useRef("Human");
52
- // Track terminal size
53
- useEffect(() => {
54
- const update = () => {
55
- setTermHeight(stdout?.rows ?? 24);
56
- setTermWidth(stdout?.columns ?? 80);
57
- };
58
- stdout?.on("resize", update);
59
- return () => { stdout?.off("resize", update); };
60
- }, [stdout]);
61
- // Health check on mount
62
- useEffect(() => {
63
- checkHealth().then((ok) => {
64
- setScreen(ok ? "menu" : "auth-error");
65
- });
66
- }, []);
67
- // Resolve human name once, then poll unread mail every 15s
68
- useEffect(() => {
69
- const fetchUnread = async () => {
70
- try {
71
- const msgs = await getMailMessages(humanNameRef.current, { unreadOnly: true });
72
- setUnreadCount(msgs.length);
73
- }
74
- catch {
75
- // ignore poll errors silently
76
- }
77
- };
78
- getConfig().then((cfg) => {
79
- humanNameRef.current = cfg.human_name ?? "Human";
80
- fetchUnread();
81
- }).catch(() => { });
82
- const timer = setInterval(fetchUnread, 15_000);
83
- return () => clearInterval(timer);
84
- }, []);
85
- useInput((input, key) => {
86
- if (key.escape && SUB_SCREENS.includes(screen) && !subViewActive) {
87
- setScreen("menu");
88
- }
89
- if (input === "q" && screen === "menu") {
90
- exit();
91
- }
92
- });
93
- const goBack = () => setScreen("menu");
94
- const handleMenuSelect = (value) => {
95
- if (value === "quit") {
96
- exit();
97
- return;
98
- }
99
- if (value === "my-mail")
100
- setUnreadCount(0);
101
- setScreen(value);
102
- };
103
- // content area = terminal height minus header (3 rows) and footer (3 rows)
104
- const contentHeight = Math.max(4, termHeight - 6);
105
- const renderContent = () => {
106
- switch (screen) {
107
- case "connecting":
108
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: `Connecting to ${serverUrl}...` }) }));
109
- case "auth-error":
110
- 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." })] }));
111
- case "menu":
112
- 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 })] }));
113
- case "list":
114
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(SessionList, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2, onSubViewChange: setSubViewActive }) }));
115
- case "send-message":
116
- 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 }) }));
117
- case "profile":
118
- return (_jsx(Box, { height: contentHeight, flexDirection: "column", paddingX: 2, paddingY: 1, children: _jsx(Profile, { serverUrl: serverUrl, password: password, onBack: goBack, contentHeight: contentHeight - 2 }) }));
119
- case "my-mail":
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"); } }) }));
121
- case "cron":
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 }) }));
125
- }
126
- };
127
- return (_jsxs(Box, { flexDirection: "column", width: termWidth, children: [_jsx(Header, { serverUrl: serverUrl, unreadCount: unreadCount }), renderContent(), screen !== "connecting" && (_jsx(Footer, { hint: FOOTER_HINTS[screen] }))] }));
128
- }
@@ -1,8 +0,0 @@
1
- interface AgentCodeProps {
2
- serverUrl: string;
3
- password: string;
4
- onBack: () => void;
5
- contentHeight: number;
6
- }
7
- export declare function AgentCode({ serverUrl, password, onBack, contentHeight }: AgentCodeProps): import("react/jsx-runtime").JSX.Element;
8
- export {};
@@ -1,73 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from "react";
3
- import { Box, Text, useInput } from "ink";
4
- import { Select, Spinner, ConfirmInput } from "@inkjs/ui";
5
- import { useApi, useAsyncState } from "../hooks/useApi.js";
6
- const MASKED = "••••••••-••••-••••-••••-••••••••••••";
7
- export function AgentCode({ serverUrl, password, onBack, contentHeight }) {
8
- const { listSessions, regenerateCode } = useApi(serverUrl, password);
9
- const { run: runList } = useAsyncState();
10
- const [sessions, setSessions] = useState([]);
11
- const [stage, setStage] = useState("loading-sessions");
12
- const [selected, setSelected] = useState(null);
13
- const [revealed, setRevealed] = useState(false);
14
- const [error, setError] = useState(null);
15
- useEffect(() => {
16
- runList(listSessions).then((rows) => {
17
- setSessions(rows ?? []);
18
- setStage("select");
19
- });
20
- }, []);
21
- // r = reveal/hide, g = trigger regenerate, when viewing
22
- useInput((input) => {
23
- if (stage !== "view")
24
- return;
25
- if (input === "r")
26
- setRevealed((v) => !v);
27
- if (input === "g")
28
- setStage("confirm-regen");
29
- });
30
- const handleSelect = (name) => {
31
- const session = sessions.find((s) => s.name === name) ?? null;
32
- setSelected(session);
33
- setRevealed(false);
34
- setStage("view");
35
- };
36
- const handleConfirmRegen = async (confirmed) => {
37
- if (!confirmed || !selected) {
38
- setStage("view");
39
- return;
40
- }
41
- setStage("regenerating");
42
- try {
43
- const updated = await regenerateCode(selected.name);
44
- setSelected(updated);
45
- setRevealed(true); // auto-reveal the new code
46
- setStage("view");
47
- }
48
- catch (err) {
49
- setError(err instanceof Error ? err.message : String(err));
50
- setStage("error");
51
- }
52
- };
53
- if (stage === "loading-sessions") {
54
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Loading sessions..." }) }));
55
- }
56
- if (stage === "regenerating") {
57
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Generating new agent code..." }) }));
58
- }
59
- if (stage === "error") {
60
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, children: "Agent Code" }), _jsxs(Text, { color: "red", children: ["Error: ", error] }), _jsx(Text, { dimColor: true, children: "Press Esc to go back" })] }));
61
- }
62
- if (stage === "select") {
63
- if (sessions.length === 0) {
64
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Text, { dimColor: true, children: "No sessions yet. Create one first." }) }));
65
- }
66
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, children: "Agent Code" }), _jsx(Text, { dimColor: true, children: "Select a session to view its agent code:" }), _jsx(Select, { options: sessions.map((s) => ({ label: s.name, value: s.name })), onChange: handleSelect })] }));
67
- }
68
- if (stage === "confirm-regen" && selected) {
69
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, children: "Agent Code" }), _jsxs(Text, { children: ["Regenerate code for ", _jsx(Text, { color: "cyan", bold: true, children: selected.name }), "?", " ", _jsx(Text, { dimColor: true, children: "The old code will stop working immediately." })] }), _jsx(ConfirmInput, { defaultChoice: "cancel", onConfirm: () => void handleConfirmRegen(true), onCancel: () => void handleConfirmRegen(false) })] }));
70
- }
71
- // stage === "view"
72
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { gap: 2, children: [_jsx(Text, { bold: true, children: "Agent Code" }), _jsx(Text, { color: "cyan", children: selected?.name })] }), _jsxs(Box, { gap: 2, marginY: 1, children: [_jsx(Text, { bold: true, children: "Code:" }), revealed ? (_jsx(Text, { color: "yellow", children: selected?.agent_code })) : (_jsx(Text, { dimColor: true, children: MASKED }))] }), _jsxs(Box, { gap: 2, children: [_jsx(Text, { dimColor: true, children: "Clock-in format:" }), _jsxs(Text, { dimColor: true, children: [revealed ? selected?.agent_code : MASKED, "@<server-url>"] })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, gap: 0, children: [_jsxs(Text, { dimColor: true, children: ["r ", revealed ? "hide" : "reveal", " code"] }), _jsx(Text, { dimColor: true, children: "g regenerate code" }), _jsx(Text, { dimColor: true, children: "Esc back to menu" })] })] }));
73
- }
@@ -1,8 +0,0 @@
1
- interface CreateSessionProps {
2
- serverUrl: string;
3
- password: string;
4
- agent: string;
5
- onBack: () => void;
6
- }
7
- export declare function CreateSession({ serverUrl, password, agent, onBack }: CreateSessionProps): import("react/jsx-runtime").JSX.Element;
8
- export {};
@@ -1,37 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
3
- import { Box, Text } from "ink";
4
- import { TextInput, Spinner } from "@inkjs/ui";
5
- import { useApi, useAsyncState } from "../hooks/useApi.js";
6
- export function CreateSession({ serverUrl, password, agent, onBack }) {
7
- const { createSession } = useApi(serverUrl, password);
8
- const { loading, error, run } = useAsyncState();
9
- const [created, setCreated] = useState(null);
10
- const [submitted, setSubmitted] = useState(false);
11
- // Auto-return to menu 1.5s after success or error
12
- useEffect(() => {
13
- if (created || error) {
14
- const timer = setTimeout(onBack, 1500);
15
- return () => clearTimeout(timer);
16
- }
17
- }, [created, error, onBack]);
18
- const handleSubmit = async (name) => {
19
- const trimmed = name.trim();
20
- if (!trimmed)
21
- return;
22
- setSubmitted(true);
23
- const result = await run(() => createSession(trimmed, agent));
24
- if (result)
25
- setCreated(result);
26
- };
27
- if (loading) {
28
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, children: "Create Session" }), _jsx(Spinner, { label: "Creating session on OpenCode..." })] }));
29
- }
30
- if (created) {
31
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, children: "Create Session" }), _jsx(Text, { color: "green", children: "Session created!" }), _jsxs(Box, { gap: 2, children: [_jsx(Text, { bold: true, children: "Name:" }), _jsx(Text, { color: "cyan", children: created.name })] }), _jsxs(Box, { gap: 2, children: [_jsx(Text, { bold: true, children: "OpenCode ID:" }), _jsx(Text, { dimColor: true, children: created.session_id })] }), _jsx(Text, { dimColor: true, children: "Returning to menu..." })] }));
32
- }
33
- if (error) {
34
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, children: "Create Session" }), _jsxs(Text, { color: "red", children: ["Error: ", error] }), _jsx(Text, { dimColor: true, children: "Returning to menu..." })] }));
35
- }
36
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, children: "Create Session" }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { children: "Name: " }), !submitted && (_jsx(TextInput, { placeholder: "e.g. john", onSubmit: handleSubmit }))] })] }));
37
- }
@@ -1,9 +0,0 @@
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 {};