pi-harness-runtime 1.1.342 → 1.1.343
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 +9 -0
- package/harness/notification-center.ts +363 -0
- package/index.ts +53 -15
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/dist/notification-center.d.ts +13 -0
- package/packages/notification/dist/notification-center.d.ts.map +1 -1
- package/packages/notification/dist/notification-center.js +24 -0
- package/packages/notification/dist/notification-center.js.map +1 -1
- package/packages/notification/dist/telegram-webhook-handler.d.ts +4 -0
- package/packages/notification/dist/telegram-webhook-handler.d.ts.map +1 -1
- package/packages/notification/dist/telegram-webhook-handler.js +4 -0
- package/packages/notification/dist/telegram-webhook-handler.js.map +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/notification/src/notification-center.ts +40 -13
- package/packages/notification/src/telegram-webhook-handler.ts +4 -0
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/dist/telegram.js +8 -1
- package/packages/voice-api/package.json +1 -1
- package/packages/voice-api/src/telegram.ts +9 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,15 @@ Give the runtime a requirement. It keeps working, survives interruption, and ret
|
|
|
25
25
|
pi install npm:pi-harness-runtime
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
### Recommended Extensions
|
|
29
|
+
|
|
30
|
+
This runtime works best with these extensions (auto-installed by pi-harness-runtime):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pi install npm:pi-mcp-adapter # MCP protocol adapter for external tools
|
|
34
|
+
pi install npm:@juicesharp/rpiv-todo # Task tracking with bd sync
|
|
35
|
+
```
|
|
36
|
+
|
|
28
37
|
Requires Pi v0.37.3+.
|
|
29
38
|
|
|
30
39
|
### Headless Server Setup (herdr clipboard bridge)
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Channel Notification Center
|
|
3
|
+
*
|
|
4
|
+
* Unified notification system supporting Telegram and LINE.
|
|
5
|
+
* Users can configure which channels to use and toggle them via inline buttons.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as https from "https";
|
|
9
|
+
|
|
10
|
+
export interface NotificationConfig {
|
|
11
|
+
telegram?: {
|
|
12
|
+
botToken: string;
|
|
13
|
+
chatId: string;
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
};
|
|
16
|
+
line?: {
|
|
17
|
+
channelAccessToken: string;
|
|
18
|
+
userId: string;
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface NotificationEvent {
|
|
24
|
+
type: string;
|
|
25
|
+
emoji: string;
|
|
26
|
+
title: string;
|
|
27
|
+
message: string;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface NotificationButtons {
|
|
32
|
+
label: string;
|
|
33
|
+
action: string;
|
|
34
|
+
style?: "primary" | "secondary" | "danger";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const DEFAULT_CONFIG_FILE = `${process.env.HOME || "/home/frappe"}/.pi-harness-runtime/notification-config.json`;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load notification configuration
|
|
41
|
+
*/
|
|
42
|
+
function loadConfig(): NotificationConfig {
|
|
43
|
+
try {
|
|
44
|
+
const { existsSync, readFileSync } = require("fs");
|
|
45
|
+
if (existsSync(DEFAULT_CONFIG_FILE)) {
|
|
46
|
+
return JSON.parse(readFileSync(DEFAULT_CONFIG_FILE, "utf-8"));
|
|
47
|
+
}
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.error("Failed to save notification config:", e);
|
|
50
|
+
}
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Save notification configuration
|
|
56
|
+
*/
|
|
57
|
+
function saveConfig(config: NotificationConfig): void {
|
|
58
|
+
try {
|
|
59
|
+
const { writeFileSync, mkdirSync, existsSync } = require("fs");
|
|
60
|
+
const dir = DEFAULT_CONFIG_FILE.substring(0, DEFAULT_CONFIG_FILE.lastIndexOf("/"));
|
|
61
|
+
if (!existsSync(dir)) {
|
|
62
|
+
mkdirSync(dir, { recursive: true });
|
|
63
|
+
}
|
|
64
|
+
writeFileSync(DEFAULT_CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.error("Failed to save notification config:", e);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Multi-Channel Notification Center
|
|
72
|
+
*/
|
|
73
|
+
export class NotificationCenter {
|
|
74
|
+
private config: NotificationConfig;
|
|
75
|
+
|
|
76
|
+
constructor(config?: Partial<NotificationConfig>) {
|
|
77
|
+
this.config = loadConfig();
|
|
78
|
+
if (config?.telegram) {
|
|
79
|
+
this.config.telegram = { ...this.config.telegram, ...config.telegram };
|
|
80
|
+
}
|
|
81
|
+
if (config?.line) {
|
|
82
|
+
this.config.line = { ...this.config.line, ...config.line };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Update configuration
|
|
88
|
+
*/
|
|
89
|
+
updateConfig(updates: Partial<NotificationConfig>): void {
|
|
90
|
+
this.config = { ...this.config, ...updates };
|
|
91
|
+
if (updates.telegram) {
|
|
92
|
+
this.config.telegram = { ...this.config.telegram!, ...updates.telegram };
|
|
93
|
+
}
|
|
94
|
+
if (updates.line) {
|
|
95
|
+
this.config.line = { ...this.config.line!, ...updates.line };
|
|
96
|
+
}
|
|
97
|
+
saveConfig(this.config);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get current configuration
|
|
102
|
+
*/
|
|
103
|
+
getConfig(): NotificationConfig {
|
|
104
|
+
return { ...this.config };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Send notification to all enabled channels
|
|
109
|
+
*/
|
|
110
|
+
async notify(event: NotificationEvent): Promise<void> {
|
|
111
|
+
const { telegram, line } = this.config;
|
|
112
|
+
const promises: Promise<void>[] = [];
|
|
113
|
+
|
|
114
|
+
if (telegram?.enabled && telegram.botToken && telegram.chatId) {
|
|
115
|
+
promises.push(this.sendTelegram(event, telegram.botToken, telegram.chatId));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (line?.enabled && line.channelAccessToken && line.userId) {
|
|
119
|
+
promises.push(this.sendLINE(event, line.channelAccessToken, line.userId));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await Promise.allSettled(promises);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Send notification with action buttons
|
|
127
|
+
*/
|
|
128
|
+
async notifyWithButtons(
|
|
129
|
+
event: NotificationEvent,
|
|
130
|
+
buttons: NotificationButtons[]
|
|
131
|
+
): Promise<void> {
|
|
132
|
+
const { telegram, line } = this.config;
|
|
133
|
+
|
|
134
|
+
if (telegram?.enabled && telegram.botToken && telegram.chatId) {
|
|
135
|
+
await this.sendTelegramWithButtons(event, buttons, telegram.botToken, telegram.chatId);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (line?.enabled && line.channelAccessToken && line.userId) {
|
|
139
|
+
await this.sendLINEWithButtons(event, buttons, line.channelAccessToken, line.userId);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Send to Telegram
|
|
145
|
+
*/
|
|
146
|
+
private async sendTelegram(
|
|
147
|
+
event: NotificationEvent,
|
|
148
|
+
botToken: string,
|
|
149
|
+
chatId: string
|
|
150
|
+
): Promise<void> {
|
|
151
|
+
const text = `${event.emoji} ${event.title}\n\n${event.message}`;
|
|
152
|
+
|
|
153
|
+
await this.telegramRequest(botToken, "sendMessage", {
|
|
154
|
+
chat_id: chatId,
|
|
155
|
+
text,
|
|
156
|
+
parse_mode: "Markdown",
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Send to Telegram with inline buttons
|
|
162
|
+
*/
|
|
163
|
+
private async sendTelegramWithButtons(
|
|
164
|
+
event: NotificationEvent,
|
|
165
|
+
buttons: NotificationButtons[],
|
|
166
|
+
botToken: string,
|
|
167
|
+
chatId: string
|
|
168
|
+
): Promise<void> {
|
|
169
|
+
const text = `${event.emoji} ${event.title}\n\n${event.message}`;
|
|
170
|
+
const inlineButtons = buttons.map((btn) => ({
|
|
171
|
+
text: btn.label,
|
|
172
|
+
callback_data: btn.action,
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
await this.telegramRequest(botToken, "sendMessage", {
|
|
176
|
+
chat_id: chatId,
|
|
177
|
+
text,
|
|
178
|
+
parse_mode: "Markdown",
|
|
179
|
+
reply_markup: {
|
|
180
|
+
inline_keyboard: [inlineButtons],
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Telegram API request
|
|
187
|
+
*/
|
|
188
|
+
private telegramRequest(botToken: string, method: string, body: Record<string, unknown>): Promise<void> {
|
|
189
|
+
return new Promise((resolve, _reject) => {
|
|
190
|
+
const data = JSON.stringify(body);
|
|
191
|
+
const options = {
|
|
192
|
+
hostname: "api.telegram.org",
|
|
193
|
+
path: `/bot${botToken}/${method}`,
|
|
194
|
+
method: "POST",
|
|
195
|
+
headers: {
|
|
196
|
+
"Content-Type": "application/json",
|
|
197
|
+
"Content-Length": Buffer.byteLength(data),
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const req = https.request(options, (res) => {
|
|
202
|
+
let response = "";
|
|
203
|
+
res.on("data", (chunk) => (response += chunk));
|
|
204
|
+
res.on("end", () => {
|
|
205
|
+
try {
|
|
206
|
+
const parsed = JSON.parse(response);
|
|
207
|
+
if (!parsed.ok) {
|
|
208
|
+
console.error(`Telegram API error: ${parsed.description}`);
|
|
209
|
+
}
|
|
210
|
+
} catch (e) {
|
|
211
|
+
console.error(`JSON parse error: ${e}`);
|
|
212
|
+
}
|
|
213
|
+
resolve();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
req.on("error", (err: Error) => {
|
|
218
|
+
console.error(`Telegram API request error: ${err.message}`);
|
|
219
|
+
resolve();
|
|
220
|
+
});
|
|
221
|
+
req.write(data);
|
|
222
|
+
req.end();
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Send to LINE
|
|
228
|
+
*/
|
|
229
|
+
private async sendLINE(
|
|
230
|
+
event: NotificationEvent,
|
|
231
|
+
accessToken: string,
|
|
232
|
+
userId: string
|
|
233
|
+
): Promise<void> {
|
|
234
|
+
const text = `${event.emoji} ${event.title}\n\n${event.message}`;
|
|
235
|
+
|
|
236
|
+
await this.lineRequest(accessToken, "push", {
|
|
237
|
+
to: userId,
|
|
238
|
+
messages: [{ type: "text", text }],
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Send to LINE with quick reply buttons
|
|
244
|
+
*/
|
|
245
|
+
private async sendLINEWithButtons(
|
|
246
|
+
event: NotificationEvent,
|
|
247
|
+
buttons: NotificationButtons[],
|
|
248
|
+
accessToken: string,
|
|
249
|
+
userId: string
|
|
250
|
+
): Promise<void> {
|
|
251
|
+
const text = `${event.emoji} ${event.title}\n\n${event.message}`;
|
|
252
|
+
|
|
253
|
+
const actions = buttons.map((btn) => ({
|
|
254
|
+
type: "message" as const,
|
|
255
|
+
label: btn.label,
|
|
256
|
+
text: btn.action,
|
|
257
|
+
}));
|
|
258
|
+
|
|
259
|
+
await this.lineRequest(accessToken, "push", {
|
|
260
|
+
to: userId,
|
|
261
|
+
messages: [
|
|
262
|
+
{ type: "text", text },
|
|
263
|
+
{
|
|
264
|
+
type: "text",
|
|
265
|
+
text: "Quick actions:",
|
|
266
|
+
quickReply: {
|
|
267
|
+
items: actions.map((action) => ({
|
|
268
|
+
type: "action",
|
|
269
|
+
action,
|
|
270
|
+
})),
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* LINE API request
|
|
279
|
+
*/
|
|
280
|
+
private lineRequest(accessToken: string, method: string, body: Record<string, unknown>): Promise<void> {
|
|
281
|
+
return new Promise((resolve, reject) => {
|
|
282
|
+
const data = JSON.stringify(body);
|
|
283
|
+
const options = {
|
|
284
|
+
hostname: "api.line.me",
|
|
285
|
+
path: `/v2/bot/message/${method}`,
|
|
286
|
+
method: "POST",
|
|
287
|
+
headers: {
|
|
288
|
+
"Content-Type": "application/json",
|
|
289
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
290
|
+
"Content-Length": Buffer.byteLength(data),
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const req = https.request(options, (res) => {
|
|
295
|
+
let response = "";
|
|
296
|
+
res.on("data", (chunk) => (response += chunk));
|
|
297
|
+
res.on("end", () => {
|
|
298
|
+
if (res.statusCode !== 200) {
|
|
299
|
+
console.error(`LINE API error: ${res.statusCode} ${response}`);
|
|
300
|
+
}
|
|
301
|
+
resolve();
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
req.on("error", reject);
|
|
306
|
+
req.write(data);
|
|
307
|
+
req.end();
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Toggle Telegram notifications
|
|
313
|
+
*/
|
|
314
|
+
toggleTelegram(enabled: boolean): void {
|
|
315
|
+
if (!this.config.telegram) {
|
|
316
|
+
this.config.telegram = { botToken: "", chatId: "", enabled };
|
|
317
|
+
} else {
|
|
318
|
+
this.config.telegram.enabled = enabled;
|
|
319
|
+
}
|
|
320
|
+
saveConfig(this.config);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Toggle LINE notifications
|
|
325
|
+
*/
|
|
326
|
+
toggleLINE(enabled: boolean): void {
|
|
327
|
+
if (!this.config.line) {
|
|
328
|
+
this.config.line = { channelAccessToken: "", userId: "", enabled };
|
|
329
|
+
} else {
|
|
330
|
+
this.config.line.enabled = enabled;
|
|
331
|
+
}
|
|
332
|
+
saveConfig(this.config);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Check if Telegram is enabled
|
|
337
|
+
*/
|
|
338
|
+
isTelegramEnabled(): boolean {
|
|
339
|
+
return this.config.telegram?.enabled ?? false;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Check if LINE is enabled
|
|
344
|
+
*/
|
|
345
|
+
isLINEEnabled(): boolean {
|
|
346
|
+
return this.config.line?.enabled ?? false;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Export singleton
|
|
351
|
+
let center: NotificationCenter | null = null;
|
|
352
|
+
|
|
353
|
+
export function getNotificationCenter(): NotificationCenter {
|
|
354
|
+
if (!center) {
|
|
355
|
+
center = new NotificationCenter();
|
|
356
|
+
}
|
|
357
|
+
return center;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function initNotificationCenter(config?: Partial<NotificationConfig>): NotificationCenter {
|
|
361
|
+
center = new NotificationCenter(config);
|
|
362
|
+
return center;
|
|
363
|
+
}
|
package/index.ts
CHANGED
|
@@ -1078,16 +1078,17 @@ async function advanceCodexTask(
|
|
|
1078
1078
|
async function initTelegram(pi: ExtensionAPI): Promise<void> {
|
|
1079
1079
|
addHarnessMessage("Telegram", "initializing", "wait");
|
|
1080
1080
|
try {
|
|
1081
|
-
const
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
} = await import("./packages/notification/dist/index.js");
|
|
1081
|
+
const notifModule = await import("./packages/notification/dist/index.js");
|
|
1082
|
+
const NotificationCenter = notifModule.NotificationCenter;
|
|
1083
|
+
const createConversationBridge = notifModule.createConversationBridge;
|
|
1085
1084
|
const { readFileSync, existsSync } = await import("node:fs");
|
|
1086
1085
|
const home = process.env.HOME || process.env.USERPROFILE || "/home/frappe";
|
|
1087
1086
|
const keysDir = `${home}/.pi-harness-runtime/keys`;
|
|
1088
1087
|
|
|
1089
1088
|
const botTokenPath = `${keysDir}/telegram-bot-token.txt`;
|
|
1090
1089
|
const chatIdPath = `${keysDir}/telegram-chat-id.txt`;
|
|
1090
|
+
const lineTokenPath = `${keysDir}/line-access-token.txt`;
|
|
1091
|
+
const lineUserIdPath = `${keysDir}/line-user-id.txt`;
|
|
1091
1092
|
|
|
1092
1093
|
if (!existsSync(botTokenPath) || !existsSync(chatIdPath)) {
|
|
1093
1094
|
logStartup("[pi-harness] Telegram (@PiHarnessRuntimeBot) not configured:");
|
|
@@ -1107,6 +1108,11 @@ async function initTelegram(pi: ExtensionAPI): Promise<void> {
|
|
|
1107
1108
|
const botToken = readFileSync(botTokenPath, "utf8").trim();
|
|
1108
1109
|
const chatId = readFileSync(chatIdPath, "utf8").trim();
|
|
1109
1110
|
|
|
1111
|
+
// Load LINE credentials
|
|
1112
|
+
const hasLineCredentials = existsSync(lineTokenPath) && existsSync(lineUserIdPath);
|
|
1113
|
+
const lineAccessToken = hasLineCredentials ? readFileSync(lineTokenPath, "utf8").trim() : null;
|
|
1114
|
+
const lineUserId = hasLineCredentials ? readFileSync(lineUserIdPath, "utf8").trim() : null;
|
|
1115
|
+
|
|
1110
1116
|
if (!botToken || !chatId) {
|
|
1111
1117
|
logStartup("[pi-harness] Telegram: bot token or chat ID is empty — skipping");
|
|
1112
1118
|
addHarnessMessage("Telegram", "bot token or chat ID is empty; notifications unavailable", "warn", [
|
|
@@ -1115,16 +1121,35 @@ async function initTelegram(pi: ExtensionAPI): Promise<void> {
|
|
|
1115
1121
|
return;
|
|
1116
1122
|
}
|
|
1117
1123
|
|
|
1124
|
+
const channels: Array<{
|
|
1125
|
+
id: string;
|
|
1126
|
+
type: string;
|
|
1127
|
+
enabled: boolean;
|
|
1128
|
+
config: Record<string, unknown>;
|
|
1129
|
+
}> = [
|
|
1130
|
+
{
|
|
1131
|
+
id: "telegram",
|
|
1132
|
+
type: "telegram",
|
|
1133
|
+
enabled: true,
|
|
1134
|
+
config: { botToken, chatId },
|
|
1135
|
+
},
|
|
1136
|
+
];
|
|
1137
|
+
|
|
1138
|
+
// Add LINE channel if credentials exist
|
|
1139
|
+
if (hasLineCredentials && lineAccessToken && lineUserId) {
|
|
1140
|
+
channels.push({
|
|
1141
|
+
id: "line",
|
|
1142
|
+
type: "line" as const,
|
|
1143
|
+
enabled: true,
|
|
1144
|
+
config: { channelAccessToken: lineAccessToken, userId: lineUserId },
|
|
1145
|
+
});
|
|
1146
|
+
addHarnessMessage("LINE", "configured and enabled", "ok");
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1118
1150
|
const center = new NotificationCenter({
|
|
1119
1151
|
enabled: true,
|
|
1120
|
-
channels:
|
|
1121
|
-
{
|
|
1122
|
-
id: "telegram",
|
|
1123
|
-
type: "telegram",
|
|
1124
|
-
enabled: true,
|
|
1125
|
-
config: { botToken, chatId },
|
|
1126
|
-
},
|
|
1127
|
-
],
|
|
1152
|
+
channels: channels as any,
|
|
1128
1153
|
});
|
|
1129
1154
|
|
|
1130
1155
|
await center.initialize();
|
|
@@ -1187,9 +1212,22 @@ async function initTelegram(pi: ExtensionAPI): Promise<void> {
|
|
|
1187
1212
|
(sharedState.todoContinuationController as any)?.explicitPause?.();
|
|
1188
1213
|
break;
|
|
1189
1214
|
default:
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1215
|
+
// Check for channel toggle actions
|
|
1216
|
+
if (parsed.action === "toggle_telegram") {
|
|
1217
|
+
const telegramEnabled = (center as any).isChannelEnabled?.("telegram") ?? true;
|
|
1218
|
+
(center as any).setChannelEnabled?.("telegram", !telegramEnabled);
|
|
1219
|
+
logToFile("[TelegramCallback]", `Telegram ${!telegramEnabled ? "enabled" : "disabled"}`);
|
|
1220
|
+
await acknowledge(`Telegram notifications ${!telegramEnabled ? "enabled" : "disabled"}`);
|
|
1221
|
+
} else if (parsed.action === "toggle_line") {
|
|
1222
|
+
const lineEnabled = (center as any).isChannelEnabled?.("line") ?? true;
|
|
1223
|
+
(center as any).setChannelEnabled?.("line", !lineEnabled);
|
|
1224
|
+
logToFile("[TelegramCallback]", `LINE ${!lineEnabled ? "enabled" : "disabled"}`);
|
|
1225
|
+
await acknowledge(`LINE notifications ${!lineEnabled ? "enabled" : "disabled"}`);
|
|
1226
|
+
} else {
|
|
1227
|
+
logToFile("[TelegramCallback]", `Unknown action: ${parsed.action}`);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1193
1231
|
|
|
1194
1232
|
// Register message handler to forward Telegram messages to Pi
|
|
1195
1233
|
center.setMessageHandler(async (text: string, _chatId: string) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/capability-registry",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.343",
|
|
4
4
|
"description": "Capability Registry for pi-harness-runtime — tracks model capabilities and enables capability-based routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|