@clawops/cli 1.7.7 → 1.7.8

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
@@ -12,6 +12,33 @@ and Cursor drive them through typed MCP tools with explicit safety controls.
12
12
 
13
13
  ---
14
14
 
15
+ ## What's new in v1.7.8
16
+
17
+ **`clawops mcp wire` never wired anything.** It wrote `gateway.mcpClients` — a key OpenClaw
18
+ does not have, checked against the config schemas of `2026.4.5`, `2026.7.1-2` and `2026.9.2`.
19
+ The real key is top-level `mcp.servers`. Nothing on this line validated the write, so clawops
20
+ stored a key nothing read, restarted your gateway, and reported success. The entry also used
21
+ stdio with `command: "clawops"`, which spawns inside the gateway container where clawops is
22
+ not installed — so even the right key would not have worked.
23
+
24
+ It delegates to `openclaw mcp add` now, which probes the server before saving: "wired" means
25
+ the gateway connected. You run the server yourself, since clawops is not on the gateway host:
26
+
27
+ ```bash
28
+ clawops mcp serve --http 18790 --bind 0.0.0.0 --token "$(openssl rand -hex 16)"
29
+ clawops mcp wire --stack prod --token <same token>
30
+ ```
31
+
32
+ **`clawops mcp serve --http` now requires a token off loopback — this may stop your server.**
33
+ It had no authentication at all while exposing every tool, `clawops_destroy` included. It now
34
+ requires a bearer token and refuses to start when bound off-loopback without one. If you run
35
+ it with `--bind 0.0.0.0`, pass `--token` (or set `CLAWOPS_MCP_TOKEN`) before upgrading.
36
+
37
+ **And it only ever served one client.** One transport for the whole process meant the first
38
+ client to connect claimed it; every later one got `"Server already initialized"`.
39
+
40
+ ---
41
+
15
42
  ## What's new in v1.7.3
16
43
 
17
44
  **README maintenance.** The release-notes section had grown to four versions and was missing
@@ -2,14 +2,12 @@
2
2
  import {
3
3
  applyPlan
4
4
  } from "./chunk-35WVQY3X.js";
5
- import "./chunk-6ZFIFDBJ.js";
6
- import "./chunk-BOPSG2LI.js";
7
5
  import {
8
6
  extractBaseOutputs
9
7
  } from "./chunk-3QJBNAHW.js";
10
8
  import {
11
9
  generatePlan
12
- } from "./chunk-4AJ4F2PJ.js";
10
+ } from "./chunk-RAAONHW3.js";
13
11
  import {
14
12
  validatePlan
15
13
  } from "./chunk-6X45DDGH.js";
