heyio 0.42.0 → 1.0.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 (100) hide show
  1. package/README.md +40 -52
  2. package/dist/api/auth.js +35 -38
  3. package/dist/api/server.js +157 -1139
  4. package/dist/config.js +49 -32
  5. package/dist/copilot/agents.js +72 -1055
  6. package/dist/copilot/client.js +6 -17
  7. package/dist/copilot/io-scheduler.js +55 -139
  8. package/dist/copilot/model-router.js +100 -72
  9. package/dist/copilot/orchestrator.js +91 -515
  10. package/dist/copilot/scheduler.js +67 -189
  11. package/dist/copilot/skills.js +41 -366
  12. package/dist/copilot/system-message.js +40 -200
  13. package/dist/copilot/tools.js +191 -2042
  14. package/dist/daemon.js +54 -201
  15. package/dist/index.js +15 -133
  16. package/dist/mcp/config.js +23 -31
  17. package/dist/mcp/index.js +2 -3
  18. package/dist/mcp/registry.js +33 -88
  19. package/dist/notify.js +18 -100
  20. package/dist/paths.js +13 -24
  21. package/dist/setup.js +35 -0
  22. package/dist/store/db.js +111 -297
  23. package/dist/store/feed.js +29 -97
  24. package/dist/store/instances.js +56 -121
  25. package/dist/store/schedules.js +21 -73
  26. package/dist/store/squads.js +35 -186
  27. package/dist/store/tasks.js +25 -168
  28. package/dist/telegram/bot.js +20 -312
  29. package/dist/telegram/handlers.js +39 -3
  30. package/dist/watchdog.js +31 -45
  31. package/dist/wiki/fs.js +38 -155
  32. package/dist/wiki/search.js +31 -44
  33. package/package.json +5 -8
  34. package/web-dist/assets/ChatView-EFFiln1H.js +11 -0
  35. package/web-dist/assets/FeedView-bN4NMOL7.js +6 -0
  36. package/web-dist/assets/LoginView-CNtasq3n.js +1 -0
  37. package/web-dist/assets/McpView-C2CHiwsi.js +1 -0
  38. package/web-dist/assets/SchedulesView-CyilLban.js +1 -0
  39. package/web-dist/assets/SettingsView-1wLXKEF4.js +1 -0
  40. package/web-dist/assets/SkillsView-BLsD-0u0.js +1 -0
  41. package/web-dist/assets/SquadDetailView-CsCw2ZLp.js +21 -0
  42. package/web-dist/assets/SquadsView-DQ3vFlyO.js +6 -0
  43. package/web-dist/assets/WikiView-19M3oqnq.js +21 -0
  44. package/web-dist/assets/api-WGvTsXaE.js +1 -0
  45. package/web-dist/assets/index-D7M5O-_l.css +1 -0
  46. package/web-dist/assets/index-DZOS9syn.js +95 -0
  47. package/web-dist/assets/plus-BOvyX1BC.js +6 -0
  48. package/web-dist/assets/trash-2-DHoetkC4.js +6 -0
  49. package/web-dist/favicon.svg +4 -1
  50. package/web-dist/index.html +7 -10
  51. package/dist/api/logout.test.js +0 -129
  52. package/dist/api/mcp.test.js +0 -285
  53. package/dist/api/wiki.test.js +0 -283
  54. package/dist/auth/session-logic.js +0 -79
  55. package/dist/auth/session-logic.test.js +0 -201
  56. package/dist/copilot/auto-complete-instance.test.js +0 -104
  57. package/dist/copilot/cron.js +0 -136
  58. package/dist/copilot/event-summary.js +0 -286
  59. package/dist/copilot/instance-deactivate.test.js +0 -119
  60. package/dist/copilot/model-router.test.js +0 -71
  61. package/dist/copilot/review-backfill.js +0 -57
  62. package/dist/copilot/session-timeout.js +0 -112
  63. package/dist/copilot/session-timeout.test.js +0 -372
  64. package/dist/copilot/skills.test.js +0 -55
  65. package/dist/copilot/universes.js +0 -469
  66. package/dist/instance-watchdog.js +0 -104
  67. package/dist/instance-watchdog.test.js +0 -183
  68. package/dist/mcp/client.js +0 -109
  69. package/dist/mcp/client.test.js +0 -99
  70. package/dist/mcp/config.test.js +0 -49
  71. package/dist/mcp/registry.test.js +0 -79
  72. package/dist/notify.test.js +0 -232
  73. package/dist/store/feed.test.js +0 -279
  74. package/dist/store/instances.test.js +0 -310
  75. package/dist/store/io-schedules.js +0 -63
  76. package/dist/store/notifications.js +0 -79
  77. package/dist/store/notifications.test.js +0 -197
  78. package/dist/store/schedule-runs.js +0 -46
  79. package/dist/store/squads.test.js +0 -405
  80. package/dist/store/tasks.test.js +0 -150
  81. package/dist/store/worktrees.js +0 -83
  82. package/dist/tui/index.js +0 -286
  83. package/dist/update.js +0 -81
  84. package/dist/watchdog.test.js +0 -83
  85. package/dist/wiki/wiki-squad.test.js +0 -54
  86. package/web-dist/assets/AgentActivityView-CedxxE6K.js +0 -1
  87. package/web-dist/assets/ChatView-DMkYQo_V.js +0 -4
  88. package/web-dist/assets/FeedView-BH4q-31V.js +0 -1
  89. package/web-dist/assets/InboxView-BVwVP4EW.js +0 -1
  90. package/web-dist/assets/LoginView-DRPDhnwu.js +0 -1
  91. package/web-dist/assets/McpView-D8yWz-lq.js +0 -1
  92. package/web-dist/assets/SchedulesView-BzzyncGF.js +0 -1
  93. package/web-dist/assets/SettingsTabs.vue_vue_type_script_setup_true_lang-oW3ySu7Y.js +0 -1
  94. package/web-dist/assets/SkillsView-oxpYuhx7.js +0 -1
  95. package/web-dist/assets/SquadsView-CaKUIKlq.js +0 -1
  96. package/web-dist/assets/StatusIndicator.vue_vue_type_script_setup_true_lang-8U15Qp_Q.js +0 -1
  97. package/web-dist/assets/WikiView-C5jXUlfW.js +0 -1
  98. package/web-dist/assets/index-BrWzNw-N.css +0 -10
  99. package/web-dist/assets/index-f67odrrt.js +0 -81
  100. package/web-dist/icons.svg +0 -24
