pi-harness-runtime 1.1.430 → 1.1.431
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/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/logging/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/package.json +1 -1
- 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/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/tooling-preference/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/two-phase-review/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/package.json +1 -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/packages/notification/adapters/email-adapter.ts +0 -126
- package/packages/notification/adapters/ntfy-adapter.ts +0 -100
- package/packages/notification/adapters/telegram-adapter.ts +0 -107
- package/packages/notification/adapters/webhook-adapter.ts +0 -75
- package/packages/notification/base-adapter.ts +0 -75
- package/packages/notification/index.ts +0 -51
- package/packages/notification/notification-center.ts +0 -286
- package/packages/notification/types.ts +0 -89
- package/packages/quota-manager/index.ts +0 -28
- package/packages/quota-manager/quota-manager.ts +0 -339
- package/packages/quota-manager/tui-usage-monitor.ts +0 -355
- package/packages/types/src/ai-providers.d.ts +0 -36
- package/packages/types/src/runtime-types.d.ts +0 -418
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Telegram Adapter — RFC-0022
|
|
3
|
-
*
|
|
4
|
-
* Sends notifications via Telegram Bot API.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
NotificationPayload,
|
|
9
|
-
NotificationResult,
|
|
10
|
-
TelegramConfig,
|
|
11
|
-
} from "../types.js";
|
|
12
|
-
import { BaseChannelAdapter } from "../base-adapter.js";
|
|
13
|
-
|
|
14
|
-
export class TelegramAdapter extends BaseChannelAdapter {
|
|
15
|
-
readonly id = "telegram";
|
|
16
|
-
readonly type = "telegram";
|
|
17
|
-
|
|
18
|
-
constructor(config: TelegramConfig) {
|
|
19
|
-
super({ id: "telegram", type: "telegram", enabled: true, config });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async initialize(): Promise<boolean> {
|
|
23
|
-
try {
|
|
24
|
-
const cfg = this.config.config as TelegramConfig;
|
|
25
|
-
// Verify bot token by calling getMe
|
|
26
|
-
const response = await fetch(
|
|
27
|
-
`https://api.telegram.org/bot${cfg.botToken}/getMe`,
|
|
28
|
-
);
|
|
29
|
-
return response.ok;
|
|
30
|
-
} catch {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async send(payload: NotificationPayload): Promise<NotificationResult> {
|
|
36
|
-
try {
|
|
37
|
-
const cfg = this.config.config as TelegramConfig;
|
|
38
|
-
const message = this.formatMessage(payload);
|
|
39
|
-
|
|
40
|
-
const response = await fetch(
|
|
41
|
-
`https://api.telegram.org/bot${cfg.botToken}/sendMessage`,
|
|
42
|
-
{
|
|
43
|
-
method: "POST",
|
|
44
|
-
headers: { "Content-Type": "application/json" },
|
|
45
|
-
body: JSON.stringify({
|
|
46
|
-
chat_id: cfg.chatId,
|
|
47
|
-
text: message,
|
|
48
|
-
parse_mode: cfg.parseMode ?? "MarkdownV2",
|
|
49
|
-
}),
|
|
50
|
-
},
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
if (!response.ok) {
|
|
54
|
-
const error = await response.text();
|
|
55
|
-
return {
|
|
56
|
-
success: false,
|
|
57
|
-
channel: this.id,
|
|
58
|
-
error: `Telegram API error: ${error}`,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return { success: true, channel: this.id };
|
|
63
|
-
} catch (error) {
|
|
64
|
-
return {
|
|
65
|
-
success: false,
|
|
66
|
-
channel: this.id,
|
|
67
|
-
error: String(error),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private formatMessage(payload: NotificationPayload): string {
|
|
73
|
-
const emoji = this.getEmoji(payload.event);
|
|
74
|
-
const title = `${emoji} ${payload.title}`;
|
|
75
|
-
const lines = [title, "", payload.message];
|
|
76
|
-
|
|
77
|
-
if (payload.details?.taskTitle) {
|
|
78
|
-
lines.push("", `Task: ${payload.details.taskTitle}`);
|
|
79
|
-
}
|
|
80
|
-
if (payload.details?.jobId) {
|
|
81
|
-
lines.push(`Job: ${payload.details.jobId}`);
|
|
82
|
-
}
|
|
83
|
-
if (payload.details?.error) {
|
|
84
|
-
lines.push("", `Error: ${payload.details.error}`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return lines.filter(Boolean).join("\n");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private getEmoji(event: NotificationPayload["event"]): string {
|
|
91
|
-
const map: Record<string, string> = {
|
|
92
|
-
JobStarted: "🚀",
|
|
93
|
-
TaskCompleted: "✅",
|
|
94
|
-
TaskFailed: "❌",
|
|
95
|
-
QuotaPaused: "⏸️",
|
|
96
|
-
ResumeScheduled: "▶️",
|
|
97
|
-
ContextCompacted: "📦",
|
|
98
|
-
OutputLimitContinued: "🔄",
|
|
99
|
-
E2EFailed: "🧪",
|
|
100
|
-
HumanReviewNeeded: "👤",
|
|
101
|
-
ReadyForClient: "🎉",
|
|
102
|
-
JobCancelled: "🚫",
|
|
103
|
-
Error: "⚠️",
|
|
104
|
-
};
|
|
105
|
-
return map[event] ?? "📢";
|
|
106
|
-
}
|
|
107
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Webhook Adapter — RFC-0022
|
|
3
|
-
*
|
|
4
|
-
* Sends notifications to a generic webhook endpoint.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
NotificationPayload,
|
|
9
|
-
NotificationResult,
|
|
10
|
-
WebhookConfig,
|
|
11
|
-
} from "../types.js";
|
|
12
|
-
import { BaseChannelAdapter } from "../base-adapter.js";
|
|
13
|
-
|
|
14
|
-
export class WebhookAdapter extends BaseChannelAdapter {
|
|
15
|
-
readonly id = "webhook";
|
|
16
|
-
readonly type = "webhook";
|
|
17
|
-
|
|
18
|
-
constructor(config: WebhookConfig) {
|
|
19
|
-
super({ id: "webhook", type: "webhook", enabled: true, config });
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async initialize(): Promise<boolean> {
|
|
23
|
-
try {
|
|
24
|
-
const cfg = this.config.config as WebhookConfig;
|
|
25
|
-
return Boolean(cfg.url && cfg.url.startsWith("http"));
|
|
26
|
-
} catch {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async send(payload: NotificationPayload): Promise<NotificationResult> {
|
|
32
|
-
try {
|
|
33
|
-
const cfg = this.config.config as WebhookConfig;
|
|
34
|
-
const headers: Record<string, string> = {
|
|
35
|
-
"Content-Type": "application/json",
|
|
36
|
-
...(cfg.headers ?? {}),
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// Add auth token if configured
|
|
40
|
-
if (cfg.authToken) {
|
|
41
|
-
headers["Authorization"] = `Bearer ${cfg.authToken}`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const response = await fetch(cfg.url, {
|
|
45
|
-
method: cfg.method ?? "POST",
|
|
46
|
-
headers,
|
|
47
|
-
body: JSON.stringify({
|
|
48
|
-
event: payload.event,
|
|
49
|
-
jobId: payload.jobId,
|
|
50
|
-
timestamp: payload.timestamp,
|
|
51
|
-
title: payload.title,
|
|
52
|
-
message: payload.message,
|
|
53
|
-
details: payload.details,
|
|
54
|
-
}),
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (!response.ok) {
|
|
58
|
-
const error = await response.text();
|
|
59
|
-
return {
|
|
60
|
-
success: false,
|
|
61
|
-
channel: this.id,
|
|
62
|
-
error: `Webhook error ${response.status}: ${error}`,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return { success: true, channel: this.id };
|
|
67
|
-
} catch (error) {
|
|
68
|
-
return {
|
|
69
|
-
success: false,
|
|
70
|
-
channel: this.id,
|
|
71
|
-
error: String(error),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notification Base Adapter — RFC-0022
|
|
3
|
-
*
|
|
4
|
-
* Abstract base class for all notification channel adapters.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
NotificationPayload,
|
|
9
|
-
NotificationChannelConfig,
|
|
10
|
-
NotificationResult,
|
|
11
|
-
} from "./types.js";
|
|
12
|
-
|
|
13
|
-
export interface ChannelAdapter {
|
|
14
|
-
readonly id: string;
|
|
15
|
-
readonly type: string;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Initialize the adapter (e.g., verify credentials)
|
|
19
|
-
*/
|
|
20
|
-
initialize(): Promise<boolean>;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Send a notification
|
|
24
|
-
*/
|
|
25
|
-
send(payload: NotificationPayload): Promise<NotificationResult>;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Check if the adapter is properly configured
|
|
29
|
-
*/
|
|
30
|
-
isConfigured(): boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export abstract class BaseChannelAdapter implements ChannelAdapter {
|
|
34
|
-
abstract readonly id: string;
|
|
35
|
-
abstract readonly type: string;
|
|
36
|
-
|
|
37
|
-
constructor(protected config: NotificationChannelConfig) {}
|
|
38
|
-
|
|
39
|
-
abstract initialize(): Promise<boolean>;
|
|
40
|
-
abstract send(payload: NotificationPayload): Promise<NotificationResult>;
|
|
41
|
-
|
|
42
|
-
isConfigured(): boolean {
|
|
43
|
-
return this.config.enabled;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Redact sensitive data from payload before sending
|
|
48
|
-
*/
|
|
49
|
-
protected redact(
|
|
50
|
-
payload: NotificationPayload,
|
|
51
|
-
patterns: RegExp[],
|
|
52
|
-
): NotificationPayload {
|
|
53
|
-
if (patterns.length === 0) return payload;
|
|
54
|
-
|
|
55
|
-
const redacted: string[] = [];
|
|
56
|
-
const details = payload.details ? { ...payload.details } : {};
|
|
57
|
-
|
|
58
|
-
for (const [key, value] of Object.entries(details)) {
|
|
59
|
-
const valStr = String(value);
|
|
60
|
-
for (const pattern of patterns) {
|
|
61
|
-
if (pattern.test(valStr)) {
|
|
62
|
-
redacted.push(key);
|
|
63
|
-
(details as Record<string, unknown>)[key] = "[REDACTED]";
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
...payload,
|
|
71
|
-
details,
|
|
72
|
-
redacted: redacted.length > 0 ? redacted : undefined,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notification Center — RFC-0022
|
|
3
|
-
*
|
|
4
|
-
* Mobile notification system for harness runtime events.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```typescript
|
|
8
|
-
* import { NotificationCenter } from "./notification/index.js";
|
|
9
|
-
*
|
|
10
|
-
* const center = new NotificationCenter({
|
|
11
|
-
* channels: [
|
|
12
|
-
* {
|
|
13
|
-
* id: "my-telegram",
|
|
14
|
-
* type: "telegram",
|
|
15
|
-
* enabled: true,
|
|
16
|
-
* config: {
|
|
17
|
-
* botToken: process.env.TELEGRAM_BOT_TOKEN,
|
|
18
|
-
* chatId: "YOUR_CHAT_ID",
|
|
19
|
-
* },
|
|
20
|
-
* },
|
|
21
|
-
* ],
|
|
22
|
-
* });
|
|
23
|
-
*
|
|
24
|
-
* await center.initialize();
|
|
25
|
-
*
|
|
26
|
-
* await center.notify("JobStarted", {
|
|
27
|
-
* jobId: "job-123",
|
|
28
|
-
* requirement: "Build a REST API",
|
|
29
|
-
* });
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
export { NotificationCenter } from "./notification-center.js";
|
|
34
|
-
export { TelegramAdapter } from "./adapters/telegram-adapter.js";
|
|
35
|
-
export { NtfyAdapter } from "./adapters/ntfy-adapter.js";
|
|
36
|
-
export { EmailAdapter } from "./adapters/email-adapter.js";
|
|
37
|
-
export { WebhookAdapter } from "./adapters/webhook-adapter.js";
|
|
38
|
-
|
|
39
|
-
export type {
|
|
40
|
-
NotificationEvent,
|
|
41
|
-
NotificationPayload,
|
|
42
|
-
NotificationConfig,
|
|
43
|
-
NotificationChannelConfig,
|
|
44
|
-
NotificationChannelType,
|
|
45
|
-
NotificationResult,
|
|
46
|
-
NotificationContext,
|
|
47
|
-
TelegramConfig,
|
|
48
|
-
NtfyConfig,
|
|
49
|
-
EmailConfig,
|
|
50
|
-
WebhookConfig,
|
|
51
|
-
} from "./types.js";
|
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notification Center — RFC-0022
|
|
3
|
-
*
|
|
4
|
-
* Main orchestration class for sending notifications across multiple channels.
|
|
5
|
-
*
|
|
6
|
-
* Security Rules (RFC-0022):
|
|
7
|
-
* - Do not send raw cookies, passwords, provider tokens
|
|
8
|
-
* - Redact sensitive data before sending
|
|
9
|
-
* - Notification failure does not crash runtime
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type {
|
|
13
|
-
NotificationEvent,
|
|
14
|
-
NotificationPayload,
|
|
15
|
-
NotificationConfig,
|
|
16
|
-
NotificationChannelConfig,
|
|
17
|
-
NotificationResult,
|
|
18
|
-
NotificationContext,
|
|
19
|
-
} from "./types.js";
|
|
20
|
-
import type { ChannelAdapter } from "./base-adapter.js";
|
|
21
|
-
import { TelegramAdapter } from "./adapters/telegram-adapter.js";
|
|
22
|
-
import { NtfyAdapter } from "./adapters/ntfy-adapter.js";
|
|
23
|
-
import { EmailAdapter } from "./adapters/email-adapter.js";
|
|
24
|
-
import { WebhookAdapter } from "./adapters/webhook-adapter.js";
|
|
25
|
-
|
|
26
|
-
export class NotificationCenter {
|
|
27
|
-
private adapters: Map<string, ChannelAdapter> = new Map();
|
|
28
|
-
private redactPatterns: RegExp[];
|
|
29
|
-
|
|
30
|
-
constructor(config?: NotificationConfig) {
|
|
31
|
-
this.redactPatterns =
|
|
32
|
-
config?.redactPatterns ?? this.getDefaultRedactPatterns();
|
|
33
|
-
|
|
34
|
-
if (config?.channels) {
|
|
35
|
-
for (const channelConfig of config.channels) {
|
|
36
|
-
if (channelConfig.enabled) {
|
|
37
|
-
this.registerAdapter(channelConfig);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Register a new adapter
|
|
45
|
-
*/
|
|
46
|
-
registerAdapter(config: NotificationChannelConfig): boolean {
|
|
47
|
-
try {
|
|
48
|
-
const adapter = this.createAdapter(config);
|
|
49
|
-
if (adapter && adapter.isConfigured()) {
|
|
50
|
-
this.adapters.set(config.id, adapter);
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
} catch (error) {
|
|
54
|
-
console.error(
|
|
55
|
-
`[NotificationCenter] Failed to register adapter: ${error}`,
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Initialize all registered adapters
|
|
63
|
-
*/
|
|
64
|
-
async initialize(): Promise<void> {
|
|
65
|
-
const adapterEntries = Array.from(this.adapters.entries());
|
|
66
|
-
for (const [id, adapter] of adapterEntries) {
|
|
67
|
-
try {
|
|
68
|
-
const ok = await adapter.initialize();
|
|
69
|
-
if (!ok) {
|
|
70
|
-
console.warn(
|
|
71
|
-
`[NotificationCenter] Adapter ${id} initialization failed`,
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
} catch (error) {
|
|
75
|
-
console.warn(
|
|
76
|
-
`[NotificationCenter] Adapter ${id} initialization error: ${error}`,
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Send a notification to all configured channels
|
|
84
|
-
*/
|
|
85
|
-
async notify(
|
|
86
|
-
event: NotificationEvent,
|
|
87
|
-
context: NotificationContext,
|
|
88
|
-
): Promise<NotificationResult[]> {
|
|
89
|
-
const payload = this.buildPayload(event, context);
|
|
90
|
-
const results: NotificationResult[] = [];
|
|
91
|
-
|
|
92
|
-
// Send to all adapters in parallel
|
|
93
|
-
const adapterEntries = Array.from(this.adapters.entries());
|
|
94
|
-
const promises = adapterEntries.map(async ([id, adapter]) => {
|
|
95
|
-
try {
|
|
96
|
-
// Redact sensitive data
|
|
97
|
-
const redactedPayload = this.redact(payload);
|
|
98
|
-
const result = await adapter.send(redactedPayload);
|
|
99
|
-
results.push(result);
|
|
100
|
-
} catch (error) {
|
|
101
|
-
// Never crash the runtime due to notification failure
|
|
102
|
-
results.push({
|
|
103
|
-
success: false,
|
|
104
|
-
channel: id,
|
|
105
|
-
error: String(error),
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
await Promise.all(promises);
|
|
111
|
-
return results;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Send notification to a specific channel
|
|
116
|
-
*/
|
|
117
|
-
async notifyChannel(
|
|
118
|
-
channelId: string,
|
|
119
|
-
event: NotificationEvent,
|
|
120
|
-
context: NotificationContext,
|
|
121
|
-
): Promise<NotificationResult> {
|
|
122
|
-
const adapter = this.adapters.get(channelId);
|
|
123
|
-
if (!adapter) {
|
|
124
|
-
return { success: false, channel: channelId, error: "Adapter not found" };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
const payload = this.redact(this.buildPayload(event, context));
|
|
129
|
-
return await adapter.send(payload);
|
|
130
|
-
} catch (error) {
|
|
131
|
-
return { success: false, channel: channelId, error: String(error) };
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Check if any notifications are configured
|
|
137
|
-
*/
|
|
138
|
-
hasChannels(): boolean {
|
|
139
|
-
return this.adapters.size > 0;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* List all configured channels
|
|
144
|
-
*/
|
|
145
|
-
listChannels(): string[] {
|
|
146
|
-
return Array.from(this.adapters.keys());
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// ─── Private Methods ────────────────────────────────────────────────
|
|
150
|
-
|
|
151
|
-
private createAdapter(
|
|
152
|
-
config: NotificationChannelConfig,
|
|
153
|
-
): ChannelAdapter | null {
|
|
154
|
-
switch (config.type) {
|
|
155
|
-
case "telegram":
|
|
156
|
-
return new TelegramAdapter(
|
|
157
|
-
config.config as import("./types.js").TelegramConfig,
|
|
158
|
-
);
|
|
159
|
-
case "ntfy":
|
|
160
|
-
return new NtfyAdapter(
|
|
161
|
-
config.config as import("./types.js").NtfyConfig,
|
|
162
|
-
);
|
|
163
|
-
case "email":
|
|
164
|
-
return new EmailAdapter(
|
|
165
|
-
config.config as import("./types.js").EmailConfig,
|
|
166
|
-
);
|
|
167
|
-
case "webhook":
|
|
168
|
-
return new WebhookAdapter(
|
|
169
|
-
config.config as import("./types.js").WebhookConfig,
|
|
170
|
-
);
|
|
171
|
-
default:
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
private buildPayload(
|
|
177
|
-
event: NotificationEvent,
|
|
178
|
-
context: NotificationContext,
|
|
179
|
-
): NotificationPayload {
|
|
180
|
-
const { title, message } = this.getEventContent(event, context);
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
event,
|
|
184
|
-
jobId: context.jobId,
|
|
185
|
-
timestamp: new Date().toISOString(),
|
|
186
|
-
title,
|
|
187
|
-
message,
|
|
188
|
-
details: {
|
|
189
|
-
jobId: context.jobId,
|
|
190
|
-
requirement: context.requirement,
|
|
191
|
-
...(context.taskId && { taskId: context.taskId }),
|
|
192
|
-
...(context.taskTitle && { taskTitle: context.taskTitle }),
|
|
193
|
-
...(context.error && { error: context.error }),
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
private getEventContent(
|
|
199
|
-
event: NotificationEvent,
|
|
200
|
-
context: NotificationContext,
|
|
201
|
-
): { title: string; message: string } {
|
|
202
|
-
const requirement =
|
|
203
|
-
context.requirement.length > 50
|
|
204
|
-
? context.requirement.slice(0, 50) + "..."
|
|
205
|
-
: context.requirement;
|
|
206
|
-
|
|
207
|
-
const map: Record<NotificationEvent, { title: string; message: string }> = {
|
|
208
|
-
JobStarted: {
|
|
209
|
-
title: "Job Started",
|
|
210
|
-
message: `Harness job started for: "${requirement}"`,
|
|
211
|
-
},
|
|
212
|
-
TaskCompleted: {
|
|
213
|
-
title: "Task Completed",
|
|
214
|
-
message: `Task "${context.taskTitle ?? "Unknown"}" completed successfully`,
|
|
215
|
-
},
|
|
216
|
-
TaskFailed: {
|
|
217
|
-
title: "Task Failed",
|
|
218
|
-
message: `Task "${context.taskTitle ?? "Unknown"}" failed${context.error ? `: ${context.error}` : ""}`,
|
|
219
|
-
},
|
|
220
|
-
QuotaPaused: {
|
|
221
|
-
title: "Quota Paused",
|
|
222
|
-
message: `Job paused due to quota limit. Will auto-resume when quota resets.`,
|
|
223
|
-
},
|
|
224
|
-
ResumeScheduled: {
|
|
225
|
-
title: "Resume Scheduled",
|
|
226
|
-
message: `Job will resume work on: "${requirement}"`,
|
|
227
|
-
},
|
|
228
|
-
ContextCompacted: {
|
|
229
|
-
title: "Context Compacted",
|
|
230
|
-
message: `Session context was compacted to continue work on: "${requirement}"`,
|
|
231
|
-
},
|
|
232
|
-
OutputLimitContinued: {
|
|
233
|
-
title: "Output Limit Continued",
|
|
234
|
-
message: `Response was continued after hitting output token limit`,
|
|
235
|
-
},
|
|
236
|
-
E2EFailed: {
|
|
237
|
-
title: "E2E Test Failed",
|
|
238
|
-
message: `End-to-end tests failed for job: "${requirement}"`,
|
|
239
|
-
},
|
|
240
|
-
HumanReviewNeeded: {
|
|
241
|
-
title: "Human Review Needed",
|
|
242
|
-
message: `Job blocked. Please review and take action.`,
|
|
243
|
-
},
|
|
244
|
-
ReadyForClient: {
|
|
245
|
-
title: "Ready for Review",
|
|
246
|
-
message: `Job completed successfully and ready for your review: "${requirement}"`,
|
|
247
|
-
},
|
|
248
|
-
JobCancelled: {
|
|
249
|
-
title: "Job Cancelled",
|
|
250
|
-
message: `Job was cancelled: "${requirement}"`,
|
|
251
|
-
},
|
|
252
|
-
Error: {
|
|
253
|
-
title: "Runtime Error",
|
|
254
|
-
message: `An error occurred${context.error ? `: ${context.error}` : ""}`,
|
|
255
|
-
},
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
return map[event] ?? { title: event, message: `Event: ${event}` };
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
private redact(payload: NotificationPayload): NotificationPayload {
|
|
262
|
-
const details = payload.details ? { ...payload.details } : {};
|
|
263
|
-
|
|
264
|
-
// Redact sensitive patterns
|
|
265
|
-
for (const pattern of this.redactPatterns) {
|
|
266
|
-
for (const [key, value] of Object.entries(details)) {
|
|
267
|
-
if (typeof value === "string" && pattern.test(value)) {
|
|
268
|
-
details[key] = "[REDACTED]";
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return { ...payload, details };
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
private getDefaultRedactPatterns(): RegExp[] {
|
|
277
|
-
return [
|
|
278
|
-
/Bearer\s+[\w-]+/gi, // Bearer tokens
|
|
279
|
-
/password["\s:=]+[^\s,}]+/gi, // passwords
|
|
280
|
-
/cookie["\s:=]+[^\s,}]+/gi, // cookies
|
|
281
|
-
/secret["\s:=]+[^\s,}]+/gi, // secrets
|
|
282
|
-
/api[_-]?key["\s:=]+[^\s,}]+/gi, // API keys
|
|
283
|
-
/auth["\s:=]+[^\s,}]+/gi, // auth tokens
|
|
284
|
-
];
|
|
285
|
-
}
|
|
286
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Notification Center Types — RFC-0022
|
|
3
|
-
*
|
|
4
|
-
* Type definitions for notification events, channels, and payloads.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export type NotificationEvent =
|
|
8
|
-
| "JobStarted"
|
|
9
|
-
| "TaskCompleted"
|
|
10
|
-
| "TaskFailed"
|
|
11
|
-
| "QuotaPaused"
|
|
12
|
-
| "ResumeScheduled"
|
|
13
|
-
| "ContextCompacted"
|
|
14
|
-
| "OutputLimitContinued"
|
|
15
|
-
| "E2EFailed"
|
|
16
|
-
| "HumanReviewNeeded"
|
|
17
|
-
| "ReadyForClient"
|
|
18
|
-
| "JobCancelled"
|
|
19
|
-
| "Error";
|
|
20
|
-
|
|
21
|
-
export interface NotificationPayload {
|
|
22
|
-
event: NotificationEvent;
|
|
23
|
-
jobId: string;
|
|
24
|
-
timestamp: string;
|
|
25
|
-
title: string;
|
|
26
|
-
message: string;
|
|
27
|
-
details?: Record<string, unknown>;
|
|
28
|
-
// Redacted fields for security
|
|
29
|
-
redacted?: string[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface NotificationConfig {
|
|
33
|
-
channels: NotificationChannelConfig[];
|
|
34
|
-
// Global settings
|
|
35
|
-
enabled?: boolean;
|
|
36
|
-
redactPatterns?: RegExp[];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface NotificationChannelConfig {
|
|
40
|
-
id: string;
|
|
41
|
-
type: NotificationChannelType;
|
|
42
|
-
enabled: boolean;
|
|
43
|
-
config: TelegramConfig | NtfyConfig | EmailConfig | WebhookConfig;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export type NotificationChannelType = "telegram" | "ntfy" | "email" | "webhook";
|
|
47
|
-
|
|
48
|
-
export interface TelegramConfig {
|
|
49
|
-
botToken: string;
|
|
50
|
-
chatId: string;
|
|
51
|
-
parseMode?: "MarkdownV2" | "HTML" | "Markdown";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface NtfyConfig {
|
|
55
|
-
server: string; // e.g., "https://ntfy.sh"
|
|
56
|
-
topic: string;
|
|
57
|
-
authToken?: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface EmailConfig {
|
|
61
|
-
smtpHost: string;
|
|
62
|
-
smtpPort: number;
|
|
63
|
-
smtpUser: string;
|
|
64
|
-
smtpPassword: string;
|
|
65
|
-
from: string;
|
|
66
|
-
to: string[];
|
|
67
|
-
tls?: boolean;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface WebhookConfig {
|
|
71
|
-
url: string;
|
|
72
|
-
method?: "POST" | "PUT";
|
|
73
|
-
headers?: Record<string, string>;
|
|
74
|
-
authToken?: string;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface NotificationResult {
|
|
78
|
-
success: boolean;
|
|
79
|
-
channel: string;
|
|
80
|
-
error?: string;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface NotificationContext {
|
|
84
|
-
jobId: string;
|
|
85
|
-
requirement: string;
|
|
86
|
-
taskId?: string;
|
|
87
|
-
taskTitle?: string;
|
|
88
|
-
error?: string;
|
|
89
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Quota Manager Package
|
|
3
|
-
*
|
|
4
|
-
* Collects quota signals from multiple sources:
|
|
5
|
-
* - API responses
|
|
6
|
-
* - Provider status
|
|
7
|
-
* - Playwright scraping
|
|
8
|
-
* - TUI messages
|
|
9
|
-
* - Local estimates
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// Core Quota Manager
|
|
13
|
-
export {
|
|
14
|
-
QuotaManager,
|
|
15
|
-
parseMiniMaxError,
|
|
16
|
-
parseOpenAIError,
|
|
17
|
-
} from "./quota-manager.js";
|
|
18
|
-
export type { QuotaSignalInput } from "./quota-manager.js";
|
|
19
|
-
|
|
20
|
-
// TUI Usage Monitor (unified message parser)
|
|
21
|
-
export {
|
|
22
|
-
TUIUsageMonitor,
|
|
23
|
-
createTUIUsageMonitor,
|
|
24
|
-
} from "./tui-usage-monitor.js";
|
|
25
|
-
export type {
|
|
26
|
-
TUIUsageSignal,
|
|
27
|
-
TUIUsageMonitorConfig,
|
|
28
|
-
} from "./tui-usage-monitor.js";
|