agent-office 0.5.0 → 0.6.1

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 +41 -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 -2232
  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,608 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState, useCallback, useMemo } from "react";
3
- import { Box, Text, useInput } from "ink";
4
- import { TextInput, Spinner, ConfirmInput } from "@inkjs/ui";
5
- import { useApi, useAsyncState } from "../hooks/useApi.js";
6
- const MASKED_CODE = "••••••••-••••-••••-••••-••••••••••••";
7
- function TailView({ serverUrl, password, sessionName, contentHeight, onClose }) {
8
- const { getMessages } = useApi(serverUrl, password);
9
- const [messages, setMessages] = useState([]);
10
- const [loading, setLoading] = useState(true);
11
- const [error, setError] = useState(null);
12
- const [scrollOffset, setScrollOffset] = useState(0);
13
- useEffect(() => {
14
- getMessages(sessionName, 50)
15
- .then((msgs) => { setMessages(msgs); setLoading(false); })
16
- .catch((err) => { setError(err instanceof Error ? err.message : String(err)); setLoading(false); });
17
- }, [sessionName]);
18
- const messageLines = useMemo(() => {
19
- const lines = [];
20
- for (const msg of messages) {
21
- for (const part of msg.parts) {
22
- if (part.type === "text" && part.text) {
23
- const textLines = part.text.split("\n");
24
- for (let i = 0; i < textLines.length; i++) {
25
- lines.push({ role: msg.role, text: i === 0 ? textLines[i] : ` ${textLines[i]}` });
26
- }
27
- lines.push({ role: msg.role, text: "" });
28
- }
29
- else if (part.type === "tool") {
30
- const toolName = typeof part.tool === "string" ? part.tool : String(part.tool ?? "unknown");
31
- lines.push({ role: msg.role, text: "" });
32
- lines.push({ role: msg.role, text: `▶ Tool: ${toolName}` });
33
- if (part.input !== undefined) {
34
- let inputStr;
35
- if (typeof part.input === "object" && part.input !== null) {
36
- inputStr = JSON.stringify(part.input, null, 2);
37
- }
38
- else {
39
- inputStr = String(part.input);
40
- }
41
- const inputLines = inputStr.split("\n");
42
- for (let i = 0; i < Math.min(inputLines.length, 15); i++) {
43
- lines.push({ role: msg.role, text: ` ${inputLines[i]}` });
44
- }
45
- if (inputLines.length > 15) {
46
- lines.push({ role: msg.role, text: ` [...] (${inputLines.length - 15} more lines)` });
47
- }
48
- }
49
- if (part.output !== undefined) {
50
- let outputPreview;
51
- if (typeof part.output === "object" && part.output !== null) {
52
- outputPreview = JSON.stringify(part.output, null, 2).slice(0, 200);
53
- }
54
- else if (typeof part.output === "string") {
55
- outputPreview = part.output.slice(0, 200);
56
- }
57
- else {
58
- outputPreview = String(part.output).slice(0, 200);
59
- }
60
- const outputType = typeof part.output === "object" ? "object" : typeof part.output;
61
- lines.push({ role: msg.role, text: ` ${"—".repeat(40)}` });
62
- lines.push({ role: msg.role, text: ` Result (${outputType}):` });
63
- const outputLines = outputPreview.split("\n");
64
- for (let i = 0; i < Math.min(outputLines.length, 5); i++) {
65
- lines.push({ role: msg.role, text: ` ${outputLines[i]}` });
66
- }
67
- if (outputLines.length > 5 || outputPreview.length >= 200) {
68
- lines.push({ role: msg.role, text: " [...]" });
69
- }
70
- }
71
- lines.push({ role: msg.role, text: "" });
72
- }
73
- }
74
- }
75
- return lines;
76
- }, [messages]);
77
- const viewHeight = contentHeight - 3;
78
- const maxOffset = Math.max(0, messageLines.length - viewHeight);
79
- useInput((_input, key) => {
80
- if (key.escape) {
81
- onClose();
82
- return;
83
- }
84
- if (key.upArrow)
85
- setScrollOffset((o) => Math.max(0, o - 1));
86
- if (key.downArrow)
87
- setScrollOffset((o) => Math.min(maxOffset, o + 1));
88
- if (key.home)
89
- setScrollOffset(0);
90
- if (key.end)
91
- setScrollOffset(maxOffset);
92
- });
93
- if (loading) {
94
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: `Fetching messages for "${sessionName}"...` }) }));
95
- }
96
- if (error) {
97
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Text, { bold: true, children: ["Messages \u2014 ", _jsx(Text, { color: "cyan", children: sessionName })] }), _jsxs(Text, { color: "red", children: ["Error: ", error] }), _jsx(Text, { dimColor: true, children: "Esc to go back" })] }));
98
- }
99
- const clampedOffset = Math.min(scrollOffset, maxOffset);
100
- const visible = messageLines.slice(clampedOffset, clampedOffset + viewHeight);
101
- return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Messages" }), _jsx(Text, { color: "cyan", children: sessionName }), _jsxs(Text, { dimColor: true, children: ["(", messages.length, " messages)"] })] }), _jsx(Box, { flexDirection: "column", height: viewHeight, overflow: "hidden", children: visible.length === 0 ? (_jsx(Text, { dimColor: true, children: "No messages in this session yet." })) : (visible.map((line, i) => (_jsx(Box, { children: line.role === "user" ? (_jsx(Text, { color: "green", children: line.text })) : (_jsx(Text, { children: line.text })) }, i)))) }), messageLines.length > viewHeight && (_jsxs(Text, { dimColor: true, children: ["[", clampedOffset + 1, "\u2013", Math.min(clampedOffset + viewHeight, messageLines.length), "/", messageLines.length, " lines] \u2191\u2193 scroll \u00B7 Home/End jump \u00B7 Esc back"] }))] }));
102
- }
103
- function InjectView({ serverUrl, password, sessionName, contentHeight, onClose }) {
104
- const { injectText } = useApi(serverUrl, password);
105
- const [stage, setStage] = useState("input");
106
- const [error, setError] = useState(null);
107
- const [submitted, setSubmitted] = useState(false);
108
- useInput((_input, key) => {
109
- if (key.escape && stage !== "submitting") {
110
- onClose();
111
- return;
112
- }
113
- if ((stage === "done" || stage === "error") && key.escape) {
114
- onClose();
115
- return;
116
- }
117
- });
118
- // Auto-close after success
119
- useEffect(() => {
120
- if (stage === "done") {
121
- const t = setTimeout(onClose, 1500);
122
- return () => clearTimeout(t);
123
- }
124
- }, [stage, onClose]);
125
- const handleSubmit = async (text) => {
126
- const trimmed = text.trim();
127
- if (!trimmed)
128
- return;
129
- setSubmitted(true);
130
- setStage("submitting");
131
- try {
132
- await injectText(sessionName, trimmed);
133
- setStage("done");
134
- }
135
- catch (err) {
136
- setError(err instanceof Error ? err.message : String(err));
137
- setStage("error");
138
- }
139
- };
140
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { gap: 2, children: [_jsx(Text, { bold: true, children: "Inject Text" }), _jsx(Text, { color: "cyan", children: sessionName })] }), stage === "input" && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { children: "Text: " }), !submitted && (_jsx(TextInput, { placeholder: "Type your message...", onSubmit: (v) => void handleSubmit(v) }))] })), stage === "submitting" && _jsx(Spinner, { label: `Injecting into "${sessionName}"...` }), stage === "done" && (_jsx(Text, { color: "green", children: "Injected. Returning..." })), stage === "error" && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "red", children: ["Error: ", error] }), _jsx(Text, { dimColor: true, children: "Esc to go back" })] }))] }));
141
- }
142
- function CoworkerMailView({ serverUrl, password, sessionName, contentHeight, onClose }) {
143
- const { getMailMessages } = useApi(serverUrl, password);
144
- const [tab, setTab] = useState("received");
145
- const [messages, setMessages] = useState([]);
146
- const [loading, setLoading] = useState(true);
147
- const [error, setError] = useState(null);
148
- const [scrollOffset, setScrollOffset] = useState(0);
149
- const loadTab = useCallback(async (t) => {
150
- setLoading(true);
151
- setScrollOffset(0);
152
- try {
153
- const msgs = await getMailMessages(sessionName, { sent: t === "sent" });
154
- setMessages(msgs);
155
- }
156
- catch (err) {
157
- setError(err instanceof Error ? err.message : String(err));
158
- }
159
- finally {
160
- setLoading(false);
161
- }
162
- }, [sessionName]);
163
- useEffect(() => { void loadTab("received"); }, [loadTab]);
164
- useInput((input, key) => {
165
- if (key.escape) {
166
- onClose();
167
- return;
168
- }
169
- if (!loading) {
170
- if (key.upArrow)
171
- setScrollOffset((o) => Math.max(0, o - 1));
172
- if (key.downArrow)
173
- setScrollOffset((o) => o + 1);
174
- if (input === "r" && tab !== "received") {
175
- setTab("received");
176
- void loadTab("received");
177
- }
178
- if (input === "s" && tab !== "sent") {
179
- setTab("sent");
180
- void loadTab("sent");
181
- }
182
- }
183
- });
184
- const renderMessages = () => {
185
- if (loading)
186
- return _jsx(Spinner, { label: "Loading..." });
187
- if (error)
188
- return _jsxs(Text, { color: "red", children: ["Error: ", error] });
189
- if (messages.length === 0)
190
- return _jsxs(Text, { dimColor: true, children: ["No ", tab, " messages."] });
191
- const lines = [];
192
- const maxNameLen = Math.max(...messages.map((m) => m.from_name.length));
193
- for (const msg of messages) {
194
- const timestamp = new Date(msg.created_at).toLocaleString();
195
- lines.push({ text: "─", color: "gray" });
196
- lines.push({ text: `${msg.from_name.padEnd(maxNameLen)} → ${msg.to_name}`, color: "cyan" });
197
- lines.push({ text: timestamp, color: "gray" });
198
- if (!msg.read)
199
- lines.push({ text: " [unread]", color: "yellow" });
200
- lines.push({ text: "" });
201
- for (const line of msg.body.split("\n")) {
202
- lines.push({ text: ` ${line}` });
203
- }
204
- lines.push({ text: "" });
205
- }
206
- const viewHeight = contentHeight - 7;
207
- const maxOffset = Math.max(0, lines.length - viewHeight);
208
- const clamped = Math.min(scrollOffset, maxOffset);
209
- const visible = lines.slice(clamped, clamped + viewHeight);
210
- return (_jsx(Box, { flexDirection: "column", height: viewHeight, overflow: "hidden", children: visible.map((line, i) => (_jsx(Text, { color: line.color, children: line.text }, i))) }));
211
- };
212
- return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Coworker Mail" }), _jsx(Text, { color: "cyan", children: sessionName }), _jsxs(Text, { dimColor: true, children: ["(", messages.length, " ", tab, ")"] })] }), _jsxs(Box, { gap: 2, marginBottom: 1, children: [tab === "received"
213
- ? _jsx(Text, { bold: true, color: "green", children: "[Received]" })
214
- : _jsx(Text, { dimColor: true, children: "Received (r)" }), tab === "sent"
215
- ? _jsx(Text, { bold: true, color: "yellow", children: "[Sent]" })
216
- : _jsx(Text, { dimColor: true, children: "Sent (s)" })] }), renderMessages()] }));
217
- }
218
- // ─── Main component ──────────────────────────────────────────────────────────
219
- export function SessionList({ serverUrl, password, contentHeight, onSubViewChange }) {
220
- const { listSessions, createSession, deleteSession, updateSessionAgent, regenerateCode, getModes, revertToStart, revertAll } = useApi(serverUrl, password);
221
- const { data: sessions, loading, error: loadError, run } = useAsyncState();
222
- const [cursor, setCursor] = useState(0);
223
- const [revealedRows, setRevealedRows] = useState(new Set());
224
- const [mode, setMode] = useState("browse");
225
- const [subView, setSubView] = useState(null);
226
- const [actionError, setActionError] = useState(null);
227
- const [actionMsg, setActionMsg] = useState(null);
228
- const [availableModes, setAvailableModes] = useState([]);
229
- const [pendingAgent, setPendingAgent] = useState(null);
230
- const [modeCursor, setModeCursor] = useState(0);
231
- const reload = () => void run(listSessions);
232
- useEffect(() => { reload(); }, []);
233
- // Notify parent when a sub-view becomes active/inactive so the App-level
234
- // Esc handler doesn't skip over this page and jump straight to the menu.
235
- useEffect(() => {
236
- onSubViewChange?.(subView !== null);
237
- }, [subView, onSubViewChange]);
238
- const rows = sessions ?? [];
239
- // Clamp cursor when list shrinks
240
- useEffect(() => {
241
- if (rows.length > 0)
242
- setCursor((c) => Math.min(c, rows.length - 1));
243
- }, [rows.length]);
244
- useInput((input, key) => {
245
- // Sub-views own Esc themselves; pass nothing else through
246
- if (subView !== null)
247
- return;
248
- if (loading)
249
- return;
250
- if (mode === "browse") {
251
- if (key.upArrow)
252
- setCursor((c) => Math.max(0, c - 1));
253
- if (key.downArrow)
254
- setCursor((c) => Math.min(rows.length - 1, c + 1));
255
- if (input === "r" && rows.length > 0) {
256
- const id = rows[cursor]?.id;
257
- if (id == null)
258
- return;
259
- setRevealedRows((prev) => {
260
- const next = new Set(prev);
261
- next.has(id) ? next.delete(id) : next.add(id);
262
- return next;
263
- });
264
- }
265
- if (input === "c") {
266
- setActionError(null);
267
- setActionMsg(null);
268
- setPendingAgent(null);
269
- setModeCursor(0);
270
- setMode("creating-loading");
271
- getModes().then((modes) => {
272
- const safeMode = Array.isArray(modes) ? modes : [];
273
- setAvailableModes(safeMode);
274
- setMode(safeMode.length > 0 ? "creating-pick-mode" : "creating-name");
275
- }).catch(() => {
276
- setAvailableModes([]);
277
- setMode("creating-name");
278
- });
279
- }
280
- if (input === "d" && rows.length > 0) {
281
- setActionError(null);
282
- setActionMsg(null);
283
- setMode("confirm-delete");
284
- }
285
- if (input === "g" && rows.length > 0) {
286
- setActionError(null);
287
- setActionMsg(null);
288
- setMode("confirm-regen");
289
- }
290
- if (input === "x" && rows.length > 0) {
291
- setActionError(null);
292
- setActionMsg(null);
293
- setMode("confirm-revert");
294
- }
295
- if (input === "X" && rows.length > 0) {
296
- setActionError(null);
297
- setActionMsg(null);
298
- setMode("confirm-revert-all");
299
- }
300
- if (input === "a" && rows.length > 0) {
301
- setActionError(null);
302
- setActionMsg(null);
303
- setModeCursor(0);
304
- setMode("changing-agent-loading");
305
- getModes().then((modes) => {
306
- const safeMode = Array.isArray(modes) ? modes : [];
307
- setAvailableModes(safeMode);
308
- if (safeMode.length > 0) {
309
- setMode("changing-agent-pick");
310
- }
311
- else {
312
- setActionError("No agent modes available");
313
- setMode("create-error");
314
- }
315
- }).catch(() => {
316
- setActionError("Failed to fetch agent modes");
317
- setMode("create-error");
318
- });
319
- }
320
- if (rows.length > 0) {
321
- if (input === "t")
322
- setSubView("tail");
323
- if (input === "i")
324
- setSubView("inject");
325
- if (input === "m")
326
- setSubView("coworker-mail");
327
- }
328
- }
329
- if (mode === "creating-pick-mode") {
330
- // +1 for the "no mode" option at index 0
331
- const total = availableModes.length + 1;
332
- if (key.upArrow)
333
- setModeCursor((c) => (c - 1 + total) % total);
334
- if (key.downArrow)
335
- setModeCursor((c) => (c + 1) % total);
336
- if (key.return) {
337
- const selected = modeCursor === 0 ? null : (availableModes[modeCursor - 1]?.name ?? null);
338
- setPendingAgent(selected);
339
- setMode("creating-name");
340
- }
341
- if (key.escape) {
342
- setMode("browse");
343
- }
344
- return;
345
- }
346
- if (mode === "changing-agent-pick") {
347
- const total = availableModes.length;
348
- if (key.upArrow)
349
- setModeCursor((c) => (c - 1 + total) % total);
350
- if (key.downArrow)
351
- setModeCursor((c) => (c + 1) % total);
352
- if (key.return) {
353
- const selected = availableModes[modeCursor]?.name;
354
- if (selected && rows[cursor]) {
355
- void handleChangeAgent(rows[cursor].name, selected);
356
- }
357
- }
358
- if (key.escape) {
359
- setMode("browse");
360
- }
361
- return;
362
- }
363
- if (mode === "creating-name" && key.escape) {
364
- setMode("browse");
365
- return;
366
- }
367
- if (mode === "confirm-revert-all" && key.escape) {
368
- setMode("browse");
369
- return;
370
- }
371
- // Dismiss feedback states with any key
372
- if (mode === "create-done" || mode === "create-error" || mode === "delete-done" || mode === "delete-error") {
373
- setMode("browse");
374
- setActionError(null);
375
- setActionMsg(null);
376
- reload();
377
- }
378
- });
379
- const handleCreate = async (name) => {
380
- const trimmed = name.trim();
381
- if (!trimmed)
382
- return;
383
- if (!pendingAgent) {
384
- setActionError("Agent mode is required");
385
- setMode("create-error");
386
- return;
387
- }
388
- setMode("creating-busy");
389
- try {
390
- await createSession(trimmed, pendingAgent);
391
- const modeNote = pendingAgent ? ` [${pendingAgent}]` : "";
392
- setActionMsg(`Coworker "${trimmed}"${modeNote} created.`);
393
- setMode("create-done");
394
- reload();
395
- }
396
- catch (err) {
397
- setActionError(err instanceof Error ? err.message : String(err));
398
- setMode("create-error");
399
- }
400
- };
401
- const handleChangeAgent = async (sessionName, agent) => {
402
- setMode("changing-agent-busy");
403
- try {
404
- await updateSessionAgent(sessionName, agent);
405
- setActionMsg(`Agent for "${sessionName}" changed to [${agent}].`);
406
- setMode("create-done");
407
- reload();
408
- }
409
- catch (err) {
410
- setActionError(err instanceof Error ? err.message : String(err));
411
- setMode("create-error");
412
- }
413
- };
414
- const handleConfirmDelete = async (confirmed) => {
415
- if (!confirmed) {
416
- setMode("browse");
417
- return;
418
- }
419
- const target = rows[cursor];
420
- if (!target) {
421
- setMode("browse");
422
- return;
423
- }
424
- setMode("deleting");
425
- try {
426
- await deleteSession(target.name);
427
- setActionMsg(`Coworker "${target.name}" deleted.`);
428
- setMode("delete-done");
429
- reload();
430
- }
431
- catch (err) {
432
- setActionError(err instanceof Error ? err.message : String(err));
433
- setMode("delete-error");
434
- }
435
- };
436
- const handleConfirmRegen = async (confirmed) => {
437
- if (!confirmed) {
438
- setMode("browse");
439
- return;
440
- }
441
- const target = rows[cursor];
442
- if (!target) {
443
- setMode("browse");
444
- return;
445
- }
446
- setMode("regenerating");
447
- try {
448
- await regenerateCode(target.name);
449
- setActionMsg(`Agent code regenerated for "${target.name}".`);
450
- setRevealedRows((prev) => {
451
- const next = new Set(prev);
452
- if (target.id != null)
453
- next.add(target.id);
454
- return next;
455
- });
456
- setMode("create-done");
457
- reload();
458
- }
459
- catch (err) {
460
- setActionError(err instanceof Error ? err.message : String(err));
461
- setMode("create-error");
462
- }
463
- };
464
- const handleConfirmRevert = async (confirmed) => {
465
- if (!confirmed) {
466
- setMode("browse");
467
- return;
468
- }
469
- const target = rows[cursor];
470
- if (!target) {
471
- setMode("browse");
472
- return;
473
- }
474
- setMode("reverting");
475
- try {
476
- await revertToStart(target.name);
477
- setActionMsg(`Session "${target.name}" reset to a fresh session.`);
478
- setMode("create-done");
479
- reload();
480
- }
481
- catch (err) {
482
- setActionError(err instanceof Error ? err.message : String(err));
483
- setMode("create-error");
484
- }
485
- };
486
- const handleConfirmRevertAll = async (confirmed) => {
487
- if (!confirmed) {
488
- setMode("browse");
489
- return;
490
- }
491
- setMode("reverting-all");
492
- try {
493
- const result = await revertAll();
494
- const failed = result.results.filter((r) => !r.ok);
495
- if (failed.length === 0) {
496
- setActionMsg(`All ${result.total} session(s) reset to fresh sessions.`);
497
- }
498
- else {
499
- setActionMsg(`Reverted ${result.total - failed.length}/${result.total} sessions. Failed: ${failed.map((f) => f.name).join(", ")}`);
500
- }
501
- setMode("create-done");
502
- reload();
503
- }
504
- catch (err) {
505
- setActionError(err instanceof Error ? err.message : String(err));
506
- setMode("create-error");
507
- }
508
- };
509
- // ── Full-screen change-agent flow ──────────────────────────────────────────
510
- if (mode === "changing-agent-loading") {
511
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Loading agent configs..." }) }));
512
- }
513
- if (mode === "changing-agent-pick") {
514
- const target = rows[cursor];
515
- return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Change Agent" }), _jsxs(Text, { dimColor: true, children: ["for \"", target?.name, "\""] }), target?.agent ? _jsxs(Text, { color: "yellow", children: ["[current: ", target.agent, "]"] }) : null] }), _jsx(Box, { flexDirection: "column", children: availableModes.map((m, i) => {
516
- const sel = i === modeCursor;
517
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: sel ? "cyan" : undefined, bold: sel, children: sel ? "▶" : " " }), _jsx(Text, { color: sel ? "cyan" : undefined, bold: sel, children: m.name }), m.description ? _jsxs(Text, { dimColor: true, children: ["\u2014 ", m.description] }) : null] }, i));
518
- }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate \u00B7 Enter select \u00B7 Esc cancel" }) })] }));
519
- }
520
- if (mode === "changing-agent-busy") {
521
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Changing agent..." }) }));
522
- }
523
- // ── Full-screen create flow ───────────────────────────────────────────────
524
- if (mode === "creating-loading") {
525
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Loading agent configs..." }) }));
526
- }
527
- if (mode === "creating-pick-mode") {
528
- const modeOptions = [
529
- { label: "No mode (default)", description: "" },
530
- ...availableModes.map((m) => ({ label: m.name, description: m.description })),
531
- ];
532
- return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Create Coworker" }), _jsx(Text, { dimColor: true, children: "Select a mode" })] }), _jsx(Box, { flexDirection: "column", children: modeOptions.map((opt, i) => {
533
- const sel = i === modeCursor;
534
- return (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: sel ? "cyan" : undefined, bold: sel, children: sel ? "▶" : " " }), _jsx(Text, { color: sel ? "cyan" : undefined, bold: sel, children: opt.label }), opt.description ? _jsxs(Text, { dimColor: true, children: ["\u2014 ", opt.description] }) : null] }, i));
535
- }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate \u00B7 Enter select \u00B7 Esc cancel" }) })] }));
536
- }
537
- if (mode === "creating-name") {
538
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { gap: 2, children: [_jsx(Text, { bold: true, children: "Create Coworker" }), pendingAgent
539
- ? _jsxs(Text, { color: "yellow", children: ["[", pendingAgent, "]"] })
540
- : _jsx(Text, { dimColor: true, children: "[no mode]" })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { children: "Name: " }), _jsx(TextInput, { placeholder: "e.g. alice", onSubmit: (v) => void handleCreate(v) })] }), _jsx(Text, { dimColor: true, children: "Enter to create \u00B7 Esc cancel" })] }));
541
- }
542
- if (mode === "creating-busy") {
543
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Creating coworker..." }) }));
544
- }
545
- if (mode === "create-done") {
546
- return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, children: [_jsx(Text, { color: "green", children: actionMsg }), _jsx(Text, { dimColor: true, children: "Press any key to continue" })] }));
547
- }
548
- if (mode === "create-error") {
549
- return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, children: [_jsxs(Text, { color: "red", children: ["Error: ", actionError] }), _jsx(Text, { dimColor: true, children: "Press any key to continue" })] }));
550
- }
551
- // ── Sub-view rendering ────────────────────────────────────────────────────
552
- const activeSession = rows[cursor];
553
- if (subView !== null && activeSession) {
554
- const closeSubView = () => setSubView(null);
555
- if (subView === "tail") {
556
- return (_jsx(TailView, { serverUrl: serverUrl, password: password, sessionName: activeSession.name, contentHeight: contentHeight, onClose: closeSubView }));
557
- }
558
- if (subView === "inject") {
559
- return (_jsx(InjectView, { serverUrl: serverUrl, password: password, sessionName: activeSession.name, contentHeight: contentHeight, onClose: closeSubView }));
560
- }
561
- if (subView === "coworker-mail") {
562
- return (_jsx(CoworkerMailView, { serverUrl: serverUrl, password: password, sessionName: activeSession.name, contentHeight: contentHeight, onClose: closeSubView }));
563
- }
564
- }
565
- // ── Initial load ──────────────────────────────────────────────────────────
566
- if (loading && rows.length === 0) {
567
- return (_jsx(Box, { height: contentHeight, alignItems: "center", justifyContent: "center", children: _jsx(Spinner, { label: "Loading coworkers..." }) }));
568
- }
569
- if (loadError) {
570
- return (_jsxs(Box, { height: contentHeight, flexDirection: "column", gap: 1, children: [_jsx(Text, { color: "red", bold: true, children: "Error" }), _jsx(Text, { children: loadError })] }));
571
- }
572
- // ── Inline action panel ───────────────────────────────────────────────────
573
- const renderActionPanel = () => {
574
- const target = rows[cursor];
575
- if (mode === "confirm-delete" && target)
576
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, color: "red", children: "Delete Coworker" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Delete ", _jsx(Text, { color: "yellow", bold: true, children: target.name }), "?", " ", _jsx(Text, { dimColor: true, children: "This also removes the OpenCode session." })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(ConfirmInput, { defaultChoice: "cancel", onConfirm: () => void handleConfirmDelete(true), onCancel: () => void handleConfirmDelete(false) }) })] }));
577
- if (mode === "deleting")
578
- return (_jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, marginBottom: 1, children: _jsx(Spinner, { label: `Deleting "${rows[cursor]?.name}"...` }) }));
579
- if (mode === "delete-done")
580
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { color: "green", children: actionMsg }), _jsx(Text, { dimColor: true, children: "Press any key to continue" })] }));
581
- if (mode === "delete-error")
582
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, marginBottom: 1, children: [_jsxs(Text, { color: "red", children: ["Error: ", actionError] }), _jsx(Text, { dimColor: true, children: "Press any key to continue" })] }));
583
- if (mode === "confirm-regen" && target)
584
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, color: "yellow", children: "Regenerate Agent Code" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Regenerate code for ", _jsx(Text, { color: "cyan", bold: true, children: target.name }), "?", " ", _jsx(Text, { dimColor: true, children: "The old code will stop working immediately." })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(ConfirmInput, { defaultChoice: "cancel", onConfirm: () => void handleConfirmRegen(true), onCancel: () => void handleConfirmRegen(false) }) })] }));
585
- if (mode === "regenerating")
586
- return (_jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginBottom: 1, children: _jsx(Spinner, { label: "Generating new agent code..." }) }));
587
- if (mode === "confirm-revert" && target)
588
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Revert to First Message" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Revert ", _jsx(Text, { color: "cyan", bold: true, children: target.name }), " to its first message and restart?", " ", _jsx(Text, { dimColor: true, children: "This clears all messages after the first one." })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(ConfirmInput, { defaultChoice: "cancel", onConfirm: () => void handleConfirmRevert(true), onCancel: () => void handleConfirmRevert(false) }) })] }));
589
- if (mode === "reverting")
590
- return (_jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: _jsx(Spinner, { label: `Reverting "${rows[cursor]?.name}" and restarting...` }) }));
591
- if (mode === "confirm-revert-all")
592
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, marginBottom: 1, children: [_jsx(Text, { bold: true, color: "red", children: "Reset All Sessions" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Revert ", _jsxs(Text, { color: "red", bold: true, children: ["all ", rows.length, " coworker(s)"] }), " to their first message and restart?", " ", _jsx(Text, { dimColor: true, children: "This clears all messages after the first one for every session." })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(ConfirmInput, { defaultChoice: "cancel", onConfirm: () => void handleConfirmRevertAll(true), onCancel: () => void handleConfirmRevertAll(false) }) })] }));
593
- if (mode === "reverting-all")
594
- return (_jsx(Box, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, marginBottom: 1, children: _jsx(Spinner, { label: `Reverting all ${rows.length} session(s) and restarting...` }) }));
595
- return null;
596
- };
597
- // ── Coworker table ────────────────────────────────────────────────────────
598
- const actionPanel = renderActionPanel();
599
- const panelHeight = actionPanel ? 5 : 0;
600
- const tableHeight = contentHeight - panelHeight - 3;
601
- return (_jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, children: "Coworkers" }), _jsxs(Text, { dimColor: true, children: ["(", rows.length, ")"] }), loading && _jsx(Spinner, {})] }), actionPanel, rows.length === 0 ? (_jsx(Box, { height: tableHeight, alignItems: "center", justifyContent: "center", children: _jsx(Text, { dimColor: true, children: "No coworkers yet. Press c to create one." }) })) : (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 2, marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: " NAME".padEnd(18) }), _jsx(Text, { bold: true, color: "cyan", children: "STATUS".padEnd(20) }), _jsx(Text, { bold: true, color: "cyan", children: "MODE".padEnd(12) }), _jsx(Text, { bold: true, color: "cyan", children: "OPENCODE SESSION ID".padEnd(36) }), _jsx(Text, { bold: true, color: "cyan", children: "AGENT CODE" })] }), rows.map((s, i) => {
602
- const selected = i === cursor;
603
- const revealed = revealedRows.has(s.id);
604
- return (_jsxs(Box, { gap: 2, children: [_jsxs(Box, { width: 18, children: [_jsx(Text, { color: selected ? "cyan" : undefined, children: selected ? "▶ " : " " }), _jsx(Text, { color: selected ? "cyan" : "green", bold: selected, children: s.name })] }), _jsx(Text, { color: selected ? "cyan" : undefined, dimColor: !selected && !s.status, children: (s.status ?? "—").padEnd(20) }), _jsx(Text, { color: selected ? "magenta" : undefined, dimColor: !selected && !s.agent, children: (s.agent ?? "—").padEnd(12) }), _jsx(Text, { dimColor: !selected, children: s.session_id.padEnd(36) }), revealed
605
- ? _jsx(Text, { color: "yellow", children: s.agent_code })
606
- : _jsx(Text, { dimColor: true, children: MASKED_CODE })] }, s.id));
607
- })] }))] }));
608
- }
@@ -1,6 +0,0 @@
1
- interface SessionSidebarProps {
2
- serverUrl: string;
3
- password: string;
4
- }
5
- export declare function SessionSidebar({ serverUrl, password }: SessionSidebarProps): import("react/jsx-runtime").JSX.Element | null;
6
- export {};
@@ -1,24 +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 { useApi } from "../hooks/useApi.js";
5
- export function SessionSidebar({ serverUrl, password }) {
6
- const { listSessions } = useApi(serverUrl, password);
7
- const [sessions, setSessions] = useState([]);
8
- useEffect(() => {
9
- // Initial fetch
10
- listSessions().then((s) => setSessions(s));
11
- // Poll every 5 seconds to refresh statuses
12
- const interval = setInterval(() => {
13
- listSessions().then((s) => setSessions(s));
14
- }, 5000);
15
- return () => clearInterval(interval);
16
- }, [listSessions]);
17
- if (sessions.length === 0)
18
- return null;
19
- return (_jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, flexDirection: "column", gap: 0, children: [_jsx(Text, { bold: true, color: "cyan", children: "Sessions" }), sessions.map((session, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, flexDirection: "row", children: [_jsx(Text, { color: getNodeColor(i), children: "\u25CF" }), _jsx(Text, { children: session.name })] }), session.status && (_jsx(Box, { children: _jsx(Text, { dimColor: true, children: session.status }) }))] }, session.id)))] }));
20
- }
21
- function getNodeColor(index) {
22
- const colors = ["green", "blue", "yellow", "magenta", "cyan", "red", "white"];
23
- return colors[index % colors.length];
24
- }
@@ -1,8 +0,0 @@
1
- interface TailMessagesProps {
2
- serverUrl: string;
3
- password: string;
4
- onBack: () => void;
5
- contentHeight: number;
6
- }
7
- export declare function TailMessages({ serverUrl, password, onBack, contentHeight }: TailMessagesProps): import("react/jsx-runtime").JSX.Element;
8
- export {};