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,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