heyio 0.17.1 β 0.18.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.
- package/README.md +5 -0
- package/dist/api/server.js +54 -48
- package/package.json +1 -1
- package/web-dist/assets/InboxView-Bl3JrKsd.js +1 -0
- package/web-dist/assets/index-6BKFZclJ.js +86 -0
- package/web-dist/assets/index-Ci93uejh.css +10 -0
- package/web-dist/index.html +2 -2
- package/web-dist/assets/InboxView-D9wKrR06.js +0 -1
- package/web-dist/assets/index-BGJDwDoi.css +0 -1
- package/web-dist/assets/index-BGjTyM3L.js +0 -84
package/README.md
CHANGED
|
@@ -313,8 +313,13 @@ npm run tui
|
|
|
313
313
|
|
|
314
314
|
# Run the daemon directly
|
|
315
315
|
npm run daemon
|
|
316
|
+
|
|
317
|
+
# Run the test suite
|
|
318
|
+
npm test
|
|
316
319
|
```
|
|
317
320
|
|
|
321
|
+
Tests use the Node.js built-in test runner with [tsx](https://github.com/privatenumber/tsx) for TypeScript support. Test files live alongside source files as `*.test.ts`.
|
|
322
|
+
|
|
318
323
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed development guidelines.
|
|
319
324
|
|
|
320
325
|
## π License
|
package/dist/api/server.js
CHANGED
|
@@ -44,7 +44,11 @@ export async function startApiServer() {
|
|
|
44
44
|
app.use((_req, res, next) => {
|
|
45
45
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
46
46
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
47
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
47
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
48
|
+
if (_req.method === "OPTIONS") {
|
|
49
|
+
res.sendStatus(204);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
48
52
|
next();
|
|
49
53
|
});
|
|
50
54
|
// Build API router
|
|
@@ -61,7 +65,9 @@ export async function startApiServer() {
|
|
|
61
65
|
supabaseAnonKey: config.supabaseAnonKey ?? null,
|
|
62
66
|
});
|
|
63
67
|
});
|
|
64
|
-
//
|
|
68
|
+
// Apply auth middleware β all routes below require a valid JWT
|
|
69
|
+
api.use(requireAuth);
|
|
70
|
+
// Skills read endpoints
|
|
65
71
|
api.get("/skills", (_req, res) => {
|
|
66
72
|
try {
|
|
67
73
|
const skills = listSkills();
|
|
@@ -93,7 +99,7 @@ export async function startApiServer() {
|
|
|
93
99
|
res.status(500).json({ error: e instanceof Error ? e.message : String(e) });
|
|
94
100
|
}
|
|
95
101
|
});
|
|
96
|
-
// Inbox read endpoints
|
|
102
|
+
// Inbox read endpoints
|
|
97
103
|
api.get("/inbox/count", (_req, res) => {
|
|
98
104
|
try {
|
|
99
105
|
const count = countInboxEntries();
|
|
@@ -114,11 +120,54 @@ export async function startApiServer() {
|
|
|
114
120
|
res.status(500).json({ error: e instanceof Error ? e.message : String(e) });
|
|
115
121
|
}
|
|
116
122
|
});
|
|
117
|
-
//
|
|
118
|
-
api.use(requireAuth);
|
|
123
|
+
// Status endpoint
|
|
119
124
|
api.get("/status", (_req, res) => {
|
|
120
125
|
res.json({ version: IO_VERSION, uptime: process.uptime() });
|
|
121
126
|
});
|
|
127
|
+
// Notifications endpoint
|
|
128
|
+
api.get("/notifications", (_req, res) => {
|
|
129
|
+
try {
|
|
130
|
+
const unreadOnly = _req.query.unread === "true";
|
|
131
|
+
const rows = unreadOnly
|
|
132
|
+
? listUnreadNotifications()
|
|
133
|
+
: (() => {
|
|
134
|
+
const rawLimit = _req.query.limit;
|
|
135
|
+
const parsed = typeof rawLimit === "string" ? Number.parseInt(rawLimit, 10) : NaN;
|
|
136
|
+
const limit = Number.isFinite(parsed) && parsed > 0 ? Math.min(parsed, 200) : 50;
|
|
137
|
+
return listRecentNotifications(limit);
|
|
138
|
+
})();
|
|
139
|
+
const unreadCount = countUnreadNotifications();
|
|
140
|
+
const notifications = rows.map(({ id, title, text, created_at, read_at, source_type, source_ref }) => {
|
|
141
|
+
let source = { type: source_type };
|
|
142
|
+
if (source_ref) {
|
|
143
|
+
try {
|
|
144
|
+
const parsed = JSON.parse(source_ref);
|
|
145
|
+
source = { type: source_type, ...parsed };
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// source_ref is not valid JSON β fall back to type-only
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return { id, title, text, created_at, read_at, source };
|
|
152
|
+
});
|
|
153
|
+
res.json({ notifications, unreadCount });
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
console.error("Error listing notifications:", e);
|
|
157
|
+
res.status(500).json({ error: "Failed to list notifications" });
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
// SSE events endpoint
|
|
161
|
+
api.get("/events", (req, res) => {
|
|
162
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
163
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
164
|
+
res.setHeader("Connection", "keep-alive");
|
|
165
|
+
res.flushHeaders();
|
|
166
|
+
sseConnections.add(res);
|
|
167
|
+
req.on("close", () => {
|
|
168
|
+
sseConnections.delete(res);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
122
171
|
// Install a skill from pasted SKILL.md content (issue #117)
|
|
123
172
|
api.post("/skills/paste", (req, res) => {
|
|
124
173
|
const { content: skillContent, slug } = req.body;
|
|
@@ -602,39 +651,6 @@ export async function startApiServer() {
|
|
|
602
651
|
res.status(500).json({ error: (e instanceof Error ? e.message : String(e)) });
|
|
603
652
|
}
|
|
604
653
|
});
|
|
605
|
-
// Notifications endpoints
|
|
606
|
-
api.get("/notifications", (_req, res) => {
|
|
607
|
-
try {
|
|
608
|
-
const unreadOnly = _req.query.unread === "true";
|
|
609
|
-
const rows = unreadOnly
|
|
610
|
-
? listUnreadNotifications()
|
|
611
|
-
: (() => {
|
|
612
|
-
const rawLimit = _req.query.limit;
|
|
613
|
-
const parsed = typeof rawLimit === "string" ? Number.parseInt(rawLimit, 10) : NaN;
|
|
614
|
-
const limit = Number.isFinite(parsed) && parsed > 0 ? Math.min(parsed, 200) : 50;
|
|
615
|
-
return listRecentNotifications(limit);
|
|
616
|
-
})();
|
|
617
|
-
const unreadCount = countUnreadNotifications();
|
|
618
|
-
const notifications = rows.map(({ id, title, text, created_at, read_at, source_type, source_ref }) => {
|
|
619
|
-
let source = { type: source_type };
|
|
620
|
-
if (source_ref) {
|
|
621
|
-
try {
|
|
622
|
-
const parsed = JSON.parse(source_ref);
|
|
623
|
-
source = { type: source_type, ...parsed };
|
|
624
|
-
}
|
|
625
|
-
catch {
|
|
626
|
-
// source_ref is not valid JSON β fall back to type-only
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
return { id, title, text, created_at, read_at, source };
|
|
630
|
-
});
|
|
631
|
-
res.json({ notifications, unreadCount });
|
|
632
|
-
}
|
|
633
|
-
catch (e) {
|
|
634
|
-
console.error("Error listing notifications:", e);
|
|
635
|
-
res.status(500).json({ error: "Failed to list notifications" });
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
654
|
api.post("/notifications/read-all", (_req, res) => {
|
|
639
655
|
try {
|
|
640
656
|
const marked = markAllNotificationsRead();
|
|
@@ -690,16 +706,6 @@ export async function startApiServer() {
|
|
|
690
706
|
});
|
|
691
707
|
res.json({ response: fullResponse });
|
|
692
708
|
});
|
|
693
|
-
api.get("/events", (req, res) => {
|
|
694
|
-
res.setHeader("Content-Type", "text/event-stream");
|
|
695
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
696
|
-
res.setHeader("Connection", "keep-alive");
|
|
697
|
-
res.flushHeaders();
|
|
698
|
-
sseConnections.add(res);
|
|
699
|
-
req.on("close", () => {
|
|
700
|
-
sseConnections.delete(res);
|
|
701
|
-
});
|
|
702
|
-
});
|
|
703
709
|
// Wiki endpoints (issue #105)
|
|
704
710
|
function extractWikiTitle(pageContent, fallback) {
|
|
705
711
|
const match = pageContent.match(/^#\s+(.+)/m);
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as L,o as Z,c as n,a,t as i,b as m,e as f,f as p,F as H,r as E,g as u,h as g,i as l,w as T,u as V,j as S,_ as v}from"./index-6BKFZclJ.js";const j={class:"flex flex-col h-full p-3 sm:p-6"},D={class:"flex justify-between items-center mb-6"},C={key:0,class:"text-[10px] font-mono text-accent bg-accent/10 border border-accent/20 px-2 py-0.5 rounded-full"},N={key:0,class:"flex-1 flex items-center justify-center"},$={key:1,class:"flex-1 flex flex-col items-center justify-center"},z={class:"w-14 h-14 rounded-xl bg-surface-2 border border-edge flex items-center justify-center mb-4"},F={key:2,class:"space-y-2 overflow-y-auto flex-1 pr-1"},A=["onClick"],B={class:"flex-1 min-w-0"},I={class:"flex items-start justify-between gap-2"},P={class:"text-sm font-semibold text-txt-primary leading-snug"},W={class:"text-[10px] text-txt-muted font-mono shrink-0 mt-0.5"},q={key:0,class:"text-xs text-txt-secondary mt-1 line-clamp-2 leading-relaxed"},G=["onClick","disabled"],J={key:0,class:"px-4 pb-4"},K=["innerHTML"],O={key:3,class:"mt-3 flex items-center gap-2 text-sm text-red-400 bg-red-500/10 border border-red-500/20 rounded-xl px-3.5 py-2.5"},U=L({__name:"InboxView",setup(Q){const o=u([]),h=u(!0),r=u(new Set),d=u(new Set),c=u(null);function b(t){try{const e=t.includes("T")||t.endsWith("Z")?t:t.replace(" ","T")+"Z";return new Date(e).toLocaleString()}catch{return t}}function _(t){return t.replace(/[#*`_~[\]()]/g,"").slice(0,200)}function w(t){const e=new Set(r.value);e.has(t)?e.delete(t):e.add(t),r.value=e}async function y(){h.value=!0,c.value=null;try{const t=await g("/api/inbox");if(t.ok){const e=await t.json();o.value=e.entries??[]}}catch{}h.value=!1}async function k(t){if(!window.confirm("Delete this inbox entry?"))return;const e=new Set(d.value);e.add(t),d.value=e;try{const s=await g(`/api/inbox/${t}`,{method:"DELETE"});if(s.ok){o.value=o.value.filter(M=>M.id!==t);const x=new Set(r.value);x.delete(t),r.value=x}else c.value=`Failed to delete entry (HTTP ${s.status})`}catch(s){c.value=s instanceof Error?s.message:"Delete failed"}finally{const s=new Set(d.value);s.delete(t),d.value=s}}return Z(y),(t,e)=>(l(),n("div",j,[a("div",D,[e[0]||(e[0]=a("div",null,[a("h2",{class:"text-xl font-semibold text-txt-primary tracking-tight"},"Inbox"),a("p",{class:"text-xs text-txt-muted mt-0.5"},"Messages & incoming items")],-1)),o.value.length>0?(l(),n("span",C,i(o.value.length)+" item"+i(o.value.length===1?"":"s"),1)):m("",!0)]),h.value?(l(),n("div",N,[...e[1]||(e[1]=[a("div",{class:"flex items-center gap-3 text-txt-muted text-sm"},[a("div",{class:"w-1.5 h-1.5 rounded-full bg-accent animate-pulse"}),f(" Loading⦠")],-1)])])):o.value.length===0?(l(),n("div",$,[a("div",z,[p(v,{paths:'<path d="M6 3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3V6a3 3 0 0 0-3-3H6Zm10 7h-3.5a.5.5 0 0 0-.5.5v.01a1.75 1.75 0 0 1-.03.3c-.04.2-.1.46-.23.72-.13.25-.3.49-.57.66-.26.18-.63.31-1.17.31-.54 0-.9-.13-1.17-.3a1.7 1.7 0 0 1-.57-.67A2.57 2.57 0 0 1 8 10.5v-.01a.5.5 0 0 0-.5-.5H4V6c0-1.1.9-2 2-2h8a2 2 0 0 1 2 2v4ZM4 11h3.05c.05.26.14.62.32.97.18.38.47.76.9 1.06.45.29 1.02.47 1.73.47s1.28-.18 1.72-.47c.44-.3.73-.68.91-1.06.18-.35.27-.7.32-.97H16v3a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-3Z"/>',size:28,class:"text-txt-muted"})]),e[2]||(e[2]=a("p",{class:"text-txt-muted text-sm font-medium"},"Inbox is empty",-1)),e[3]||(e[3]=a("p",{class:"text-txt-muted/60 text-xs mt-1"},"No messages right now",-1))])):(l(),n("ul",F,[(l(!0),n(H,null,E(o.value,s=>(l(),n("li",{key:s.id,class:"group bg-surface-2/50 border border-edge rounded-xl hover:border-edge-bright hover:shadow-card transition-all duration-200 overflow-hidden animate-fade-in glow-inner"},[a("div",{class:"flex items-start gap-3 p-4 cursor-pointer",onClick:x=>w(s.id)},[e[4]||(e[4]=a("div",{class:"mt-1.5 w-1.5 h-1.5 rounded-full bg-accent shadow-glow-sm shrink-0"},null,-1)),a("div",B,[a("div",I,[a("span",P,i(s.title),1),a("span",W,i(b(s.created_at)),1)]),r.value.has(s.id)?m("",!0):(l(),n("p",q,i(_(s.body)),1))]),a("button",{onClick:T(x=>k(s.id),["stop"]),disabled:d.value.has(s.id),class:"opacity-0 group-hover:opacity-100 shrink-0 p-1.5 rounded-lg text-txt-muted hover:text-red-400 hover:bg-red-500/10 border border-transparent hover:border-red-500/20 disabled:opacity-30 transition-all duration-200",title:"Delete entry"},[p(v,{paths:'<path d="M8.5 4h3a1.5 1.5 0 0 0-3 0Zm-1 0a2.5 2.5 0 0 1 5 0h5a.5.5 0 0 1 0 1h-1.05l-1.2 10.34A3 3 0 0 1 12.27 18H7.73a3 3 0 0 1-2.98-2.66L3.55 5H2.5a.5.5 0 0 1 0-1h5ZM5.74 15.23A2 2 0 0 0 7.73 17h4.54a2 2 0 0 0 1.99-1.77L15.44 5H4.56l1.18 10.23ZM8.5 7.5c.28 0 .5.22.5.5v6a.5.5 0 0 1-1 0V8c0-.28.22-.5.5-.5ZM12 8a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V8Z"/>',size:14})],8,G)],8,A),r.value.has(s.id)?(l(),n("div",J,[a("div",{class:"text-sm text-txt-secondary bg-surface-0/60 rounded-xl p-4 border border-edge/50 wiki-content leading-relaxed",innerHTML:V(S)(s.body)},null,8,K)])):m("",!0)]))),128))])),c.value?(l(),n("div",O,[p(v,{paths:'<path d="m4.09 4.22.06-.07a.5.5 0 0 1 .63-.06l.07.06L10 9.29l5.15-5.14a.5.5 0 0 1 .63-.06l.07.06c.18.17.2.44.06.63l-.06.07L10.71 10l5.14 5.15c.18.17.2.44.06.63l-.06.07a.5.5 0 0 1-.63.06l-.07-.06L10 10.71l-5.15 5.14a.5.5 0 0 1-.63.06l-.07-.06a.5.5 0 0 1-.06-.63l.06-.07L9.29 10 4.15 4.85a.5.5 0 0 1-.06-.63l.06-.07-.06.07Z"/>',size:16,class:"shrink-0"}),f(" "+i(c.value),1)])):m("",!0)]))}});export{U as default};
|