@gemslibe/rbo 0.2.0 → 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 CHANGED
@@ -10,6 +10,17 @@ npm install -g @gemslibe/rbo
10
10
 
11
11
  Requires Node.js ≥ 22.14. See the monorepo [`docs/ops/getting-started.md`](../../docs/ops/getting-started.md) for Controller/Agent setup and MCP client wiring.
12
12
 
13
+ Global reinstall/uninstall runs `scripts/stop-running-rbo.mjs` (`preinstall` /
14
+ `preuninstall`) to stop live Controller/Agent processes so native deps can be replaced on
15
+ Windows. Only global installs trigger the stop (`npm_config_global=true`). Set
16
+ `RBO_SKIP_INSTALL_STOP=1` to disable.
17
+
18
+ `rbo controller init` / `rbo agent init` write live operator configs
19
+ (`~/.rbo/controller.json` and `~/.rbo/agent/agent.json`). Daemons load those files on start;
20
+ environment variables override the file when set. The package also ships matching templates at
21
+ `config/controller.json` and `config/agent.json`. Use `--force` on init to rewrite a config back
22
+ to defaults.
23
+
13
24
  ## Binaries
14
25
 
15
26
  | Command | Purpose |
@@ -0,0 +1,34 @@
1
+ {
2
+ "schema_version": 1,
3
+ "initialized_at": "1970-01-01T00:00:00.000Z",
4
+ "controller_url": "",
5
+ "controller_fingerprint": "",
6
+ "display_name": "rbo-agent",
7
+ "max_jobs": 1,
8
+ "repo_cache_dir": null,
9
+ "secret_map": null,
10
+ "git_allowlist": {
11
+ "schemes": ["https", "ssh"],
12
+ "hosts": ["github.com"]
13
+ },
14
+ "repo_cache": {
15
+ "max_size_gb": 100,
16
+ "min_free_disk_gb": 30,
17
+ "retention_days": 30
18
+ },
19
+ "build_cache": {
20
+ "enabled_kinds": ["ccache", "sccache", "npm", "pnpm", "pip"],
21
+ "max_size_gb": 20,
22
+ "min_free_disk_gb": 5,
23
+ "retention_days": 14,
24
+ "allow_read_risk_levels": ["safe", "normal"],
25
+ "allow_write_risk_levels": ["safe", "normal"]
26
+ },
27
+ "log_spool_max_bytes": 536870912,
28
+ "log_send_queue_max": 64,
29
+ "disconnect_grace_seconds": 60,
30
+ "orphan_timeout_seconds": 300,
31
+ "reconcile_deadline_seconds": 120,
32
+ "disk_min_free_bytes": null,
33
+ "configured_priority": null
34
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "mcp_host": "127.0.0.1",
3
+ "mcp_port": 7410,
4
+ "agent_plane_port": 7411,
5
+ "controller_public_host": "127.0.0.1",
6
+ "data_plane_base_url": null,
7
+ "allowed_project_roots": [],
8
+ "allowed_artifact_destinations": [],
9
+ "git_allowlist": {
10
+ "schemes": ["https", "ssh"],
11
+ "hosts": ["github.com"]
12
+ },
13
+ "local_max_concurrent_jobs": 1,
14
+ "disconnect_grace_seconds": 60,
15
+ "orphan_timeout_seconds": 300,
16
+ "reconcile_deadline_seconds": 120,
17
+ "allow_local_fallback": true,
18
+ "max_git_bundle_bytes": 536870912,
19
+ "local_fallback_max_host_cpu_percent": 80
20
+ }
@@ -7,7 +7,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
 
9
9
  // ../../packages/shared/dist/versions.js
10
- var RBO_STDIO_ADAPTER_VERSION = "0.2.0";
10
+ var RBO_STDIO_ADAPTER_VERSION = "0.4.0";
11
11
 
12
12
  // ../../packages/shared/dist/errors.js
13
13
  import { z } from "zod";
