openclaw-bridge 0.3.2 → 0.4.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 (52) hide show
  1. package/README.md +43 -16
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +809 -0
  4. package/dist/config.d.ts +3 -0
  5. package/dist/config.js +64 -0
  6. package/dist/discovery.d.ts +4 -0
  7. package/dist/discovery.js +6 -0
  8. package/dist/file-ops.d.ts +22 -0
  9. package/dist/file-ops.js +253 -0
  10. package/dist/heartbeat.d.ts +21 -0
  11. package/dist/heartbeat.js +152 -0
  12. package/dist/index.d.ts +9 -0
  13. package/dist/index.js +624 -0
  14. package/dist/manager/hub-client.d.ts +18 -0
  15. package/dist/manager/hub-client.js +89 -0
  16. package/dist/manager/local-manager.d.ts +12 -0
  17. package/dist/manager/local-manager.js +117 -0
  18. package/dist/manager/pm2-bridge.d.ts +17 -0
  19. package/dist/manager/pm2-bridge.js +113 -0
  20. package/dist/message-relay.d.ts +32 -0
  21. package/dist/message-relay.js +229 -0
  22. package/dist/permissions.d.ts +3 -0
  23. package/dist/permissions.js +14 -0
  24. package/dist/registry.d.ts +13 -0
  25. package/dist/registry.js +103 -0
  26. package/dist/restart.d.ts +15 -0
  27. package/dist/restart.js +107 -0
  28. package/dist/router.d.ts +11 -0
  29. package/dist/router.js +18 -0
  30. package/dist/session.d.ts +11 -0
  31. package/dist/session.js +21 -0
  32. package/dist/types.d.ts +90 -0
  33. package/dist/types.js +1 -0
  34. package/openclaw.plugin.json +6 -92
  35. package/package.json +15 -5
  36. package/src/cli.ts +0 -842
  37. package/src/config.ts +0 -72
  38. package/src/discovery.ts +0 -17
  39. package/src/file-ops.ts +0 -320
  40. package/src/heartbeat.ts +0 -196
  41. package/src/index.ts +0 -681
  42. package/src/manager/hub-client.ts +0 -114
  43. package/src/manager/local-manager.ts +0 -121
  44. package/src/manager/pm2-bridge.ts +0 -125
  45. package/src/message-relay.ts +0 -184
  46. package/src/permissions.ts +0 -18
  47. package/src/registry.ts +0 -107
  48. package/src/restart.ts +0 -137
  49. package/src/router.ts +0 -40
  50. package/src/session.ts +0 -33
  51. package/src/types.ts +0 -100
  52. package/tsconfig.json +0 -14
