pi-courier 0.1.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 (86) hide show
  1. package/README.md +363 -0
  2. package/README.zh-CN.md +362 -0
  3. package/deploy/pi-msg-bridge.service +26 -0
  4. package/deploy/pi-msg-bridge.user.service +37 -0
  5. package/dist/auth/challenge-auth.d.ts +76 -0
  6. package/dist/auth/challenge-auth.d.ts.map +1 -0
  7. package/dist/auth/challenge-auth.js +337 -0
  8. package/dist/auth/challenge-auth.js.map +1 -0
  9. package/dist/cli.d.ts +3 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +265 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/config.d.ts +10 -0
  14. package/dist/config.d.ts.map +1 -0
  15. package/dist/config.js +49 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/formatting.d.ts +22 -0
  18. package/dist/formatting.d.ts.map +1 -0
  19. package/dist/formatting.js +74 -0
  20. package/dist/formatting.js.map +1 -0
  21. package/dist/lock.d.ts +3 -0
  22. package/dist/lock.d.ts.map +1 -0
  23. package/dist/lock.js +75 -0
  24. package/dist/lock.js.map +1 -0
  25. package/dist/rpc/command-map.d.ts +19 -0
  26. package/dist/rpc/command-map.d.ts.map +1 -0
  27. package/dist/rpc/command-map.js +191 -0
  28. package/dist/rpc/command-map.js.map +1 -0
  29. package/dist/rpc/message-router.d.ts +30 -0
  30. package/dist/rpc/message-router.d.ts.map +1 -0
  31. package/dist/rpc/message-router.js +110 -0
  32. package/dist/rpc/message-router.js.map +1 -0
  33. package/dist/rpc/pi-rpc.d.ts +79 -0
  34. package/dist/rpc/pi-rpc.d.ts.map +1 -0
  35. package/dist/rpc/pi-rpc.js +199 -0
  36. package/dist/rpc/pi-rpc.js.map +1 -0
  37. package/dist/setup.d.ts +10 -0
  38. package/dist/setup.d.ts.map +1 -0
  39. package/dist/setup.js +159 -0
  40. package/dist/setup.js.map +1 -0
  41. package/dist/standalone.d.ts +13 -0
  42. package/dist/standalone.d.ts.map +1 -0
  43. package/dist/standalone.js +184 -0
  44. package/dist/standalone.js.map +1 -0
  45. package/dist/transports/discord.d.ts +33 -0
  46. package/dist/transports/discord.d.ts.map +1 -0
  47. package/dist/transports/discord.js +216 -0
  48. package/dist/transports/discord.js.map +1 -0
  49. package/dist/transports/interface.d.ts +42 -0
  50. package/dist/transports/interface.d.ts.map +1 -0
  51. package/dist/transports/interface.js +2 -0
  52. package/dist/transports/interface.js.map +1 -0
  53. package/dist/transports/manager.d.ts +54 -0
  54. package/dist/transports/manager.d.ts.map +1 -0
  55. package/dist/transports/manager.js +98 -0
  56. package/dist/transports/manager.js.map +1 -0
  57. package/dist/transports/matrix-utils.d.ts +31 -0
  58. package/dist/transports/matrix-utils.d.ts.map +1 -0
  59. package/dist/transports/matrix-utils.js +99 -0
  60. package/dist/transports/matrix-utils.js.map +1 -0
  61. package/dist/transports/matrix-utils.test.d.ts +2 -0
  62. package/dist/transports/matrix-utils.test.d.ts.map +1 -0
  63. package/dist/transports/matrix-utils.test.js +246 -0
  64. package/dist/transports/matrix-utils.test.js.map +1 -0
  65. package/dist/transports/matrix.d.ts +34 -0
  66. package/dist/transports/matrix.d.ts.map +1 -0
  67. package/dist/transports/matrix.js +240 -0
  68. package/dist/transports/matrix.js.map +1 -0
  69. package/dist/transports/slack.d.ts +31 -0
  70. package/dist/transports/slack.d.ts.map +1 -0
  71. package/dist/transports/slack.js +203 -0
  72. package/dist/transports/slack.js.map +1 -0
  73. package/dist/transports/telegram.d.ts +38 -0
  74. package/dist/transports/telegram.d.ts.map +1 -0
  75. package/dist/transports/telegram.js +167 -0
  76. package/dist/transports/telegram.js.map +1 -0
  77. package/dist/transports/whatsapp.d.ts +31 -0
  78. package/dist/transports/whatsapp.d.ts.map +1 -0
  79. package/dist/transports/whatsapp.js +235 -0
  80. package/dist/transports/whatsapp.js.map +1 -0
  81. package/dist/types.d.ts +69 -0
  82. package/dist/types.d.ts.map +1 -0
  83. package/dist/types.js +2 -0
  84. package/dist/types.js.map +1 -0
  85. package/extensions/index.ts +42 -0
  86. package/package.json +71 -0
