agenticros 0.1.0 → 0.1.1

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 (41) hide show
  1. package/dist/commands/config.d.ts.map +1 -1
  2. package/dist/commands/config.js +37 -1
  3. package/dist/commands/config.js.map +1 -1
  4. package/dist/commands/down.d.ts +6 -4
  5. package/dist/commands/down.d.ts.map +1 -1
  6. package/dist/commands/down.js +16 -6
  7. package/dist/commands/down.js.map +1 -1
  8. package/dist/commands/logs.js +2 -2
  9. package/dist/commands/logs.js.map +1 -1
  10. package/dist/commands/status.d.ts.map +1 -1
  11. package/dist/commands/status.js +10 -6
  12. package/dist/commands/status.js.map +1 -1
  13. package/dist/commands/up.d.ts +1 -0
  14. package/dist/commands/up.d.ts.map +1 -1
  15. package/dist/commands/up.js +12 -0
  16. package/dist/commands/up.js.map +1 -1
  17. package/dist/index.js +4 -3
  18. package/dist/index.js.map +1 -1
  19. package/dist/runners/sim.d.ts +2 -0
  20. package/dist/runners/sim.d.ts.map +1 -1
  21. package/dist/runners/sim.js +2 -0
  22. package/dist/runners/sim.js.map +1 -1
  23. package/package.json +1 -1
  24. package/runtime/BUNDLE.json +1 -1
  25. package/runtime/packages/core/src/transport/local/transport.ts +25 -5
  26. package/runtime/ros2_ws/src/agenticros_sim/CMakeLists.txt +24 -0
  27. package/runtime/ros2_ws/src/agenticros_sim/README.md +120 -0
  28. package/runtime/ros2_ws/src/agenticros_sim/config/agenticros-sim.config.json +28 -0
  29. package/runtime/ros2_ws/src/agenticros_sim/config/amr_bridge.yaml +111 -0
  30. package/runtime/ros2_ws/src/agenticros_sim/config/amr_view.rviz +172 -0
  31. package/runtime/ros2_ws/src/agenticros_sim/env-hooks/gz_resource_path.dsv.in +3 -0
  32. package/runtime/ros2_ws/src/agenticros_sim/env-hooks/gz_resource_path.sh.in +7 -0
  33. package/runtime/ros2_ws/src/agenticros_sim/launch/sim_amr.launch.py +159 -0
  34. package/runtime/ros2_ws/src/agenticros_sim/models/agenticros_amr/model.config +17 -0
  35. package/runtime/ros2_ws/src/agenticros_sim/models/agenticros_amr/model.sdf +244 -0
  36. package/runtime/ros2_ws/src/agenticros_sim/package.xml +27 -0
  37. package/runtime/ros2_ws/src/agenticros_sim/worlds/agenticros_indoor.sdf +183 -0
  38. package/runtime/scripts/configure_for_sim.sh +64 -0
  39. package/runtime/scripts/sim/run_sim.sh +146 -0
  40. package/runtime/scripts/test-follow-me-sim.mjs +135 -0
  41. package/runtime/scripts/test-mcp-e2e.mjs +184 -0
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Drive ros2_follow_me_start (mode=depth) against the AMR sim.
4
+ *
5
+ * Sequence:
6
+ * 1. start follow-me in depth mode
7
+ * 2. poll status every 1.5s for 15s, capturing detection events
8
+ * 3. stop follow-me
9
+ * 4. send a zero cmd_vel just in case
10
+ *
11
+ * Reports: did the depth loop see a target, what distance/lateral did it pick,
12
+ * how many cmd_vel commands were issued.
13
+ */
14
+
15
+ import { spawn } from "node:child_process";
16
+ import { fileURLToPath } from "node:url";
17
+ import { dirname, join, resolve } from "node:path";
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const repoRoot = resolve(__dirname, "..");
21
+ const serverDist = join(repoRoot, "packages/agenticros-claude-code/dist/index.js");
22
+
23
+ const child = spawn(process.execPath, [serverDist], {
24
+ stdio: ["pipe", "pipe", "pipe"],
25
+ env: { ...process.env },
26
+ });
27
+
28
+ child.stderr.on("data", (d) => {
29
+ process.stderr.write(`[mcp-stderr] ${d}`);
30
+ });
31
+
32
+ let nextId = 1;
33
+ const pending = new Map();
34
+ let buf = "";
35
+
36
+ child.stdout.on("data", (chunk) => {
37
+ buf += chunk.toString();
38
+ let nl;
39
+ while ((nl = buf.indexOf("\n")) !== -1) {
40
+ const line = buf.slice(0, nl).trim();
41
+ buf = buf.slice(nl + 1);
42
+ if (!line) continue;
43
+ let msg;
44
+ try {
45
+ msg = JSON.parse(line);
46
+ } catch {
47
+ continue;
48
+ }
49
+ if (msg.id !== undefined && pending.has(msg.id)) {
50
+ const { resolve, reject } = pending.get(msg.id);
51
+ pending.delete(msg.id);
52
+ if (msg.error) reject(new Error(msg.error.message));
53
+ else resolve(msg.result);
54
+ }
55
+ }
56
+ });
57
+
58
+ function rpc(method, params = {}, timeoutMs = 15000) {
59
+ const id = nextId++;
60
+ return new Promise((resolveOuter, reject) => {
61
+ const t = setTimeout(() => {
62
+ pending.delete(id);
63
+ reject(new Error(`Timeout: ${method}`));
64
+ }, timeoutMs);
65
+ pending.set(id, {
66
+ resolve: (v) => {
67
+ clearTimeout(t);
68
+ resolveOuter(v);
69
+ },
70
+ reject: (e) => {
71
+ clearTimeout(t);
72
+ reject(e);
73
+ },
74
+ });
75
+ child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
76
+ });
77
+ }
78
+
79
+ function pickText(result) {
80
+ return result?.content?.map((c) => c.text ?? "").join("\n") ?? "";
81
+ }
82
+
83
+ async function main() {
84
+ console.log("=== follow_me (depth) E2E ===");
85
+
86
+ await rpc("initialize", {
87
+ protocolVersion: "2024-11-05",
88
+ capabilities: { tools: {} },
89
+ clientInfo: { name: "fm-e2e", version: "0.0.1" },
90
+ });
91
+ await rpc("notifications/initialized", {}).catch(() => {});
92
+
93
+ console.log("\n-- start follow_me mode=depth --");
94
+ const start = await rpc("tools/call", {
95
+ name: "ros2_follow_me_start",
96
+ arguments: { mode: "depth", targetDistance: 1.5 },
97
+ });
98
+ console.log(pickText(start));
99
+
100
+ console.log("\n-- poll status for 15s --");
101
+ const polls = [];
102
+ for (let i = 0; i < 10; i++) {
103
+ await new Promise((r) => setTimeout(r, 1500));
104
+ const s = await rpc("tools/call", { name: "ros2_follow_me_status", arguments: {} });
105
+ const text = pickText(s);
106
+ polls.push({ t: (i + 1) * 1.5, text });
107
+ console.log(`t+${((i + 1) * 1.5).toFixed(1)}s :: ${text.replace(/\s+/g, " ").slice(0, 200)}`);
108
+ }
109
+
110
+ console.log("\n-- stop follow_me --");
111
+ const stop = await rpc("tools/call", {
112
+ name: "ros2_follow_me_stop",
113
+ arguments: {},
114
+ });
115
+ console.log(pickText(stop));
116
+
117
+ console.log("\n-- safety zero-twist --");
118
+ await rpc("tools/call", {
119
+ name: "ros2_publish",
120
+ arguments: {
121
+ topic: "/cmd_vel",
122
+ type: "geometry_msgs/msg/Twist",
123
+ message: { linear: { x: 0, y: 0, z: 0 }, angular: { x: 0, y: 0, z: 0 } },
124
+ },
125
+ });
126
+
127
+ child.kill("SIGTERM");
128
+ console.log("\n=== Done ===");
129
+ }
130
+
131
+ main().catch((e) => {
132
+ console.error("Failed:", e);
133
+ child.kill("SIGKILL");
134
+ process.exit(1);
135
+ });
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Spin up the @agenticros/claude-code MCP server as a child process and exercise
4
+ * a real JSON-RPC session over stdio. Used for end-to-end testing of the MCP
5
+ * tools against a live sim (or real robot).
6
+ *
7
+ * Usage:
8
+ * node scripts/test-mcp-e2e.mjs
9
+ *
10
+ * Honours AGENTICROS_CONFIG_PATH if set; otherwise uses ~/.agenticros/config.json.
11
+ */
12
+
13
+ import { spawn } from "node:child_process";
14
+ import { fileURLToPath } from "node:url";
15
+ import { dirname, join, resolve } from "node:path";
16
+
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const repoRoot = resolve(__dirname, "..");
19
+ const serverDist = join(repoRoot, "packages/agenticros-claude-code/dist/index.js");
20
+
21
+ const child = spawn(process.execPath, [serverDist], {
22
+ stdio: ["pipe", "pipe", "pipe"],
23
+ env: { ...process.env },
24
+ });
25
+
26
+ child.stderr.on("data", (d) => {
27
+ process.stderr.write(`[mcp-stderr] ${d}`);
28
+ });
29
+ child.on("exit", (code, sig) => {
30
+ process.stderr.write(`[mcp] exited code=${code} sig=${sig}\n`);
31
+ });
32
+
33
+ let nextId = 1;
34
+ const pending = new Map();
35
+ let buf = "";
36
+
37
+ child.stdout.on("data", (chunk) => {
38
+ buf += chunk.toString();
39
+ let nl;
40
+ while ((nl = buf.indexOf("\n")) !== -1) {
41
+ const line = buf.slice(0, nl).trim();
42
+ buf = buf.slice(nl + 1);
43
+ if (!line) continue;
44
+ let msg;
45
+ try {
46
+ msg = JSON.parse(line);
47
+ } catch (e) {
48
+ process.stderr.write(`[mcp] non-json line: ${line}\n`);
49
+ continue;
50
+ }
51
+ if (msg.id !== undefined && pending.has(msg.id)) {
52
+ const { resolve, reject } = pending.get(msg.id);
53
+ pending.delete(msg.id);
54
+ if (msg.error) reject(new Error(`${msg.error.code}: ${msg.error.message}`));
55
+ else resolve(msg.result);
56
+ }
57
+ }
58
+ });
59
+
60
+ function rpc(method, params = {}, timeoutMs = 20000) {
61
+ const id = nextId++;
62
+ const payload = { jsonrpc: "2.0", id, method, params };
63
+ return new Promise((resolveOuter, reject) => {
64
+ const t = setTimeout(() => {
65
+ pending.delete(id);
66
+ reject(new Error(`Timeout: ${method}`));
67
+ }, timeoutMs);
68
+ pending.set(id, {
69
+ resolve: (v) => {
70
+ clearTimeout(t);
71
+ resolveOuter(v);
72
+ },
73
+ reject: (e) => {
74
+ clearTimeout(t);
75
+ reject(e);
76
+ },
77
+ });
78
+ child.stdin.write(JSON.stringify(payload) + "\n");
79
+ });
80
+ }
81
+
82
+ function summarise(content, max = 4000) {
83
+ if (!Array.isArray(content)) return JSON.stringify(content).slice(0, max);
84
+ return content
85
+ .map((c) => {
86
+ if (c.type === "text") {
87
+ const t = c.text ?? "";
88
+ return t.length > max ? `${t.slice(0, max)} … (+${t.length - max} chars)` : t;
89
+ }
90
+ if (c.type === "image") return `[image base64 len=${(c.data ?? "").length} mime=${c.mimeType}]`;
91
+ return `[${c.type}]`;
92
+ })
93
+ .join("\n");
94
+ }
95
+
96
+ async function callTool(name, args = {}, timeoutMs = 20000) {
97
+ const t0 = Date.now();
98
+ try {
99
+ const result = await rpc("tools/call", { name, arguments: args }, timeoutMs);
100
+ const ms = Date.now() - t0;
101
+ const ok = result?.isError ? "ERR" : "ok ";
102
+ console.log(`[${ok}] (${ms.toString().padStart(5)}ms) ${name}`);
103
+ console.log(` ${summarise(result?.content)}`);
104
+ return result;
105
+ } catch (e) {
106
+ const ms = Date.now() - t0;
107
+ console.log(`[ERR] (${ms.toString().padStart(5)}ms) ${name} ${e.message}`);
108
+ return null;
109
+ }
110
+ }
111
+
112
+ async function main() {
113
+ console.log("=== MCP E2E harness ===");
114
+
115
+ console.log("\n-- initialize --");
116
+ const init = await rpc("initialize", {
117
+ protocolVersion: "2024-11-05",
118
+ capabilities: { tools: {} },
119
+ clientInfo: { name: "agenticros-e2e", version: "0.0.1" },
120
+ });
121
+ console.log(`server: ${init.serverInfo?.name} v${init.serverInfo?.version}`);
122
+ await rpc("notifications/initialized", {}).catch(() => {});
123
+
124
+ console.log("\n-- tools/list --");
125
+ const tl = await rpc("tools/list", {});
126
+ console.log(` ${tl.tools?.length ?? 0} tools advertised`);
127
+ for (const t of tl.tools ?? []) {
128
+ console.log(` - ${t.name}`);
129
+ }
130
+
131
+ console.log("\n-- ros2_list_topics --");
132
+ await callTool("ros2_list_topics", {}, 30000);
133
+
134
+ console.log("\n-- ros2_publish /cmd_vel (linear.x=0.2) --");
135
+ await callTool(
136
+ "ros2_publish",
137
+ {
138
+ topic: "/cmd_vel",
139
+ type: "geometry_msgs/msg/Twist",
140
+ message: { linear: { x: 0.2, y: 0, z: 0 }, angular: { x: 0, y: 0, z: 0 } },
141
+ },
142
+ 10000,
143
+ );
144
+
145
+ console.log("\n-- ros2_subscribe_once /imu/data --");
146
+ await callTool(
147
+ "ros2_subscribe_once",
148
+ { topic: "/imu/data", type: "sensor_msgs/msg/Imu", timeoutMs: 5000 },
149
+ 10000,
150
+ );
151
+
152
+ console.log("\n-- ros2_subscribe_once /scan --");
153
+ await callTool(
154
+ "ros2_subscribe_once",
155
+ { topic: "/scan", type: "sensor_msgs/msg/LaserScan", timeoutMs: 5000 },
156
+ 10000,
157
+ );
158
+
159
+ console.log("\n-- ros2_camera_snapshot (RGB) --");
160
+ await callTool("ros2_camera_snapshot", {}, 15000);
161
+
162
+ console.log("\n-- ros2_depth_distance --");
163
+ await callTool("ros2_depth_distance", {}, 15000);
164
+
165
+ console.log("\n-- stop --");
166
+ await callTool(
167
+ "ros2_publish",
168
+ {
169
+ topic: "/cmd_vel",
170
+ type: "geometry_msgs/msg/Twist",
171
+ message: { linear: { x: 0, y: 0, z: 0 }, angular: { x: 0, y: 0, z: 0 } },
172
+ },
173
+ 5000,
174
+ );
175
+
176
+ console.log("\n=== Done ===");
177
+ child.kill("SIGTERM");
178
+ }
179
+
180
+ main().catch((e) => {
181
+ console.error("Harness failed:", e);
182
+ child.kill("SIGKILL");
183
+ process.exit(1);
184
+ });