@clawops/cli 1.2.1 → 1.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.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  buildContext
4
- } from "./chunk-ACYJBSLJ.js";
5
- import "./chunk-BRPU7AQC.js";
4
+ } from "./chunk-T5EX55GP.js";
5
+ import "./chunk-A2I76FTA.js";
6
6
  import "./chunk-CX5SL5HP.js";
7
7
  import "./chunk-KGXPLI7W.js";
8
8
  export {
@@ -1,21 +1,40 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/providers/gcp/index.ts
4
- import process from "process";
4
+ import process2 from "process";
5
5
 
6
6
  // src/providers/gcp/program.ts
7
7
  var GATEWAY_PORT = 18789;
8
8
  var SSH_PORT = 22;
9
9
  var gcpProgram = async () => {
10
- const [pulumi, gcp] = await Promise.all([
10
+ const [pulumi, gcp, { resolveIngressCidrs, detectEgressIp }] = await Promise.all([
11
11
  import("@pulumi/pulumi"),
12
- import("@pulumi/gcp")
12
+ import("@pulumi/gcp"),
13
+ import("./firewall-YYDOWDDP.js")
13
14
  ]);
14
15
  const cfg = new pulumi.Config();
15
16
  const instanceType = cfg.get("instanceType") ?? "e2-standard-2";
16
17
  const region = cfg.get("region") ?? "us-central1";
17
18
  const openclawVersion = cfg.get("openclawVersion") ?? "latest";
18
19
  const zone = cfg.get("zone") ?? `${region}-a`;
20
+ const accessMode = cfg.get("accessMode") ?? "restricted";
21
+ const allowedCidrs = cfg.get("allowedCidrs") ?? "";
22
+ const sshCidrs = cfg.get("sshCidrs") ?? "";
23
+ const gatewayCidrs = cfg.get("gatewayCidrs") ?? "";
24
+ const sshPublicKey = cfg.get("sshPublicKey");
25
+ if (!sshPublicKey) {
26
+ throw new Error(
27
+ 'Stack config "sshPublicKey" is required for the GCP adapter. Set it with: pulumi config set --stack <name> sshPublicKey "ssh-ed25519 ..."'
28
+ );
29
+ }
30
+ const detectedIp = accessMode === "auto" ? await detectEgressIp("https://checkip.amazonaws.com") : "";
31
+ if (accessMode === "open") {
32
+ process.stderr.write(
33
+ "[clawops] WARNING: accessMode=open allows 0.0.0.0/0 on SSH and gateway ports. Only use this for development/sandbox stacks.\n"
34
+ );
35
+ }
36
+ const sshIngressCidrs = resolveIngressCidrs(accessMode, allowedCidrs, sshCidrs, detectedIp);
37
+ const gatewayIngressCidrs = resolveIngressCidrs(accessMode, allowedCidrs, gatewayCidrs, detectedIp);
19
38
  const network = new gcp.compute.Network("clawops-network", {
20
39
  autoCreateSubnetworks: false,
21
40
  description: "clawops managed network"
@@ -25,17 +44,22 @@ var gcpProgram = async () => {
25
44
  region,
26
45
  network: network.id
27
46
  });
28
- new gcp.compute.Firewall("clawops-firewall", {
29
- network: network.selfLink,
30
- allows: [
31
- {
32
- protocol: "tcp",
33
- ports: [String(SSH_PORT), String(GATEWAY_PORT)]
34
- }
35
- ],
36
- sourceRanges: ["0.0.0.0/0"],
37
- targetTags: ["clawops"]
38
- });
47
+ if (sshIngressCidrs.length > 0) {
48
+ new gcp.compute.Firewall("clawops-firewall-ssh", {
49
+ network: network.selfLink,
50
+ allows: [{ protocol: "tcp", ports: [String(SSH_PORT)] }],
51
+ sourceRanges: sshIngressCidrs,
52
+ targetTags: ["clawops"]
53
+ });
54
+ }
55
+ if (gatewayIngressCidrs.length > 0) {
56
+ new gcp.compute.Firewall("clawops-firewall-gateway", {
57
+ network: network.selfLink,
58
+ allows: [{ protocol: "tcp", ports: [String(GATEWAY_PORT)] }],
59
+ sourceRanges: gatewayIngressCidrs,
60
+ targetTags: ["clawops"]
61
+ });
62
+ }
39
63
  const address = new gcp.compute.Address("clawops-address", { region });
40
64
  const instance = new gcp.compute.Instance("clawops-instance", {
41
65
  machineType: instanceType,
@@ -60,6 +84,8 @@ var gcpProgram = async () => {
60
84
  }
61
85
  ],
62
86
  metadata: {
87
+ // GCP guest agent reads 'ssh-keys' and populates /home/<user>/.ssh/authorized_keys
88
+ "ssh-keys": `clawops:${sshPublicKey}`,
63
89
  "startup-script": makeStartupScript(openclawVersion)
64
90
  },
65
91
  serviceAccount: {
@@ -154,6 +180,11 @@ var gcpAdapter = {
154
180
  };
155
181
  },
156
182
  normalizeInstanceType(alias) {
183
+ if (alias === "gpu") {
184
+ throw new Error(
185
+ "GPU instances are not yet supported on GCP. Use --provider aws (g4dn.xlarge) or --provider azure (Standard_NC6s_v3) for GPU workloads."
186
+ );
187
+ }
157
188
  const mapped = INSTANCE_TYPE_MAP[alias];
158
189
  if (!mapped) throw new Error(`Unknown instance alias: ${alias}`);
159
190
  return mapped;
@@ -166,9 +197,10 @@ var gcpAdapter = {
166
197
  },
167
198
  async validateConfig() {
168
199
  const errors = [];
169
- const hasKeyFile = Boolean(process.env["GOOGLE_APPLICATION_CREDENTIALS"]);
170
- const hasUserCreds = Boolean(process.env["CLOUDSDK_AUTH_ACCESS_TOKEN"]);
171
- if (!hasKeyFile && !hasUserCreds) {
200
+ const hasKeyFile = Boolean(process2.env["GOOGLE_APPLICATION_CREDENTIALS"]);
201
+ const hasToken = Boolean(process2.env["GOOGLE_OAUTH_ACCESS_TOKEN"]);
202
+ const hasAdcFile = await checkAdcFile();
203
+ if (!hasKeyFile && !hasToken && !hasAdcFile) {
172
204
  const onGcp = await checkInstanceMetadata();
173
205
  if (!onGcp) {
174
206
  errors.push(
@@ -179,6 +211,17 @@ var gcpAdapter = {
179
211
  return { ok: errors.length === 0, errors };
180
212
  }
181
213
  };
214
+ async function checkAdcFile() {
215
+ try {
216
+ const { accessSync } = await import("fs");
217
+ const { join } = await import("path");
218
+ const home = process2.env["HOME"] ?? process2.env["USERPROFILE"] ?? "";
219
+ accessSync(join(home, ".config", "gcloud", "application_default_credentials.json"));
220
+ return true;
221
+ } catch {
222
+ return false;
223
+ }
224
+ }
182
225
  async function checkInstanceMetadata() {
183
226
  try {
184
227
  const res = await fetch(
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  generatePlan,
4
4
  planId
5
- } from "./chunk-SSB6SGPQ.js";
5
+ } from "./chunk-UAA6YOOP.js";
6
6
  import "./chunk-YTH4L2GN.js";
7
- import "./chunk-ACYJBSLJ.js";
8
- import "./chunk-BRPU7AQC.js";
7
+ import "./chunk-T5EX55GP.js";
8
+ import "./chunk-A2I76FTA.js";
9
9
  import "./chunk-CX5SL5HP.js";
10
10
  import "./chunk-KGXPLI7W.js";
11
11
  export {
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ listOverlays,
4
+ loadOverlay,
5
+ saveOverlay
6
+ } from "./chunk-6ZFIFDBJ.js";
7
+ export {
8
+ listOverlays,
9
+ loadOverlay,
10
+ saveOverlay
11
+ };
@@ -2,7 +2,7 @@
2
2
 
3
3
  // package.json
4
4
  var name = "@clawops/cli";
5
- var version = "1.2.1";
5
+ var version = "1.4.0";
6
6
  var description = "Deploy and manage self-hosted OpenClaw instances across clouds";
7
7
  var type = "module";
8
8
  var bin = {
@@ -48,6 +48,7 @@ var dependencies = {
48
48
  "@pulumi/docker": "^4.0.0",
49
49
  "@pulumi/gcp": "^7.0.0",
50
50
  "@pulumi/pulumi": "^3.0.0",
51
+ "@pulumi/random": "^4.20.0",
51
52
  ajv: "^8.0.0",
52
53
  "ajv-formats": "^3.0.0",
53
54
  chalk: "^5.0.0",
@@ -8,7 +8,7 @@ import {
8
8
  deepMerge,
9
9
  readRemoteConfig,
10
10
  restartGateway
11
- } from "./chunk-UDNZUSKA.js";
11
+ } from "./chunk-ZFNPM2WG.js";
12
12
  export {
13
13
  OPENCLAW_CONFIG,
14
14
  OPENCLAW_CONFIG_LINUX,
@@ -1,36 +1,39 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ extractBaseOutputs
4
+ } from "./chunk-3QJBNAHW.js";
2
5
  import {
3
6
  generatePlan
4
- } from "./chunk-SSB6SGPQ.js";
7
+ } from "./chunk-UAA6YOOP.js";
5
8
  import {
6
9
  applyPlan
7
- } from "./chunk-LU63NZD3.js";
10
+ } from "./chunk-ZONXY3C6.js";
8
11
  import {
9
12
  validatePlan
10
13
  } from "./chunk-YTH4L2GN.js";
14
+ import "./chunk-6ZFIFDBJ.js";
11
15
  import "./chunk-BOPSG2LI.js";
12
- import {
13
- extractBaseOutputs
14
- } from "./chunk-3QJBNAHW.js";
15
16
  import {
16
17
  errText,
18
+ formatUptime,
19
+ gatherSnapshot,
17
20
  handleConfigGet,
18
21
  handleConfigSet,
19
22
  handleConfigUnset,
20
23
  handleConfigValidate,
21
24
  okText,
22
25
  resolveConn
23
- } from "./chunk-QJ6ERXHN.js";
26
+ } from "./chunk-JYQZJMD3.js";
24
27
  import {
25
28
  buildContext
26
- } from "./chunk-ACYJBSLJ.js";
27
- import "./chunk-BRPU7AQC.js";
29
+ } from "./chunk-T5EX55GP.js";
28
30
  import {
29
31
  acquireSession,
30
32
  drainPool
31
33
  } from "./chunk-ZVOEQCNW.js";
32
34
  import "./chunk-4U3LTLWZ.js";
33
- import "./chunk-UDNZUSKA.js";
35
+ import "./chunk-ZFNPM2WG.js";
36
+ import "./chunk-A2I76FTA.js";
34
37
  import {
35
38
  getConfig,
36
39
  getConfigDir
@@ -139,7 +142,7 @@ var clawops_statusAnnotations = {
139
142
  };
140
143
  var clawops_logs_tailSchema = z.object({
141
144
  stackName: z.string().optional(),
142
- tailLines: z.number().int().default(100),
145
+ tailLines: z.number().int().optional().default(100),
143
146
  sinceMin: z.number().int().optional()
144
147
  });
145
148
  var clawops_logs_tailAnnotations = {
@@ -150,6 +153,18 @@ var clawops_logs_tailAnnotations = {
150
153
  openWorldHint: true,
151
154
  toolsets: ["cli", "read"]
152
155
  };
156
+ var clawops_monitorSchema = z.object({
157
+ stackName: z.string().optional(),
158
+ tailLines: z.number().int().optional().default(5)
159
+ });
160
+ var clawops_monitorAnnotations = {
161
+ title: "Monitor Stack Health",
162
+ readOnlyHint: true,
163
+ destructiveHint: false,
164
+ idempotentHint: true,
165
+ openWorldHint: true,
166
+ toolsets: ["cli", "read"]
167
+ };
153
168
  var clawops_stacks_listSchema = z.object({});
154
169
  var clawops_stacks_listAnnotations = {
155
170
  title: "List Stacks",
@@ -186,9 +201,9 @@ var clawops_upSchema = z.object({
186
201
  stackName: z.string().optional(),
187
202
  provider: z.enum(["aws", "gcp", "azure", "local"]).optional(),
188
203
  region: z.string().optional(),
189
- instanceType: z.enum(["micro", "small", "medium", "large", "gpu"]).default("small"),
204
+ instanceType: z.enum(["micro", "small", "medium", "large", "gpu"]).optional().default("small"),
190
205
  openclawVersion: z.string().optional(),
191
- dryRun: z.boolean().default(false)
206
+ dryRun: z.boolean().optional().default(false)
192
207
  });
193
208
  var clawops_upAnnotations = {
194
209
  title: "Provision and Deploy Stack",
@@ -200,7 +215,7 @@ var clawops_upAnnotations = {
200
215
  };
201
216
  var clawops_destroySchema = z.object({
202
217
  stackName: z.string(),
203
- yes: z.boolean().default(false)
218
+ yes: z.boolean().optional().default(false)
204
219
  });
205
220
  var clawops_destroyAnnotations = {
206
221
  title: "Destroy Stack (DESTRUCTIVE)",
@@ -212,7 +227,7 @@ var clawops_destroyAnnotations = {
212
227
  };
213
228
  var clawops_applySchema = z.object({
214
229
  planPath: z.string(),
215
- yes: z.boolean().default(false)
230
+ yes: z.boolean().optional().default(false)
216
231
  });
217
232
  var clawops_applyAnnotations = {
218
233
  title: "Apply Maker Plan",
@@ -241,7 +256,7 @@ var clawops_config_setSchema = z.object({
241
256
  stackName: z.string().optional(),
242
257
  key: z.string(),
243
258
  value: z.string(),
244
- restart: z.boolean().default(false)
259
+ restart: z.boolean().optional().default(false)
245
260
  });
246
261
  var clawops_config_setAnnotations = {
247
262
  title: "Set OpenClaw Config Value",
@@ -254,7 +269,7 @@ var clawops_config_setAnnotations = {
254
269
  var clawops_config_unsetSchema = z.object({
255
270
  stackName: z.string().optional(),
256
271
  key: z.string(),
257
- restart: z.boolean().default(false)
272
+ restart: z.boolean().optional().default(false)
258
273
  });
259
274
  var clawops_config_unsetAnnotations = {
260
275
  title: "Unset OpenClaw Config Key",
@@ -301,8 +316,8 @@ var clawops_gateway_restartAnnotations = {
301
316
  var clawops_workflow_deploy_appSchema = z.object({
302
317
  provider: z.enum(["aws", "gcp", "azure", "local"]),
303
318
  region: z.string().optional(),
304
- stackName: z.string().default("default"),
305
- instanceType: z.enum(["micro", "small", "medium", "large", "gpu"]).default("small")
319
+ stackName: z.string().optional().default("default"),
320
+ instanceType: z.enum(["micro", "small", "medium", "large", "gpu"]).optional().default("small")
306
321
  });
307
322
  var clawops_workflow_deploy_appAnnotations = {
308
323
  title: "Deploy OpenClaw (End-to-End Workflow)",
@@ -338,6 +353,7 @@ var TOOLSETS = {
338
353
  cli: [
339
354
  "clawops_status",
340
355
  "clawops_logs_tail",
356
+ "clawops_monitor",
341
357
  "clawops_config_get",
342
358
  "clawops_agents_list",
343
359
  "clawops_up",
@@ -358,6 +374,7 @@ var TOOLSETS = {
358
374
  read: [
359
375
  "clawops_status",
360
376
  "clawops_logs_tail",
377
+ "clawops_monitor",
361
378
  "clawops_stacks_list",
362
379
  "clawops_config_get",
363
380
  "clawops_agents_list",
@@ -613,7 +630,7 @@ async function handleUp(input, server) {
613
630
  );
614
631
  }
615
632
  const { localOpts } = stackConfig;
616
- const { localBootstrap } = await import("./bootstrap-UYCOEJQX.js");
633
+ const { localBootstrap } = await import("./bootstrap-G4UZ2FKH.js");
617
634
  const ac = new AbortController();
618
635
  const state = await localBootstrap({
619
636
  host: localOpts.host,
@@ -834,6 +851,62 @@ async function handleTaskStatus(input, _server) {
834
851
  return { content: [{ type: "text", text: JSON.stringify(record, null, 2) }] };
835
852
  }
836
853
 
854
+ // src/mcp/tools/cli/monitor.ts
855
+ async function handleMonitor(input, _server) {
856
+ const { acquireSession: acquireSession2, drainPool: drainPool2 } = await import("./pool-FBFHATDG.js");
857
+ const ctx = buildContext({ stack: input.stackName });
858
+ const tailLines = input.tailLines ?? 5;
859
+ let conn;
860
+ if (ctx.adapter.name === "local") {
861
+ if (!ctx.localState) {
862
+ return text2(JSON.stringify({ error: "Stack is not bootstrapped. Run `clawops up` first." }));
863
+ }
864
+ const ls = ctx.localState;
865
+ conn = { host: ls.sshHost, port: ls.sshPort, user: ls.sshUser, privateKeyPath: ls.privateKeyPath, knownHostsPath: ls.knownHostsPath };
866
+ } else {
867
+ const stack = await ctx.getStack();
868
+ const outputMap = await stack.outputs();
869
+ const outputs = Object.fromEntries(
870
+ Object.entries(outputMap).map(([k, v]) => [k, v.value])
871
+ );
872
+ if (!outputs["publicIp"]) {
873
+ return text2(JSON.stringify({ error: "Stack has no outputs. Run `clawops up` first." }));
874
+ }
875
+ const base = extractBaseOutputs(outputs);
876
+ conn = ctx.adapter.getConnectionInfo({
877
+ ...base,
878
+ privateKeyPath: ctx.config.ssh.keyPath,
879
+ knownHostsPath: ctx.config.ssh.knownHostsPath
880
+ });
881
+ }
882
+ const ac = new AbortController();
883
+ const { session, release } = await acquireSession2({ ...conn, signal: ac.signal });
884
+ try {
885
+ const snap = await gatherSnapshot(session, ac.signal, tailLines);
886
+ return text2(JSON.stringify({
887
+ gateway: snap.gateway,
888
+ container: {
889
+ status: snap.container.status,
890
+ image: snap.container.image,
891
+ restartCount: snap.container.restartCount,
892
+ memUsage: snap.container.memUsage,
893
+ cpuPct: snap.container.cpuPct,
894
+ uptime: formatUptime(snap.container.startedAt)
895
+ },
896
+ disk: snap.disk,
897
+ logLines: snap.logLines,
898
+ capturedAt: snap.capturedAt.toISOString()
899
+ }, null, 2));
900
+ } finally {
901
+ release();
902
+ drainPool2();
903
+ ac.abort();
904
+ }
905
+ }
906
+ function text2(t) {
907
+ return { content: [{ type: "text", text: t }] };
908
+ }
909
+
837
910
  // src/mcp/tools/workflow/deploy_app.ts
838
911
  async function handleWorkflowDeployApp(input, server) {
839
912
  const parts = [`## Deployment Workflow \u2014 ${input.provider} / ${input.stackName}
@@ -946,6 +1019,7 @@ function makeEntry(schema, annotations, handler) {
946
1019
  var TOOL_REGISTRY = {
947
1020
  clawops_status: makeEntry(clawops_statusSchema, clawops_statusAnnotations, handleStatus),
948
1021
  clawops_logs_tail: makeEntry(clawops_logs_tailSchema, clawops_logs_tailAnnotations, handleLogsTail),
1022
+ clawops_monitor: makeEntry(clawops_monitorSchema, clawops_monitorAnnotations, handleMonitor),
949
1023
  clawops_stacks_list: makeEntry(clawops_stacks_listSchema, clawops_stacks_listAnnotations, handleStacksList),
950
1024
  clawops_config_get: makeEntry(clawops_config_getSchema, clawops_config_getAnnotations, handleConfigGet),
951
1025
  clawops_agents_list: makeEntry(clawops_agents_listSchema, clawops_agents_listAnnotations, handleAgentsList),
@@ -1058,19 +1132,19 @@ function registerResources(server) {
1058
1132
  async (uri, { name }) => {
1059
1133
  const stackName = Array.isArray(name) ? name[0] : name;
1060
1134
  const filePath = path3.join(getConfigDir(), "state", `${stackName}.last-run.json`);
1061
- let text2;
1135
+ let text3;
1062
1136
  try {
1063
1137
  const raw = readFileSync2(filePath, "utf-8");
1064
1138
  const parsed = JSON.parse(raw);
1065
- text2 = parsed.output ?? "(no output recorded)";
1139
+ text3 = parsed.output ?? "(no output recorded)";
1066
1140
  } catch {
1067
- text2 = `No last-run output found for stack "${stackName}".`;
1141
+ text3 = `No last-run output found for stack "${stackName}".`;
1068
1142
  }
1069
1143
  return {
1070
1144
  contents: [{
1071
1145
  uri: uri.href,
1072
1146
  mimeType: "text/plain",
1073
- text: text2
1147
+ text: text3
1074
1148
  }]
1075
1149
  };
1076
1150
  }
@@ -1231,7 +1305,7 @@ If none of the above resolves the issue:
1231
1305
 
1232
1306
  // src/mcp/server.ts
1233
1307
  async function serveMcp(opts) {
1234
- const { version } = await import("./package-SE3M4NJA.js");
1308
+ const { version } = await import("./package-WAJRGBBM.js");
1235
1309
  const server = new McpServer({ name: "clawops", version });
1236
1310
  registerTools(server, opts);
1237
1311
  registerResources(server);
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ localStateToConnectionInfo,
4
+ readLocalState,
5
+ stateDir,
6
+ statePath,
7
+ writeLocalState
8
+ } from "./chunk-A2I76FTA.js";
9
+ import "./chunk-CX5SL5HP.js";
10
+ import "./chunk-KGXPLI7W.js";
11
+ export {
12
+ localStateToConnectionInfo,
13
+ readLocalState,
14
+ stateDir,
15
+ statePath,
16
+ writeLocalState
17
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawops/cli",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
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
  "@pulumi/docker": "^4.0.0",
33
33
  "@pulumi/gcp": "^7.0.0",
34
34
  "@pulumi/pulumi": "^3.0.0",
35
+ "@pulumi/random": "^4.20.0",
35
36
  "ajv": "^8.0.0",
36
37
  "ajv-formats": "^3.0.0",
37
38
  "chalk": "^5.0.0",