@kingcrab/pi-imessage 0.0.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.
- package/README.md +120 -0
- package/dist/agent.d.ts +22 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +341 -0
- package/dist/agent.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +179 -0
- package/dist/cli.js.map +1 -0
- package/dist/imessage.d.ts +32 -0
- package/dist/imessage.d.ts.map +1 -0
- package/dist/imessage.js +65 -0
- package/dist/imessage.js.map +1 -0
- package/dist/logger.d.ts +33 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +103 -0
- package/dist/logger.js.map +1 -0
- package/dist/main.d.ts +5 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +66 -0
- package/dist/main.js.map +1 -0
- package/dist/pipeline.d.ts +26 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +48 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/queue.d.ts +16 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +47 -0
- package/dist/queue.js.map +1 -0
- package/dist/self-echo.d.ts +19 -0
- package/dist/self-echo.d.ts.map +1 -0
- package/dist/self-echo.js +54 -0
- package/dist/self-echo.js.map +1 -0
- package/dist/send.d.ts +17 -0
- package/dist/send.d.ts.map +1 -0
- package/dist/send.js +107 -0
- package/dist/send.js.map +1 -0
- package/dist/settings.d.ts +39 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +74 -0
- package/dist/settings.js.map +1 -0
- package/dist/store.d.ts +41 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +72 -0
- package/dist/store.js.map +1 -0
- package/dist/tasks.d.ts +88 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +260 -0
- package/dist/tasks.js.map +1 -0
- package/dist/types.d.ts +77 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -0
- package/dist/watch.d.ts +21 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +137 -0
- package/dist/watch.js.map +1 -0
- package/dist/web/data.d.ts +11 -0
- package/dist/web/data.d.ts.map +1 -0
- package/dist/web/data.js +56 -0
- package/dist/web/data.js.map +1 -0
- package/dist/web/html.d.ts +5 -0
- package/dist/web/html.d.ts.map +1 -0
- package/dist/web/html.js +16 -0
- package/dist/web/html.js.map +1 -0
- package/dist/web/index.d.ts +15 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +116 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/render.d.ts +5 -0
- package/dist/web/render.d.ts.map +1 -0
- package/dist/web/render.js +50 -0
- package/dist/web/render.js.map +1 -0
- package/dist/web/templates/page.eta +85 -0
- package/package.json +42 -0
package/dist/web/html.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** HTML utility functions. */
|
|
2
|
+
/** Format as "[YYYY-MM-DD HH:MM:SS]" in local time. */
|
|
3
|
+
export function formatTime(iso) {
|
|
4
|
+
const date = new Date(iso);
|
|
5
|
+
const year = date.getFullYear();
|
|
6
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
7
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
8
|
+
const hour = String(date.getHours()).padStart(2, "0");
|
|
9
|
+
const minute = String(date.getMinutes()).padStart(2, "0");
|
|
10
|
+
const second = String(date.getSeconds()).padStart(2, "0");
|
|
11
|
+
return `[${year}-${month}-${day} ${hour}:${minute}:${second}]`;
|
|
12
|
+
}
|
|
13
|
+
export function anchorId(guid) {
|
|
14
|
+
return guid.replace(/[^a-zA-Z0-9]/g, "_");
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/web/html.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAE9B,uDAAuD;AACvD,MAAM,UAAU,UAAU,CAAC,GAAW;IACrC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1D,OAAO,IAAI,IAAI,IAAI,KAAK,IAAI,GAAG,IAAI,IAAI,IAAI,MAAM,IAAI,MAAM,GAAG,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAY;IACpC,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Web server: serves the chat log UI and SSE updates. */
|
|
2
|
+
import type { Settings } from "../settings.js";
|
|
3
|
+
export interface WebServerConfig {
|
|
4
|
+
workingDir: string;
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
getSettings: () => Settings;
|
|
8
|
+
setSettings: (settings: Settings) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface WebServer {
|
|
11
|
+
start(): void;
|
|
12
|
+
stop(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare function createWebServer(config: WebServerConfig): WebServer;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAI1D,OAAO,KAAK,EAAiB,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI9D,MAAM,WAAW,eAAe;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,QAAQ,CAAC;IAC5B,WAAW,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,SAAS;IACzB,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CA6GlE"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/** Web server: serves the chat log UI and SSE updates. */
|
|
2
|
+
import { existsSync, watch } from "node:fs";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { getChatBlocks } from "./data.js";
|
|
5
|
+
import { renderPage } from "./render.js";
|
|
6
|
+
export function createWebServer(config) {
|
|
7
|
+
const { workingDir, host, port, getSettings, setSettings } = config;
|
|
8
|
+
const sseClients = new Set();
|
|
9
|
+
let watcher = null;
|
|
10
|
+
let debounceTimer = null;
|
|
11
|
+
function broadcast() {
|
|
12
|
+
if (debounceTimer)
|
|
13
|
+
return;
|
|
14
|
+
debounceTimer = setTimeout(() => {
|
|
15
|
+
debounceTimer = null;
|
|
16
|
+
for (const client of sseClients) {
|
|
17
|
+
client.write("event: update\ndata: {}\n\n");
|
|
18
|
+
}
|
|
19
|
+
}, 300);
|
|
20
|
+
}
|
|
21
|
+
function startWatcher() {
|
|
22
|
+
if (!existsSync(workingDir))
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
watcher = watch(workingDir, { recursive: true }, (_event, filename) => {
|
|
26
|
+
if (filename?.endsWith("log.jsonl"))
|
|
27
|
+
broadcast();
|
|
28
|
+
});
|
|
29
|
+
watcher.on("error", () => { });
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// workingDir may not exist yet
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Toggle reply for a chatGuid by updating whitelist/blacklist. Does not touch "*" wildcards. */
|
|
36
|
+
function toggleChatReply(chatGuid, enabled) {
|
|
37
|
+
const { chatAllowlist, ...rest } = getSettings();
|
|
38
|
+
const whitelist = chatAllowlist.whitelist.filter((id) => id !== chatGuid);
|
|
39
|
+
const blacklist = chatAllowlist.blacklist.filter((id) => id !== chatGuid);
|
|
40
|
+
if (enabled) {
|
|
41
|
+
whitelist.push(chatGuid);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
blacklist.push(chatGuid);
|
|
45
|
+
}
|
|
46
|
+
setSettings({ ...rest, chatAllowlist: { whitelist, blacklist } });
|
|
47
|
+
}
|
|
48
|
+
function handleRequest(request, response) {
|
|
49
|
+
const url = new URL(request.url ?? "/", `http://localhost:${port}`);
|
|
50
|
+
if (url.pathname === "/events") {
|
|
51
|
+
response.writeHead(200, {
|
|
52
|
+
"Content-Type": "text/event-stream",
|
|
53
|
+
"Cache-Control": "no-cache",
|
|
54
|
+
Connection: "keep-alive",
|
|
55
|
+
});
|
|
56
|
+
response.write("retry: 5000\n\n");
|
|
57
|
+
sseClients.add(response);
|
|
58
|
+
request.on("close", () => sseClients.delete(response));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (request.method === "POST" && url.pathname === "/toggle") {
|
|
62
|
+
const chunks = [];
|
|
63
|
+
request.on("data", (chunk) => chunks.push(chunk));
|
|
64
|
+
request.on("end", () => {
|
|
65
|
+
try {
|
|
66
|
+
const body = JSON.parse(Buffer.concat(chunks).toString());
|
|
67
|
+
toggleChatReply(body.chatGuid, body.enabled);
|
|
68
|
+
console.log(`[web] toggled reply for ${body.chatGuid}: enabled=${body.enabled}`);
|
|
69
|
+
response.writeHead(200, { "Content-Type": "application/json" });
|
|
70
|
+
response.end(JSON.stringify({ ok: true }));
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error("[web] toggle error:", error);
|
|
74
|
+
response.writeHead(400);
|
|
75
|
+
response.end("bad request");
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const blocks = getChatBlocks(workingDir);
|
|
81
|
+
const settings = getSettings();
|
|
82
|
+
const html = renderPage(blocks, settings);
|
|
83
|
+
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
84
|
+
response.end(html);
|
|
85
|
+
}
|
|
86
|
+
const server = createServer(handleRequest);
|
|
87
|
+
return {
|
|
88
|
+
start() {
|
|
89
|
+
startWatcher();
|
|
90
|
+
server.listen(port, host, () => {
|
|
91
|
+
console.log(`[web] UI available at http://${host}:${port}`);
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
stop() {
|
|
95
|
+
if (debounceTimer)
|
|
96
|
+
clearTimeout(debounceTimer);
|
|
97
|
+
watcher?.close();
|
|
98
|
+
for (const client of sseClients)
|
|
99
|
+
client.end();
|
|
100
|
+
sseClients.clear();
|
|
101
|
+
return new Promise((resolve, reject) => {
|
|
102
|
+
server.close((error) => {
|
|
103
|
+
if (error) {
|
|
104
|
+
console.error("[web] server close error:", error);
|
|
105
|
+
reject(error);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.log("[web] server closed");
|
|
109
|
+
resolve();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Render the full HTML page from chat blocks using Eta templates. */
|
|
2
|
+
import type { Settings } from "../settings.js";
|
|
3
|
+
import type { ChatBlock } from "./data.js";
|
|
4
|
+
export declare function renderPage(blocks: ChatBlock[], settings: Settings): string;
|
|
5
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/web/render.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAMtE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AA6D3C,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAG1E"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Render the full HTML page from chat blocks using Eta templates. */
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { Eta } from "eta";
|
|
5
|
+
import { isReplyEnabled } from "../settings.js";
|
|
6
|
+
import { firstLinePreview, senderLabel } from "../store.js";
|
|
7
|
+
import { anchorId, formatTime } from "./html.js";
|
|
8
|
+
const MAX_MESSAGES = 15;
|
|
9
|
+
const TEN_MINUTES_MS = 10 * 60 * 1000;
|
|
10
|
+
const templateDir = join(dirname(fileURLToPath(import.meta.url)), "templates");
|
|
11
|
+
const eta = new Eta({ views: templateDir, autoEscape: true });
|
|
12
|
+
/** Map MessageType to a display channel tag. */
|
|
13
|
+
function channelLabel(messageType) {
|
|
14
|
+
if (messageType === "group")
|
|
15
|
+
return "[GROUP]";
|
|
16
|
+
if (messageType === "sms")
|
|
17
|
+
return "[SMS]";
|
|
18
|
+
return "[DM]";
|
|
19
|
+
}
|
|
20
|
+
/** Prepare the row data for a single chat card. */
|
|
21
|
+
function prepareCard(block) {
|
|
22
|
+
const recent = block.messages.slice(-MAX_MESSAGES);
|
|
23
|
+
const rows = [];
|
|
24
|
+
for (let i = 0; i < recent.length; i++) {
|
|
25
|
+
const message = recent[i];
|
|
26
|
+
if (!message)
|
|
27
|
+
continue;
|
|
28
|
+
const prev = recent[i - 1];
|
|
29
|
+
if (prev) {
|
|
30
|
+
const gapMs = new Date(message.date).getTime() - new Date(prev.date).getTime();
|
|
31
|
+
if (gapMs > TEN_MINUTES_MS) {
|
|
32
|
+
rows.push({ gap: true, time: "", arrow: "", channel: "", sender: "", text: "" });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
rows.push({
|
|
36
|
+
gap: false,
|
|
37
|
+
time: formatTime(message.date),
|
|
38
|
+
arrow: message.isBot ? "->" : "<-",
|
|
39
|
+
channel: channelLabel(message.messageType),
|
|
40
|
+
sender: senderLabel(message),
|
|
41
|
+
text: firstLinePreview(message.text),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return { rows };
|
|
45
|
+
}
|
|
46
|
+
export function renderPage(blocks, settings) {
|
|
47
|
+
const replyEnabledMap = (chatGuid) => isReplyEnabled(settings, chatGuid);
|
|
48
|
+
return eta.render("page", { blocks, prepareCard, anchorId, replyEnabledMap });
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/web/render.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html><head><meta charset="UTF-8"><title>blue</title>
|
|
3
|
+
<style>
|
|
4
|
+
* { margin: 0; padding: 0 }
|
|
5
|
+
body { background: #111; color: #ccc; font: 13px/1.6 monospace; padding: 40px 40px 40px 240px; max-width: 900px }
|
|
6
|
+
nav { position: fixed; top: 0; left: 0; width: 210px; height: 100vh; overflow-y: auto; padding: 40px 24px 40px }
|
|
7
|
+
.toc-label { font-size: 11px; opacity: .35; margin-bottom: 16px }
|
|
8
|
+
nav a { display: block; color: inherit; text-decoration: none; line-height: 2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; opacity: .55 }
|
|
9
|
+
nav a:hover { opacity: 1 }
|
|
10
|
+
.card { background: #1a1a1a; margin-bottom: 24px; scroll-margin-top: 40px }
|
|
11
|
+
.card-body { max-height: 300px; overflow: hidden; display: flex; flex-direction: column; justify-content: flex-end }
|
|
12
|
+
.card-header { padding: 12px 16px; border-bottom: 1px solid #252525; display: flex; align-items: center; justify-content: space-between; position: sticky; top: 0; backdrop-filter: blur(8px); background: rgba(26,26,26,0.75) }
|
|
13
|
+
.card-messages { padding: 14px 16px }
|
|
14
|
+
.meta { font-size: 11px; opacity: .4 }
|
|
15
|
+
.toggle { background: none; border: none; color: #ccc; font: 11px monospace; cursor: pointer; opacity: .4 }
|
|
16
|
+
.toggle:hover { opacity: .8 }
|
|
17
|
+
table { border-collapse: collapse }
|
|
18
|
+
td { vertical-align: top; padding: 1px 0 }
|
|
19
|
+
.c-fix { white-space: nowrap; padding-right: 8px }
|
|
20
|
+
.c-sender { text-align: right }
|
|
21
|
+
.c-msg { white-space: pre-wrap; word-break: break-word }
|
|
22
|
+
</style></head><body>
|
|
23
|
+
|
|
24
|
+
<nav>
|
|
25
|
+
<div class="toc-label">Recent Chats</div>
|
|
26
|
+
<% it.blocks.forEach(function(block) { %>
|
|
27
|
+
<a href="#<%= it.anchorId(block.guid) %>"><%= block.displayName %></a>
|
|
28
|
+
<% }) %>
|
|
29
|
+
</nav>
|
|
30
|
+
|
|
31
|
+
<% if (it.blocks.length === 0) { %>
|
|
32
|
+
<p style="opacity:.4">no chats in the last 7 days</p>
|
|
33
|
+
<% } else { %>
|
|
34
|
+
<% it.blocks.forEach(function(block) { %>
|
|
35
|
+
<% const card = it.prepareCard(block) %>
|
|
36
|
+
<div class="card" id="<%= it.anchorId(block.guid) %>">
|
|
37
|
+
<div class="card-body">
|
|
38
|
+
<div class="card-header">
|
|
39
|
+
<span><%= block.displayName %> <span class="meta"><%= block.messages.length %> msgs</span></span>
|
|
40
|
+
<% const enabled = it.replyEnabledMap(block.guid) %>
|
|
41
|
+
<button class="toggle"
|
|
42
|
+
onclick="toggleReply('<%= block.guid %>', <%= !enabled %>)">
|
|
43
|
+
<%= enabled ? '[on]' : '[off]' %>
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="card-messages">
|
|
47
|
+
<table>
|
|
48
|
+
<% card.rows.forEach(function(row) { %>
|
|
49
|
+
<% if (row.gap) { %>
|
|
50
|
+
<tr><td colspan="6"> </td></tr>
|
|
51
|
+
<% } else { %>
|
|
52
|
+
<tr>
|
|
53
|
+
<td class="c-fix c-time"><%= row.time %></td>
|
|
54
|
+
<td class="c-fix c-source">[sid]</td>
|
|
55
|
+
<td class="c-fix c-arrow"><%= row.arrow %></td>
|
|
56
|
+
<td class="c-fix c-channel"><%= row.channel %></td>
|
|
57
|
+
<td class="c-fix c-sender"><%= row.sender %>:</td>
|
|
58
|
+
<td class="c-msg"><%= row.text %></td>
|
|
59
|
+
</tr>
|
|
60
|
+
<% } %>
|
|
61
|
+
<% }) %>
|
|
62
|
+
</table>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<% }) %>
|
|
67
|
+
<% } %>
|
|
68
|
+
|
|
69
|
+
<script>
|
|
70
|
+
function toggleReply(chatGuid, enabled) {
|
|
71
|
+
const btn = event.target;
|
|
72
|
+
btn.textContent = enabled ? '[on]' : '[off]';
|
|
73
|
+
btn.setAttribute('onclick', "toggleReply('" + chatGuid + "', " + !enabled + ")");
|
|
74
|
+
fetch('/toggle', {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify({ chatGuid, enabled })
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const es = new EventSource("/events");
|
|
81
|
+
es.addEventListener("update", () => location.reload());
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
</script>
|
|
85
|
+
</body></html>
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kingcrab/pi-imessage",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"pi-imessage": "./dist/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": ["dist", "!dist/__tests__", "README.md"],
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20.0.0"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc && mkdir -p dist/web/templates && cp src/web/templates/* dist/web/templates/",
|
|
14
|
+
"prepublishOnly": "npm run build",
|
|
15
|
+
"dev": "tsx watch src/main.ts",
|
|
16
|
+
"start": "tsx src/main.ts",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"typecheck": "tsc --noEmit",
|
|
19
|
+
"lint": "biome check .",
|
|
20
|
+
"format": "biome format --write .",
|
|
21
|
+
"check": "tsc --noEmit && biome check ."
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@mariozechner/pi-agent-core": "latest",
|
|
25
|
+
"@mariozechner/pi-ai": "latest",
|
|
26
|
+
"@mariozechner/pi-coding-agent": "latest",
|
|
27
|
+
"better-sqlite3": "^12.6.2",
|
|
28
|
+
"dotenv": "^17.3.1",
|
|
29
|
+
"eta": "^4.5.1",
|
|
30
|
+
"sharp": "^0.34.5"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@biomejs/biome": "^1.9.4",
|
|
34
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
35
|
+
"@types/node": "^22.0.0",
|
|
36
|
+
"@types/sharp": "^0.31.1",
|
|
37
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
38
|
+
"tsx": "^4.0.0",
|
|
39
|
+
"typescript": "^5.7.3",
|
|
40
|
+
"vitest": "^3.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|