@@ -37,11 +35,6 @@ import {
37
35
  import {
38
36
  buildContext
39
37
  } from "./chunk-P5LKWRPL.js";
40
- import "./chunk-EI7CQQ7D.js";
41
- import "./chunk-PVUVI35Y.js";
42
- import "./chunk-3XK66DKK.js";
43
- import "./chunk-Q7NQY5HV.js";
44
- import "./chunk-A2I76FTA.js";
45
38
  import {
46
39
  getConfig,
47
40
  getConfigDir
@@ -53,7 +46,7 @@ import {
53
46
 
54
47
  // src/mcp/server.ts
55
48
  import { createServer } from "http";
56
- import { randomUUID as randomUUID5 } from "crypto";
49
+ import { randomUUID as randomUUID5, timingSafeEqual } from "crypto";
57
50
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
58
51
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
59
52
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -1308,27 +1301,58 @@ If none of the above resolves the issue:
1308
1301
  }
1309
1302
 
1310
1303
  // src/mcp/server.ts
1304
+ var MCP_HTTP_PORT = 18790;
1305
+ var LOOPBACK = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
1306
+ function isLoopbackBind(bind) {
1307
+ return LOOPBACK.has(bind);
1308
+ }
1309
+ function authRequirement(bind, token) {
1310
+ if (isLoopbackBind(bind) || token) return { ok: true };
1311
+ return {
1312
+ ok: false,
1313
+ error: `Refusing to serve MCP on ${bind} without a token. This server exposes every clawops tool, including destructive ones, and has no other authentication. Pass --token <value> (or set CLAWOPS_MCP_TOKEN), or bind to 127.0.0.1 and reach it through an SSH tunnel.`
1314
+ };
1315
+ }
1316
+ function tokenMatches(expected, presented) {
1317
+ const a = Buffer.from(expected);
1318
+ const b = Buffer.from(presented);
1319
+ if (a.length !== b.length) return false;
1320
+ return timingSafeEqual(a, b);
1321
+ }
1322
+ function bearerFrom(header) {
1323
+ if (!header) return void 0;
1324
+ const match = /^Bearer[ ]+(.+)$/i.exec(header.trim());
1325
+ return match?.[1]?.trim();
1326
+ }
1311
1327
  async function serveMcp(opts) {
1312
- const { version } = await import("./package-DHSANJT6.js");
1313
- const server = new McpServer({ name: "clawops", version });
1314
- registerTools(server, opts);
1315
- registerResources(server);
1316
- registerPrompts(server);
1328
+ const { version } = await import("./package-QYZFN3FP.js");
1329
+ const makeServer = () => {
1330
+ const server = new McpServer({ name: "clawops", version });
1331
+ registerTools(server, opts);
1332
+ registerResources(server);
1333
+ registerPrompts(server);
1334
+ return server;
1335
+ };
1317
1336
  if (opts.port) {
1318
1337
  const bind = opts.bind ?? "127.0.0.1";
1319
- const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID5() });
1338
+ const token = opts.token ?? process.env["CLAWOPS_MCP_TOKEN"];
1339
+ const allowed = authRequirement(bind, token);
1340
+ if (!allowed.ok) throw new Error(allowed.error);
1341
+ const sessions = /* @__PURE__ */ new Map();
1320
1342
  const httpServer = createServer((req, res) => {
1321
- void transport.handleRequest(req, res);
1343
+ void handleHttp(req, res, sessions, makeServer, token);
1322
1344
  });
1323
1345
  await new Promise((resolve, reject) => {
1324
1346
  httpServer.once("error", reject);
1325
1347
  httpServer.listen(opts.port, bind, resolve);
1326
1348
  });
1327
- process.stderr.write(`[clawops] MCP HTTP server listening on ${bind}:${opts.port}
1328
- `);
1329
- await server.connect(transport);
1349
+ process.stderr.write(
1350
+ `[clawops] MCP HTTP server listening on ${bind}:${opts.port}${token ? " (bearer token required)" : " (loopback, no token)"}
1351
+ `
1352
+ );
1330
1353
  await new Promise((resolve) => httpServer.once("close", resolve));
1331
1354
  } else {
1355
+ const server = makeServer();
1332
1356
  const transport = new StdioServerTransport();
1333
1357
  await server.connect(transport);
1334
1358
  await new Promise((resolve) => {
@@ -1336,6 +1360,55 @@ async function serveMcp(opts) {
1336
1360
  });
1337
1361
  }
1338
1362
  }
1363
+ async function handleHttp(req, res, sessions, makeServer, token) {
1364
+ if (token) {
1365
+ const presented = bearerFrom(req.headers["authorization"]);
1366
+ if (!presented || !tokenMatches(token, presented)) {
1367
+ res.writeHead(401, { "content-type": "application/json", "www-authenticate": "Bearer" });
1368
+ res.end(
1369
+ JSON.stringify({
1370
+ jsonrpc: "2.0",
1371
+ error: { code: -32001, message: "Unauthorized" },
1372
+ id: null
1373
+ })
1374
+ );
1375
+ return;
1376
+ }
1377
+ }
1378
+ const sessionId = req.headers["mcp-session-id"];
1379
+ const existing = typeof sessionId === "string" ? sessions.get(sessionId) : void 0;
1380
+ if (existing) {
1381
+ await existing.handleRequest(req, res);
1382
+ return;
1383
+ }
1384
+ if (typeof sessionId === "string") {
1385
+ res.writeHead(404, { "content-type": "application/json" });
1386
+ res.end(
1387
+ JSON.stringify({
1388
+ jsonrpc: "2.0",
1389
+ error: { code: -32001, message: "Unknown session" },
1390
+ id: null
1391
+ })
1392
+ );
1393
+ return;
1394
+ }
1395
+ const transport = new StreamableHTTPServerTransport({
1396
+ sessionIdGenerator: () => randomUUID5(),
1397
+ onsessioninitialized: (id) => {
1398
+ sessions.set(id, transport);
1399
+ }
1400
+ });
1401
+ transport.onclose = () => {
1402
+ if (transport.sessionId) sessions.delete(transport.sessionId);
1403
+ };
1404
+ await makeServer().connect(transport);
1405
+ await transport.handleRequest(req, res);
1406
+ }
1407
+
1339
1408
  export {
1409
+ MCP_HTTP_PORT,
1410
+ isLoopbackBind,
1411
+ authRequirement,
1412
+ bearerFrom,
1340
1413
  serveMcp
1341
1414
  };
@@ -45,7 +45,7 @@ async function generatePlan(intent, _opts) {
45
45
  "plan/apply is not supported for the local provider. Use `clawops up` directly."
46
46
  );
47
47
  }