@@ -1,83 +0,0 @@
1
- import { execSync } from "child_process";
2
- import { existsSync, rmSync, mkdirSync } from "fs";
3
- import path from "path";
4
- /**
5
- * Create a git worktree for a squad instance.
6
- * @param projectPath - The main project directory (must be a git repo)
7
- * @param instanceId - Used to name the worktree subdirectory
8
- * @param branchName - The branch to create for this worktree
9
- * @param baseBranch - The branch to base the worktree on (default: "main")
10
- * @returns The absolute path to the created worktree
11
- */
12
- export function createWorktree(projectPath, instanceId, branchName, baseBranch = "main") {
13
- const worktreeDir = path.join(projectPath, ".io-worktrees", instanceId);
14
- const parentDir = path.join(projectPath, ".io-worktrees");
15
- if (!existsSync(parentDir)) {
16
- mkdirSync(parentDir, { recursive: true });
17
- }
18
- // Fetch latest to ensure base branch is up to date
19
- try {
20
- execSync(`git fetch origin ${baseBranch}`, { cwd: projectPath, stdio: "pipe" });
21
- }
22
- catch {
23
- // Best effort — might be offline or no remote
24
- }
25
- // Create the worktree with a new branch from the base
26
- execSync(`git worktree add "${worktreeDir}" -b "${branchName}" "origin/${baseBranch}"`, { cwd: projectPath, stdio: "pipe" });
27
- return worktreeDir;
28
- }
29
- /**
30
- * Remove a git worktree. Falls back to rm -rf + prune if git remove fails.
31
- */
32
- export function removeWorktree(projectPath, worktreePath) {
33
- try {
34
- execSync(`git worktree remove "${worktreePath}" --force`, {
35
- cwd: projectPath,
36
- stdio: "pipe",
37
- });
38
- }
39
- catch {
40
- // Fallback: force remove directory and prune
41
- if (existsSync(worktreePath)) {
42
- rmSync(worktreePath, { recursive: true, force: true });
43
- }
44
- try {
45
- execSync("git worktree prune", { cwd: projectPath, stdio: "pipe" });
46
- }
47
- catch {
48
- // best effort
49
- }
50
- }
51
- }
52
- /**
53
- * List all worktrees for a project.
54
- */
55
- export function listWorktrees(projectPath) {
56
- try {
57
- const output = execSync("git worktree list --porcelain", {
58
- cwd: projectPath,
59
- encoding: "utf-8",
60
- });
61
- const entries = [];
62
- let currentPath = "";
63
- for (const line of output.split("\n")) {
64
- if (line.startsWith("worktree ")) {
65
- currentPath = line.slice("worktree ".length);
66
- }
67
- else if (line.startsWith("branch ")) {
68
- entries.push({ path: currentPath, branch: line.slice("branch refs/heads/".length) });
69
- }
70
- }
71
- return entries;
72
- }
73
- catch {
74
- return [];
75
- }
76
- }
77
- /**
78
- * Check if a worktree path exists on disk.
79
- */
80
- export function worktreeExists(worktreePath) {
81
- return existsSync(worktreePath);
82
- }
83
- //# sourceMappingURL=worktrees.js.map
package/dist/tui/index.js DELETED
@@ -1,286 +0,0 @@
1
- import { createInterface } from "readline";
2
- import { listRecentTasks, getTask } from "../store/tasks.js";
3
- import { getTaskEvents } from "../copilot/agents.js";
4
- import { summarize } from "../copilot/event-summary.js";
5
- import { listFeedEntries, deleteFeedEntry, countUnreadFeedEntries, } from "../store/feed.js";
6
- let messageHandler;
7
- export function setMessageHandler(handler) {
8
- messageHandler = handler;
9
- }
10
- const WELCOME_BANNER = `
11
- ╔══════════════════════════════════════╗
12
- ║ IO — AI Assistant ║
13
- ╚══════════════════════════════════════╝
14
- Type a message to chat. Commands:
15
- /status — show status
16
- /activity [id|N] — show summarized activity for a task (default: most recent)
17
- /verbose — toggle verbose mode (raw event detail in /activity)
18
- /inbox — list deliverables from the unified feed
19
- /inbox delete <id> — delete a feed entry by ID
20
- /inbox clear — delete all deliverables from the feed
21
- /quit — exit
22
- `;
23
- let verbose = false;
24
- // Held so printBackgroundNotification can redraw the prompt around notifications.
25
- // Assigned inside startTui(); undefined when running headless (no TUI).
26
- let activeInterface;
27
- const NOTIFICATION_MAX_LINES = 6;
28
- const NOTIFICATION_WRAP_WIDTH = 78;
29
- /** Hard-wrap a single line to at most `width` visible chars, returning segments. */
30
- function wrapLine(line, width) {
31
- const segments = [];
32
- while (line.length > width) {
33
- segments.push(line.slice(0, width));
34
- line = line.slice(width);
35
- }
36
- segments.push(line);
37
- return segments;
38
- }
39
- /**
40
- * Print a background notification in a bordered block above the prompt,
41
- * preserving any in-progress readline input the user has typed.
42
- *
43
- * Format:
44
- * ╭─🔔 Background update: <title>
45
- * │ <line1>
46
- * │ […N more lines — see /notifications] (if truncated)
47
- * ╰─
48
- *
49
- * Safe to call at any time, even before startTui(). Never throws.
50
- */
51
- export function printBackgroundNotification(opts) {
52
- try {
53
- // Build display lines from text, hard-wrapping long lines.
54
- const rawLines = opts.text.split(/\r?\n/);
55
- const displayLines = [];
56
- for (const raw of rawLines) {
57
- for (const seg of wrapLine(raw, NOTIFICATION_WRAP_WIDTH)) {
58
- displayLines.push(seg);
59
- }
60
- }
61
- const truncated = displayLines.length > NOTIFICATION_MAX_LINES;
62
- const visible = truncated ? displayLines.slice(0, NOTIFICATION_MAX_LINES) : displayLines;
63
- const extra = displayLines.length - NOTIFICATION_MAX_LINES;
64
- const top = "\u256d\u2500\ud83d\udd14 Background update: " + opts.title + "\n";
65
- const body = visible.map((l) => "\u2502 " + l).join("\n");
66
- const overflow = truncated
67
- ? "\n\u2502 [\u2026" + extra + " more line" + (extra === 1 ? "" : "s") + " \u2014 see /notifications]"
68
- : "";
69
- const bottom = "\n\u2570\u2500\n";
70
- const block = top + body + overflow + bottom;
71
- if (!activeInterface) {
72
- // Headless daemon — no readline interface live; plain log is fine.
73
- process.stdout.write(block);
74
- return;
75
- }
76
- // Capture whatever the user has typed so far.
77
- // readline stores the pending input in `rl.line` (stable internal property).
78
- const currentLine = activeInterface.line ?? "";
79
- // Clear the current prompt+input line, print the notification, then
80
- // redraw the prompt with the user's buffer.
81
- process.stdout.write("\r\x1b[K");
82
- process.stdout.write(block);
83
- if (currentLine === "") {
84
- activeInterface.prompt(true);
85
- }
86
- else {
87
- process.stdout.write("io> " + currentLine);
88
- }
89
- }
90
- catch (err) {
91
- console.error("[io] printBackgroundNotification failed:", err instanceof Error ? err.message : String(err));
92
- }
93
- }
94
- function renderActivity(taskIdArg) {
95
- const recent = listRecentTasks(20);
96
- let task = undefined;
97
- if (!taskIdArg) {
98
- task = recent[0];
99
- }
100
- else if (/^\d+$/.test(taskIdArg)) {
101
- task = recent[parseInt(taskIdArg, 10) - 1];
102
- }
103
- else {
104
- task = getTask(taskIdArg);
105
- }
106
- if (!task) {
107
- console.log("[io] No task found for activity view.");
108
- return;
109
- }
110
- const events = getTaskEvents(task.task_id);
111
- if (events.length === 0) {
112
- console.log(`[io] No buffered activity for task ${task.task_id} (${task.status}).`);
113
- return;
114
- }
115
- const activity = summarize(events);
116
- console.log(`[io] Activity for task ${task.task_id} (${task.agent_slug}, ${task.status}) — ${activity.length} entries${verbose ? " (verbose)" : ""}`);
117
- for (const e of activity) {
118
- const ts = new Date(e.ts).toISOString().slice(11, 19);
119
- console.log(` ${ts} ${e.icon} ${e.summary}`);
120
- if (verbose) {
121
- if (e.detail) {
122
- for (const line of e.detail.split(/\r?\n/))
123
- console.log(` ${line}`);
124
- }
125
- else if (e.raw && typeof e.raw === "object") {
126
- console.log(" " + JSON.stringify(e.raw).slice(0, 400));
127
- }
128
- }
129
- }
130
- }
131
- function renderInbox() {
132
- const entries = listFeedEntries({ type: "inbox" });
133
- if (entries.length === 0) {
134
- console.log("\u2705 Inbox is empty.");
135
- return;
136
- }
137
- console.log(`\u2705 Inbox (${entries.length} ${entries.length === 1 ? "entry" : "entries"})`);
138
- console.log("\u2500".repeat(60));
139
- for (const entry of entries) {
140
- const ts = new Date(entry.created_at).toLocaleString();
141
- const unreadMarker = entry.read_at === null ? "\u25cf " : " ";
142
- console.log(`${unreadMarker}[${entry.id}] ${entry.title} \u2014 ${ts}`);
143
- const preview = entry.body.length > 200 ? entry.body.slice(0, 200) + "\u2026" : entry.body;
144
- for (const line of preview.split(/\r?\n/)) {
145
- console.log(` ${line}`);
146
- }
147
- console.log("");
148
- }
149
- }
150
- function clearLine() {
151
- process.stdout.write("\r\x1b[K");
152
- }
153
- export async function startTui() {
154
- const rl = createInterface({
155
- input: process.stdin,
156
- output: process.stdout,
157
- });
158
- // Keep a module-level reference so printBackgroundNotification can redraw
159
- // the prompt without disturbing the user's in-progress input.
160
- activeInterface = rl;
161
- console.log(WELCOME_BANNER);
162
- rl.setPrompt("io> ");
163
- rl.prompt();
164
- rl.on("line", async (input) => {
165
- const trimmed = input.trim();
166
- if (!trimmed) {
167
- rl.prompt();
168
- return;
169
- }
170
- if (trimmed === "/quit") {
171
- console.log("[io] Goodbye!");
172
- rl.close();
173
- process.exit(0);
174
- }
175
- if (trimmed === "/status") {
176
- const unreadCount = countUnreadFeedEntries();
177
- console.log(`[io] Uptime: ${Math.floor(process.uptime())}s`);
178
- if (unreadCount > 0) {
179
- console.log(`[io] \u2705 Unread feed items: ${unreadCount} ${unreadCount === 1 ? "item" : "items"}`);
180
- }
181
- rl.prompt();
182
- return;
183
- }
184
- if (trimmed === "/verbose") {
185
- verbose = !verbose;
186
- console.log(`[io] Verbose mode ${verbose ? "ON" : "OFF"}`);
187
- rl.prompt();
188
- return;
189
- }
190
- if (trimmed === "/activity" || trimmed.startsWith("/activity ")) {
191
- const arg = trimmed.slice("/activity".length).trim() || undefined;
192
- try {
193
- renderActivity(arg);
194
- }
195
- catch (err) {
196
- console.error(`[io] /activity failed: ${err instanceof Error ? err.message : String(err)}`);
197
- }
198
- rl.prompt();
199
- return;
200
- }
201
- if (trimmed === "/inbox" || trimmed.startsWith("/inbox ")) {
202
- const sub = trimmed.slice("/inbox".length).trim();
203
- try {
204
- if (sub === "") {
205
- renderInbox();
206
- }
207
- else if (sub === "clear") {
208
- const entries = listFeedEntries({ type: "inbox" });
209
- let deleted = 0;
210
- for (const entry of entries) {
211
- if (deleteFeedEntry(entry.id))
212
- deleted++;
213
- }
214
- console.log(`[io] Cleared ${deleted} inbox ${deleted === 1 ? "entry" : "entries"}.`);
215
- }
216
- else if (sub.startsWith("delete ")) {
217
- const rawId = sub.slice("delete ".length).trim();
218
- const id = Number.parseInt(rawId, 10);
219
- if (Number.isNaN(id)) {
220
- console.log(`[io] Invalid ID: "${rawId}". Usage: /inbox delete <id>`);
221
- }
222
- else if (deleteFeedEntry(id)) {
223
- console.log(`[io] Deleted feed entry #${id}.`);
224
- }
225
- else {
226
- console.log(`[io] Feed entry #${id} not found.`);
227
- }
228
- }
229
- else {
230
- console.log(`[io] Unknown inbox subcommand: "${sub}". Try /inbox, /inbox delete <id>, or /inbox clear.`);
231
- }
232
- }
233
- catch (err) {
234
- console.error(`[io] /inbox failed: ${err instanceof Error ? err.message : String(err)}`);
235
- }
236
- rl.prompt();
237
- return;
238
- }
239
- if (!messageHandler) {
240
- console.log("[io] No message handler registered.");
241
- rl.prompt();
242
- return;
243
- }
244
- // Show typing indicator
245
- process.stdout.write("...");
246
- let accumulated = "";
247
- let firstChunk = true;
248
- try {
249
- await messageHandler(trimmed, (text, done) => {
250
- if (firstChunk) {
251
- clearLine();
252
- firstChunk = false;
253
- }
254
- if (done) {
255
- // Finalize the current message as its own line, then reset state
256
- // so the next message in a multi-turn response starts a fresh bubble.
257
- clearLine();
258
- if (accumulated) {
259
- process.stdout.write(accumulated + "\n");
260
- }
261
- accumulated = "";
262
- firstChunk = true;
263
- }
264
- else {
265
- accumulated += text;
266
- clearLine();
267
- process.stdout.write(accumulated);
268
- }
269
- });
270
- // Restore the prompt once the handler promise fully resolves (i.e. after
271
- // all turns are done), rather than inside the done callback so that
272
- // multi-turn responses don't render a premature prompt between messages.
273
- rl.prompt();
274
- }
275
- catch (err) {
276
- clearLine();
277
- console.error(`[io] Error: ${err instanceof Error ? err.message : String(err)}`);
278
- rl.prompt();
279
- }
280
- });
281
- rl.on("close", () => {
282
- console.log("\n[io] Goodbye!");
283
- process.exit(0);
284
- });
285
- }
286
- //# sourceMappingURL=index.js.map
package/dist/update.js DELETED
@@ -1,81 +0,0 @@
1
- import { execSync } from "child_process";
2
- import { readFileSync } from "fs";
3
- import { fileURLToPath } from "url";
4
- import { dirname, join } from "path";
5
- const PACKAGE_NAME = "heyio";
6
- function getInstalledVersion() {
7
- try {
8
- // Ask npm what version is installed globally — run from /tmp to avoid local package interference
9
- const output = execSync(`npm list -g ${PACKAGE_NAME} --depth=0 --json 2>/dev/null`, {
10
- encoding: "utf-8",
11
- timeout: 10_000,
12
- cwd: "/tmp",
13
- });
14
- const data = JSON.parse(output);
15
- return data.dependencies?.[PACKAGE_NAME]?.version ?? "0.0.0";
16
- }
17
- catch {
18
- // Fallback: read package.json relative to this file
19
- try {
20
- const __dirname = dirname(fileURLToPath(import.meta.url));
21
- const pkgPath = join(__dirname, "..", "package.json");
22
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
23
- return pkg.version ?? "0.0.0";
24
- }
25
- catch {
26
- return "0.0.0";
27
- }
28
- }
29
- }
30
- export async function checkForUpdate() {
31
- try {
32
- const current = getInstalledVersion();
33
- const latest = execSync(`npm view ${PACKAGE_NAME} version 2>/dev/null`, {
34
- encoding: "utf-8",
35
- timeout: 10_000,
36
- cwd: "/tmp",
37
- }).trim();
38
- if (!latest)
39
- return { updateAvailable: false, current, latest: current };
40
- const updateAvailable = latest !== current && compareSemver(current, latest) < 0;
41
- return { updateAvailable, current, latest };
42
- }
43
- catch {
44
- return { updateAvailable: false, current: "unknown", latest: "unknown" };
45
- }
46
- }
47
- /**
48
- * Auto-update: install the latest version globally and return true if updated.
49
- * Returns false if already up-to-date or if the update fails.
50
- */
51
- export async function autoUpdate() {
52
- const info = await checkForUpdate();
53
- if (!info.updateAvailable)
54
- return false;
55
- console.log(`[io] ⬆ Updating: v${info.current} → v${info.latest}...`);
56
- try {
57
- execSync(`npm install -g ${PACKAGE_NAME}@latest`, {
58
- encoding: "utf-8",
59
- timeout: 60_000,
60
- stdio: "pipe",
61
- cwd: "/tmp",
62
- });
63
- console.log(`[io] ✓ Updated to v${info.latest}. Restarting...`);
64
- return true;
65
- }
66
- catch (err) {
67
- console.error(`[io] ⚠ Auto-update failed:`, err instanceof Error ? err.message : err);
68
- return false;
69
- }
70
- }
71
- function compareSemver(a, b) {
72
- const pa = a.split(".").map(Number);
73
- const pb = b.split(".").map(Number);
74
- for (let i = 0; i < 3; i++) {
75
- const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
76
- if (diff !== 0)
77
- return diff;
78
- }
79
- return 0;
80
- }
81
- //# sourceMappingURL=update.js.map
@@ -1,83 +0,0 @@
1
- import { describe, it, beforeEach } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { startWatchdog } from "./watchdog.js";
4
- describe("watchdog", () => {
5
- let stop;
6
- beforeEach(() => {
7
- if (stop) {
8
- stop();
9
- stop = undefined;
10
- }
11
- });
12
- it("calls onStall when stall exceeds warn threshold", async () => {
13
- const stalls = [];
14
- stop = startWatchdog({
15
- checkIntervalMs: 30,
16
- warnThresholdMs: 40,
17
- fatalThresholdMs: 5000,
18
- onStall: (ms) => stalls.push(ms),
19
- });
20
- // Block event loop longer than checkInterval + warnThreshold
21
- const start = Date.now();
22
- while (Date.now() - start < 120) {
23
- // busy-wait
24
- }
25
- // Let the interval fire
26
- await new Promise((resolve) => setTimeout(resolve, 50));
27
- assert.ok(stalls.length > 0, "onStall should have been called");
28
- assert.ok(stalls[0] >= 40, `stall duration ${stalls[0]} should be >= 40ms`);
29
- stop();
30
- stop = undefined;
31
- });
32
- it("calls onFatal when stall exceeds fatal threshold", async () => {
33
- const fatals = [];
34
- stop = startWatchdog({
35
- checkIntervalMs: 30,
36
- warnThresholdMs: 20,
37
- fatalThresholdMs: 50,
38
- onStall: () => { },
39
- onFatal: (ms) => fatals.push(ms),
40
- });
41
- // Block event loop longer than checkInterval + fatalThreshold
42
- const start = Date.now();
43
- while (Date.now() - start < 150) {
44
- // busy-wait
45
- }
46
- await new Promise((resolve) => setTimeout(resolve, 50));
47
- assert.ok(fatals.length > 0, "onFatal should have been called");
48
- assert.ok(fatals[0] >= 50, `fatal duration ${fatals[0]} should be >= 50ms`);
49
- stop();
50
- stop = undefined;
51
- });
52
- it("does not fire when event loop is healthy", async () => {
53
- const stalls = [];
54
- const fatals = [];
55
- stop = startWatchdog({
56
- checkIntervalMs: 30,
57
- warnThresholdMs: 500,
58
- fatalThresholdMs: 1000,
59
- onStall: (ms) => stalls.push(ms),
60
- onFatal: (ms) => fatals.push(ms),
61
- });
62
- // Wait without blocking — interval fires on time
63
- await new Promise((resolve) => setTimeout(resolve, 100));
64
- assert.equal(stalls.length, 0, "onStall should not fire for healthy loop");
65
- assert.equal(fatals.length, 0, "onFatal should not fire for healthy loop");
66
- stop();
67
- stop = undefined;
68
- });
69
- it("stop function cleans up the interval", () => {
70
- stop = startWatchdog({
71
- checkIntervalMs: 30,
72
- warnThresholdMs: 10,
73
- fatalThresholdMs: 20,
74
- onStall: () => { },
75
- onFatal: () => { },
76
- });
77
- // Should not throw on double-stop
78
- stop();
79
- stop();
80
- stop = undefined;
81
- });
82
- });
83
- //# sourceMappingURL=watchdog.test.js.map
@@ -1,54 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readSquadWikiPages, writePage, deletePage } from "./fs.js";
4
- describe("readSquadWikiPages", () => {
5
- it("returns empty array for non-existent squad", () => {
6
- const pages = readSquadWikiPages("nonexistent-squad-xyz");
7
- assert.ok(Array.isArray(pages));
8
- assert.equal(pages.length, 0);
9
- });
10
- it("returns pages under pages/squads/{slug}/ prefix", () => {
11
- const testSlug = `test-squad-${Date.now()}`;
12
- const pagePath = `pages/squads/${testSlug}/workflow.md`;
13
- try {
14
- writePage(pagePath, "# Workflow Rules\nAlways use feature branches.");
15
- const pages = readSquadWikiPages(testSlug);
16
- assert.equal(pages.length, 1);
17
- assert.equal(pages[0].path, pagePath);
18
- assert.ok(pages[0].content.includes("feature branches"));
19
- }
20
- finally {
21
- deletePage(pagePath);
22
- }
23
- });
24
- it("filters out empty pages", () => {
25
- const testSlug = `test-squad-empty-${Date.now()}`;
26
- const pagePath = `pages/squads/${testSlug}/empty.md`;
27
- try {
28
- writePage(pagePath, "");
29
- const pages = readSquadWikiPages(testSlug);
30
- assert.equal(pages.length, 0);
31
- }
32
- finally {
33
- deletePage(pagePath);
34
- }
35
- });
36
- it("returns multiple pages for a squad", () => {
37
- const testSlug = `test-squad-multi-${Date.now()}`;
38
- const page1 = `pages/squads/${testSlug}/workflow.md`;
39
- const page2 = `pages/squads/${testSlug}/coding-standards.md`;
40
- try {
41
- writePage(page1, "# Workflow\nUse PRs.");
42
- writePage(page2, "# Standards\nESLint required.");
43
- const pages = readSquadWikiPages(testSlug);
44
- assert.equal(pages.length, 2);
45
- const paths = pages.map(p => p.path).sort();
46
- assert.deepEqual(paths, [page2, page1].sort());
47
- }
48
- finally {
49
- deletePage(page1);
50
- deletePage(page2);
51
- }
52
- });
53
- });
54
- //# sourceMappingURL=wiki-squad.test.js.map
@@ -1 +0,0 @@
1
- import{d as S,o as T,K as N,c as n,a as e,w as R,v as U,F as g,m as _,t as i,g as h,r as l,q as j,E as m,L as A,j as d,s as L,n as V,b as w,G as E,I as F}from"./index-f67odrrt.js";import{_ as B}from"./StatusIndicator.vue_vue_type_script_setup_true_lang-8U15Qp_Q.js";const D={class:"grid h-full min-h-0 gap-5 p-5 xl:grid-cols-[340px_minmax(0,1fr)]"},I={class:"flex min-h-0 flex-col overflow-hidden rounded-lg border border-border bg-card"},O={class:"border-b border-border px-4 py-4"},P={class:"min-h-0 flex-1 overflow-y-auto p-3"},z=["onClick"],J={class:"flex items-start justify-between gap-3"},M={class:"min-w-0 flex-1"},q={class:"truncate text-sm font-medium"},G={class:"mt-1 font-mono text-[10px] uppercase tracking-[0.18em] text-muted-foreground/55"},K={class:"flex items-center gap-2"},W={class:"font-mono text-[10px] uppercase text-muted-foreground/70"},H={class:"mt-2 font-mono text-[10px] text-muted-foreground/45"},Q={class:"grid min-h-0 gap-5 lg:grid-rows-[auto_minmax(180px,0.7fr)_minmax(0,1fr)]"},X={class:"rounded-lg border border-border bg-card p-5"},Y={class:"flex flex-wrap items-start justify-between gap-3"},Z={class:"mt-2 text-lg font-semibold"},ee=["disabled"],te={class:"mt-4 overflow-x-auto rounded-lg border border-white/[0.06] bg-black/30 p-4 font-mono text-xs leading-6 text-foreground/75"},oe={class:"min-h-0 overflow-hidden rounded-lg border border-border bg-card"},se={class:"h-full overflow-y-auto p-4"},re={class:"whitespace-pre-wrap break-words"},ae={key:0,class:"rounded-lg border border-dashed border-border px-4 py-10 text-center font-mono text-xs uppercase tracking-[0.22em] text-muted-foreground/45"},ne={class:"min-h-0 overflow-hidden rounded-lg border border-primary/20 bg-card"},de={class:"h-full overflow-y-auto px-4 py-4 font-mono text-xs leading-6 text-foreground/75"},ie={key:0,class:"py-8 text-center uppercase tracking-[0.22em] text-muted-foreground/45"},le={key:1,class:"py-8 text-center uppercase tracking-[0.22em] text-muted-foreground/45"},me=S({__name:"AgentActivityView",setup(ce){const c=l([]),a=l(""),v=l(null),f=l([]),u=l([]),x=l("");let s=null;const k=j(()=>{const r=x.value.trim().toLowerCase();return r?c.value.filter(t=>`${t.task_id} ${t.agent_slug} ${t.description} ${t.status}`.toLowerCase().includes(r)):c.value});async function y(){const r=await m("/api/tasks");r.ok&&(c.value=(await r.json()).tasks,!a.value&&c.value[0]&&await b(c.value[0].task_id))}async function C(r){s==null||s.close(),u.value=[];const t=await A(`/api/tasks/${encodeURIComponent(r)}/events`);s=new EventSource(t),s.onmessage=o=>{u.value=[...u.value.slice(-120),o.data]},s.onerror=()=>{s==null||s.close(),s=null}}async function b(r){a.value=r;const[t,o]=await Promise.all([m(`/api/tasks/${encodeURIComponent(r)}`),m(`/api/tasks/${encodeURIComponent(r)}/activity`)]);v.value=t.ok?await t.json():null,f.value=o.ok?(await o.json()).activity??[]:[],await C(r)}async function $(){a.value&&(await m(`/api/tasks/${encodeURIComponent(a.value)}/cancel`,{method:"POST"}),await Promise.all([b(a.value),y()]))}return T(y),N(()=>{s==null||s.close()}),(r,t)=>(d(),n("div",D,[e("section",I,[e("div",O,[t[1]||(t[1]=e("div",{class:"mb-3 font-mono text-[10px] uppercase tracking-[0.28em] text-primary"},"Task Activity",-1)),R(e("input",{"onUpdate:modelValue":t[0]||(t[0]=o=>x.value=o),class:"w-full rounded border border-border bg-sidebar px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground/35",placeholder:"Filter by task, agent, or status"},null,512),[[U,x.value]])]),e("div",P,[(d(!0),n(g,null,_(k.value,o=>(d(),n("button",{key:o.task_id,class:L(["mb-2 w-full rounded-lg border px-4 py-3 text-left transition-colors",a.value===o.task_id?"border-primary/40 bg-primary/10":"border-border bg-sidebar/40 hover:bg-white/[0.03]"]),onClick:p=>b(o.task_id)},[e("div",J,[e("div",M,[e("div",q,i(o.description),1),e("div",G,i(o.agent_slug)+" · "+i(o.task_id),1)]),e("div",K,[V(B,{status:w(E)(o.status)},null,8,["status"]),e("span",W,i(o.status),1)])]),e("div",H,i(w(F)(o.started_at)),1)],10,z))),128))])]),e("section",Q,[e("div",X,[e("div",Y,[e("div",null,[t[2]||(t[2]=e("div",{class:"font-mono text-[10px] uppercase tracking-[0.28em] text-accent-foreground"},"Task Detail",-1)),e("div",Z,i(a.value||"Select a task"),1)]),e("button",{class:"rounded border border-destructive/40 px-3 py-1.5 font-mono text-xs text-destructive transition-colors hover:bg-destructive/10",disabled:!a.value,onClick:$},"cancel task",8,ee)]),e("pre",te,i(v.value?JSON.stringify(v.value,null,2):"No task selected."),1)]),e("div",oe,[t[3]||(t[3]=e("div",{class:"border-b border-border px-4 py-3 font-mono text-[10px] uppercase tracking-[0.28em] text-muted-foreground/60"},"Activity log",-1)),e("div",se,[(d(!0),n(g,null,_(f.value,(o,p)=>(d(),n("article",{key:p,class:"mb-3 rounded-lg border border-white/[0.06] bg-sidebar/60 px-4 py-3 font-mono text-xs text-foreground/75"},[e("pre",re,i(JSON.stringify(o,null,2)),1)]))),128)),a.value&&!f.value.length?(d(),n("div",ae,"No activity reported")):h("",!0)])]),e("div",ne,[t[4]||(t[4]=e("div",{class:"border-b border-border px-4 py-3 font-mono text-[10px] uppercase tracking-[0.28em] text-primary"},"Live event preview",-1)),e("div",de,[(d(!0),n(g,null,_(u.value,(o,p)=>(d(),n("div",{key:p,class:"border-b border-border/40 py-2"},i(o),1))),128)),a.value&&!u.value.length?(d(),n("div",ie,"Waiting for live events")):h("",!0),a.value?h("",!0):(d(),n("div",le,"Select a task to attach the stream"))])])])]))}});export{me as default};
@@ -1,4 +0,0 @@
1
- import{d as S,k as M,l as O,c as s,a as e,b as i,F as x,m as f,g as b,f as y,w as T,v as I,e as L,n as N,p as $,r as v,q as h,j as n,t as c,s as u,x as j,_ as V,y as g}from"./index-f67odrrt.js";const B={class:"h-full overflow-y-auto p-3 md:p-5"},D={class:"mx-auto flex h-full max-w-[980px] flex-col overflow-hidden rounded-lg border border-border bg-card"},z={class:"flex shrink-0 items-center justify-between border-b border-border px-5 py-4"},E={class:"flex items-center gap-2"},H=["disabled"],K={key:0,class:"mb-6 rounded-lg border border-primary/20 bg-primary/5 p-6"},P={class:"mt-4 flex flex-wrap gap-2"},q=["onClick"],F={class:"space-y-3"},R={key:0,class:"whitespace-pre-wrap text-sm leading-relaxed"},W=["innerHTML"],A={key:2,class:"mt-2 font-mono text-[10px] uppercase tracking-[0.22em] text-primary/70"},U=["onKeydown"],G={class:"mt-3 flex items-center justify-between gap-3"},J={type:"submit",class:"flex items-center gap-1 rounded bg-primary/15 px-3 py-1.5 font-mono text-xs text-primary transition-colors hover:bg-primary/25"},Z=S({__name:"ChatView",setup(Q){const a=M(),d=v(""),l=v(null),w=["Summarize current squad health","List unread feed items that need action","What schedules should I watch today?","Show recent MCP changes"],p=h(()=>a.messages.length?a.messages:[{id:"welcome",role:"assistant",text:"IO online. What do you need?",createdAt:new Date().toISOString(),streaming:!1}]),_=h(()=>p.value.map(o=>`${o.id}:${o.text.length}:${o.streaming}`).join("|"));function k(){l.value&&(l.value.scrollTop=l.value.scrollHeight)}async function m(){const o=d.value.trim();o&&(d.value="",await a.sendMessage(o))}async function C(o){d.value=o,await g()}return O(_,async()=>{await g(),k()}),(o,r)=>(n(),s("div",B,[e("section",D,[e("div",z,[r[3]||(r[3]=e("div",null,[e("div",{class:"font-mono text-[10px] uppercase tracking-[0.3em] text-primary"},"Command Console"),e("div",{class:"mt-2 text-sm text-foreground/80"},"Direct operator channel into the orchestrator stream.")],-1)),e("div",E,[e("button",{class:"rounded border border-border px-3 py-1.5 font-mono text-xs text-muted-foreground transition-colors hover:border-primary/40 hover:text-primary",onClick:r[0]||(r[0]=t=>i(a).clearMessages())},"clear"),e("button",{class:"rounded border border-destructive/40 px-3 py-1.5 font-mono text-xs text-destructive transition-colors hover:bg-destructive/10",disabled:!i(a).isLoading,onClick:r[1]||(r[1]=t=>i(a).abortRun())},"abort",8,H)])]),e("div",{ref_key:"scrollPanel",ref:l,class:"flex-1 overflow-y-auto px-5 py-5"},[i(a).messages.length?b("",!0):(n(),s("div",K,[r[4]||(r[4]=e("pre",{class:"font-mono text-xs leading-5 text-primary"},`╔════════════════════════════════════╗
2
- ║ IO // MISSION CONTROL CONSOLE ║
3
- ║ route prompts, inspect streams ║
4
- ╚════════════════════════════════════╝`,-1)),e("div",P,[(n(),s(x,null,f(w,t=>e("button",{key:t,class:"rounded-full border border-border bg-sidebar px-3 py-1.5 font-mono text-xs text-foreground/75 transition-colors hover:border-primary/40 hover:text-primary",onClick:X=>C(t)},c(t),9,q)),64))])])),e("div",F,[(n(!0),s(x,null,f(p.value,t=>(n(),s("article",{key:t.id,class:u(["flex",t.role==="user"?"justify-end":"justify-start"])},[e("div",{class:u(["max-w-[82%] rounded-lg border px-4 py-3",t.role==="user"?"rounded-br-sm border-primary/20 bg-primary/15 font-mono text-primary":"rounded-bl-sm border-white/[0.06] bg-white/[0.04] text-foreground/75"])},[e("div",{class:u(["mb-2 font-mono text-[10px] uppercase tracking-[0.2em]",t.role==="user"?"text-primary":"text-muted-foreground/60"])},c(t.role),3),t.role==="user"?(n(),s("div",R,c(t.text),1)):(n(),s("div",{key:1,class:"wiki-content text-sm",innerHTML:i(j)(t.text||(t.streaming?"…":""))},null,8,W)),t.streaming?(n(),s("div",A,"streaming…")):b("",!0)],2)],2))),128))])],512),e("form",{class:"shrink-0 border-t border-border px-5 py-4",onSubmit:y(m,["prevent"])},[T(e("textarea",{"onUpdate:modelValue":r[2]||(r[2]=t=>d.value=t),rows:"4",class:"w-full rounded-lg border border-border bg-sidebar px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground/40",placeholder:"Type a prompt for IO. Ctrl+Enter sends.",onKeydown:L(y(m,["ctrl","prevent"]),["enter"])},null,40,U),[[I,d.value]]),e("div",G,[r[6]||(r[6]=e("div",{class:"font-mono text-[11px] text-muted-foreground/55"},"Ctrl+. opens the floating command bar.",-1)),e("button",J,[N(V,{name:"zap",class:"h-3 w-3"}),r[5]||(r[5]=$(" dispatch ",-1))])])],32)])]))}});export{Z as default};
@@ -1 +0,0 @@
1
- import{d as w,o as C,c as s,a as t,F as b,m as g,r as c,q as S,E as p,j as a,z as j,b as x,B as F,t as i,s as h,I as T,g as y,n as M,A as z,x as B,T as E,U as I}from"./index-f67odrrt.js";const L={class:"h-full overflow-y-auto p-5"},N={class:"mx-auto max-w-4xl"},O={key:0,class:"rounded-lg border border-border bg-card px-5 py-10 font-mono text-xs uppercase tracking-[0.22em] text-muted-foreground/50"},V={key:1,class:"rounded-lg border border-dashed border-border bg-card/30 px-5 py-10 font-mono text-xs uppercase tracking-[0.22em] text-muted-foreground/50"},A={key:2,class:"space-y-4"},R={class:"border-b border-border/50 px-5 py-3"},D=["onClick"],H={class:"flex items-start gap-3"},P={class:"mt-1.5 shrink-0"},U={key:0,class:"h-1.5 w-1.5 rounded-full bg-primary",style:{boxShadow:"var(--glow-cyan)"}},$={key:1,class:"h-1.5 w-1.5"},q={class:"min-w-0 flex-1"},G={class:"mb-1 flex flex-wrap items-center gap-1.5"},J={class:"font-mono text-[10px] text-muted-foreground/50"},K={class:"text-[10px] text-muted-foreground/50"},Q={class:"flex items-center justify-between gap-2"},W={class:"font-mono text-[10px] text-muted-foreground/40"},X={key:0,class:"text-[10px] text-primary/60"},Y={key:0,class:"mt-3 rounded border border-white/[0.06] bg-black/30 p-3 text-xs text-foreground/60"},Z=["innerHTML"],oe=w({__name:"FeedView",setup(ee){const f=c([]),m=c({}),l=c(null),u=c(!1),_=S(()=>{var r;const o=new Map;for(const n of f.value){const e=n.squad_slug??"io",d=m.value[e];o.has(e)||o.set(e,{key:e,label:(d==null?void 0:d.name)??I(e),color:(d==null?void 0:d.color)??"#8a8a99",entries:[]}),(r=o.get(e))==null||r.entries.push(n)}return[...o.values()]});async function v(){u.value=!0;try{const[o,r]=await Promise.all([p("/api/feed?limit=120"),p("/api/squads")]);if(o.ok&&(f.value=(await o.json()).entries),r.ok){const n=await r.json();m.value=Object.fromEntries(n.squads.map(e=>[e.slug??e.id??e.name.toLowerCase(),{name:e.name,universe:e.universe,color:e.color??"#8a8a99"}]))}}finally{u.value=!1}}async function k(o){l.value=l.value===o.id?null:o.id,o.read_at||(await p(`/api/feed/${o.id}/read`,{method:"POST"}).catch(()=>null),o.read_at=new Date().toISOString())}return C(v),(o,r)=>(a(),s("div",L,[t("div",N,[t("div",{class:"mb-5 flex items-center justify-between gap-3"},[r[0]||(r[0]=t("div",null,[t("div",{class:"font-mono text-[10px] uppercase tracking-[0.3em] text-primary"},"Activity Feed"),t("div",{class:"mt-2 text-sm text-foreground/70"},"Unread events keep the cyan indicator and expand inline for detail.")],-1)),t("button",{class:"rounded border border-border px-3 py-1.5 font-mono text-xs text-muted-foreground transition-colors hover:border-primary/40 hover:text-primary",onClick:v},"refresh")]),u.value?(a(),s("div",O,"Syncing feed…")):_.value.length?(a(),s("div",A,[(a(!0),s(b,null,g(_.value,n=>(a(),s("section",{key:n.key,class:"overflow-hidden rounded-lg border border-border bg-card"},[t("div",R,[t("span",{class:"rounded px-1.5 py-0.5 font-mono text-[10px]",style:j({color:n.color,backgroundColor:x(F)(n.color,.15)})},i(n.label),5)]),(a(!0),s(b,null,g(n.entries,e=>(a(),s("article",{key:e.id,class:h(["cursor-pointer border-b border-border/40 px-5 py-4 transition-colors last:border-b-0 hover:bg-white/[0.02]",e.read_at?"":"bg-white/[0.015]"]),onClick:d=>k(e)},[t("div",H,[t("div",P,[e.read_at?(a(),s("div",$)):(a(),s("div",U))]),t("div",q,[t("div",G,[t("span",J,i(e.source_ref??e.instance_id??e.task_id??e.type),1),r[1]||(r[1]=t("span",{class:"font-mono text-[10px] text-muted-foreground/40"},"·",-1)),t("span",K,i(e.source_type??"IO"),1)]),t("div",{class:h(["mb-1 text-sm leading-snug",e.read_at?"text-foreground/70":"text-foreground"])},i(e.title),3),t("div",Q,[t("span",W,i(x(T)(e.created_at)),1),e.body?(a(),s("span",X,i(l.value===e.id?"▲ collapse":"▼ details"),1)):y("",!0)]),M(E,{"enter-active-class":"duration-150 ease-out","enter-from-class":"opacity-0 -translate-y-1","enter-to-class":"opacity-100 translate-y-0","leave-active-class":"duration-100 ease-in","leave-from-class":"opacity-100 translate-y-0","leave-to-class":"opacity-0 -translate-y-1"},{default:z(()=>[l.value===e.id&&e.body?(a(),s("div",Y,[t("div",{class:"wiki-content text-xs font-mono leading-relaxed",innerHTML:x(B)(e.body)},null,8,Z)])):y("",!0)]),_:2},1024)])])],10,D))),128))]))),128))])):(a(),s("div",V,"No feed activity."))])]))}});export{oe as default};
@@ -1 +0,0 @@
1
- import{d as g,o as _,c as d,a as e,t as o,F as h,m as k,g as y,b as x,x as w,I as b,r as m,q as C,E as f,j as i,s as j}from"./index-f67odrrt.js";const E={class:"grid h-full min-h-0 gap-5 p-5 xl:grid-cols-[360px_minmax(0,1fr)]"},I={class:"flex min-h-0 flex-col overflow-hidden rounded-lg border border-border bg-card"},L={class:"border-b border-border px-5 py-4"},M={class:"mt-2 flex items-end justify-between gap-3"},T={class:"text-3xl font-semibold"},B={class:"min-h-0 flex-1 overflow-y-auto p-3"},F=["onClick"],N={class:"flex items-center justify-between gap-3"},R={class:"truncate text-sm font-medium"},S={class:"font-mono text-[10px] uppercase tracking-[0.2em] text-accent-foreground"},V={class:"mt-2 text-sm leading-6 text-foreground/65"},$={class:"mt-3 font-mono text-[11px] uppercase tracking-[0.18em] text-muted-foreground/55"},q={key:0,class:"rounded-lg border border-dashed border-border px-4 py-12 text-center font-mono text-xs uppercase tracking-[0.22em] text-muted-foreground/45"},D={class:"flex min-h-0 flex-col overflow-hidden rounded-lg border border-border bg-card"},H={class:"border-b border-border px-6 py-4"},z={class:"mt-2 text-lg font-semibold"},P={class:"min-h-0 flex-1 overflow-y-auto px-6 py-6"},A={key:0,class:"grid gap-5 lg:grid-cols-[1fr_220px]"},G=["innerHTML"],J={class:"rounded-lg border border-white/[0.06] bg-sidebar/60 px-5 py-5 font-mono text-xs uppercase tracking-[0.18em] text-muted-foreground/55"},K={class:"mt-2 text-foreground"},O={class:"mt-2 text-accent-foreground"},Q={class:"mt-2 text-foreground/80"},U={key:1,class:"flex h-full items-center justify-center font-mono text-xs uppercase tracking-[0.22em] text-muted-foreground/45"},Z=g({__name:"InboxView",setup(W){const a=m([]),c=m(null),p=m(0),s=C(()=>a.value.find(n=>n.id===c.value)??null);async function u(){var l;const[n,t]=await Promise.all([f("/api/inbox/count"),f("/api/inbox")]);n.ok&&(p.value=(await n.json()).count??0),t.ok&&(a.value=(await t.json()).entries,c.value=((l=a.value[0])==null?void 0:l.id)??null)}async function v(n){await f(`/api/inbox/${n}`,{method:"DELETE"}),await u()}return _(u),(n,t)=>{var l;return i(),d("div",E,[e("section",I,[e("div",L,[t[2]||(t[2]=e("div",{class:"font-mono text-[10px] uppercase tracking-[0.28em] text-accent-foreground"},"Inbox",-1)),e("div",M,[e("div",null,[e("div",T,o(p.value),1),t[1]||(t[1]=e("div",{class:"font-mono text-xs uppercase tracking-[0.18em] text-muted-foreground/55"},"active inbox entries",-1))]),e("button",{class:"rounded border border-border px-3 py-1.5 font-mono text-xs text-muted-foreground transition-colors hover:border-accent-foreground/40 hover:text-accent-foreground",onClick:u},"refresh")])]),e("div",B,[(i(!0),d(h,null,k(a.value,r=>(i(),d("button",{key:r.id,class:j(["mb-2 w-full rounded-lg border px-4 py-4 text-left transition-colors",c.value===r.id?"border-accent-foreground/40 bg-accent-foreground/10":"border-border bg-sidebar/40 hover:bg-white/[0.03]"]),onClick:X=>c.value=r.id},[e("div",N,[e("div",R,o(r.title),1),e("div",S,o(r.type),1)]),e("div",V,o(r.body),1),e("div",$,o(x(b)(r.created_at)),1)],10,F))),128)),a.value.length?y("",!0):(i(),d("div",q,"Inbox is empty"))])]),e("section",D,[e("div",H,[t[3]||(t[3]=e("div",{class:"font-mono text-[10px] uppercase tracking-[0.28em] text-primary"},"Selected message",-1)),e("div",z,o(((l=s.value)==null?void 0:l.title)??"No message selected"),1)]),e("div",P,[s.value?(i(),d("div",A,[e("article",{class:"wiki-content rounded-lg border border-white/[0.06] bg-sidebar/60 px-5 py-5 text-sm leading-7 text-foreground/75",innerHTML:x(w)(s.value.body)},null,8,G),e("aside",J,[t[4]||(t[4]=e("div",null,"source",-1)),e("div",K,o(s.value.source_type??"inbox"),1),t[5]||(t[5]=e("div",{class:"mt-4"},"reference",-1)),e("div",O,o(s.value.source_ref??s.value.squad_slug??"—"),1),t[6]||(t[6]=e("div",{class:"mt-4"},"received",-1)),e("div",Q,o(x(b)(s.value.created_at)),1),e("button",{class:"mt-6 w-full rounded border border-destructive/40 px-4 py-2 text-destructive transition-colors hover:bg-destructive/10",onClick:t[0]||(t[0]=r=>v(s.value.id))},"dismiss")])])):(i(),d("div",U,"Select a message from the left rail"))])])])}}});export{Z as default};
@@ -1 +0,0 @@
1
- import{d as y,u as v,o as w,c as i,a as e,t as c,b as r,w as g,v as f,e as h,f as k,g as _,h as C,r as p,i as V,j as d}from"./index-f67odrrt.js";const E={class:"dark flex h-full items-center justify-center bg-background px-6"},M={class:"w-full max-w-md rounded-lg border border-border bg-card px-8 py-8 shadow-[0_20px_60px_rgba(0,0,0,0.45)]"},S={class:"mt-2 text-sm leading-6 text-muted-foreground"},q={key:0,class:"mt-6 space-y-4"},A={class:"block"},B={class:"block"},K=["onKeydown"],j={key:0,class:"rounded border border-destructive/25 bg-destructive/10 px-4 py-3 text-sm text-destructive"},D=["disabled"],N=y({__name:"LoginView",setup(I){const m=C(),b=V(),o=v(),l=p(""),u=p(""),n=p("");async function x(){n.value="";try{await o.signIn(l.value,u.value);const s=typeof b.query.redirect=="string"?b.query.redirect:"/chat";m.push(s)}catch(s){n.value=s instanceof Error?s.message:"Sign-in failed."}}return w(()=>{o.init()}),(s,t)=>(d(),i("div",E,[e("section",M,[t[5]||(t[5]=e("div",{class:"font-mono text-xl font-bold tracking-tight text-primary text-glow-cyan"},"IO",-1)),t[6]||(t[6]=e("div",{class:"mt-3 text-2xl font-semibold"},"Mission Control Login",-1)),e("p",S,c(r(o).authEnabled?"Authenticate against Supabase to access squads, feed, wiki, and orchestration controls.":"Authentication is disabled for this workspace."),1),r(o).authEnabled?(d(),i("div",q,[e("label",A,[t[3]||(t[3]=e("span",{class:"mb-2 block font-mono text-[11px] uppercase tracking-[0.18em] text-muted-foreground/70"},"email",-1)),g(e("input",{"onUpdate:modelValue":t[0]||(t[0]=a=>l.value=a),type:"email",class:"w-full rounded border border-border bg-sidebar px-4 py-3 text-sm text-foreground"},null,512),[[f,l.value]])]),e("label",B,[t[4]||(t[4]=e("span",{class:"mb-2 block font-mono text-[11px] uppercase tracking-[0.18em] text-muted-foreground/70"},"password",-1)),g(e("input",{"onUpdate:modelValue":t[1]||(t[1]=a=>u.value=a),type:"password",class:"w-full rounded border border-border bg-sidebar px-4 py-3 text-sm text-foreground",onKeydown:h(k(x,["prevent"]),["enter"])},null,40,K),[[f,u.value]])]),n.value?(d(),i("div",j,c(n.value),1)):_("",!0),e("button",{class:"w-full rounded bg-primary/15 px-4 py-3 font-mono text-sm text-primary transition-colors hover:bg-primary/25",disabled:r(o).loading,onClick:x},c(r(o).loading?"connecting…":"sign in"),9,D)])):(d(),i("button",{key:1,class:"mt-6 w-full rounded bg-primary/15 px-4 py-3 font-mono text-sm text-primary transition-colors hover:bg-primary/25",onClick:t[2]||(t[2]=a=>r(m).push("/chat"))},"continue to mission control"))])]))}});export{N as default};