@agentmeshhq/agent 0.1.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 +111 -0
- package/dist/cli/attach.d.ts +1 -0
- package/dist/cli/attach.js +18 -0
- package/dist/cli/attach.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +98 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +1 -0
- package/dist/cli/init.js +55 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/list.d.ts +1 -0
- package/dist/cli/list.js +45 -0
- package/dist/cli/list.js.map +1 -0
- package/dist/cli/nudge.d.ts +1 -0
- package/dist/cli/nudge.js +72 -0
- package/dist/cli/nudge.js.map +1 -0
- package/dist/cli/start.d.ts +8 -0
- package/dist/cli/start.js +37 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/stop.d.ts +1 -0
- package/dist/cli/stop.js +33 -0
- package/dist/cli/stop.js.map +1 -0
- package/dist/config/loader.d.ts +10 -0
- package/dist/config/loader.js +65 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +32 -0
- package/dist/config/schema.js +11 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/core/daemon.d.ts +20 -0
- package/dist/core/daemon.js +164 -0
- package/dist/core/daemon.js.map +1 -0
- package/dist/core/heartbeat.d.ts +14 -0
- package/dist/core/heartbeat.js +42 -0
- package/dist/core/heartbeat.js.map +1 -0
- package/dist/core/injector.d.ts +8 -0
- package/dist/core/injector.js +84 -0
- package/dist/core/injector.js.map +1 -0
- package/dist/core/registry.d.ts +27 -0
- package/dist/core/registry.js +52 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/tmux.d.ts +11 -0
- package/dist/core/tmux.js +112 -0
- package/dist/core/tmux.js.map +1 -0
- package/dist/core/websocket.d.ts +25 -0
- package/dist/core/websocket.js +65 -0
- package/dist/core/websocket.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
- package/src/cli/attach.ts +22 -0
- package/src/cli/index.ts +101 -0
- package/src/cli/init.ts +87 -0
- package/src/cli/list.ts +62 -0
- package/src/cli/nudge.ts +84 -0
- package/src/cli/start.ts +50 -0
- package/src/cli/stop.ts +39 -0
- package/src/config/loader.ts +81 -0
- package/src/config/schema.ts +44 -0
- package/src/core/daemon.ts +213 -0
- package/src/core/heartbeat.ts +54 -0
- package/src/core/injector.ts +128 -0
- package/src/core/registry.ts +105 -0
- package/src/core/tmux.ts +139 -0
- package/src/core/websocket.ts +94 -0
- package/src/index.ts +9 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const DEFAULT_CONFIG = {
|
|
2
|
+
hubUrl: "https://agentmeshhq.dev",
|
|
3
|
+
defaults: {
|
|
4
|
+
command: "opencode",
|
|
5
|
+
model: "claude-sonnet-4",
|
|
6
|
+
},
|
|
7
|
+
agents: [],
|
|
8
|
+
};
|
|
9
|
+
export const CONFIG_PATH = `${process.env.HOME}/.agentmesh/config.json`;
|
|
10
|
+
export const STATE_PATH = `${process.env.HOME}/.agentmesh/state.json`;
|
|
11
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,MAAM,EAAE,yBAAyB;IACjC,QAAQ,EAAE;QACR,OAAO,EAAE,UAAU;QACnB,KAAK,EAAE,iBAAiB;KACzB;IACD,MAAM,EAAE,EAAE;CACX,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,yBAAyB,CAAC;AACxE,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DaemonOptions {
|
|
2
|
+
name: string;
|
|
3
|
+
command?: string;
|
|
4
|
+
workdir?: string;
|
|
5
|
+
model?: string;
|
|
6
|
+
daemonize?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class AgentDaemon {
|
|
9
|
+
private agentName;
|
|
10
|
+
private config;
|
|
11
|
+
private agentConfig;
|
|
12
|
+
private ws;
|
|
13
|
+
private heartbeat;
|
|
14
|
+
private token;
|
|
15
|
+
private agentId;
|
|
16
|
+
private isRunning;
|
|
17
|
+
constructor(options: DaemonOptions);
|
|
18
|
+
start(): Promise<void>;
|
|
19
|
+
stop(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { createSession, destroySession, sessionExists, getSessionName } from "./tmux.js";
|
|
2
|
+
import { AgentWebSocket } from "./websocket.js";
|
|
3
|
+
import { Heartbeat } from "./heartbeat.js";
|
|
4
|
+
import { registerAgent, checkInbox } from "./registry.js";
|
|
5
|
+
import { injectStartupMessage, handleWebSocketEvent } from "./injector.js";
|
|
6
|
+
import { addAgentToState, removeAgentFromState, loadConfig, getAgentState, } from "../config/loader.js";
|
|
7
|
+
export class AgentDaemon {
|
|
8
|
+
agentName;
|
|
9
|
+
config;
|
|
10
|
+
agentConfig;
|
|
11
|
+
ws = null;
|
|
12
|
+
heartbeat = null;
|
|
13
|
+
token = null;
|
|
14
|
+
agentId = null;
|
|
15
|
+
isRunning = false;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
const config = loadConfig();
|
|
18
|
+
if (!config) {
|
|
19
|
+
throw new Error("No config found. Run 'agentmesh init' first.");
|
|
20
|
+
}
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.agentName = options.name;
|
|
23
|
+
// Find or create agent config
|
|
24
|
+
let agentConfig = config.agents.find((a) => a.name === options.name);
|
|
25
|
+
if (!agentConfig) {
|
|
26
|
+
agentConfig = {
|
|
27
|
+
name: options.name,
|
|
28
|
+
command: options.command || config.defaults.command,
|
|
29
|
+
workdir: options.workdir,
|
|
30
|
+
model: options.model || config.defaults.model,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Override with provided options
|
|
34
|
+
if (options.command)
|
|
35
|
+
agentConfig.command = options.command;
|
|
36
|
+
if (options.workdir)
|
|
37
|
+
agentConfig.workdir = options.workdir;
|
|
38
|
+
if (options.model)
|
|
39
|
+
agentConfig.model = options.model;
|
|
40
|
+
this.agentConfig = agentConfig;
|
|
41
|
+
}
|
|
42
|
+
async start() {
|
|
43
|
+
if (this.isRunning) {
|
|
44
|
+
console.error("Daemon already running");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log(`Starting agent: ${this.agentName}`);
|
|
48
|
+
// Check if session already exists
|
|
49
|
+
const sessionName = getSessionName(this.agentName);
|
|
50
|
+
const sessionAlreadyExists = sessionExists(sessionName);
|
|
51
|
+
// Create tmux session if it doesn't exist
|
|
52
|
+
if (!sessionAlreadyExists) {
|
|
53
|
+
console.log(`Creating tmux session: ${sessionName}`);
|
|
54
|
+
const created = createSession(this.agentName, this.agentConfig.command, this.agentConfig.workdir);
|
|
55
|
+
if (!created) {
|
|
56
|
+
throw new Error("Failed to create tmux session");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.log(`Reconnecting to existing session: ${sessionName}`);
|
|
61
|
+
}
|
|
62
|
+
// Register with hub
|
|
63
|
+
console.log("Registering with AgentMesh hub...");
|
|
64
|
+
const existingState = getAgentState(this.agentName);
|
|
65
|
+
const registration = await registerAgent({
|
|
66
|
+
url: this.config.hubUrl,
|
|
67
|
+
apiKey: this.config.apiKey,
|
|
68
|
+
workspace: this.config.workspace,
|
|
69
|
+
agentId: existingState?.agentId || this.agentConfig.agentId,
|
|
70
|
+
agentName: this.agentName,
|
|
71
|
+
model: this.agentConfig.model || this.config.defaults.model,
|
|
72
|
+
});
|
|
73
|
+
this.agentId = registration.agentId;
|
|
74
|
+
this.token = registration.token;
|
|
75
|
+
console.log(`Registered as: ${this.agentId}`);
|
|
76
|
+
// Save state
|
|
77
|
+
addAgentToState({
|
|
78
|
+
name: this.agentName,
|
|
79
|
+
agentId: this.agentId,
|
|
80
|
+
pid: process.pid,
|
|
81
|
+
tmuxSession: sessionName,
|
|
82
|
+
startedAt: new Date().toISOString(),
|
|
83
|
+
token: this.token,
|
|
84
|
+
});
|
|
85
|
+
// Start heartbeat
|
|
86
|
+
console.log("Starting heartbeat...");
|
|
87
|
+
this.heartbeat = new Heartbeat({
|
|
88
|
+
url: this.config.hubUrl,
|
|
89
|
+
token: this.token,
|
|
90
|
+
intervalMs: 30000,
|
|
91
|
+
onError: (error) => {
|
|
92
|
+
console.error("Heartbeat error:", error.message);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
this.heartbeat.start();
|
|
96
|
+
// Connect WebSocket
|
|
97
|
+
console.log("Connecting WebSocket...");
|
|
98
|
+
const wsUrl = this.config.hubUrl.replace("https://", "wss://").replace("http://", "ws://");
|
|
99
|
+
this.ws = new AgentWebSocket({
|
|
100
|
+
url: `${wsUrl}/ws/v1`,
|
|
101
|
+
token: this.token,
|
|
102
|
+
onMessage: (event) => {
|
|
103
|
+
handleWebSocketEvent(this.agentName, event);
|
|
104
|
+
},
|
|
105
|
+
onConnect: () => {
|
|
106
|
+
console.log("WebSocket connected");
|
|
107
|
+
},
|
|
108
|
+
onDisconnect: () => {
|
|
109
|
+
console.log("WebSocket disconnected");
|
|
110
|
+
},
|
|
111
|
+
onError: (error) => {
|
|
112
|
+
console.error("WebSocket error:", error.message);
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
this.ws.connect();
|
|
116
|
+
// Check inbox and auto-nudge
|
|
117
|
+
console.log("Checking inbox...");
|
|
118
|
+
try {
|
|
119
|
+
const inboxItems = await checkInbox(this.config.hubUrl, this.config.workspace, this.token);
|
|
120
|
+
injectStartupMessage(this.agentName, inboxItems.length);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.error("Failed to check inbox:", error);
|
|
124
|
+
injectStartupMessage(this.agentName, 0);
|
|
125
|
+
}
|
|
126
|
+
this.isRunning = true;
|
|
127
|
+
console.log(`
|
|
128
|
+
Agent "${this.agentName}" is running.
|
|
129
|
+
|
|
130
|
+
Attach to session:
|
|
131
|
+
agentmesh attach ${this.agentName}
|
|
132
|
+
|
|
133
|
+
Stop agent:
|
|
134
|
+
agentmesh stop ${this.agentName}
|
|
135
|
+
|
|
136
|
+
Nudge agent:
|
|
137
|
+
agentmesh nudge ${this.agentName} "Your message"
|
|
138
|
+
`);
|
|
139
|
+
// Handle shutdown
|
|
140
|
+
process.on("SIGINT", () => this.stop());
|
|
141
|
+
process.on("SIGTERM", () => this.stop());
|
|
142
|
+
}
|
|
143
|
+
async stop() {
|
|
144
|
+
console.log(`\nStopping agent: ${this.agentName}`);
|
|
145
|
+
this.isRunning = false;
|
|
146
|
+
// Stop heartbeat
|
|
147
|
+
if (this.heartbeat) {
|
|
148
|
+
this.heartbeat.stop();
|
|
149
|
+
this.heartbeat = null;
|
|
150
|
+
}
|
|
151
|
+
// Disconnect WebSocket
|
|
152
|
+
if (this.ws) {
|
|
153
|
+
this.ws.disconnect();
|
|
154
|
+
this.ws = null;
|
|
155
|
+
}
|
|
156
|
+
// Destroy tmux session
|
|
157
|
+
destroySession(this.agentName);
|
|
158
|
+
// Remove from state
|
|
159
|
+
removeAgentFromState(this.agentName);
|
|
160
|
+
console.log("Agent stopped.");
|
|
161
|
+
process.exit(0);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/core/daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface HeartbeatConfig {
|
|
2
|
+
url: string;
|
|
3
|
+
token: string;
|
|
4
|
+
intervalMs: number;
|
|
5
|
+
onError?: (error: Error) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare class Heartbeat {
|
|
8
|
+
private config;
|
|
9
|
+
private intervalId;
|
|
10
|
+
constructor(config: HeartbeatConfig);
|
|
11
|
+
start(): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
private sendHeartbeat;
|
|
14
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export class Heartbeat {
|
|
2
|
+
config;
|
|
3
|
+
intervalId = null;
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.config = config;
|
|
6
|
+
}
|
|
7
|
+
start() {
|
|
8
|
+
if (this.intervalId) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
// Send initial heartbeat
|
|
12
|
+
this.sendHeartbeat();
|
|
13
|
+
// Schedule recurring heartbeats
|
|
14
|
+
this.intervalId = setInterval(() => {
|
|
15
|
+
this.sendHeartbeat();
|
|
16
|
+
}, this.config.intervalMs);
|
|
17
|
+
}
|
|
18
|
+
stop() {
|
|
19
|
+
if (this.intervalId) {
|
|
20
|
+
clearInterval(this.intervalId);
|
|
21
|
+
this.intervalId = null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async sendHeartbeat() {
|
|
25
|
+
try {
|
|
26
|
+
const response = await fetch(`${this.config.url}/api/v1/agents/heartbeat`, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${this.config.token}`,
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`Heartbeat failed: ${response.status}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
this.config.onError?.(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/core/heartbeat.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,SAAS;IACZ,MAAM,CAAkB;IACxB,UAAU,GAA0B,IAAI,CAAC;IAEjD,YAAY,MAAuB;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,yBAAyB;QACzB,IAAI,CAAC,aAAa,EAAE,CAAC;QAErB,gCAAgC;QAChC,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;YACjC,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,aAAa;QACzB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,0BAA0B,EAAE;gBACzE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;oBAC5C,cAAc,EAAE,kBAAkB;iBACnC;aACF,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,KAAc,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { InboxItem } from "./registry.js";
|
|
2
|
+
import type { WebSocketEvent } from "./websocket.js";
|
|
3
|
+
export declare function injectStartupMessage(agentName: string, pendingCount: number): void;
|
|
4
|
+
export declare function injectHandoffReceived(agentName: string, event: WebSocketEvent): void;
|
|
5
|
+
export declare function injectNudge(agentName: string, event: WebSocketEvent): void;
|
|
6
|
+
export declare function injectBlockerResolved(agentName: string, event: WebSocketEvent): void;
|
|
7
|
+
export declare function injectInboxItems(agentName: string, items: InboxItem[]): void;
|
|
8
|
+
export declare function handleWebSocketEvent(agentName: string, event: WebSocketEvent): void;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { sendKeys } from "./tmux.js";
|
|
2
|
+
export function injectStartupMessage(agentName, pendingCount) {
|
|
3
|
+
if (pendingCount === 0) {
|
|
4
|
+
const message = `[AgentMesh] Connected and ready. No pending items in inbox.`;
|
|
5
|
+
sendKeys(agentName, message);
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const message = `[AgentMesh] Welcome back! You have ${pendingCount} pending handoff${pendingCount === 1 ? "" : "s"} in your inbox.
|
|
9
|
+
Use agentmesh_check_inbox to see them, or agentmesh_accept_handoff to start working.`;
|
|
10
|
+
sendKeys(agentName, message);
|
|
11
|
+
}
|
|
12
|
+
export function injectHandoffReceived(agentName, event) {
|
|
13
|
+
const fromName = event.from_agent?.display_name ||
|
|
14
|
+
event.from_agent_id ||
|
|
15
|
+
"Unknown";
|
|
16
|
+
const scope = event.scope || "No scope provided";
|
|
17
|
+
const reason = event.reason || "No reason provided";
|
|
18
|
+
const handoffId = event.handoff_id || event.id || "unknown";
|
|
19
|
+
const message = `[AgentMesh] New handoff from ${fromName}
|
|
20
|
+
|
|
21
|
+
Scope: ${scope}
|
|
22
|
+
Reason: ${reason}
|
|
23
|
+
Handoff ID: ${handoffId}
|
|
24
|
+
|
|
25
|
+
Accept this handoff and begin work.`;
|
|
26
|
+
sendKeys(agentName, message);
|
|
27
|
+
}
|
|
28
|
+
export function injectNudge(agentName, event) {
|
|
29
|
+
const fromName = event.from?.name ||
|
|
30
|
+
event.from_name ||
|
|
31
|
+
"Someone";
|
|
32
|
+
const message = event.message || "Check your inbox";
|
|
33
|
+
const formatted = `[AgentMesh] Nudge from ${fromName}:
|
|
34
|
+
${message}`;
|
|
35
|
+
sendKeys(agentName, formatted);
|
|
36
|
+
}
|
|
37
|
+
export function injectBlockerResolved(agentName, event) {
|
|
38
|
+
const description = event.description || "A blocker has been resolved";
|
|
39
|
+
const resolvedBy = event.resolved_by?.display_name ||
|
|
40
|
+
event.resolved_by_name ||
|
|
41
|
+
"Another agent";
|
|
42
|
+
const message = `[AgentMesh] Blocker resolved!
|
|
43
|
+
|
|
44
|
+
Blocker: ${description}
|
|
45
|
+
Resolved by: ${resolvedBy}
|
|
46
|
+
|
|
47
|
+
You can now proceed with your work.`;
|
|
48
|
+
sendKeys(agentName, message);
|
|
49
|
+
}
|
|
50
|
+
export function injectInboxItems(agentName, items) {
|
|
51
|
+
if (items.length === 0) {
|
|
52
|
+
sendKeys(agentName, "[AgentMesh] Your inbox is empty.");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let message = `[AgentMesh] You have ${items.length} pending item${items.length === 1 ? "" : "s"}:\n\n`;
|
|
56
|
+
for (const item of items) {
|
|
57
|
+
const fromName = item.from_agent?.display_name || item.from_agent_id;
|
|
58
|
+
message += `- From: ${fromName}\n`;
|
|
59
|
+
message += ` Scope: ${item.scope}\n`;
|
|
60
|
+
message += ` ID: ${item.id}\n\n`;
|
|
61
|
+
}
|
|
62
|
+
message += "Use agentmesh_accept_handoff with the ID to start working.";
|
|
63
|
+
sendKeys(agentName, message);
|
|
64
|
+
}
|
|
65
|
+
export function handleWebSocketEvent(agentName, event) {
|
|
66
|
+
switch (event.type) {
|
|
67
|
+
case "handoff_received":
|
|
68
|
+
case "handoff.received":
|
|
69
|
+
injectHandoffReceived(agentName, event);
|
|
70
|
+
break;
|
|
71
|
+
case "nudge":
|
|
72
|
+
case "agent.nudge":
|
|
73
|
+
injectNudge(agentName, event);
|
|
74
|
+
break;
|
|
75
|
+
case "blocker_resolved":
|
|
76
|
+
case "blocker.resolved":
|
|
77
|
+
injectBlockerResolved(agentName, event);
|
|
78
|
+
break;
|
|
79
|
+
default:
|
|
80
|
+
// Unknown event type, ignore
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=injector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injector.js","sourceRoot":"","sources":["../../src/core/injector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface RegisterOptions {
|
|
2
|
+
url: string;
|
|
3
|
+
apiKey: string;
|
|
4
|
+
workspace: string;
|
|
5
|
+
agentId?: string;
|
|
6
|
+
agentName: string;
|
|
7
|
+
model: string;
|
|
8
|
+
capabilities?: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface RegisterResult {
|
|
11
|
+
agentId: string;
|
|
12
|
+
token: string;
|
|
13
|
+
}
|
|
14
|
+
export interface InboxItem {
|
|
15
|
+
id: string;
|
|
16
|
+
from_agent_id: string;
|
|
17
|
+
from_agent?: {
|
|
18
|
+
display_name?: string;
|
|
19
|
+
};
|
|
20
|
+
scope: string;
|
|
21
|
+
reason: string;
|
|
22
|
+
status: string;
|
|
23
|
+
created_at: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function registerAgent(options: RegisterOptions): Promise<RegisterResult>;
|
|
26
|
+
export declare function checkInbox(url: string, workspace: string, token: string): Promise<InboxItem[]>;
|
|
27
|
+
export declare function sendNudge(url: string, agentId: string, message: string, token: string): Promise<boolean>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
export async function registerAgent(options) {
|
|
3
|
+
const agentId = options.agentId || randomUUID();
|
|
4
|
+
const response = await fetch(`${options.url}/api/v1/agents/register`, {
|
|
5
|
+
method: "POST",
|
|
6
|
+
headers: {
|
|
7
|
+
"x-agentmesh-secret": options.apiKey,
|
|
8
|
+
"Content-Type": "application/json",
|
|
9
|
+
},
|
|
10
|
+
body: JSON.stringify({
|
|
11
|
+
agent_id: agentId,
|
|
12
|
+
display_name: options.agentName,
|
|
13
|
+
model: options.model,
|
|
14
|
+
capabilities: options.capabilities || ["coding", "review", "debugging"],
|
|
15
|
+
workspace: options.workspace,
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
const error = await response.text();
|
|
20
|
+
throw new Error(`Failed to register agent: ${error}`);
|
|
21
|
+
}
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
const token = data.data?.token || data.token;
|
|
24
|
+
if (!token) {
|
|
25
|
+
throw new Error("No token in registration response");
|
|
26
|
+
}
|
|
27
|
+
return { agentId, token };
|
|
28
|
+
}
|
|
29
|
+
export async function checkInbox(url, workspace, token) {
|
|
30
|
+
const response = await fetch(`${url}/api/v1/workspaces/${workspace}/inbox`, {
|
|
31
|
+
headers: {
|
|
32
|
+
Authorization: `Bearer ${token}`,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error(`Failed to check inbox: ${response.status}`);
|
|
37
|
+
}
|
|
38
|
+
const data = await response.json();
|
|
39
|
+
return (data.data || []).filter((item) => item.status === "pending");
|
|
40
|
+
}
|
|
41
|
+
export async function sendNudge(url, agentId, message, token) {
|
|
42
|
+
const response = await fetch(`${url}/api/v1/agents/${agentId}/nudge`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
Authorization: `Bearer ${token}`,
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify({ message }),
|
|
49
|
+
});
|
|
50
|
+
return response.ok;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA6BzC,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAwB;IAExB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,UAAU,EAAE,CAAC;IAEhD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,yBAAyB,EAAE;QACpE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,oBAAoB,EAAE,OAAO,CAAC,MAAM;YACpC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,QAAQ,EAAE,OAAO;YACjB,YAAY,EAAE,OAAO,CAAC,SAAS;YAC/B,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC;YACvE,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC;IAE7C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,SAAiB,EACjB,KAAa;IAEb,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,GAAG,GAAG,sBAAsB,SAAS,QAAQ,EAC7C;QACE,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC;KACF,CACF,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,CAC7B,CAAC,IAAe,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAC/C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,OAAe,EACf,OAAe,EACf,KAAa;IAEb,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,GAAG,kBAAkB,OAAO,QAAQ,EAAE;QACpE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,KAAK,EAAE;YAChC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;KAClC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC,EAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function getSessionName(agentName: string): string;
|
|
2
|
+
export declare function sessionExists(sessionName: string): boolean;
|
|
3
|
+
export declare function createSession(agentName: string, command: string, workdir?: string): boolean;
|
|
4
|
+
export declare function destroySession(agentName: string): boolean;
|
|
5
|
+
export declare function sendKeys(agentName: string, message: string): boolean;
|
|
6
|
+
export declare function attachSession(agentName: string): void;
|
|
7
|
+
export declare function listSessions(): string[];
|
|
8
|
+
export declare function getSessionInfo(agentName: string): {
|
|
9
|
+
exists: boolean;
|
|
10
|
+
command?: string;
|
|
11
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { execSync, spawn } from "node:child_process";
|
|
2
|
+
const SESSION_PREFIX = "agentmesh-";
|
|
3
|
+
export function getSessionName(agentName) {
|
|
4
|
+
return `${SESSION_PREFIX}${agentName}`;
|
|
5
|
+
}
|
|
6
|
+
export function sessionExists(sessionName) {
|
|
7
|
+
try {
|
|
8
|
+
execSync(`tmux has-session -t "${sessionName}" 2>/dev/null`);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function createSession(agentName, command, workdir) {
|
|
16
|
+
const sessionName = getSessionName(agentName);
|
|
17
|
+
if (sessionExists(sessionName)) {
|
|
18
|
+
console.error(`Session ${sessionName} already exists`);
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const args = ["new-session", "-d", "-s", sessionName];
|
|
23
|
+
if (workdir) {
|
|
24
|
+
args.push("-c", workdir);
|
|
25
|
+
}
|
|
26
|
+
args.push(command);
|
|
27
|
+
execSync(`tmux ${args.join(" ")}`);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error(`Failed to create tmux session: ${error}`);
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function destroySession(agentName) {
|
|
36
|
+
const sessionName = getSessionName(agentName);
|
|
37
|
+
if (!sessionExists(sessionName)) {
|
|
38
|
+
return true; // Already gone
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
execSync(`tmux kill-session -t "${sessionName}"`);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error(`Failed to destroy tmux session: ${error}`);
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function sendKeys(agentName, message) {
|
|
50
|
+
const sessionName = getSessionName(agentName);
|
|
51
|
+
if (!sessionExists(sessionName)) {
|
|
52
|
+
console.error(`Session ${sessionName} does not exist`);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
// Escape special characters for tmux
|
|
57
|
+
const escapedMessage = message
|
|
58
|
+
.replace(/\\/g, "\\\\")
|
|
59
|
+
.replace(/"/g, '\\"')
|
|
60
|
+
.replace(/\$/g, "\\$")
|
|
61
|
+
.replace(/`/g, "\\`");
|
|
62
|
+
execSync(`tmux send-keys -t "${sessionName}" "${escapedMessage}" Enter`);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error(`Failed to send keys: ${error}`);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function attachSession(agentName) {
|
|
71
|
+
const sessionName = getSessionName(agentName);
|
|
72
|
+
if (!sessionExists(sessionName)) {
|
|
73
|
+
console.error(`Session ${sessionName} does not exist`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
// Attach to the session (replaces current process)
|
|
77
|
+
const tmux = spawn("tmux", ["attach-session", "-t", sessionName], {
|
|
78
|
+
stdio: "inherit",
|
|
79
|
+
});
|
|
80
|
+
tmux.on("exit", (code) => {
|
|
81
|
+
process.exit(code ?? 0);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
export function listSessions() {
|
|
85
|
+
try {
|
|
86
|
+
const output = execSync("tmux list-sessions -F '#{session_name}'", {
|
|
87
|
+
encoding: "utf-8",
|
|
88
|
+
});
|
|
89
|
+
return output
|
|
90
|
+
.trim()
|
|
91
|
+
.split("\n")
|
|
92
|
+
.filter((s) => s.startsWith(SESSION_PREFIX))
|
|
93
|
+
.map((s) => s.replace(SESSION_PREFIX, ""));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export function getSessionInfo(agentName) {
|
|
100
|
+
const sessionName = getSessionName(agentName);
|
|
101
|
+
if (!sessionExists(sessionName)) {
|
|
102
|
+
return { exists: false };
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const command = execSync(`tmux list-panes -t "${sessionName}" -F "#{pane_current_command}"`, { encoding: "utf-8" }).trim();
|
|
106
|
+
return { exists: true, command };
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return { exists: true };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=tmux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux.js","sourceRoot":"","sources":["../../src/core/tmux.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface WebSocketConfig {
|
|
2
|
+
url: string;
|
|
3
|
+
token: string;
|
|
4
|
+
onMessage: (data: WebSocketEvent) => void;
|
|
5
|
+
onConnect?: () => void;
|
|
6
|
+
onDisconnect?: () => void;
|
|
7
|
+
onError?: (error: Error) => void;
|
|
8
|
+
}
|
|
9
|
+
export interface WebSocketEvent {
|
|
10
|
+
type: string;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
export declare class AgentWebSocket {
|
|
14
|
+
private ws;
|
|
15
|
+
private config;
|
|
16
|
+
private reconnectAttempts;
|
|
17
|
+
private maxReconnectAttempts;
|
|
18
|
+
private reconnectDelay;
|
|
19
|
+
private shouldReconnect;
|
|
20
|
+
constructor(config: WebSocketConfig);
|
|
21
|
+
connect(): void;
|
|
22
|
+
private scheduleReconnect;
|
|
23
|
+
disconnect(): void;
|
|
24
|
+
isConnected(): boolean;
|
|
25
|
+
}
|