48
- const { version } = await import("./package-DHSANJT6.js");
48
+ const { version } = await import("./package-QYZFN3FP.js");
49
49
  const config = getConfig();
50
50
  const instanceType = intent.instanceType ?? "small";
51
51
  const { guardOpenclawVersion, defaultOpenclawVersion } = await import("./version-guard-VUCNZXQS.js");
package/dist/cli.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- monitor_default,
4
- validateOpenclawConfig
5
- } from "./chunk-FNS56UQY.js";
6
2
  import {
7
3
  MCP_APPS,
8
4
  buildMcpEntry,
9
5
  writeAppConfigs
10
6
  } from "./chunk-OIGTOLB3.js";
7
+ import {
8
+ monitor_default,
9
+ validateOpenclawConfig
10
+ } from "./chunk-FNS56UQY.js";
11
11
  import "./chunk-DQDAWNGB.js";
12
12
  import {
13
13
  IMAGE_INSPECT_CMD,
@@ -501,7 +501,7 @@ var plan_default = defineCommand5({
501
501
  },
502
502
  async run({ args }) {
503
503
  const { buildContext } = await import("./context-5LCUG2QR.js");
504
- const { generatePlan } = await import("./generate-PAGV574G.js");
504
+ const { generatePlan } = await import("./generate-ELD7QF5Z.js");
505
505
  const ctx = buildContext(args);
506
506
  if (ctx.adapter.name === "local") {
507
507
  throw new UsageError(
@@ -1895,17 +1895,19 @@ var serveCmd = defineCommand17({
1895
1895
  },
1896
1896
  args: {
1897
1897
  http: { type: "string", description: "HTTP port for standalone mode" },
1898
- bind: { type: "string", description: "Bind address for HTTP mode" },
1898
+ bind: { type: "string", description: "Bind address for HTTP mode (default 127.0.0.1)" },
1899
+ token: { type: "string", description: "Bearer token required on every HTTP request. Required unless bound to loopback; also read from CLAWOPS_MCP_TOKEN" },
1899
1900
  "read-only": { type: "boolean", description: "Only register read toolset" },
1900
1901
  "no-destructive": { type: "boolean", description: "Filter out destructive tools" },
1901
1902
  toolsets: { type: "string", description: "Comma-separated toolsets to enable" },
1902
1903
  inspector: { type: "boolean", description: "Enable MCP inspector" }
1903
1904
  },
1904
1905
  async run({ args }) {
1905
- const { serveMcp } = await import("./server-U7C2RQWL.js");
1906
+ const { serveMcp } = await import("./server-FWTOSSZP.js");
1906
1907
  await serveMcp({
1907
1908
  port: args.http ? Number(args.http) : void 0,
1908
1909
  bind: args.bind,
1910
+ token: args.token,
1909
1911
  readOnly: Boolean(args["read-only"]),
1910
1912
  noDestructive: Boolean(args["no-destructive"]),
1911
1913
  toolsets: args.toolsets ? args.toolsets.split(",").map((s) => s.trim()) : void 0,
@@ -1974,13 +1976,16 @@ var wireCmd = defineCommand17({
1974
1976
  },
1975
1977
  args: {
1976
1978
  stack: { type: "string", description: "Target stack name" },
1977
- force: { type: "boolean", description: "Apply even if gateway version is below minimum" }
1979
+ url: { type: "string", description: "Where the gateway should reach clawops (default http://host.docker.internal:18790/)" },
1980
+ token: { type: "string", description: "Bearer token the clawops MCP server requires" },
1981
+ rewire: { type: "boolean", description: "Replace an existing clawops entry" }
1978
1982
  },
1979
1983
  async run({ args }) {
1980
1984
  const { buildContext } = await import("./context-5LCUG2QR.js");
1981
1985
  const { acquireSession, drainPool } = await import("./pool-QGFQRMGU.js");
1982
1986
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
1983
- const { wireGatewayMcp } = await import("./mcp-wire-FR3W2CB6.js");
1987
+ const { wireGatewayMcp } = await import("./mcp-wire-ZMIVFAUC.js");
1988
+ const { MCP_HTTP_PORT } = await import("./server-FWTOSSZP.js");
1984
1989
  const ac = new AbortController();
1985
1990
  process19.on("SIGINT", () => {
1986
1991
  ac.abort();
@@ -2021,21 +2026,37 @@ var wireCmd = defineCommand17({
2021
2026
  const spin = spinner(`Connecting to ${conn.host}...`);
2022
2027
  const { session, release } = await acquireSession({ ...conn, signal: ac.signal });
2023
2028
  try {
2024
- spin.text = "Reading gateway config...";
2025
- const result = await wireGatewayMcp(session, ac.signal, { force: Boolean(args.force) });
2026
- if (result.status === "version-blocked") {
2027
- spin.fail("Gateway version is too old for MCP client support.");
2028
- warn(`Gateway version ${result.version} requires at least 2026.4 for MCP client support.`);
2029
- info("Upgrade OpenClaw and re-run, or bypass with: clawops mcp wire --force");
2029
+ spin.text = "Asking the gateway to connect to clawops...";
2030
+ const result = await wireGatewayMcp(session, ac.signal, {
2031
+ url: typeof args.url === "string" ? args.url : void 0,
2032
+ token: typeof args.token === "string" ? args.token : void 0,
2033
+ rewire: Boolean(args.rewire)
2034
+ });
2035
+ if (result.status === "unsupported") {
2036
+ spin.fail("This gateway has no `openclaw mcp add`, so clawops cannot wire it.");
2037
+ info("OpenClaw 2026.4.5 ships `openclaw mcp` with only `list` and `serve`.");
2038
+ info("Upgrade the gateway to 2026.7.1-2 or later, then re-run this command.");
2030
2039
  process19.exit(1);
2031
2040
  }
2032
- if (result.rewired) {
2033
- spin.succeed("Re-wiring clawops MCP client \u2014 previous entry replaced.");
2034
- } else {
2035
- spin.succeed("Gateway MCP client wired.");
2041
+ if (result.status === "exists") {
2042
+ spin.info("The gateway already has a clawops MCP server configured.");
2043
+ info(`Pointing at ${result.url}. Replace it with: clawops mcp wire --rewire`);
2044
+ return;
2036
2045
  }
2046
+ if (result.status === "probe-failed") {
2047
+ spin.fail("The gateway could not connect to clawops. Nothing was changed.");
2048
+ failure(result.error);
2049
+ warn("clawops does not run on the gateway host. Start it where the gateway can reach it:");
2050
+ info(` clawops mcp serve --http ${MCP_HTTP_PORT} --bind 0.0.0.0 --token <token>`);
2051
+ info(`Then re-run: clawops mcp wire --url ${result.url} --token <token>`);
2052
+ process19.exit(1);
2053
+ }
2054
+ spin.succeed(
2055
+ result.rewired ? "Re-wired the gateway to clawops \u2014 previous entry replaced." : "Gateway wired to clawops, and the connection was verified."
2056
+ );
2037
2057
  success("The gateway's AI can now run clawops commands.");
2038
2058
  info('Try asking it: "check if my stack is healthy"');
2059
+ warn("The MCP server exposes destructive tools. Keep it bound where only the gateway can reach it.");
2039
2060
  } catch (err) {
2040
2061
  spin.fail("Failed to wire gateway MCP client.");
2041
2062
  failure(err instanceof Error ? err.message : String(err));
@@ -3004,7 +3025,7 @@ async function maybeWireGatewayMcp(opts) {
3004
3025
  if (!wireGateway) return;
3005
3026
  const { connect } = await import("./ssh-CNJNLM3E.js");
3006
3027
  const { getConfigDir: getConfigDir2 } = await import("./store-SDUR52Z5.js");
3007
- const { wireGatewayMcp } = await import("./mcp-wire-FR3W2CB6.js");
3028
+ const { wireGatewayMcp } = await import("./mcp-wire-ZMIVFAUC.js");
3008
3029
  const knownHostsPath = path3.join(getConfigDir2(), "known_hosts");
3009
3030
  const spin = spinner("Wiring gateway MCP client...");
3010
3031
  const session = await connect({
@@ -3017,9 +3038,16 @@ async function maybeWireGatewayMcp(opts) {
3017
3038
  });
3018
3039
  try {
3019
3040
  const result = await wireGatewayMcp(session, opts.signal ?? AbortSignal.timeout(3e4));
3020
- if (result.status === "version-blocked") {
3021
- spin.warn(`Gateway version ${result.version} is too old for MCP client support (requires \u2265 2026.4).`);
3022
- info("Upgrade OpenClaw and run: clawops mcp wire --stack " + opts.stackName);
3041
+ if (result.status === "unsupported") {
3042
+ spin.warn("This gateway has no `openclaw mcp add`, so nothing was wired.");
3043
+ info("OpenClaw 2026.4.5 ships `openclaw mcp` with only `list` and `serve`.");
3044
+ info("Upgrade to 2026.7.1-2 and run: clawops mcp wire --stack " + opts.stackName);
3045
+ } else if (result.status === "probe-failed") {
3046
+ spin.warn("The gateway could not reach clawops, so nothing was wired.");
3047
+ info("clawops does not run on the gateway host. Start it where the gateway can reach it,");
3048
+ info("then run: clawops mcp wire --stack " + opts.stackName + " --url <url> --token <token>");
3049
+ } else if (result.status === "exists") {
3050
+ spin.info("The gateway is already wired to clawops.");
3023
3051
  } else {
3024
3052
  spin.succeed(result.rewired ? "Gateway MCP client re-wired." : "Gateway MCP client wired.");
3025
3053
  success("The gateway's AI can now run clawops commands.");
@@ -3522,7 +3550,7 @@ import { accessSync as accessSync2, constants as constants2 } from "fs";
3522
3550
  import { execSync as execSync2 } from "child_process";
3523
3551
  var ISSUES_URL = `${REPO_URL}/issues/new`;
3524
3552
  var BODY_CAP = 2e3;
3525
- var VERSION = true ? "1.7.7" : "dev";
3553
+ var VERSION = true ? "1.7.8" : "dev";
3526
3554
  async function collectContext() {
3527
3555
  const nodeVersion = process22.version;
3528
3556
  const os3 = `${process22.platform} ${process22.arch}`;
@@ -3762,7 +3790,7 @@ function handleError(err) {
3762
3790
  }
3763
3791
 
3764
3792
  // src/cli/index.ts
3765
- var CLI_VERSION = true ? "1.7.7" : "dev";
3793
+ var CLI_VERSION = true ? "1.7.8" : "dev";
3766
3794
  var main = defineCommand23({
3767
3795
  meta: {
3768
3796
  name: "clawops",
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  generatePlan,
4
4
  planId
5
- } from "./chunk-4AJ4F2PJ.js";
5
+ } from "./chunk-RAAONHW3.js";
6
6
  import "./chunk-6X45DDGH.js";
7
7
  import "./chunk-P5LKWRPL.js";
8
8
  import "./chunk-PVUVI35Y.js";
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ MCP_HTTP_PORT
4
+ } from "./chunk-QQNKBTYZ.js";
5
+ import "./chunk-35WVQY3X.js";
6
+ import "./chunk-6ZFIFDBJ.js";
7
+ import "./chunk-BOPSG2LI.js";
8
+ import "./chunk-3QJBNAHW.js";
9
+ import "./chunk-RAAONHW3.js";
10
+ import "./chunk-6X45DDGH.js";
11
+ import "./chunk-FNS56UQY.js";
12
+ import "./chunk-DQDAWNGB.js";
13
+ import "./chunk-SEZMVVAS.js";
14
+ import "./chunk-P5LKWRPL.js";
15
+ import "./chunk-EI7CQQ7D.js";
16
+ import "./chunk-PVUVI35Y.js";
17
+ import "./chunk-3XK66DKK.js";
18
+ import "./chunk-Q7NQY5HV.js";
19
+ import "./chunk-A2I76FTA.js";
20
+ import "./chunk-CX5SL5HP.js";
21
+ import "./chunk-KGXPLI7W.js";
22
+
23
+ // src/cli/mcp-wire.ts
24
+ var GATEWAY_MCP_NAME = "clawops";
25
+ function defaultGatewayMcpUrl(port = MCP_HTTP_PORT) {
26
+ return `http://host.docker.internal:${port}/`;
27
+ }
28
+ function shellQuote(value) {
29
+ return `'${value.replace(/'/g, `'\\''`)}'`;
30
+ }
31
+ var OC = "docker exec openclaw openclaw mcp";
32
+ async function supportsMcpAdd(session, signal) {
33
+ const help = await session.exec(`${OC} add --help`, signal);
34
+ return help.code === 0;
35
+ }
36
+ async function wireGatewayMcp(session, signal, opts = {}) {
37
+ const url = opts.url ?? defaultGatewayMcpUrl();
38
+ if (!await supportsMcpAdd(session, signal)) return { status: "unsupported", url };
39
+ const existing = await session.exec(`${OC} show ${GATEWAY_MCP_NAME}`, signal);
40
+ const alreadyWired = existing.code === 0;
41
+ if (alreadyWired && !opts.rewire) return { status: "exists", url };
42
+ if (alreadyWired) {
43
+ const removed = await session.exec(`${OC} unset ${GATEWAY_MCP_NAME}`, signal);
44
+ if (removed.code !== 0) {
45
+ return {
46
+ status: "probe-failed",
47
+ error: `Could not remove the existing entry: ${removed.stderr.trim() || removed.stdout.trim()}`,
48
+ url
49
+ };
50
+ }
51
+ }
52
+ const flags = [
53
+ `--transport streamable-http`,
54
+ `--url ${shellQuote(url)}`,
55
+ ...opts.token ? [`--header ${shellQuote(`Authorization=Bearer ${opts.token}`)}`] : []
56
+ ].join(" ");
57
+ const added = await session.exec(`${OC} add ${GATEWAY_MCP_NAME} ${flags}`, signal);
58
+ if (added.code !== 0) {
59
+ return {
60
+ status: "probe-failed",
61
+ error: added.stderr.trim() || added.stdout.trim() || `exit ${added.code}`,
62
+ url
63
+ };
64
+ }
65
+ await session.exec(`${OC} reload`, signal);
66
+ return { status: "wired", rewired: alreadyWired, url };
67
+ }
68
+ export {
69
+ GATEWAY_MCP_NAME,
70
+ defaultGatewayMcpUrl,
71
+ wireGatewayMcp
72
+ };
@@ -2,7 +2,7 @@
2
2
 
3
3
  // package.json
4
4
  var name = "@clawops/cli";
5
- var version = "1.7.7";
5
+ var version = "1.7.8";
6
6
  var description = "Deploy and manage self-hosted OpenClaw instances across clouds";
7
7
  var type = "module";
8
8
  var bin = {
@@ -35,6 +35,7 @@ var scripts = {
35
35
  test: "vitest run",
36
36
  "test:pulumi": "vitest run tests/providers/aws/program.test.ts tests/providers/azure/program.test.ts",
37
37
  "test:changed": "vitest --changed",
38
+ "test:mutation": "node scripts/dev/mutation-check.mjs",
38
39
  "test:integration": "vitest run --config vitest.integration.config.ts",
39
40
  typecheck: "NODE_OPTIONS=--max-old-space-size=4096 tsc --noEmit",
40
41
  lint: "eslint --max-warnings=0 src tests scripts",
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ MCP_HTTP_PORT,
4
+ authRequirement,
5
+ bearerFrom,
6
+ isLoopbackBind,
7
+ serveMcp
8
+ } from "./chunk-QQNKBTYZ.js";
9
+ import "./chunk-35WVQY3X.js";
10
+ import "./chunk-6ZFIFDBJ.js";
11
+ import "./chunk-BOPSG2LI.js";
12
+ import "./chunk-3QJBNAHW.js";
13
+ import "./chunk-RAAONHW3.js";
14
+ import "./chunk-6X45DDGH.js";
15
+ import "./chunk-FNS56UQY.js";
16
+ import "./chunk-DQDAWNGB.js";
17
+ import "./chunk-SEZMVVAS.js";
18
+ import "./chunk-P5LKWRPL.js";
19
+ import "./chunk-EI7CQQ7D.js";
20
+ import "./chunk-PVUVI35Y.js";
21
+ import "./chunk-3XK66DKK.js";
22
+ import "./chunk-Q7NQY5HV.js";
23
+ import "./chunk-A2I76FTA.js";
24
+ import "./chunk-CX5SL5HP.js";
25
+ import "./chunk-KGXPLI7W.js";
26
+ export {
27
+ MCP_HTTP_PORT,
28
+ authRequirement,
29
+ bearerFrom,
30
+ isLoopbackBind,
31
+ serveMcp
32
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawops/cli",
3
- "version": "1.7.7",
3
+ "version": "1.7.8",
4
4
  "description": "Deploy and manage self-hosted OpenClaw instances across clouds",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,6 +32,7 @@
32
32
  "test": "vitest run",
33
33
  "test:pulumi": "vitest run tests/providers/aws/program.test.ts tests/providers/azure/program.test.ts",
34
34
  "test:changed": "vitest --changed",
35
+ "test:mutation": "node scripts/dev/mutation-check.mjs",
35
36
  "test:integration": "vitest run --config vitest.integration.config.ts",
36
37
  "typecheck": "NODE_OPTIONS=--max-old-space-size=4096 tsc --noEmit",
37
38
  "lint": "eslint --max-warnings=0 src tests scripts",
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- atomicWriteConfig,
4
- deepMerge,
5
- readRemoteConfig,
6
- restartGateway
7
- } from "./chunk-SEZMVVAS.js";
8
- import "./chunk-EI7CQQ7D.js";
9
-
10
- // src/cli/mcp-wire.ts
11
- var GATEWAY_MCP_MIN_VERSION = "2026.4";
12
- var GATEWAY_MCP_ENTRY = {
13
- command: "clawops",
14
- args: ["mcp", "serve"],
15
- transport: "stdio"
16
- };
17
- function versionAtLeast(version, min) {
18
- const [vy = 0, vm = 0] = version.split(".").map(Number);
19
- const [my = 0, mm = 0] = min.split(".").map(Number);
20
- if (vy !== my) return vy > my;
21
- return vm >= mm;
22
- }
23
- async function wireGatewayMcp(session, signal, opts = {}) {
24
- const cfg = await readRemoteConfig(session, signal);
25
- const version = cfg["meta"]?.["lastTouchedVersion"];
26
- if (version && !versionAtLeast(version, GATEWAY_MCP_MIN_VERSION) && !opts.force) {
27
- return { status: "version-blocked", version };
28
- }
29
- const mcpClients = cfg["gateway"]?.["mcpClients"];
30
- const rewired = !!mcpClients?.["clawops"];
31
- const updated = deepMerge(cfg, {
32
- gateway: {
33
- mcpClients: {
34
- clawops: GATEWAY_MCP_ENTRY
35
- }
36
- }
37
- });
38
- await atomicWriteConfig(session, updated, signal);
39
- await restartGateway(session, signal);
40
- return { status: "wired", rewired };
41
- }
42
- export {
43
- GATEWAY_MCP_ENTRY,
44
- GATEWAY_MCP_MIN_VERSION,
45
- wireGatewayMcp
46
- };