agenticros 0.1.19 → 0.2.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 (90) hide show
  1. package/dist/commands/init.d.ts.map +1 -1
  2. package/dist/commands/init.js +114 -30
  3. package/dist/commands/init.js.map +1 -1
  4. package/dist/commands/skills.d.ts.map +1 -1
  5. package/dist/commands/skills.js +125 -2
  6. package/dist/commands/skills.js.map +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/menu.js +22 -4
  10. package/dist/menu.js.map +1 -1
  11. package/dist/util/env.d.ts +2 -0
  12. package/dist/util/env.d.ts.map +1 -1
  13. package/dist/util/env.js +2 -0
  14. package/dist/util/env.js.map +1 -1
  15. package/dist/util/marketplace.d.ts +57 -0
  16. package/dist/util/marketplace.d.ts.map +1 -0
  17. package/dist/util/marketplace.js +144 -0
  18. package/dist/util/marketplace.js.map +1 -0
  19. package/dist/util/skills.d.ts +6 -6
  20. package/dist/util/skills.d.ts.map +1 -1
  21. package/dist/util/skills.js +14 -9
  22. package/dist/util/skills.js.map +1 -1
  23. package/package.json +12 -11
  24. package/runtime/BUNDLE.json +1 -1
  25. package/runtime/README.md +59 -2
  26. package/runtime/packages/agenticros/openclaw.plugin.json +1 -1
  27. package/runtime/packages/agenticros/src/config-page.ts +1 -1
  28. package/runtime/packages/agenticros/src/skill-loader.ts +109 -38
  29. package/runtime/packages/agenticros-claude-code/README.md +52 -9
  30. package/runtime/packages/agenticros-claude-code/dist/__tests__/capabilities-mcp.test.d.ts +2 -0
  31. package/runtime/packages/agenticros-claude-code/dist/__tests__/capabilities-mcp.test.d.ts.map +1 -0
  32. package/runtime/packages/agenticros-claude-code/dist/__tests__/capabilities-mcp.test.js +773 -0
  33. package/runtime/packages/agenticros-claude-code/dist/__tests__/capabilities-mcp.test.js.map +1 -0
  34. package/runtime/packages/agenticros-claude-code/dist/__tests__/follow-me-registry.test.d.ts +2 -0
  35. package/runtime/packages/agenticros-claude-code/dist/__tests__/follow-me-registry.test.d.ts.map +1 -0
  36. package/runtime/packages/agenticros-claude-code/dist/__tests__/follow-me-registry.test.js +97 -0
  37. package/runtime/packages/agenticros-claude-code/dist/__tests__/follow-me-registry.test.js.map +1 -0
  38. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.d.ts +2 -2
  39. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.d.ts.map +1 -1
  40. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.js +5 -5
  41. package/runtime/packages/agenticros-claude-code/dist/find-object/find-object.js.map +1 -1
  42. package/runtime/packages/agenticros-claude-code/dist/follow-me/depth-loop.d.ts +109 -0
  43. package/runtime/packages/agenticros-claude-code/dist/follow-me/depth-loop.d.ts.map +1 -0
  44. package/runtime/packages/agenticros-claude-code/dist/follow-me/depth-loop.js +392 -0
  45. package/runtime/packages/agenticros-claude-code/dist/follow-me/depth-loop.js.map +1 -0
  46. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.d.ts +8 -3
  47. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.d.ts.map +1 -1
  48. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.js +30 -12
  49. package/runtime/packages/agenticros-claude-code/dist/follow-me/loop.js.map +1 -1
  50. package/runtime/packages/agenticros-claude-code/dist/index.js +5 -5
  51. package/runtime/packages/agenticros-claude-code/dist/index.js.map +1 -1
  52. package/runtime/packages/agenticros-claude-code/dist/mission-registry.d.ts +16 -0
  53. package/runtime/packages/agenticros-claude-code/dist/mission-registry.d.ts.map +1 -0
  54. package/runtime/packages/agenticros-claude-code/dist/mission-registry.js +19 -0
  55. package/runtime/packages/agenticros-claude-code/dist/mission-registry.js.map +1 -0
  56. package/runtime/packages/agenticros-claude-code/dist/tools.d.ts +9 -0
  57. package/runtime/packages/agenticros-claude-code/dist/tools.d.ts.map +1 -1
  58. package/runtime/packages/agenticros-claude-code/dist/tools.js +700 -54
  59. package/runtime/packages/agenticros-claude-code/dist/tools.js.map +1 -1
  60. package/runtime/packages/agenticros-claude-code/dist/transport.d.ts +28 -8
  61. package/runtime/packages/agenticros-claude-code/dist/transport.d.ts.map +1 -1
  62. package/runtime/packages/agenticros-claude-code/dist/transport.js +37 -34
  63. package/runtime/packages/agenticros-claude-code/dist/transport.js.map +1 -1
  64. package/runtime/packages/agenticros-claude-code/src/__tests__/capabilities-mcp.test.ts +2 -1
  65. package/runtime/packages/core/LICENSE +192 -0
  66. package/runtime/packages/core/README.md +88 -0
  67. package/runtime/packages/core/package.json +36 -3
  68. package/runtime/packages/core/src/__tests__/capabilities.test.ts +49 -44
  69. package/runtime/packages/core/src/capabilities.ts +19 -45
  70. package/runtime/packages/core/src/config.ts +1 -1
  71. package/runtime/scripts/configure_agenticros.sh +0 -0
  72. package/runtime/scripts/configure_for_sim.sh +0 -0
  73. package/runtime/scripts/fix-openclaw-control-ui-path.sh +0 -0
  74. package/runtime/scripts/install_cli.sh +0 -0
  75. package/runtime/scripts/install_rosbridge_from_source.sh +0 -0
  76. package/runtime/scripts/onboard_robot.sh +0 -0
  77. package/runtime/scripts/pack-runtime.mjs +9 -3
  78. package/runtime/scripts/refresh-skill-deps.mjs +0 -0
  79. package/runtime/scripts/run_demo_native.sh +0 -0
  80. package/runtime/scripts/run_nemoclaw_host_stack.sh +0 -0
  81. package/runtime/scripts/run_robot_rosbridge.sh +0 -0
  82. package/runtime/scripts/setup_gateway_plugin.sh +0 -0
  83. package/runtime/scripts/setup_robot.sh +0 -0
  84. package/runtime/scripts/setup_workspace.sh +0 -0
  85. package/runtime/scripts/sim/run_sim.sh +0 -0
  86. package/runtime/scripts/smoke_test_nemoclaw.sh +0 -0
  87. package/runtime/scripts/start_demo.sh +0 -0
  88. package/runtime/scripts/sync-skill-tools.mjs +2 -2
  89. package/runtime/scripts/use-openclaw-2026.2.26.sh +0 -0
  90. package/runtime/scripts/use-openclaw-2026.3.11.sh +0 -0