@@ -0,0 +1,167 @@
1
+ import TelegramBot from "node-telegram-bot-api";
2
+ /**
3
+ * Telegram transport provider using node-telegram-bot-api
4
+ */
5
+ export class TelegramProvider {
6
+ token;
7
+ auth;
8
+ type = "telegram";
9
+ bot;
10
+ _isConnected = false;
11
+ messageHandler;
12
+ errorHandler;
13
+ lastProcessedMessageId = 0;
14
+ constructor(token, auth) {
15
+ this.token = token;
16
+ this.auth = auth;
17
+ }
18
+ get isConnected() {
19
+ return this._isConnected;
20
+ }
21
+ /**
22
+ * Convert standard markdown to Telegram MarkdownV1 format.
23
+ *
24
+ * Telegram MarkdownV1 syntax: *bold*, _italic_, `code`, ```pre```, [text](url).
25
+ * Per docs, only `_ * [ \`` need backslash-escaping when literal — otherwise
26
+ * a stray `_` (e.g. in a snake_case tool name like `hud_canvas`) is parsed
27
+ * as an opening italic marker with no close, returning HTTP 400.
28
+ *
29
+ * Strategy: lift valid markdown patterns into NUL-sentinel placeholders,
30
+ * escape the remaining specials, then restore the placeholders.
31
+ */
32
+ formatForTelegram(text) {
33
+ const lifted = [];
34
+ const lift = (s) => `\x00${lifted.push(s) - 1}\x00`;
35
+ // Lift valid markdown patterns in priority order — they bypass the escape step below.
36
+ text = text.replace(/```[\s\S]*?```/g, lift); // code blocks
37
+ text = text.replace(/`[^`]+`/g, lift); // inline code
38
+ text = text.replace(/\*\*([^*]+?)\*\*/g, (_, c) => lift(`*${c}*`)); // **bold** → *bold*
39
+ text = text.replace(/(?<!\*)\*(?!\*)([^*]+?)(?<!\*)\*(?!\*)/g, (_, c) => lift(`_${c}_`)); // *italic* → _italic_
40
+ text = text.replace(/\[[^\]]+\]\([^)]+\)/g, lift); // [text](url)
41
+ // Escape literal markdown specials so Telegram treats them as text.
42
+ text = text.replace(/[_*[`]/g, "\\$&");
43
+ return text.replace(/\x00(\d+)\x00/g, (_, i) => lifted[parseInt(i, 10)]);
44
+ }
45
+ async connect() {
46
+ if (this._isConnected)
47
+ return;
48
+ this.bot = new TelegramBot(this.token, {
49
+ polling: {
50
+ interval: 300,
51
+ autoStart: false,
52
+ },
53
+ });
54
+ // Handle incoming messages
55
+ this.bot.on("message", async (msg) => {
56
+ try {
57
+ await this.handleMessage(msg);
58
+ }
59
+ catch (err) {
60
+ if (this.errorHandler) {
61
+ this.errorHandler(err);
62
+ }
63
+ }
64
+ });
65
+ // Handle polling errors
66
+ this.bot.on("polling_error", (err) => {
67
+ if (this.errorHandler) {
68
+ this.errorHandler(err);
69
+ }
70
+ });
71
+ await this.bot.startPolling();
72
+ this._isConnected = true;
73
+ }
74
+ async disconnect() {
75
+ if (!this._isConnected || !this.bot)
76
+ return;
77
+ await this.bot.stopPolling();
78
+ this._isConnected = false;
79
+ this.bot = undefined;
80
+ }
81
+ async sendMessage(chatId, text) {
82
+ if (!this.bot) {
83
+ throw new Error("Telegram bot not connected");
84
+ }
85
+ if (!text?.trim())
86
+ return;
87
+ const formattedText = this.formatForTelegram(text);
88
+ await this.bot.sendMessage(chatId, formattedText, { parse_mode: "Markdown" });
89
+ }
90
+ async sendTyping(chatId) {
91
+ if (!this.bot)
92
+ return;
93
+ await this.bot.sendChatAction(chatId, "typing");
94
+ }
95
+ onMessage(handler) {
96
+ this.messageHandler = handler;
97
+ }
98
+ onError(handler) {
99
+ this.errorHandler = handler;
100
+ }
101
+ async handleMessage(msg) {
102
+ // Filter out old messages (prevent stale message processing on reconnect)
103
+ if (msg.message_id <= this.lastProcessedMessageId) {
104
+ return;
105
+ }
106
+ this.lastProcessedMessageId = msg.message_id;
107
+ // Only process text messages
108
+ if (!msg.text || !msg.from) {
109
+ return;
110
+ }
111
+ const chatId = msg.chat.id.toString();
112
+ const userId = msg.from.id.toString();
113
+ const username = msg.from.username || msg.from.first_name || "unknown";
114
+ const isGroupChat = msg.chat.type !== "private";
115
+ // Check if bot was mentioned in group chat
116
+ let wasMentioned = false;
117
+ if (isGroupChat && this.bot) {
118
+ const botInfo = await this.bot.getMe();
119
+ const botUsername = botInfo.username;
120
+ wasMentioned =
121
+ msg.text.includes(`@${botUsername}`) ||
122
+ (msg.entities?.some((entity) => entity.type === "mention" &&
123
+ msg.text.substring(entity.offset, entity.offset + entity.length) ===
124
+ `@${botUsername}`) ?? false);
125
+ }
126
+ // Check authorization (with sendMessage callback for challenge notifications)
127
+ const sendMessageToUser = async (cId, text) => {
128
+ if (this.bot) {
129
+ await this.bot.sendMessage(cId, text, { parse_mode: "Markdown" });
130
+ }
131
+ };
132
+ const isAuthorized = await this.auth.checkAuthorization(userId, chatId, username, isGroupChat, wasMentioned, sendMessageToUser, this.type);
133
+ // Handle admin commands and challenge codes in DM (before auth check!)
134
+ if (!isGroupChat && (msg.text.startsWith("/") || msg.text.match(/^\d{6}$/))) {
135
+ const handled = await this.auth.handleAdminCommand(msg.text, chatId, userId, async (text) => await this.sendMessage(chatId, text), this.type);
136
+ if (handled) {
137
+ return;
138
+ }
139
+ }
140
+ if (!isAuthorized) {
141
+ return; // Auth handler already sent challenge/error messages
142
+ }
143
+ // Strip bot mention from message if present
144
+ let content = msg.text;
145
+ if (wasMentioned && this.bot) {
146
+ const botInfo = await this.bot.getMe();
147
+ const botUsername = botInfo.username;
148
+ content = content.replace(new RegExp(`@${botUsername}\\s*`, "g"), "").trim();
149
+ }
150
+ // Forward to message handler
151
+ if (this.messageHandler && content) {
152
+ const externalMessage = {
153
+ chatId,
154
+ transport: this.type,
155
+ content,
156
+ username,
157
+ userId,
158
+ timestamp: new Date((msg.date || 0) * 1000),
159
+ messageId: msg.message_id.toString(),
160
+ isGroupChat,
161
+ wasMentioned,
162
+ };
163
+ this.messageHandler(externalMessage);
164
+ }
165
+ }
166
+ }
167
+ //# sourceMappingURL=telegram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/transports/telegram.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ChallengeAuth } from "../auth/challenge-auth.js";
2
+ import type { ExternalMessage } from "../types.js";
3
+ import type { ITransportProvider } from "./interface.js";
4
+ /**
5
+ * WhatsApp transport provider using @whiskeysockets/baileys
6
+ */
7
+ export declare class WhatsAppProvider implements ITransportProvider {
8
+ private auth;
9
+ readonly type = "whatsapp";
10
+ private socket;
11
+ private _isConnected;
12
+ private authPath;
13
+ private messageHandler?;
14
+ private errorHandler?;
15
+ private lastProcessedMessageId;
16
+ private debug;
17
+ private isManualConnect;
18
+ constructor(config: {
19
+ authPath?: string;
20
+ debug?: boolean;
21
+ }, auth: ChallengeAuth);
22
+ get isConnected(): boolean;
23
+ connect(manual?: boolean): Promise<void>;
24
+ disconnect(): Promise<void>;
25
+ sendMessage(chatId: string, text: string): Promise<void>;
26
+ sendTyping(chatId: string): Promise<void>;
27
+ onMessage(handler: (message: ExternalMessage) => void): void;
28
+ onError(handler: (error: Error) => void): void;
29
+ private handleMessage;
30
+ }
31
+ //# sourceMappingURL=whatsapp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp.d.ts","sourceRoot":"","sources":["../../src/transports/whatsapp.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAUzD;;GAEG;AACH,qBAAa,gBAAiB,YAAW,kBAAkB;IAYvD,OAAO,CAAC,IAAI;IAXd,QAAQ,CAAC,IAAI,cAAc;IAC3B,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAC,CAAqC;IAC5D,OAAO,CAAC,YAAY,CAAC,CAAyB;IAC9C,OAAO,CAAC,sBAAsB,CAAM;IACpC,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,eAAe,CAAS;gBAEpB,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAChD,IAAI,EAAE,aAAa;IAW7B,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,OAAO,CAAC,MAAM,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IA4G/C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASxD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB/C,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI;IAI5D,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;YAIhC,aAAa;CA0F5B"}
@@ -0,0 +1,235 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import * as qrcode from "qrcode-terminal";
4
+ async function loadBaileys() {
5
+ const baileys = await import("@whiskeysockets/baileys");
6
+ return baileys;
7
+ }
8
+ /**
9
+ * WhatsApp transport provider using @whiskeysockets/baileys
10
+ */
11
+ export class WhatsAppProvider {
12
+ auth;
13
+ type = "whatsapp";
14
+ socket = null;
15
+ _isConnected = false;
16
+ authPath;
17
+ messageHandler;
18
+ errorHandler;
19
+ lastProcessedMessageId = "";
20
+ debug;
21
+ isManualConnect = false;
22
+ constructor(config, auth) {
23
+ this.auth = auth;
24
+ // Default auth path to ~/.pi/msg-bridge-whatsapp-auth
25
+ this.authPath = config.authPath || path.join(process.env.HOME || process.env.USERPROFILE || ".", ".pi", "msg-bridge-whatsapp-auth");
26
+ this.debug = config.debug || false;
27
+ }
28
+ get isConnected() {
29
+ return this._isConnected;
30
+ }
31
+ async connect(manual = false) {
32
+ if (this._isConnected)
33
+ return;
34
+ this.isManualConnect = manual;
35
+ // Ensure auth directory exists
36
+ if (!fs.existsSync(this.authPath)) {
37
+ fs.mkdirSync(this.authPath, { recursive: true, mode: 0o700 });
38
+ }
39
+ else {
40
+ // Ensure existing directory has secure permissions
41
+ try {
42
+ fs.chmodSync(this.authPath, 0o700);
43
+ }
44
+ catch (err) {
45
+ console.warn("Failed to set auth directory permissions:", err);
46
+ }
47
+ }
48
+ const baileys = await loadBaileys();
49
+ const { state, saveCreds } = await baileys.useMultiFileAuthState(this.authPath);
50
+ // Suppress Baileys logger unless debug mode
51
+ const silentLogger = {
52
+ level: 'silent',
53
+ child: () => silentLogger,
54
+ trace: () => { },
55
+ debug: () => { },
56
+ info: () => { },
57
+ warn: () => { },
58
+ error: () => { },
59
+ fatal: () => { },
60
+ };
61
+ this.socket = baileys.default({
62
+ auth: state,
63
+ printQRInTerminal: false, // We'll handle QR display ourselves
64
+ logger: this.debug ? undefined : silentLogger,
65
+ });
66
+ // Handle connection updates - set up IMMEDIATELY after socket creation
67
+ this.socket.ev.on("connection.update", async (update) => {
68
+ const { connection, lastDisconnect, qr } = update;
69
+ if (this.debug) {
70
+ console.log("[WhatsApp] connection.update event received:", JSON.stringify(update, null, 2));
71
+ }
72
+ if (qr && this.isManualConnect) {
73
+ console.log("\n📱 Scan this QR code with WhatsApp to authenticate:\n");
74
+ qrcode.generate(qr, { small: true });
75
+ console.log("\n");
76
+ }
77
+ if (connection === "close") {
78
+ const statusCode = lastDisconnect?.error?.output?.statusCode;
79
+ const shouldReconnect = statusCode !== baileys.DisconnectReason.loggedOut;
80
+ if (this.debug) {
81
+ console.log(`[WhatsApp] Connection closed. Status: ${statusCode}, Reconnect: ${shouldReconnect}`);
82
+ }
83
+ this._isConnected = false;
84
+ if (this.errorHandler && statusCode !== baileys.DisconnectReason.loggedOut) {
85
+ this.errorHandler(new Error(`connection closed (status ${statusCode})`));
86
+ }
87
+ // If unauthorized (401), clear invalid auth
88
+ if (statusCode === 401) {
89
+ try {
90
+ if (fs.existsSync(this.authPath)) {
91
+ fs.rmSync(this.authPath, { recursive: true, force: true });
92
+ if (this.isManualConnect) {
93
+ console.log("🔄 WhatsApp session expired. Cleared credentials. Reconnecting with QR code...");
94
+ // Reconnect after clearing auth to get fresh QR (manual configure only)
95
+ setTimeout(() => this.connect(true), 1000);
96
+ }
97
+ else {
98
+ console.log("🔄 WhatsApp session expired. Cleared credentials. Run /msg-bridge configure whatsapp to reconnect.");
99
+ }
100
+ }
101
+ }
102
+ catch (err) {
103
+ console.error("Failed to clear auth:", err);
104
+ }
105
+ }
106
+ else if (shouldReconnect) {
107
+ // Reconnect after 3 seconds
108
+ setTimeout(() => this.connect(), 3000);
109
+ }
110
+ else if (this.debug) {
111
+ console.warn("⚠️ WhatsApp logged out. Run /msg-bridge configure whatsapp to reconnect.");
112
+ }
113
+ }
114
+ else if (connection === "open") {
115
+ this._isConnected = true;
116
+ console.log("✅ WhatsApp connected!");
117
+ }
118
+ });
119
+ // Save credentials when updated
120
+ this.socket.ev.on("creds.update", saveCreds);
121
+ // Handle incoming messages
122
+ this.socket.ev.on("messages.upsert", async ({ messages, type }) => {
123
+ if (type !== "notify") {
124
+ return;
125
+ }
126
+ for (const msg of messages) {
127
+ await this.handleMessage(msg);
128
+ }
129
+ });
130
+ }
131
+ async disconnect() {
132
+ if (!this._isConnected || !this.socket)
133
+ return;
134
+ this.socket.end(undefined);
135
+ this.socket = null;
136
+ this._isConnected = false;
137
+ console.log("[WhatsApp] Disconnected");
138
+ }
139
+ async sendMessage(chatId, text) {
140
+ if (!this.socket) {
141
+ throw new Error("WhatsApp not connected");
142
+ }
143
+ if (!text?.trim())
144
+ return;
145
+ await this.socket.sendMessage(chatId, { text });
146
+ }
147
+ async sendTyping(chatId) {
148
+ if (!this.socket)
149
+ return;
150
+ try {
151
+ await this.socket.sendPresenceUpdate("composing", chatId);
152
+ // Auto-stop composing after 3 seconds
153
+ setTimeout(async () => {
154
+ try {
155
+ await this.socket?.sendPresenceUpdate("paused", chatId);
156
+ }
157
+ catch {
158
+ // Ignore
159
+ }
160
+ }, 3000);
161
+ }
162
+ catch (error) {
163
+ console.warn("[WhatsApp] Failed to send typing:", error);
164
+ }
165
+ }
166
+ onMessage(handler) {
167
+ this.messageHandler = handler;
168
+ }
169
+ onError(handler) {
170
+ this.errorHandler = handler;
171
+ }
172
+ async handleMessage(msg) {
173
+ // Debug: log message structure
174
+ if (this.debug) {
175
+ console.log("[WhatsApp] Message received:", JSON.stringify(msg, null, 2));
176
+ }
177
+ // Skip non-text messages
178
+ const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text;
179
+ if (!text) {
180
+ return;
181
+ }
182
+ const messageId = msg.key.id || String(Date.now());
183
+ // Filter out old/duplicate messages
184
+ if (messageId === this.lastProcessedMessageId) {
185
+ return;
186
+ }
187
+ this.lastProcessedMessageId = messageId;
188
+ const chatId = msg.key.remoteJid || "";
189
+ // Try multiple fields for username
190
+ const pushName = msg.pushName || msg.verifiedBizName || msg.key.fromMe ? "You" : "Unknown";
191
+ // Skip status broadcasts
192
+ if (chatId === "status@broadcast") {
193
+ return;
194
+ }
195
+ // Detect if DM or group
196
+ // Group JIDs end with @g.us, individual JIDs end with @s.whatsapp.net
197
+ const isGroupChat = chatId.endsWith("@g.us");
198
+ const userId = chatId.split("@")[0]; // Phone number
199
+ // For WhatsApp, mentions work differently - we'll consider it as not mentioned for now
200
+ const wasMentioned = false;
201
+ // Check authorization
202
+ const sendMessageToUser = async (cId, text) => {
203
+ if (this.socket) {
204
+ await this.socket.sendMessage(cId, { text });
205
+ }
206
+ };
207
+ const isAuthorized = await this.auth.checkAuthorization(userId, chatId, pushName, isGroupChat, wasMentioned, sendMessageToUser, this.type);
208
+ // Handle admin commands and challenge codes in DM
209
+ if (!isGroupChat && (text.startsWith("/") || text.match(/^\d{6}$/))) {
210
+ const handled = await this.auth.handleAdminCommand(text, chatId, userId, async (text) => await this.sendMessage(chatId, text), this.type);
211
+ if (handled) {
212
+ return;
213
+ }
214
+ }
215
+ if (!isAuthorized) {
216
+ return; // Auth handler already sent challenge/error messages
217
+ }
218
+ // Forward to message handler
219
+ if (this.messageHandler) {
220
+ const externalMessage = {
221
+ chatId,
222
+ transport: this.type,
223
+ content: text.trim(),
224
+ username: pushName,
225
+ userId,
226
+ timestamp: new Date(Number(msg.messageTimestamp) * 1000),
227
+ messageId,
228
+ isGroupChat,
229
+ wasMentioned,
230
+ };
231
+ this.messageHandler(externalMessage);
232
+ }
233
+ }
234
+ }
235
+ //# sourceMappingURL=whatsapp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp.js","sourceRoot":"","sources":["../../src/transports/whatsapp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * External message received from a messenger transport
3
+ */
4
+ export interface ExternalMessage {
5
+ /** Unique chat/channel identifier */
6
+ chatId: string;
7
+ /** Transport type (telegram, whatsapp, etc) */
8
+ transport: string;
9
+ /** Message content/text */
10
+ content: string;
11
+ /** Sender username */
12
+ username: string;
13
+ /** Sender user ID */
14
+ userId: string;
15
+ /** Message timestamp */
16
+ timestamp: Date;
17
+ /** Unique message identifier */
18
+ messageId: string;
19
+ /** Is this a group/channel message? */
20
+ isGroupChat: boolean;
21
+ /** Was the bot mentioned? (for group chats) */
22
+ wasMentioned?: boolean;
23
+ }
24
+ /**
25
+ * Configuration for msg-bridge extension
26
+ */
27
+ export interface MsgBridgeConfig {
28
+ matrix?: {
29
+ homeserverUrl: string;
30
+ accessToken: string;
31
+ encryption?: boolean;
32
+ };
33
+ auth?: {
34
+ trustedUsers?: string[];
35
+ adminUserId?: string;
36
+ channels?: Record<string, {
37
+ enabled: boolean;
38
+ mode: "all" | "mentions" | "trusted-only";
39
+ }>;
40
+ };
41
+ hideToolCalls?: boolean;
42
+ autoConnect?: boolean;
43
+ showWidget?: boolean;
44
+ debug?: boolean;
45
+ /** pi 的工作目录(pi 子进程的 cwd;`--workdir` 参数优先) */
46
+ workdir?: string;
47
+ /** pi 会话存储目录(对应 pi 的 --session-dir) */
48
+ sessionDir?: string;
49
+ /** 显式指定 pi 的 cli.js 路径(默认:PI_CLI_PATH → which pi → 本地 node_modules) */
50
+ cliPath?: string;
51
+ }
52
+ /**
53
+ * Pending remote chat session tracking
54
+ */
55
+ export interface PendingRemoteChat {
56
+ chatId: string;
57
+ transport: string;
58
+ username: string;
59
+ messageId: string;
60
+ }
61
+ /**
62
+ * Transport connection status
63
+ */
64
+ export interface TransportStatus {
65
+ type: string;
66
+ connected: boolean;
67
+ error?: string;
68
+ }
69
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,sBAAsB;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,WAAW,EAAE,OAAO,CAAC;IACrB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,IAAI,CAAC,EAAE;QACL,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,OAAO,EAAE,OAAO,CAAC;YAAC,IAAI,EAAE,KAAK,GAAG,UAAU,GAAG,cAAc,CAAA;SAAE,CAAC,CAAC;KAC5F,CAAC;IACF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * pi extension entry — makes pi-courier discoverable in the pi
3
+ * package catalog (pi.dev/packages) and installable via `pi install`.
4
+ *
5
+ * The bridge itself runs as a standalone app (an RPC client of pi); this
6
+ * extension only registers a `/pi-courier` command that shows how to use it.
7
+ * It is fully self-contained (no imports) so it survives pi's production
8
+ * installs (`npm install --omit=dev`).
9
+ */
10
+
11
+ /**
12
+ * Minimal structural type for the pi ExtensionAPI we use.
13
+ * (Avoids importing @earendil-works/pi-ai in the shipped extension.)
14
+ */
15
+ interface PiApi {
16
+ registerCommand(
17
+ name: string,
18
+ options: {
19
+ description?: string;
20
+ handler: (args: string, ctx: { ui: { notify: (message: string, type?: string) => void } }) => Promise<void>;
21
+ },
22
+ ): void;
23
+ }
24
+
25
+ const USAGE = [
26
+ "pi-courier — 通过 Matrix 远程使用 pi(消息桥接)。",
27
+ "",
28
+ "安装: npm install -g pi-courier",
29
+ "配置: pi-courier setup",
30
+ "运行: pi-courier run(前台)或 pi-courier enable(systemd 开机自启)",
31
+ "仓库: https://github.com/Hi-Barry/pi-courier",
32
+ ].join("\n");
33
+
34
+ export default function register(pi: PiApi): void {
35
+ pi.registerCommand("pi-courier", {
36
+ description: "Show how to use pi-courier (run pi from Matrix)",
37
+ handler: async (_args, ctx) => {
38
+ ctx.ui.notify(USAGE, "info");
39
+ console.log(USAGE);
40
+ },
41
+ });
42
+ }
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "pi-courier",
3
+ "version": "0.1.0",
4
+ "description": "Run pi coding agent from Matrix. Slash commands, skills and prompts fully work from messengers via the RPC protocol.",
5
+ "type": "module",
6
+ "main": "dist/standalone.js",
7
+ "bin": {
8
+ "pi-courier": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist/",
12
+ "deploy/",
13
+ "extensions/",
14
+ "README.md",
15
+ "README.zh-CN.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "prepare": "npm run build",
20
+ "watch": "tsc --watch",
21
+ "clean": "rm -rf dist",
22
+ "start": "node dist/standalone.js",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "vitest run",
25
+ "lint": "biome check src/",
26
+ "lint:fix": "biome check --fix src/"
27
+ },
28
+ "keywords": [
29
+ "pi-package",
30
+ "pi",
31
+ "pi-coding-agent",
32
+ "messenger",
33
+ "bridge",
34
+ "matrix",
35
+ "telegram",
36
+ "rpc",
37
+ "remote",
38
+ "chat",
39
+ "bot"
40
+ ],
41
+ "pi": {
42
+ "extensions": [
43
+ "./extensions"
44
+ ]
45
+ },
46
+ "license": "MIT",
47
+ "engines": {
48
+ "node": ">=20.0.0"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/Hi-Barry/pi-remote.git"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "dependencies": {
58
+ "matrix-bot-sdk": "^0.8.0"
59
+ },
60
+ "peerDependencies": {
61
+ "@earendil-works/pi-ai": ">=0.83",
62
+ "@earendil-works/pi-coding-agent": ">=0.83"
63
+ },
64
+ "devDependencies": {
65
+ "@biomejs/biome": "^2.4.14",
66
+ "@types/node": "^25.6.2",
67
+ "fast-check": "^4.5.3",
68
+ "typescript": "^6.0.3",
69
+ "vitest": "^4.1.5"
70
+ }
71
+ }