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.
- package/README.md +43 -16
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +809 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +64 -0
- package/dist/discovery.d.ts +4 -0
- package/dist/discovery.js +6 -0
- package/dist/file-ops.d.ts +22 -0
- package/dist/file-ops.js +253 -0
- package/dist/heartbeat.d.ts +21 -0
- package/dist/heartbeat.js +152 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +624 -0
- package/dist/manager/hub-client.d.ts +18 -0
- package/dist/manager/hub-client.js +89 -0
- package/dist/manager/local-manager.d.ts +12 -0
- package/dist/manager/local-manager.js +117 -0
- package/dist/manager/pm2-bridge.d.ts +17 -0
- package/dist/manager/pm2-bridge.js +113 -0
- package/dist/message-relay.d.ts +32 -0
- package/dist/message-relay.js +229 -0
- package/dist/permissions.d.ts +3 -0
- package/dist/permissions.js +14 -0
- package/dist/registry.d.ts +13 -0
- package/dist/registry.js +103 -0
- package/dist/restart.d.ts +15 -0
- package/dist/restart.js +107 -0
- package/dist/router.d.ts +11 -0
- package/dist/router.js +18 -0
- package/dist/session.d.ts +11 -0
- package/dist/session.js +21 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.js +1 -0
- package/openclaw.plugin.json +6 -92
- package/package.json +15 -5
- package/src/cli.ts +0 -842
- package/src/config.ts +0 -72
- package/src/discovery.ts +0 -17
- package/src/file-ops.ts +0 -320
- package/src/heartbeat.ts +0 -196
- package/src/index.ts +0 -681
- package/src/manager/hub-client.ts +0 -114
- package/src/manager/local-manager.ts +0 -121
- package/src/manager/pm2-bridge.ts +0 -125
- package/src/message-relay.ts +0 -184
- package/src/permissions.ts +0 -18
- package/src/registry.ts +0 -107
- package/src/restart.ts +0 -137
- package/src/router.ts +0 -40
- package/src/session.ts +0 -33
- package/src/types.ts +0 -100
- 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
|
-
}
|