@@ -20,8 +20,8 @@
20
20
 
21
21
  import { createRequire } from "node:module";
22
22
  import { pathToFileURL } from "node:url";
23
- import { readFileSync, readdirSync, existsSync } from "node:fs";
24
- import { join } from "node:path";
23
+ import { readFileSync, existsSync } from "node:fs";
24
+ import { dirname, join } from "node:path";
25
25
  import type { OpenClawPluginApi } from "./plugin-api.js";
26
26
  import type { AgenticROSConfig } from "@agenticros/core";
27
27
  import type { RegisterSkill, SkillContext } from "./skill-api.js";
@@ -41,12 +41,40 @@ export function getLoadedSkillIds(): string[] {
41
41
  return [...loadedSkillIds];
42
42
  }
43
43
 
44
- function deriveSkillId(packageName: string): string {
45
- const lower = packageName.toLowerCase();
46
- if (lower.startsWith("agenticros-skill-")) {
47
- return lower.slice("agenticros-skill-".length);
44
+ /**
45
+ * Read the `agenticros` block from a skill's package.json. The block is now
46
+ * the single source of truth for the skill id, display name, and capability
47
+ * manifest. The legacy `agenticrosSkill: true | { capabilities }` form is
48
+ * NOT supported — every skill must declare a fully-formed `agenticros` block.
49
+ */
50
+ interface AgenticROSBlock {
51
+ id: string;
52
+ displayName?: string;
53
+ description?: string;
54
+ categories?: string[];
55
+ screenshots?: string[];
56
+ demoVideoUrl?: string;
57
+ capabilities?: unknown[];
58
+ }
59
+
60
+ interface SkillManifest {
61
+ name?: string;
62
+ main?: string;
63
+ agenticros?: AgenticROSBlock;
64
+ }
65
+
66
+ function readSkillManifest(pkgJsonPath: string): SkillManifest | null {
67
+ try {
68
+ return JSON.parse(readFileSync(pkgJsonPath, "utf-8")) as SkillManifest;
69
+ } catch {
70
+ return null;
48
71
  }
49
- return lower.replace(/^@[^/]+\//, "").replace(/[^a-z0-9]/g, "");
72
+ }
73
+
74
+ function isValidBlock(block: unknown): block is AgenticROSBlock {
75
+ if (!block || typeof block !== "object") return false;
76
+ const b = block as { id?: unknown };
77
+ return typeof b.id === "string" && /^[a-z0-9][a-z0-9-]*$/.test(b.id);
50
78
  }
51
79
 
52
80
  /**
@@ -56,7 +84,7 @@ function deriveSkillId(packageName: string): string {
56
84
  * — including any `api.registerTool(...)` the skill makes — completes before
57
85
  * we return, and therefore before the OpenClaw host snapshots `captured.tools`.
58
86
  *
59
- * Returns the derived skill id on success, or `null` (with a logged warning /
87
+ * Returns the declared skill id on success, or `null` (with a logged warning /
60
88
  * error) on any failure mode: missing export, top-level await (which surfaces
61
89
  * as `ERR_REQUIRE_ASYNC_MODULE`), or thrown initialization error.
62
90
  */
@@ -66,8 +94,8 @@ function loadSkillModuleSync(
66
94
  context: SkillContext,
67
95
  entryPath: string,
68
96
  packageName: string,
97
+ skillId: string,
69
98
  ): string | null {
70
- const skillId = deriveSkillId(packageName);
71
99
  let mod: unknown;
72
100
  try {
73
101
  mod = require(entryPath);
@@ -110,44 +138,80 @@ function loadSkillModuleSync(
110
138
  }
111
139
  }
112
140
 
141
+ interface ResolvedSkill {
142
+ entry: string;
143
+ packageName: string;
144
+ skillId: string;
145
+ }
146
+
113
147
  /**
114
- * Resolve a package name to its main entry path (Node resolution).
148
+ * Resolve an npm package name to a `ResolvedSkill` (entry + name + skill id).
149
+ * Reads the package's package.json to extract `pkg.agenticros.id`; refuses
150
+ * to register the skill if the block is missing or malformed.
115
151
  */
116
- function resolvePackageEntry(packageName: string): string | null {
152
+ function resolveSkillByPackage(
153
+ api: OpenClawPluginApi,
154
+ packageName: string,
155
+ ): ResolvedSkill | null {
156
+ let entry: string;
117
157
  try {
118
- const pkgPath = require.resolve(packageName, { paths: [process.cwd()] });
119
- return pkgPath;
158
+ entry = require.resolve(packageName, { paths: [process.cwd()] });
120
159
  } catch {
160
+ api.logger.warn(`Skill package not found: ${packageName}`);
161
+ return null;
162
+ }
163
+ // Walk up from the resolved entry to find the package.json.
164
+ const pkgJsonPath = findPackageJsonForEntry(entry);
165
+ if (!pkgJsonPath) {
166
+ api.logger.warn(`Skill ${packageName}: cannot locate package.json next to ${entry}`);
121
167
  return null;
122
168
  }
169
+ const manifest = readSkillManifest(pkgJsonPath);
170
+ if (!manifest || !isValidBlock(manifest.agenticros)) {
171
+ api.logger.warn(
172
+ `Skill ${packageName}: missing or invalid \`agenticros\` block in package.json. ` +
173
+ "Declare an `agenticros.id` (kebab-case) to register as a skill.",
174
+ );
175
+ return null;
176
+ }
177
+ return {
178
+ entry,
179
+ packageName: manifest.name ?? packageName,
180
+ skillId: manifest.agenticros!.id,
181
+ };
182
+ }
183
+
184
+ /** Walk up from `entry` (a file inside the package) to find its package.json. */
185
+ function findPackageJsonForEntry(entry: string): string | null {
186
+ let dir = dirname(entry);
187
+ for (let i = 0; i < 6; i++) {
188
+ const candidate = join(dir, "package.json");
189
+ if (existsSync(candidate)) return candidate;
190
+ const parent = dirname(dir);
191
+ if (parent === dir) return null;
192
+ dir = parent;
193
+ }
194
+ return null;
123
195
  }
124
196
 
125
197
  /**
126
- * Scan a directory for package.json with "agenticrosSkill": true and return entry path.
198
+ * Scan a directory for a package.json with a valid `agenticros` block and
199
+ * return its entry path + declared id.
127
200
  */
128
- function findSkillInPath(dirPath: string): { entry: string; packageName: string } | null {
201
+ function findSkillInPath(dirPath: string): ResolvedSkill | null {
129
202
  if (!existsSync(dirPath)) return null;
130
203
  const pkgPath = join(dirPath, "package.json");
131
204
  if (!existsSync(pkgPath)) return null;
132
- let pkg: {
133
- agenticrosSkill?: boolean | Record<string, unknown>;
134
- main?: string;
135
- name?: string;
136
- };
137
- try {
138
- pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
139
- } catch {
140
- return null;
141
- }
142
- // Accept either the legacy boolean (`"agenticrosSkill": true`) or the
143
- // Phase-1 object form (`"agenticrosSkill": { capabilities: [...] }`).
144
- // Anything truthy registers the package as a skill; the capability
145
- // schema is read separately by @agenticros/core.
146
- if (!pkg.agenticrosSkill) return null;
205
+ const pkg = readSkillManifest(pkgPath);
206
+ if (!pkg || !isValidBlock(pkg.agenticros)) return null;
147
207
  const main = pkg.main ?? "index.js";
148
208
  const entry = join(dirPath, main);
149
209
  if (!existsSync(entry)) return null;
150
- return { entry, packageName: pkg.name ?? "unknown" };
210
+ return {
211
+ entry,
212
+ packageName: pkg.name ?? "unknown",
213
+ skillId: pkg.agenticros!.id,
214
+ };
151
215
  }
152
216
 
153
217
  /**
@@ -174,19 +238,25 @@ export function loadSkills(
174
238
  loadedSkillIds.length = 0;
175
239
 
176
240
  for (const pkgName of packages) {
177
- const entryPath = resolvePackageEntry(pkgName);
178
- if (!entryPath) {
179
- api.logger.warn(`Skill package not found: ${pkgName}`);
180
- continue;
181
- }
182
- const skillId = loadSkillModuleSync(api, config, context, entryPath, pkgName);
241
+ const resolved = resolveSkillByPackage(api, pkgName);
242
+ if (!resolved) continue;
243
+ const skillId = loadSkillModuleSync(
244
+ api,
245
+ config,
246
+ context,
247
+ resolved.entry,
248
+ resolved.packageName,
249
+ resolved.skillId,
250
+ );
183
251
  if (skillId) loadedSkillIds.push(skillId);
184
252
  }
185
253
 
186
254
  for (const dir of paths) {
187
255
  const resolved = findSkillInPath(dir);
188
256
  if (!resolved) {
189
- api.logger.warn(`No agenticros skill in path: ${dir}`);
257
+ api.logger.warn(
258
+ `No agenticros skill in path: ${dir} (missing \`agenticros\` block in package.json).`,
259
+ );
190
260
  continue;
191
261
  }
192
262
  const skillId = loadSkillModuleSync(
@@ -195,6 +265,7 @@ export function loadSkills(
195
265
  context,
196
266
  resolved.entry,
197
267
  resolved.packageName,
268
+ resolved.skillId,
198
269
  );
199
270
  if (skillId && !loadedSkillIds.includes(skillId)) loadedSkillIds.push(skillId);
200
271
  }
@@ -1,6 +1,6 @@
1
1
  # AgenticROS Claude Code adapter
2
2
 
3
- MCP (Model Context Protocol) server that exposes AgenticROS ROS2 tools to **Claude Code CLI** and to the **Claude desktop app** on macOS (including **Claude Dispatch** on iPhone when paired to Claude on your Mac). Use natural language to control and query your ROS2 robot (e.g. "what do you see?", "move 1m forward").
3
+ MCP (Model Context Protocol) server that exposes AgenticROS ROS2 tools to **Claude Code CLI** and to the **Claude desktop app** on macOS (including **Claude Dispatch** on iPhone when paired to Claude on your Mac). Use natural language to control and query your ROS2 robot (e.g. *"what do you see?"*, *"find a chair and drive toward it"*, *"list every robot that can follow a person"*).
4
4
 
5
5
  This adapter does **not** provide the config or teleop web UI; use the [OpenClaw plugin](../../packages/agenticros) for that, or run the gateway for the browser-based teleop page.
6
6
 
@@ -94,6 +94,38 @@ claude
94
94
 
95
95
  Scope options: `--scope user` (default), `--scope project` (shared via `.mcp.json`).
96
96
 
97
+ ## Codex CLI (OpenAI)
98
+
99
+ The same MCP server works unmodified with the **OpenAI Codex CLI** — Codex is a vanilla MCP client, and our server speaks the standard `2024-11-05` MCP protocol that every MCP-compatible client uses. Two ways to register it:
100
+
101
+ **Option A — `codex mcp add` (recommended)**
102
+
103
+ ```bash
104
+ cd /path/to/agenticros
105
+ codex mcp add agenticros \
106
+ -- node "$(pwd)/packages/agenticros-claude-code/dist/index.js"
107
+ ```
108
+
109
+ **Option B — direct edit of `~/.codex/config.toml`**
110
+
111
+ ```toml
112
+ [mcp_servers.agenticros]
113
+ command = "node"
114
+ args = ["/ABSOLUTE/PATH/TO/agenticros/packages/agenticros-claude-code/dist/index.js"]
115
+ enabled = true
116
+ startup_timeout_sec = 30
117
+
118
+ [mcp_servers.agenticros.env]
119
+ AGENTICROS_ROBOT_NAMESPACE = "robotYOUR_NAMESPACE_NO_DASHES"
120
+ ```
121
+
122
+ Then start a Codex session and run `/mcp` to verify — you should see `agenticros` connected with **15 tools** (more if you have skills like `agenticros-skill-followme` or `agenticros-skill-find` registered). Same tools, same tool names, same JSON shapes as in Claude Code; everything in the **Tools** section below applies identically.
123
+
124
+ Two Codex-specific notes:
125
+
126
+ - **Absolute path required.** Codex's working directory at server-spawn time is not the AgenticROS repo root, so `args = ["packages/agenticros-claude-code/dist/index.js"]` will fail. Use the full absolute path.
127
+ - **Project-scoped config.** Codex also supports a `.codex/config.toml` in a project directory (and a `mcp.json` in cwd on recent builds) — handy for per-repo MCP setups when you don't want `agenticros` enabled globally. See OpenAI's [Codex config reference](https://developers.openai.com/codex/config-reference) for the precedence rules.
128
+
97
129
  ## Claude desktop app + Claude Dispatch (iOS)
98
130
 
99
131
  The Claude **desktop** app uses a different MCP config file than Claude Code:
@@ -134,18 +166,29 @@ The server exposes the same ROS2 tools as the OpenClaw plugin:
134
166
 
135
167
  | Tool | Description |
136
168
  |------|-------------|
169
+ | **Inspection** | |
137
170
  | `ros2_list_topics` | List topics and types |
138
- | `ros2_publish` | Publish to a topic (e.g. cmd_vel) |
171
+ | `ros2_list_capabilities` | Typed verb manifest (built-in + skill-declared) — the planning surface for agents |
172
+ | **Fleet** | |
173
+ | `ros2_list_robots` | List configured robots — id, name, kind, capabilities, online status |
174
+ | `ros2_discover_robots` | Online discovery: detect `/<ns>/cmd_vel` namespaces + classify reachability |
175
+ | `ros2_find_robots_for` | Ranked filter by capability + kind + online — *"who can `follow_person` right now?"* |
176
+ | **Missions** | |
177
+ | `run_mission` | Execute a multi-step mission **or** compile a natural-language `goal` into one. Chains capabilities via `{{stepId.outputs.field}}` template refs; returns a `mission_id` |
178
+ | `mission_cancel` | Cancel an in-flight mission by `mission_id` at the next step boundary (idempotent, safe on unknown ids) |
179
+ | **Direct ROS** | |
180
+ | `ros2_publish` | Publish to a topic (e.g. cmd_vel) — safety-clamped |
139
181
  | `ros2_subscribe_once` | Get next message from a topic |
140
182
  | `ros2_service_call` | Call a ROS2 service |
141
183
  | `ros2_action_goal` | Send goal to an action server |
142
184
  | `ros2_param_get` / `ros2_param_set` | Get/set node parameters |
143
- | `ros2_camera_snapshot` | "What do you see" — one frame from camera topic |
185
+ | `ros2_camera_snapshot` | *"What do you see"* — one frame from camera topic |
144
186
  | `ros2_depth_distance` | Distance in meters from depth camera |
145
- | `memory_remember` *(when enabled)* | Store a durable fact for the robot — shared with OpenClaw, Claude Desktop, Gemini |
146
- | `memory_recall` *(when enabled)* | Semantic search across long-term memory |
147
- | `memory_forget` *(when enabled)* | Delete by id, query, or whole namespace |
148
- | `memory_status` *(when enabled)* | Health check + record count + embedder info |
187
+ | **Memory** *(only when enabled)* | |
188
+ | `memory_remember` | Store a durable fact for the robot — shared with OpenClaw, Claude Desktop, Gemini |
189
+ | `memory_recall` | Semantic search across long-term memory (also reads `mission:<id>` step transcripts) |
190
+ | `memory_forget` | Delete by id, query, or whole namespace |
191
+ | `memory_status` | Health check + record count + embedder info |
149
192
 
150
193
  Safety limits (max linear/angular velocity) from config are applied before `ros2_publish`.
151
194
 
@@ -255,6 +298,6 @@ If `ros2_publish` runs but the robot doesn’t move:
255
298
 
256
299
  In the chat you can type `/mcp` to see MCP server status. If the transport isn’t running (e.g. no Zenoh router), tool calls will fail with connection errors until the ROS2 side is up.
257
300
 
258
- ## "Follow me" and skills
301
+ ## Skills
259
302
 
260
- The OpenClaw plugin can load skills (e.g. **follow me** via `agenticros-skill-followme`). This MCP adapter does not load skills in v1; only the core tools above are available. For "follow me", use the OpenClaw plugin or a future version of this adapter that adds skill loading or a dedicated `follow_robot` tool.
303
+ Skill packages (e.g. `agenticros-skill-followme`, `agenticros-skill-find`) extend the capability registry with new verbs like `follow_person` and `find_object`. The MCP server reads each skill's `package.json` `agenticros` block (`id`, `capabilities[]`) at startup and surfaces those verbs through `ros2_list_capabilities` and `run_mission` — no per-skill MCP tool needed. To register a skill for this adapter, install it with `agenticros skills install <slug>` from the marketplace, or add its path under `skillPaths[]` in `~/.agenticros/config.json` (or use the `agenticros skills add <path>` CLI). See [docs/skills.md](../../docs/skills.md) for the full skill contract.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=capabilities-mcp.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities-mcp.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/capabilities-mcp.test.ts"],"names":[],"mappings":""}