package/src/restart.ts DELETED
@@ -1,137 +0,0 @@
1
- import { exec } from "node:child_process";
2
- import { promisify } from "node:util";
3
- import type { BridgeConfig, RegistryEntry, PluginLogger } from "./types.js";
4
- import type { BridgeRegistry } from "./registry.js";
5
-
6
- const execAsync = promisify(exec);
7
- const IS_WIN = process.platform === "win32";
8
-
9
- export class BridgeRestart {
10
- private config: BridgeConfig;
11
- private machineId: string;
12
- private registry: BridgeRegistry;
13
- private logger: PluginLogger;
14
-
15
- constructor(
16
- config: BridgeConfig,
17
- machineId: string,
18
- registry: BridgeRegistry,
19
- logger: PluginLogger,
20
- ) {
21
- this.config = config;
22
- this.machineId = machineId;
23
- this.registry = registry;
24
- this.logger = logger;
25
- }
26
-
27
- async restart(target: RegistryEntry): Promise<{ success: boolean; message: string }> {
28
- if (target.machineId !== this.machineId) {
29
- return this.restartRemote(target);
30
- }
31
- return this.restartLocal(target);
32
- }
33
-
34
- private async restartLocal(
35
- target: RegistryEntry,
36
- ): Promise<{ success: boolean; message: string }> {
37
- this.logger.info(`openclaw-bridge: restarting ${target.agentId} on local machine`);
38
-
39
- try {
40
- if (IS_WIN) {
41
- const { stdout } = await execAsync(
42
- `netstat -ano | findstr :${target.port} | findstr LISTENING`,
43
- );
44
- const lines = stdout.trim().split("\n");
45
- for (const line of lines) {
46
- const pid = line.trim().split(/\s+/).pop();
47
- if (pid && /^\d+$/.test(pid)) {
48
- await execAsync(`taskkill /F /PID ${pid}`).catch(() => {});
49
- }
50
- }
51
- } else {
52
- await execAsync(`lsof -ti:${target.port} | xargs kill -9`).catch(() => {});
53
- }
54
-
55
- const instanceDir = target.workspacePath.replace(/[\\/]workspace[\\/]?$/, "");
56
- const runScript = IS_WIN
57
- ? `${instanceDir}\\run.ps1`
58
- : `${instanceDir}/run.sh`;
59
-
60
- if (IS_WIN) {
61
- await execAsync(
62
- `powershell -Command "Start-Process powershell -ArgumentList '-File','${runScript}' -WindowStyle Hidden"`,
63
- );
64
- } else {
65
- await execAsync(`nohup bash "${runScript}" > /dev/null 2>&1 &`);
66
- }
67
-
68
- const deadline = Date.now() + 60_000;
69
- while (Date.now() < deadline) {
70
- await new Promise((r) => setTimeout(r, 3_000));
71
- const found = await this.registry.findAgent(
72
- target.agentId,
73
- this.config.offlineThresholdMs ?? 120_000,
74
- );
75
- if (found && found.status === "online") {
76
- this.logger.info(`openclaw-bridge: ${target.agentId} restarted successfully`);
77
- return { success: true, message: `${target.agentId} restarted and back online` };
78
- }
79
- }
80
-
81
- return {
82
- success: false,
83
- message: `${target.agentId} process started but did not re-register within 60s`,
84
- };
85
- } catch (err) {
86
- return { success: false, message: `Restart failed: ${String(err)}` };
87
- }
88
- }
89
-
90
- private async restartRemote(
91
- target: RegistryEntry,
92
- ): Promise<{ success: boolean; message: string }> {
93
- if (!this.config.fileRelay?.baseUrl) {
94
- return {
95
- success: false,
96
- message: "Cannot restart remote gateway: fileRelay not configured",
97
- };
98
- }
99
-
100
- const baseUrl = this.config.fileRelay.baseUrl.replace(/\/+$/, "");
101
- const headers: Record<string, string> = { "Content-Type": "application/json" };
102
- if (this.config.fileRelay.apiKey) headers["X-API-Key"] = this.config.fileRelay.apiKey;
103
-
104
- const res = await fetch(`${baseUrl}/api/v1/commands/enqueue`, {
105
- method: "POST",
106
- headers,
107
- body: JSON.stringify({
108
- fromAgent: this.config.agentId,
109
- toAgent: target.agentId,
110
- type: "restart",
111
- payload: {},
112
- }),
113
- });
114
-
115
- if (!res.ok) {
116
- return { success: false, message: `FileRelay command enqueue failed: ${res.status}` };
117
- }
118
-
119
- const { id: cmdId } = (await res.json()) as { id: string };
120
-
121
- const deadline = Date.now() + 90_000;
122
- while (Date.now() < deadline) {
123
- await new Promise((r) => setTimeout(r, 5_000));
124
- const resultRes = await fetch(`${baseUrl}/api/v1/commands/result/${cmdId}`, { headers });
125
- if (!resultRes.ok) continue;
126
- const result = (await resultRes.json()) as { status: string };
127
- if (result.status === "ok") {
128
- return { success: true, message: `Restart command acknowledged by ${target.agentId}` };
129
- }
130
- if (result.status === "error") {
131
- return { success: false, message: `Remote restart failed` };
132
- }
133
- }
134
-
135
- return { success: false, message: "Restart command timed out (90s)" };
136
- }
137
- }
package/src/router.ts DELETED
@@ -1,40 +0,0 @@
1
- import type { RegistryEntry, ChannelInfo } from './types.js';
2
-
3
- export interface MessageContext {
4
- channel: ChannelInfo;
5
- isGroupChannel: boolean;
6
- }
7
-
8
- export interface RouteDecision {
9
- method: 'channel_direct' | 'hub_relay';
10
- channel?: ChannelInfo;
11
- fallback: 'hub_relay';
12
- }
13
-
14
- export function decideRoute(
15
- currentContext: MessageContext,
16
- targetAgentId: string,
17
- registry: RegistryEntry[]
18
- ): RouteDecision {
19
- const target = registry.find(a => a.agentId === targetAgentId);
20
- if (!target || target.status !== 'online') {
21
- return { method: 'hub_relay', fallback: 'hub_relay' };
22
- }
23
-
24
- if (currentContext.isGroupChannel && target.channels) {
25
- const targetInSameChannel = target.channels.some(
26
- ch => ch.type === currentContext.channel.type
27
- && ch.channelId === currentContext.channel.channelId
28
- );
29
-
30
- if (targetInSameChannel) {
31
- return {
32
- method: 'channel_direct',
33
- channel: currentContext.channel,
34
- fallback: 'hub_relay',
35
- };
36
- }
37
- }
38
-
39
- return { method: 'hub_relay', fallback: 'hub_relay' };
40
- }
package/src/session.ts DELETED
@@ -1,33 +0,0 @@
1
- export interface ProxySession {
2
- sessionId: string;
3
- originAgent: string;
4
- currentAgent: string;
5
- currentAgentName: string;
6
- }
7
-
8
- let activeSession: ProxySession | null = null;
9
-
10
- export function setSession(session: ProxySession): void {
11
- activeSession = session;
12
- console.log(`[session] SET handoff: sessionId=${session.sessionId} target=${session.currentAgent}`);
13
- }
14
-
15
- export function getSession(): ProxySession | null {
16
- return activeSession;
17
- }
18
-
19
- export function clearSession(): void {
20
- console.log(`[session] CLEAR handoff (was: ${activeSession?.sessionId || 'none'})`);
21
- activeSession = null;
22
- }
23
-
24
- export function updateCurrentAgent(agentId: string, agentName: string): void {
25
- if (activeSession) {
26
- activeSession.currentAgent = agentId;
27
- activeSession.currentAgentName = agentName;
28
- }
29
- }
30
-
31
- export function isInHandoff(): boolean {
32
- return activeSession !== null;
33
- }
package/src/types.ts DELETED
@@ -1,100 +0,0 @@
1
- export interface MessageRelayConfig {
2
- url: string; // ws://host:3080/ws
3
- apiKey: string;
4
- }
5
-
6
- export interface LocalManagerConfig {
7
- enabled: boolean;
8
- hubUrl: string;
9
- managerPass: string;
10
- }
11
-
12
- export interface BridgeConfig {
13
- role: "normal" | "superuser";
14
- agentId: string;
15
- agentName: string;
16
- registry: {
17
- provider?: string;
18
- baseUrl: string;
19
- apiKey?: string;
20
- };
21
- fileRelay?: {
22
- baseUrl: string;
23
- apiKey?: string;
24
- };
25
- messageRelay?: MessageRelayConfig;
26
- heartbeatIntervalMs?: number;
27
- offlineThresholdMs?: number;
28
- description?: string;
29
- supportsVision?: boolean;
30
- localManager?: LocalManagerConfig;
31
- }
32
-
33
- export interface ChannelInfo {
34
- type: string; // "discord", "slack", "web", etc.
35
- channelId: string; // platform-specific channel identifier
36
- name: string; // human-readable name, e.g. "#creators", "DM-user1"
37
- }
38
-
39
- export interface RegistryEntry {
40
- type: "gateway-registry";
41
- agentId: string;
42
- agentName: string;
43
- machineId: string;
44
- host: string;
45
- port: number;
46
- workspacePath: string;
47
- discordId: string | null;
48
- discordConnected?: boolean;
49
- role: "normal" | "superuser";
50
- capabilities: string[];
51
- channels: ChannelInfo[];
52
- registeredAt: string;
53
- lastHeartbeat: string;
54
- status: "online" | "offline";
55
- memMB?: number;
56
- description?: string;
57
- supportsVision?: boolean;
58
- }
59
-
60
- export interface DiscoverResult {
61
- agents: RegistryEntry[];
62
- }
63
-
64
- export type PluginLogger = {
65
- debug?: (message: string) => void;
66
- info: (message: string) => void;
67
- warn: (message: string) => void;
68
- error: (message: string) => void;
69
- };
70
-
71
- export type HookAgentContext = {
72
- agentId?: string;
73
- sessionId?: string;
74
- sessionKey?: string;
75
- };
76
-
77
- export type OpenClawPluginApi = {
78
- pluginConfig?: unknown;
79
- logger: PluginLogger;
80
- registerTool: (
81
- tool: {
82
- name: string;
83
- label: string;
84
- description: string;
85
- parameters: unknown;
86
- execute: (_toolCallId: string, params: Record<string, unknown>) => Promise<unknown>;
87
- },
88
- opts?: { name?: string; names?: string[] },
89
- ) => void;
90
- registerService: (service: {
91
- id: string;
92
- start: (ctx?: unknown) => void | Promise<void>;
93
- stop?: (ctx?: unknown) => void | Promise<void>;
94
- }) => void;
95
- on: (
96
- hookName: string,
97
- handler: (event: unknown, ctx?: HookAgentContext) => unknown,
98
- opts?: { priority?: number },
99
- ) => void;
100
- };
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ES2022",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "outDir": "dist",
10
- "rootDir": "src",
11
- "declaration": true
12
- },
13
- "include": ["src"]
14
- }