fix: correct Abiba platform label (pi not openclaw), add plugin skeletons, shared config, fix health port
- Replace all 'openclaw' references with 'pi' (pi coding harness) - Fix Git remote URL in CONTEXT.md - Harmonize health port to 9200 across all docs - Add config.yaml.example with full schema - Add pi-zulip-extension skeleton (TypeScript, pi Extension API) - Add hermes-zulip-plugin skeleton (Python, BasePlatformAdapter) - Add agent-zero-plugin skeleton (Python, A0 plugin API) - Update ADR-007 with pi extension docs reference - Document private topic ACL v1 limitation in CONTEXT.md
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
zulip>=0.9.0
|
||||||
|
pyyaml>=6.0
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
"""
|
||||||
|
Agent Zero Zulip Plugin — Core plugin for Agent Zero
|
||||||
|
|
||||||
|
Connects kagentz (Agent Zero on CT 105) to the Sysloggh Zulip agent mesh
|
||||||
|
via the Agent Zero plugin API.
|
||||||
|
|
||||||
|
Config: config.yaml (per-agent, deployed alongside plugin)
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Agent Zero Zulip Plugin — Core implementation
|
||||||
|
# See ADR-005 for @mention detection, ADR-009 for error handling
|
||||||
|
# Full implementation in issue #13. This skeleton establishes the package.
|
||||||
|
|
||||||
|
|
||||||
|
class AgentZeroZulipPlugin:
|
||||||
|
"""Zulip plugin for Agent Zero. Connects kagentz to #agent-hub,
|
||||||
|
detects @mentions, routes messages to/from the Agent Zero agent."""
|
||||||
|
|
||||||
|
def __init__(self, config: dict) -> None:
|
||||||
|
self.config = config
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
|
async def connect(self) -> None:
|
||||||
|
raise NotImplementedError("Skeleton — implement in issue #13")
|
||||||
|
|
||||||
|
async def disconnect(self) -> None:
|
||||||
|
raise NotImplementedError("Skeleton — implement in issue #13")
|
||||||
|
|
||||||
|
async def handle_message(self, event: dict) -> dict | None:
|
||||||
|
raise NotImplementedError("Skeleton — implement in issue #13")
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# config.yaml.example — Shared configuration schema for all 3 platform plugins
|
||||||
|
# Copy to config.yaml and fill in per-agent values. Do NOT commit config.yaml.
|
||||||
|
#
|
||||||
|
# Fields marked ${ENV_VAR} should be set from environment variables.
|
||||||
|
# All fields are required unless marked (optional).
|
||||||
|
|
||||||
|
agent:
|
||||||
|
name: "tanko" # Agent name (lowercase, no spaces)
|
||||||
|
display_name: "Tanko" # Human-readable display name
|
||||||
|
zulip_bot_name: "tanko-bot" # Zulip bot username (ADR-003 naming)
|
||||||
|
owner_email: "jerome@sysloggh.net" # Human owner for private topic ACL
|
||||||
|
private_topic: "tanko-private" # Private topic name (ADR-001 naming)
|
||||||
|
|
||||||
|
zulip:
|
||||||
|
server_url: "https://chat.sysloggh.net"
|
||||||
|
email: "tanko-bot@chat.sysloggh.net"
|
||||||
|
api_key: "${ZULIP_API_KEY}" # Zulip bot API key — use env var, never hardcode
|
||||||
|
stream: "agent-hub" # Zulip stream for agent communication
|
||||||
|
all_bots_user_id: 1234 # User ID of the "All Bots" bot (ADR-006)
|
||||||
|
|
||||||
|
error_handling:
|
||||||
|
timeout_seconds: 30 # Max wait for agent response
|
||||||
|
retry_count: 3 # Retry attempts before graceful degradation
|
||||||
|
retry_delay_seconds: 5 # Delay between retries
|
||||||
|
graceful_message: "{{agent_name}} is processing your request — please wait..."
|
||||||
|
|
||||||
|
monitoring:
|
||||||
|
health_endpoint_enabled: true
|
||||||
|
health_port: 9200 # Health check HTTP port (all platforms)
|
||||||
|
log_level: "INFO" # DEBUG, INFO, WARNING, ERROR
|
||||||
+15
-17
@@ -1,11 +1,9 @@
|
|||||||
# Zulip Multi-Platform Agent Communication — Architecture
|
# Zulip Multi-Platform Agent Communication — Architecture
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
A production-ready system enabling 6 AI agents across 3 platforms (Hermes Python, Agent Zero, pi TypeScript) to communicate through Zulip via dedicated per-agent bot users.
|
||||||
A production-ready system enabling 6 AI agents across 3 platforms (Hermes Python, Agent Zero, PI/openclaw TypeScript) to communicate through Zulip via dedicated per-agent bot users.
|
|
||||||
|
|
||||||
## Architecture Diagram
|
## Architecture Diagram
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
│ Zulip Server (CT 117) │
|
│ Zulip Server (CT 117) │
|
||||||
@@ -27,7 +25,7 @@ A production-ready system enabling 6 AI agents across 3 platforms (Hermes Python
|
|||||||
└─────────┘ └─────────┘ └─────────┘ └─────────┘
|
└─────────┘ └─────────┘ └─────────┘ └─────────┘
|
||||||
|
|
||||||
┌──────────────┐ ┌──────────────┐
|
┌──────────────┐ ┌──────────────┐
|
||||||
│ Agent Zero │ │ PI/openclaw │
|
│ Agent Zero │ │ pi │
|
||||||
│ Plugin │ │ Extension │
|
│ Plugin │ │ Extension │
|
||||||
│ (Python) │ │ (TypeScript) │
|
│ (Python) │ │ (TypeScript) │
|
||||||
│ │ │ │
|
│ │ │ │
|
||||||
@@ -37,7 +35,6 @@ A production-ready system enabling 6 AI agents across 3 platforms (Hermes Python
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Message Flow (Normal)
|
## Message Flow (Normal)
|
||||||
|
|
||||||
```
|
```
|
||||||
User creates topic "project-x" in #agent-hub
|
User creates topic "project-x" in #agent-hub
|
||||||
└── User types: @tanko-bot analyze this data
|
└── User types: @tanko-bot analyze this data
|
||||||
@@ -52,7 +49,6 @@ User creates topic "project-x" in #agent-hub
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Message Flow (@all-bots)
|
## Message Flow (@all-bots)
|
||||||
|
|
||||||
```
|
```
|
||||||
User types: @all-bots status report
|
User types: @all-bots status report
|
||||||
└── Zulip fires event to ALL 6 bots (same narrow)
|
└── Zulip fires event to ALL 6 bots (same narrow)
|
||||||
@@ -76,21 +72,20 @@ User types: @all-bots status report
|
|||||||
- Implements: Agent Zero plugin API
|
- Implements: Agent Zero plugin API
|
||||||
- Config: `config.yaml` per-agent
|
- Config: `config.yaml` per-agent
|
||||||
|
|
||||||
### PI/openclaw (TypeScript) — openclaw Extension API
|
### pi (TypeScript) — pi Extension API
|
||||||
- Path: `openclaw-zulip-extension/src/`
|
- Path: `pi-zulip-extension/src/`
|
||||||
- Implements: openclaw Extension
|
- Implements: pi extension (TypeScript module in `~/.pi/agent/extensions/`)
|
||||||
- Config: `config.yaml` per-agent
|
- Config: `config.yaml` per-agent
|
||||||
|
- Reference: pi extension docs at `/usr/lib/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md`
|
||||||
|
|
||||||
## Reliability
|
## Reliability
|
||||||
|
|
||||||
- **Reconnection**: Zulip SDK handles backoff/reconnect natively
|
- **Reconnection**: Zulip SDK handles backoff/reconnect natively
|
||||||
- **Retry**: 3 attempts with 5s backoff on agent timeout
|
- **Retry**: 3 attempts with 5s backoff on agent timeout
|
||||||
- **Error Response**: User-visible message on failure
|
- **Error Response**: User-visible message on failure
|
||||||
- **Monitoring**: Health endpoint (`/health`) on each plugin
|
- **Monitoring**: Health endpoint (`/health`) on each plugin (port 9200)
|
||||||
- **Logging**: Per-plugin log files
|
- **Logging**: Per-plugin log files
|
||||||
|
|
||||||
## Config Template
|
## Config Template
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# config.yaml — Per-agent Zulip plugin configuration
|
# config.yaml — Per-agent Zulip plugin configuration
|
||||||
agent:
|
agent:
|
||||||
@@ -106,16 +101,19 @@ zulip:
|
|||||||
api_key: "${ZULIP_API_KEY}" # From env var
|
api_key: "${ZULIP_API_KEY}" # From env var
|
||||||
all_bots_user_id: 1234
|
all_bots_user_id: 1234
|
||||||
|
|
||||||
agent:
|
error_handling:
|
||||||
max_retries: 3
|
timeout_seconds: 30
|
||||||
|
retry_count: 3
|
||||||
retry_delay_seconds: 5
|
retry_delay_seconds: 5
|
||||||
health_port: 8080
|
|
||||||
|
monitoring:
|
||||||
|
health_endpoint_enabled: true
|
||||||
|
health_port: 9200
|
||||||
```
|
```
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
1. Zulip server running on CT 117
|
1. Zulip server running on CT 117
|
||||||
2. Zulip bot users created for all 6 agents + All Bots
|
2. Zulip bot users created for all 6 agents + All Bots
|
||||||
3. `#agent-hub` stream created with all bots subscribed
|
3. `#agent-hub` stream created with all bots subscribed
|
||||||
4. SSH key access to all 6 agent CTs
|
4. SSH key access to all 6 agent CTs
|
||||||
5. Gitea repo initialized
|
5. Gitea repo initialized at `git@git.sysloggh.net:SyslogSolution/zulip-platform-plugins.git`
|
||||||
|
|||||||
+11
-11
@@ -6,7 +6,7 @@ This document captures the shared language and architectural invariants for the
|
|||||||
|
|
||||||
| Term | Definition |
|
| Term | Definition |
|
||||||
|------|------------|
|
|------|------------|
|
||||||
| **Agent** | An AI agent operating on a platform (Hermes/Agent Zero/PI/openclaw) that can receive and respond to Zulip messages |
|
| **Agent** | An AI agent operating on a platform (Hermes/Agent Zero/pi) that can receive and respond to Zulip messages |
|
||||||
| **Adapter / Plugin** | A platform-native component that bridges Zulip events to the agent framework |
|
| **Adapter / Plugin** | A platform-native component that bridges Zulip events to the agent framework |
|
||||||
| **@mention** | A Zulip mention (`@**Tanko Bot**`) that triggers the named agent to respond |
|
| **@mention** | A Zulip mention (`@**Tanko Bot**`) that triggers the named agent to respond |
|
||||||
| **@all-bots** | A dedicated Zulip bot user that, when @mentioned, triggers ALL agents across all platforms |
|
| **@all-bots** | A dedicated Zulip bot user that, when @mentioned, triggers ALL agents across all platforms |
|
||||||
@@ -15,10 +15,9 @@ This document captures the shared language and architectural invariants for the
|
|||||||
| **Owner** | The human user associated with an agent bot, stored in per-agent config |
|
| **Owner** | The human user associated with an agent bot, stored in per-agent config |
|
||||||
|
|
||||||
## Naming Conventions
|
## Naming Conventions
|
||||||
|
|
||||||
- Agent Zulip bot display names: lowercase, dash-separated, `-bot` suffix — e.g., `tanko-bot`, `mumuni-bot`
|
- Agent Zulip bot display names: lowercase, dash-separated, `-bot` suffix — e.g., `tanko-bot`, `mumuni-bot`
|
||||||
- Agent Zulip bot emails: `<name>@chat.sysloggh.net` — e.g., `tanko-bot@chat.sysloggh.net`
|
- Agent Zulip bot emails: `@chat.sysloggh.net` — e.g., `tanko-bot@chat.sysloggh.net`
|
||||||
- Private topics: `<agent-name>-private` — e.g., `tanko-private`
|
- Private topics: `{name}-private` — e.g., `tanko-private`
|
||||||
- Owner privacy: agent should never expose owner name to other agents or users
|
- Owner privacy: agent should never expose owner name to other agents or users
|
||||||
|
|
||||||
## Agent Registry
|
## Agent Registry
|
||||||
@@ -30,30 +29,31 @@ This document captures the shared language and architectural invariants for the
|
|||||||
| koonimo | Hermes (Python) | koonimo-bot | amdpve CT 113 | koonimo-private |
|
| koonimo | Hermes (Python) | koonimo-bot | amdpve CT 113 | koonimo-private |
|
||||||
| koby | Hermes (Python) | koby-bot | amdpve CT 111 | koby-private |
|
| koby | Hermes (Python) | koby-bot | amdpve CT 111 | koby-private |
|
||||||
| kagentz | Agent Zero | kagentz-bot | amdpve CT 105 | kagentz-private |
|
| kagentz | Agent Zero | kagentz-bot | amdpve CT 105 | kagentz-private |
|
||||||
| abiba | PI/openclaw (TypeScript) | abiba-bot | amdpve CT 100 | abiba-private |
|
| abiba | pi (TypeScript) | abiba-bot | amdpve CT 100 | abiba-private |
|
||||||
|
|
||||||
## @all-bots Resolution
|
## @all-bots Resolution
|
||||||
|
|
||||||
@all-bots refers to all 6 agents across all frameworks (Hermes, Agent Zero, PI/openclaw). When @all-bots is mentioned in any topic:
|
@all-bots refers to all 6 agents across all frameworks (Hermes, Agent Zero, pi). When @all-bots is mentioned in any topic:
|
||||||
|
|
||||||
1. Each per-agent bot receives the event (since they all share `narrow: stream agent-hub`)
|
1. Each per-agent bot receives the event (since they all share `narrow: stream agent-hub`)
|
||||||
2. Each bot checks: "Is `All Bots` in `mentioned_users`?"
|
2. Each bot checks: "Is `All Bots` in `mentioned_users`?"
|
||||||
3. If yes, bot forwards the message to its agent
|
3. If yes, bot forwards the message to its agent
|
||||||
4. @all-bots is a **dedicated Zulip bot user** with display name `All Bots` and email `all-bots@chat.sysloggh.net`
|
4. @all-bots is a **dedicated Zulip bot user** with display name `All Bots` and email `all-bots@chat.sysloggh.net`
|
||||||
|
|
||||||
## Response Rules
|
## Response Rules
|
||||||
|
|
||||||
- **Own topic**: Agent always responds (to its private topic)
|
- **Own topic**: Agent always responds (to its private topic)
|
||||||
- **Collaboration topic** (any topic that isn't a private topic): Agent responds only when @mentioned
|
- **Collaboration topic** (any topic that isn't a private topic): Agent responds only when @mentioned
|
||||||
- **@all-bots**: All agents respond
|
- **@all-bots**: All agents respond
|
||||||
- **Private topics**: Only the agent owner + bot are participants; bot enforces this
|
- **Private topics**: Only the agent owner + bot are participants; bot enforces this via config owner_email matching
|
||||||
|
|
||||||
|
## Private Topic ACL (v1 — documented limitation)
|
||||||
|
|
||||||
|
In v1, private topic access is enforced by matching `sender_email == config.owner_email` on each message. **This is not cryptographically authenticated** — Zulip does not enforce sender identity. A user who knows the owner email can impersonate them. This is an acceptable limitation for v1 internal use. v2 should use Zulip invite-only stream permissions or shared-secret challenge-response.
|
||||||
|
|
||||||
## Monorepo Structure
|
## Monorepo Structure
|
||||||
|
|
||||||
All 3 platform plugins live in a single Gitea repository at `git@192.168.68.17:homelab/zulip-platform-plugins.git`
|
All 3 platform plugins live in a single Gitea repository at `git@git.sysloggh.net:SyslogSolution/zulip-platform-plugins.git`
|
||||||
|
|
||||||
## Guardian Directives
|
## Guardian Directives
|
||||||
|
|
||||||
- All destructive actions require user confirmation before execution
|
- All destructive actions require user confirmation before execution
|
||||||
- Plugin updates are version-controlled through Gitea
|
- Plugin updates are version-controlled through Gitea
|
||||||
- Config files are per-agent and pushed alongside code
|
- Config files are per-agent and pushed alongside code
|
||||||
|
|||||||
@@ -3,12 +3,19 @@
|
|||||||
**Date**: 2026-04-28
|
**Date**: 2026-04-28
|
||||||
|
|
||||||
### Decision
|
### Decision
|
||||||
Each platform uses its native plugin interface: Hermes `BasePlatformAdapter`, openclaw extension API, Agent Zero plugin system.
|
|
||||||
|
Each platform uses its native plugin interface: Hermes `BasePlatformAdapter`, pi extension API, Agent Zero plugin system.
|
||||||
|
|
||||||
### Context
|
### Context
|
||||||
|
|
||||||
Each platform has matured, battle-tested interfaces for receiving and sending messages. A unified A2A contract would add unnecessary abstraction and latency for local agent communication. Platform-native contracts are reliable, handle connection-state/retry automatically, and follow each platform's established patterns.
|
Each platform has matured, battle-tested interfaces for receiving and sending messages. A unified A2A contract would add unnecessary abstraction and latency for local agent communication. Platform-native contracts are reliable, handle connection-state/retry automatically, and follow each platform's established patterns.
|
||||||
|
|
||||||
|
- **Hermes**: `BasePlatformAdapter` — Python, gateway-based, handles connection lifecycle
|
||||||
|
- **pi**: Extension API — TypeScript modules in `~/.pi/agent/extensions/`, registers tools via `pi.registerTool()`, hooks lifecycle events. See pi docs at `/usr/lib/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md`
|
||||||
|
- **Agent Zero**: A0 plugin system — Python, agent-native plugin API
|
||||||
|
|
||||||
### Consequences
|
### Consequences
|
||||||
|
|
||||||
- Three separate plugin implementations
|
- Three separate plugin implementations
|
||||||
- Each plugin leverages platform-specific reliability features
|
- Each plugin leverages platform-specific reliability features
|
||||||
- Plugin developers need familiarity with each platform
|
- Plugin developers need familiarity with each platform
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
zulip>=0.9.0
|
||||||
|
zulip-bots>=0.9.0
|
||||||
|
pyyaml>=6.0
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
Hermes Zulip Plugin — Core adapter for Hermes Python agents
|
||||||
|
|
||||||
|
Implements BasePlatformAdapter to connect Hermes agents (Tanko, Mumuni,
|
||||||
|
Koonimo, Koby) to the Sysloggh Zulip agent mesh.
|
||||||
|
|
||||||
|
Path: hermes-zulip-plugin/src/hermes_zulip/
|
||||||
|
Config: config.yaml (per-agent, deployed alongside plugin)
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Core adapter — implements BasePlatformAdapter for Zulip
|
||||||
|
# See ADR-005 for @mention detection, ADR-009 for error handling
|
||||||
|
# Full implementation in issue #5. This skeleton establishes the package.
|
||||||
|
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
|
||||||
|
class ZulipAdapter:
|
||||||
|
"""Zulip adapter for Hermes agents. Connects to Zulip, listens for
|
||||||
|
@mentions in #agent-hub, routes messages via BasePlatformAdapter."""
|
||||||
|
|
||||||
|
def __init__(self, config: Dict[str, Any]) -> None:
|
||||||
|
self.config = config
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
|
async def connect(self) -> None:
|
||||||
|
raise NotImplementedError("Skeleton — implement in issue #5")
|
||||||
|
|
||||||
|
async def disconnect(self) -> None:
|
||||||
|
raise NotImplementedError("Skeleton — implement in issue #5")
|
||||||
|
|
||||||
|
async def send_message(self, topic: str, content: str) -> Dict[str, Any]:
|
||||||
|
raise NotImplementedError("Skeleton — implement in issue #5")
|
||||||
|
|
||||||
|
async def on_event(self, event: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||||
|
raise NotImplementedError("Skeleton — implement in issue #5")
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "pi-zulip-extension",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "pi extension for Zulip agent communication — connects Abiba to the Sysloggh agent mesh",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"check": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"zulip-js": "^2.0.0",
|
||||||
|
"yaml": "^2.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@earendil-works/pi-coding-agent": "*",
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
},
|
||||||
|
"keywords": ["pi", "zulip", "agent", "abiba", "sysloggh"],
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"private": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/**
|
||||||
|
* pi-zulip-extension — Zulip agent communication plugin for pi agents
|
||||||
|
*
|
||||||
|
* Deploy to: ~/.pi/agent/extensions/zulip.ts
|
||||||
|
* Config: config.yaml (alongside extension, or symlinked)
|
||||||
|
*
|
||||||
|
* Connects a pi agent (Abiba) to the Sysloggh Zulip agent mesh.
|
||||||
|
* Listens for @mentions of abiba-bot in #agent-hub and forwards
|
||||||
|
* to the pi agent. Responses are posted back to the same Zulip topic.
|
||||||
|
*
|
||||||
|
* @see ADR-007: Platform-native plugin contracts
|
||||||
|
* @see docs/ARCHITECTURE.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
import { parse } from "yaml";
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Config
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
interface ZulipConfig {
|
||||||
|
agent: {
|
||||||
|
name: string;
|
||||||
|
display_name: string;
|
||||||
|
zulip_bot_name: string;
|
||||||
|
owner_email: string;
|
||||||
|
private_topic: string;
|
||||||
|
};
|
||||||
|
zulip: {
|
||||||
|
server_url: string;
|
||||||
|
email: string;
|
||||||
|
api_key: string;
|
||||||
|
stream: string;
|
||||||
|
all_bots_user_id: number;
|
||||||
|
};
|
||||||
|
error_handling: {
|
||||||
|
timeout_seconds: number;
|
||||||
|
retry_count: number;
|
||||||
|
retry_delay_seconds: number;
|
||||||
|
graceful_message: string;
|
||||||
|
};
|
||||||
|
monitoring: {
|
||||||
|
health_endpoint_enabled: boolean;
|
||||||
|
health_port: number;
|
||||||
|
log_level: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadConfig(): ZulipConfig {
|
||||||
|
const raw = readFileSync("config.yaml", "utf-8");
|
||||||
|
const cfg = parse(raw) as ZulipConfig;
|
||||||
|
cfg.zulip.api_key = process.env["ZULIP_API_KEY"] ?? cfg.zulip.api_key;
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Extension entry point
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export default function (pi: ExtensionAPI) {
|
||||||
|
const config = loadConfig();
|
||||||
|
|
||||||
|
// Health endpoint
|
||||||
|
if (config.monitoring.health_endpoint_enabled) {
|
||||||
|
startHealthServer(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
pi.on("session_start", async (_event, ctx) => {
|
||||||
|
ctx.ui.notify(
|
||||||
|
`Zulip extension loaded for ${config.agent.display_name} (${config.agent.zulip_bot_name})`,
|
||||||
|
"info"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Diagnostic tool
|
||||||
|
pi.registerTool({
|
||||||
|
name: "zulip_status",
|
||||||
|
label: "Zulip Status",
|
||||||
|
description: "Check Zulip connection status and bot identity",
|
||||||
|
parameters: {},
|
||||||
|
execute: async () => ({
|
||||||
|
connected: false, // TODO: track connection state
|
||||||
|
bot: config.agent.zulip_bot_name,
|
||||||
|
stream: config.zulip.stream,
|
||||||
|
server: config.zulip.server_url,
|
||||||
|
owner: config.agent.owner_email,
|
||||||
|
private_topic: config.agent.private_topic,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Health endpoint
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function startHealthServer(config: ZulipConfig) {
|
||||||
|
const http = require("http") as typeof import("http");
|
||||||
|
const port = config.monitoring.health_port;
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
http
|
||||||
|
.createServer((_req: any, res: any) => {
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
res.end(
|
||||||
|
JSON.stringify({
|
||||||
|
status: "ok",
|
||||||
|
agent: config.agent.name,
|
||||||
|
uptime_seconds: Math.floor((Date.now() - startTime) / 1000),
|
||||||
|
zulip_connected: false,
|
||||||
|
last_message_time: null,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.listen(port, "127.0.0.1", () => {
|
||||||
|
console.log(`[zulip-extension] Health endpoint on :${port}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"declaration": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user