@@ -686,6 +686,21 @@ var ARTIFACT_MATERIALIZE_INPUT = {
686
686
  var AGENT_PROBE_INPUT = {
687
687
  agent_id: z4.string().min(1)
688
688
  };
689
+ var JOB_RUN_INPUT = {
690
+ command: z4.string().min(1).optional(),
691
+ project_root: z4.string().min(1).optional(),
692
+ job_id: z4.string().min(1).optional(),
693
+ cwd: z4.string().default("."),
694
+ timeout_seconds: z4.number().positive().max(3600).default(3600),
695
+ wait_seconds: z4.number().int().min(0).max(3600).optional(),
696
+ /** Max seconds this MCP response blocks; default keeps typical 60s clients safe. */
697
+ mcp_wait_slice_seconds: z4.number().int().min(1).max(55).default(50),
698
+ artifacts: JobRequestSchema.shape.artifacts,
699
+ risk_level: JobRequestSchema.shape.risk_level,
700
+ client_request_id: z4.string().min(1).optional(),
701
+ name: z4.string().optional(),
702
+ include_log_tail_lines: z4.number().int().min(0).max(1e3).default(80)
703
+ };
689
704
  var MCP_TOOL_DEFS = [
690
705
  {
691
706
  name: "agents_list",
@@ -697,6 +712,11 @@ var MCP_TOOL_DEFS = [
697
712
  description: "Submit a build/test job and capture an immutable workspace snapshot.",
698
713
  inputShape: JOB_SUBMIT_INPUT
699
714
  },
715
+ {
716
+ name: "job_run",
717
+ description: "Run a command remotely (snapshot + execute). Waits up to mcp_wait_slice_seconds (default 50). If still running, returns resume:true \u2014 call again with the same job_id. Preferred for interactive AI clients.",
718
+ inputShape: JOB_RUN_INPUT
719
+ },
700
720
  {
701
721
  name: "job_confirm",
702
722
  description: "Confirm a destructive or hardware-risk job after snapshot capture.",
@@ -761,6 +781,32 @@ var CompatibilityMatrixSchema = z5.object({
761
781
  });
762
782
 
763
783
  // ../mcp-stdio/src/proxy.ts
784
+ var STDIO_JOB_RUN_HEARTBEAT_MS = 5e3;
785
+ function startProgressHeartbeat(input) {
786
+ const { progressToken, sendNotification } = input;
787
+ if (progressToken === void 0) {
788
+ return { stop: () => void 0 };
789
+ }
790
+ let progress = 0;
791
+ const prefix = input.messagePrefix ?? "job_run";
792
+ const timer = setInterval(() => {
793
+ progress += 1;
794
+ void sendNotification({
795
+ method: "notifications/progress",
796
+ params: {
797
+ progressToken,
798
+ progress,
799
+ message: `${prefix} waiting (heartbeat ${progress})`
800
+ }
801
+ }).catch(() => void 0);
802
+ }, input.intervalMs ?? STDIO_JOB_RUN_HEARTBEAT_MS);
803
+ timer.unref?.();
804
+ return {
805
+ stop: () => {
806
+ clearInterval(timer);
807
+ }
808
+ };
809
+ }
764
810
  function createStdioProxyServer(options) {
765
811
  const server = new McpServer({
766
812
  name: "rbo-mcp-stdio",
@@ -771,21 +817,30 @@ function createStdioProxyServer(options) {
771
817
  server.registerTool(
772
818
  def.name,
773
819
  { description: def.description, inputSchema: def.inputShape },
774
- async (args) => {
775
- const response = await fetch(`${baseUrl}/internal/v1/tools/${def.name}`, {
776
- method: "POST",
777
- headers: {
778
- "content-type": "application/json",
779
- "x-rbo-client-id": options.clientId,
780
- "x-rbo-client-transport": "stdio"
781
- },
782
- body: JSON.stringify(args ?? {})
783
- });
784
- const text = await response.text();
785
- return {
786
- content: [{ type: "text", text }],
787
- isError: !response.ok
788
- };
820
+ async (args, extra) => {
821
+ const heartbeat = def.name === "job_run" ? startProgressHeartbeat({
822
+ progressToken: extra._meta?.progressToken,
823
+ sendNotification: (notification) => extra.sendNotification(notification)
824
+ }) : { stop: () => void 0 };
825
+ try {
826
+ const response = await fetch(`${baseUrl}/internal/v1/tools/${def.name}`, {
827
+ method: "POST",
828
+ headers: {
829
+ "content-type": "application/json",
830
+ "x-rbo-client-id": options.clientId,
831
+ "x-rbo-client-transport": "stdio"
832
+ },
833
+ body: JSON.stringify(args ?? {}),
834
+ signal: extra.signal
835
+ });
836
+ const text = await response.text();
837
+ return {
838
+ content: [{ type: "text", text }],
839
+ isError: !response.ok
840
+ };
841
+ } finally {
842
+ heartbeat.stop();
843
+ }
789
844
  }
790
845
  );
791
846
  }