@agentmeshhq/agent 0.1.10 → 0.1.12
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/dist/__tests__/injector.test.d.ts +1 -0
- package/dist/__tests__/injector.test.js +26 -0
- package/dist/__tests__/injector.test.js.map +1 -0
- package/dist/cli/build.d.ts +6 -0
- package/dist/cli/build.js +111 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/deploy.d.ts +9 -0
- package/dist/cli/deploy.js +130 -0
- package/dist/cli/deploy.js.map +1 -0
- package/dist/cli/index.js +155 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/list.js +18 -6
- package/dist/cli/list.js.map +1 -1
- package/dist/cli/local.d.ts +9 -0
- package/dist/cli/local.js +139 -0
- package/dist/cli/local.js.map +1 -0
- package/dist/cli/migrate.d.ts +8 -0
- package/dist/cli/migrate.js +167 -0
- package/dist/cli/migrate.js.map +1 -0
- package/dist/cli/slack.d.ts +3 -0
- package/dist/cli/slack.js +57 -0
- package/dist/cli/slack.js.map +1 -0
- package/dist/cli/start.d.ts +4 -0
- package/dist/cli/start.js +5 -0
- package/dist/cli/start.js.map +1 -1
- package/dist/cli/test.d.ts +8 -0
- package/dist/cli/test.js +110 -0
- package/dist/cli/test.js.map +1 -0
- package/dist/config/schema.d.ts +2 -0
- package/dist/core/daemon.d.ts +13 -0
- package/dist/core/daemon.js +100 -27
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/injector.d.ts +6 -1
- package/dist/core/injector.js +64 -1
- package/dist/core/injector.js.map +1 -1
- package/dist/core/tmux.js +11 -13
- package/dist/core/tmux.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/injector.test.ts +29 -0
- package/src/cli/build.ts +137 -0
- package/src/cli/deploy.ts +153 -0
- package/src/cli/index.ts +156 -0
- package/src/cli/list.ts +18 -6
- package/src/cli/local.ts +174 -0
- package/src/cli/migrate.ts +210 -0
- package/src/cli/slack.ts +69 -0
- package/src/cli/start.ts +8 -0
- package/src/cli/test.ts +141 -0
- package/src/config/schema.ts +2 -0
- package/src/core/daemon.ts +123 -35
- package/src/core/injector.ts +98 -1
- package/src/core/tmux.ts +12 -14
package/src/cli/local.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { spawn, execSync, spawnSync } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
|
|
5
|
+
const COMPOSE_FILE = "docker/docker-compose.local.yml";
|
|
6
|
+
|
|
7
|
+
interface LocalStackPorts {
|
|
8
|
+
postgres: number;
|
|
9
|
+
valkey: number;
|
|
10
|
+
hub: number;
|
|
11
|
+
admin: number;
|
|
12
|
+
nginx: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function findProjectRoot(): string {
|
|
16
|
+
// Try to find the project root by looking for docker-compose files
|
|
17
|
+
let dir = process.cwd();
|
|
18
|
+
for (let i = 0; i < 10; i++) {
|
|
19
|
+
const composePath = path.join(dir, COMPOSE_FILE);
|
|
20
|
+
try {
|
|
21
|
+
execSync(`test -f "${composePath}"`, { stdio: "ignore" });
|
|
22
|
+
return dir;
|
|
23
|
+
} catch {
|
|
24
|
+
dir = path.dirname(dir);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
throw new Error("Could not find AgentMesh project root. Make sure you're in the agentmesh repository.");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getContainerStatus(containerName: string): { running: boolean; healthy: boolean; port?: string } {
|
|
31
|
+
try {
|
|
32
|
+
const result = execSync(
|
|
33
|
+
`docker inspect --format='{{.State.Running}}:{{.State.Health.Status}}' ${containerName} 2>/dev/null`,
|
|
34
|
+
{ encoding: "utf-8" }
|
|
35
|
+
).trim();
|
|
36
|
+
const [running, health] = result.split(":");
|
|
37
|
+
return {
|
|
38
|
+
running: running === "true",
|
|
39
|
+
healthy: health === "healthy",
|
|
40
|
+
};
|
|
41
|
+
} catch {
|
|
42
|
+
return { running: false, healthy: false };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getContainerPort(containerName: string, internalPort: number): string | null {
|
|
47
|
+
try {
|
|
48
|
+
const result = execSync(
|
|
49
|
+
`docker port ${containerName} ${internalPort} 2>/dev/null | head -1`,
|
|
50
|
+
{ encoding: "utf-8" }
|
|
51
|
+
).trim();
|
|
52
|
+
// Format: 0.0.0.0:5432 -> 5432
|
|
53
|
+
const match = result.match(/:(\d+)$/);
|
|
54
|
+
return match ? match[1] : null;
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function localUp(options: { detach?: boolean } = {}): Promise<void> {
|
|
61
|
+
const projectRoot = findProjectRoot();
|
|
62
|
+
const composePath = path.join(projectRoot, COMPOSE_FILE);
|
|
63
|
+
|
|
64
|
+
console.log(pc.cyan("Starting AgentMesh local development stack..."));
|
|
65
|
+
console.log();
|
|
66
|
+
|
|
67
|
+
const args = ["compose", "-f", composePath, "up"];
|
|
68
|
+
if (options.detach !== false) {
|
|
69
|
+
args.push("-d");
|
|
70
|
+
}
|
|
71
|
+
args.push("--build");
|
|
72
|
+
|
|
73
|
+
const result = spawnSync("docker", args, {
|
|
74
|
+
cwd: projectRoot,
|
|
75
|
+
stdio: "inherit",
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (result.status !== 0) {
|
|
79
|
+
console.error(pc.red("Failed to start local stack"));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (options.detach !== false) {
|
|
84
|
+
console.log();
|
|
85
|
+
console.log(pc.green("Local stack started successfully!"));
|
|
86
|
+
console.log();
|
|
87
|
+
await localStatus();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function localDown(): Promise<void> {
|
|
92
|
+
const projectRoot = findProjectRoot();
|
|
93
|
+
const composePath = path.join(projectRoot, COMPOSE_FILE);
|
|
94
|
+
|
|
95
|
+
console.log(pc.cyan("Stopping AgentMesh local development stack..."));
|
|
96
|
+
|
|
97
|
+
const result = spawnSync("docker", ["compose", "-f", composePath, "down"], {
|
|
98
|
+
cwd: projectRoot,
|
|
99
|
+
stdio: "inherit",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (result.status !== 0) {
|
|
103
|
+
console.error(pc.red("Failed to stop local stack"));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log(pc.green("Local stack stopped."));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function localStatus(): Promise<void> {
|
|
111
|
+
const services = [
|
|
112
|
+
{ name: "postgres", container: "agentmesh_postgres", port: 5432 },
|
|
113
|
+
{ name: "valkey", container: "agentmesh_valkey", port: 6379 },
|
|
114
|
+
{ name: "hub-api", container: "agentmesh_hub_api", port: 3777 },
|
|
115
|
+
{ name: "hub-worker", container: "agentmesh_hub_worker", port: null },
|
|
116
|
+
{ name: "admin", container: "agentmesh_admin", port: 3778 },
|
|
117
|
+
{ name: "nginx", container: "agentmesh_nginx", port: 80 },
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
console.log(pc.bold("AgentMesh Local Stack Status"));
|
|
121
|
+
console.log();
|
|
122
|
+
console.log(pc.dim("Service".padEnd(15) + "Status".padEnd(12) + "Health".padEnd(10) + "Port"));
|
|
123
|
+
console.log(pc.dim("-".repeat(50)));
|
|
124
|
+
|
|
125
|
+
let anyRunning = false;
|
|
126
|
+
|
|
127
|
+
for (const service of services) {
|
|
128
|
+
const status = getContainerStatus(service.container);
|
|
129
|
+
const port = service.port ? getContainerPort(service.container, service.port) : null;
|
|
130
|
+
|
|
131
|
+
const statusText = status.running ? pc.green("running") : pc.dim("stopped");
|
|
132
|
+
const healthText = status.running
|
|
133
|
+
? status.healthy
|
|
134
|
+
? pc.green("healthy")
|
|
135
|
+
: pc.yellow("starting")
|
|
136
|
+
: pc.dim("-");
|
|
137
|
+
const portText = port ? pc.cyan(port) : pc.dim("-");
|
|
138
|
+
|
|
139
|
+
console.log(
|
|
140
|
+
`${service.name.padEnd(15)}${statusText.padEnd(20)}${healthText.padEnd(18)}${portText}`
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (status.running) anyRunning = true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.log();
|
|
147
|
+
|
|
148
|
+
if (anyRunning) {
|
|
149
|
+
const nginxPort = getContainerPort("agentmesh_nginx", 80) || "3000";
|
|
150
|
+
console.log(pc.bold("Access:"));
|
|
151
|
+
console.log(` Admin UI: ${pc.cyan(`http://localhost:${nginxPort}/admin`)}`);
|
|
152
|
+
console.log(` API: ${pc.cyan(`http://localhost:${nginxPort}/api`)}`);
|
|
153
|
+
console.log();
|
|
154
|
+
console.log(pc.bold("Commands:"));
|
|
155
|
+
console.log(` Stop: ${pc.cyan("agentmesh local down")}`);
|
|
156
|
+
console.log(` Logs: ${pc.cyan("docker compose -f docker/docker-compose.local.yml logs -f")}`);
|
|
157
|
+
} else {
|
|
158
|
+
console.log(pc.dim("No services running. Start with: agentmesh local up"));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function localLogs(options: { follow?: boolean; service?: string } = {}): Promise<void> {
|
|
163
|
+
const projectRoot = findProjectRoot();
|
|
164
|
+
const composePath = path.join(projectRoot, COMPOSE_FILE);
|
|
165
|
+
|
|
166
|
+
const args = ["compose", "-f", composePath, "logs"];
|
|
167
|
+
if (options.follow) args.push("-f");
|
|
168
|
+
if (options.service) args.push(options.service);
|
|
169
|
+
|
|
170
|
+
spawn("docker", args, {
|
|
171
|
+
cwd: projectRoot,
|
|
172
|
+
stdio: "inherit",
|
|
173
|
+
});
|
|
174
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { spawnSync, execSync } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
|
|
5
|
+
function findProjectRoot(): string {
|
|
6
|
+
let dir = process.cwd();
|
|
7
|
+
for (let i = 0; i < 10; i++) {
|
|
8
|
+
const packageJson = path.join(dir, "package.json");
|
|
9
|
+
const pnpmWorkspace = path.join(dir, "pnpm-workspace.yaml");
|
|
10
|
+
try {
|
|
11
|
+
execSync(`test -f "${packageJson}" && test -f "${pnpmWorkspace}"`, { stdio: "ignore" });
|
|
12
|
+
return dir;
|
|
13
|
+
} catch {
|
|
14
|
+
dir = path.dirname(dir);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
throw new Error("Could not find AgentMesh project root. Make sure you're in the agentmesh repository.");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isLocalStackRunning(): boolean {
|
|
21
|
+
try {
|
|
22
|
+
const result = execSync(
|
|
23
|
+
'docker inspect --format="{{.State.Running}}" agentmesh_postgres 2>/dev/null',
|
|
24
|
+
{ encoding: "utf-8" }
|
|
25
|
+
).trim();
|
|
26
|
+
return result === "true";
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getLocalDbUrl(): string {
|
|
33
|
+
// Default local stack connection
|
|
34
|
+
return "postgresql://postgres:postgres@localhost:5432/agentmesh";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface MigrateOptions {
|
|
38
|
+
dev?: boolean;
|
|
39
|
+
staging?: boolean;
|
|
40
|
+
generate?: boolean;
|
|
41
|
+
status?: boolean;
|
|
42
|
+
rollback?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function migrate(options: MigrateOptions = {}): Promise<void> {
|
|
46
|
+
const projectRoot = findProjectRoot();
|
|
47
|
+
|
|
48
|
+
if (options.generate) {
|
|
49
|
+
await generateMigration(projectRoot);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (options.status) {
|
|
54
|
+
await migrationStatus(projectRoot, options);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (options.rollback) {
|
|
59
|
+
console.error(pc.red("Rollback is not supported. Use a new migration to revert changes."));
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await runMigrations(projectRoot, options);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function generateMigration(projectRoot: string): Promise<void> {
|
|
67
|
+
console.log(pc.cyan("Generating migration from schema changes..."));
|
|
68
|
+
console.log();
|
|
69
|
+
|
|
70
|
+
const result = spawnSync("pnpm", ["--filter", "@agentmesh/hub", "db:generate"], {
|
|
71
|
+
cwd: projectRoot,
|
|
72
|
+
stdio: "inherit",
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (result.status !== 0) {
|
|
76
|
+
console.error(pc.red("Migration generation failed"));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log();
|
|
81
|
+
console.log(pc.green("Migration generated successfully!"));
|
|
82
|
+
console.log(pc.dim("Review the migration in packages/hub/drizzle/"));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function migrationStatus(projectRoot: string, options: MigrateOptions): Promise<void> {
|
|
86
|
+
const env = getEnvironmentConfig(options);
|
|
87
|
+
|
|
88
|
+
console.log(pc.cyan(`Checking migration status for ${env.name}...`));
|
|
89
|
+
console.log();
|
|
90
|
+
|
|
91
|
+
// Check if we can connect
|
|
92
|
+
if (!env.canConnect) {
|
|
93
|
+
console.error(pc.red(`Cannot connect to ${env.name}: ${env.reason}`));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const result = spawnSync("pnpm", ["--filter", "@agentmesh/hub", "db:status"], {
|
|
98
|
+
cwd: projectRoot,
|
|
99
|
+
stdio: "inherit",
|
|
100
|
+
env: {
|
|
101
|
+
...process.env,
|
|
102
|
+
DATABASE_URL: env.databaseUrl,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (result.status !== 0) {
|
|
107
|
+
console.error(pc.red("Failed to check migration status"));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface EnvironmentConfig {
|
|
113
|
+
name: string;
|
|
114
|
+
databaseUrl: string;
|
|
115
|
+
canConnect: boolean;
|
|
116
|
+
reason?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getEnvironmentConfig(options: MigrateOptions): EnvironmentConfig {
|
|
120
|
+
if (options.dev) {
|
|
121
|
+
// Dev environment uses cloud database via environment variable
|
|
122
|
+
const devUrl = process.env.AGENTMESH_DEV_DATABASE_URL;
|
|
123
|
+
if (!devUrl) {
|
|
124
|
+
return {
|
|
125
|
+
name: "dev (agentmeshhq.dev)",
|
|
126
|
+
databaseUrl: "",
|
|
127
|
+
canConnect: false,
|
|
128
|
+
reason: "AGENTMESH_DEV_DATABASE_URL environment variable not set",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
name: "dev (agentmeshhq.dev)",
|
|
133
|
+
databaseUrl: devUrl,
|
|
134
|
+
canConnect: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (options.staging) {
|
|
139
|
+
const stagingUrl = process.env.AGENTMESH_STAGING_DATABASE_URL;
|
|
140
|
+
if (!stagingUrl) {
|
|
141
|
+
return {
|
|
142
|
+
name: "staging",
|
|
143
|
+
databaseUrl: "",
|
|
144
|
+
canConnect: false,
|
|
145
|
+
reason: "AGENTMESH_STAGING_DATABASE_URL environment variable not set",
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
name: "staging",
|
|
150
|
+
databaseUrl: stagingUrl,
|
|
151
|
+
canConnect: true,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Default: local stack
|
|
156
|
+
const localRunning = isLocalStackRunning();
|
|
157
|
+
if (!localRunning) {
|
|
158
|
+
return {
|
|
159
|
+
name: "local",
|
|
160
|
+
databaseUrl: getLocalDbUrl(),
|
|
161
|
+
canConnect: false,
|
|
162
|
+
reason: "Local stack is not running. Start it with: agentmesh local up",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
name: "local",
|
|
168
|
+
databaseUrl: getLocalDbUrl(),
|
|
169
|
+
canConnect: true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function runMigrations(projectRoot: string, options: MigrateOptions): Promise<void> {
|
|
174
|
+
const env = getEnvironmentConfig(options);
|
|
175
|
+
|
|
176
|
+
console.log(pc.cyan(`Running migrations against ${env.name}...`));
|
|
177
|
+
console.log();
|
|
178
|
+
|
|
179
|
+
if (!env.canConnect) {
|
|
180
|
+
console.error(pc.red(`Cannot connect to ${env.name}: ${env.reason}`));
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Safety confirmation for non-local environments
|
|
185
|
+
if (options.dev || options.staging) {
|
|
186
|
+
console.log(pc.yellow(`WARNING: You are about to run migrations against ${env.name}`));
|
|
187
|
+
console.log(pc.yellow("This will modify the database schema."));
|
|
188
|
+
console.log();
|
|
189
|
+
console.log(pc.dim("Press Ctrl+C to cancel, or wait 5 seconds to continue..."));
|
|
190
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
191
|
+
console.log();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const result = spawnSync("pnpm", ["--filter", "@agentmesh/hub", "db:migrate"], {
|
|
195
|
+
cwd: projectRoot,
|
|
196
|
+
stdio: "inherit",
|
|
197
|
+
env: {
|
|
198
|
+
...process.env,
|
|
199
|
+
DATABASE_URL: env.databaseUrl,
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (result.status !== 0) {
|
|
204
|
+
console.error(pc.red("Migration failed"));
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
console.log();
|
|
209
|
+
console.log(pc.green(`Migrations applied successfully to ${env.name}!`));
|
|
210
|
+
}
|
package/src/cli/slack.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { loadConfig, getAgentState } from "../config/loader.js";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
|
|
4
|
+
export async function slack(
|
|
5
|
+
action: string,
|
|
6
|
+
channel: string | undefined,
|
|
7
|
+
message: string | undefined,
|
|
8
|
+
options: { name?: string }
|
|
9
|
+
): Promise<void> {
|
|
10
|
+
const config = loadConfig();
|
|
11
|
+
|
|
12
|
+
if (!config) {
|
|
13
|
+
console.log(pc.red("No config found. Run 'agentmesh init' first."));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (action === "respond" || action === "reply") {
|
|
18
|
+
if (!channel) {
|
|
19
|
+
console.log(pc.red("Channel is required. Usage: agentmesh slack respond <channel> <message>"));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!message) {
|
|
24
|
+
console.log(pc.red("Message is required. Usage: agentmesh slack respond <channel> <message>"));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Get token from agent state or config
|
|
29
|
+
const agentName = options.name || "concierge";
|
|
30
|
+
const agentState = getAgentState(agentName);
|
|
31
|
+
const token = agentState?.token;
|
|
32
|
+
|
|
33
|
+
if (!token) {
|
|
34
|
+
console.log(pc.red(`No token found for agent "${agentName}". Is the agent running?`));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(
|
|
40
|
+
`${config.hubUrl}/api/v1/integrations/slack/respond`,
|
|
41
|
+
{
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: `Bearer ${token}`,
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify({ channel, text: message }),
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (response.ok) {
|
|
52
|
+
console.log(pc.green(`Message sent to Slack channel ${channel}`));
|
|
53
|
+
} else {
|
|
54
|
+
const error = await response.text();
|
|
55
|
+
console.log(pc.red(`Failed to send: ${error}`));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.log(pc.red(`Failed to send: ${(error as Error).message}`));
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
console.log(pc.yellow("Usage:"));
|
|
64
|
+
console.log(" agentmesh slack respond <channel> <message> - Send a message to Slack");
|
|
65
|
+
console.log("");
|
|
66
|
+
console.log(pc.dim("Examples:"));
|
|
67
|
+
console.log(' agentmesh slack respond C0123456 "Hello from AgentMesh!"');
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/cli/start.ts
CHANGED
|
@@ -14,6 +14,10 @@ export interface StartOptions {
|
|
|
14
14
|
foreground?: boolean;
|
|
15
15
|
noContext?: boolean;
|
|
16
16
|
autoSetup?: boolean;
|
|
17
|
+
/** Run opencode serve instead of tmux TUI (for Integration Service) */
|
|
18
|
+
serve?: boolean;
|
|
19
|
+
/** Port for opencode serve (default: 3001) */
|
|
20
|
+
servePort?: number;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
export async function start(options: StartOptions): Promise<void> {
|
|
@@ -70,6 +74,10 @@ export async function start(options: StartOptions): Promise<void> {
|
|
|
70
74
|
if (options.model) args.push("--model", options.model);
|
|
71
75
|
if (options.noContext) args.push("--no-context");
|
|
72
76
|
if (options.autoSetup) args.push("--auto-setup");
|
|
77
|
+
if (options.serve) {
|
|
78
|
+
args.push("--serve");
|
|
79
|
+
if (options.servePort) args.push("--serve-port", String(options.servePort));
|
|
80
|
+
}
|
|
73
81
|
|
|
74
82
|
// Spawn detached background process
|
|
75
83
|
const child = spawn("node", [cliPath, ...args], {
|
package/src/cli/test.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { spawnSync, execSync } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
|
|
5
|
+
function findProjectRoot(): string {
|
|
6
|
+
let dir = process.cwd();
|
|
7
|
+
for (let i = 0; i < 10; i++) {
|
|
8
|
+
const packageJson = path.join(dir, "package.json");
|
|
9
|
+
const pnpmWorkspace = path.join(dir, "pnpm-workspace.yaml");
|
|
10
|
+
try {
|
|
11
|
+
execSync(`test -f "${packageJson}" && test -f "${pnpmWorkspace}"`, { stdio: "ignore" });
|
|
12
|
+
return dir;
|
|
13
|
+
} catch {
|
|
14
|
+
dir = path.dirname(dir);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
throw new Error("Could not find AgentMesh project root. Make sure you're in the agentmesh repository.");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isLocalStackRunning(): boolean {
|
|
21
|
+
try {
|
|
22
|
+
const result = execSync(
|
|
23
|
+
'docker inspect --format="{{.State.Running}}" agentmesh_postgres 2>/dev/null',
|
|
24
|
+
{ encoding: "utf-8" }
|
|
25
|
+
).trim();
|
|
26
|
+
return result === "true";
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface TestOptions {
|
|
33
|
+
package?: string;
|
|
34
|
+
e2e?: boolean;
|
|
35
|
+
watch?: boolean;
|
|
36
|
+
coverage?: boolean;
|
|
37
|
+
updateSnapshots?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function test(options: TestOptions = {}): Promise<void> {
|
|
41
|
+
const projectRoot = findProjectRoot();
|
|
42
|
+
|
|
43
|
+
if (options.e2e) {
|
|
44
|
+
await runE2ETests(projectRoot, options);
|
|
45
|
+
} else {
|
|
46
|
+
await runUnitTests(projectRoot, options);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function runUnitTests(projectRoot: string, options: TestOptions): Promise<void> {
|
|
51
|
+
console.log(pc.cyan("Running unit tests..."));
|
|
52
|
+
|
|
53
|
+
// Check if local stack is running for integration tests
|
|
54
|
+
const localStackUp = isLocalStackRunning();
|
|
55
|
+
if (localStackUp) {
|
|
56
|
+
console.log(pc.dim("Local stack detected - integration tests will use local services"));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const args = ["pnpm"];
|
|
60
|
+
|
|
61
|
+
if (options.package) {
|
|
62
|
+
args.push("--filter", options.package);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
args.push("test");
|
|
66
|
+
|
|
67
|
+
if (options.watch) {
|
|
68
|
+
args.push("--", "--watch");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (options.coverage) {
|
|
72
|
+
args.push("--", "--coverage");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (options.updateSnapshots) {
|
|
76
|
+
args.push("--", "-u");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log();
|
|
80
|
+
|
|
81
|
+
const result = spawnSync(args[0], args.slice(1), {
|
|
82
|
+
cwd: projectRoot,
|
|
83
|
+
stdio: "inherit",
|
|
84
|
+
env: {
|
|
85
|
+
...process.env,
|
|
86
|
+
// If local stack is running, use it for tests
|
|
87
|
+
...(localStackUp && {
|
|
88
|
+
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/agentmesh",
|
|
89
|
+
REDIS_URL: "redis://localhost:6380/0",
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (result.status !== 0) {
|
|
95
|
+
console.error(pc.red("Tests failed"));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log();
|
|
100
|
+
console.log(pc.green("All tests passed!"));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function runE2ETests(projectRoot: string, options: TestOptions): Promise<void> {
|
|
104
|
+
console.log(pc.cyan("Running E2E tests..."));
|
|
105
|
+
|
|
106
|
+
// E2E tests require the local stack to be running
|
|
107
|
+
const localStackUp = isLocalStackRunning();
|
|
108
|
+
if (!localStackUp) {
|
|
109
|
+
console.log(pc.yellow("Warning: Local stack is not running."));
|
|
110
|
+
console.log(pc.dim("Start it with: agentmesh local up"));
|
|
111
|
+
console.log();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const args = ["pnpm", "--filter", "@agentmesh/admin", "test:e2e"];
|
|
115
|
+
|
|
116
|
+
if (options.updateSnapshots) {
|
|
117
|
+
args.push("--", "--update-snapshots");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log();
|
|
121
|
+
|
|
122
|
+
const result = spawnSync(args[0], args.slice(1), {
|
|
123
|
+
cwd: projectRoot,
|
|
124
|
+
stdio: "inherit",
|
|
125
|
+
env: {
|
|
126
|
+
...process.env,
|
|
127
|
+
// Point to local stack if running
|
|
128
|
+
...(localStackUp && {
|
|
129
|
+
PLAYWRIGHT_BASE_URL: "http://localhost:3000",
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (result.status !== 0) {
|
|
135
|
+
console.error(pc.red("E2E tests failed"));
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log();
|
|
140
|
+
console.log(pc.green("E2E tests passed!"));
|
|
141
|
+
}
|
package/src/config/schema.ts
CHANGED
|
@@ -39,6 +39,8 @@ export interface AgentState {
|
|
|
39
39
|
tmuxSession: string;
|
|
40
40
|
startedAt: string;
|
|
41
41
|
token?: string;
|
|
42
|
+
workdir?: string;
|
|
43
|
+
assignedProject?: string;
|
|
42
44
|
/** The effective runtime model (resolved from CLI > agent > defaults) */
|
|
43
45
|
runtimeModel?: string;
|
|
44
46
|
/** The runner type (opencode, claude, custom) */
|