@clawops/cli 1.3.0 → 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.
@@ -56,17 +56,17 @@ function makeProviderProxy(name) {
56
56
  if (resolved) return resolved;
57
57
  switch (name) {
58
58
  case "gcp": {
59
- const mod = await import("./gcp-SKURG3L6.js");
59
+ const mod = await import("./gcp-BXDTC6EK.js");
60
60
  resolved = mod.default;
61
61
  return resolved;
62
62
  }
63
63
  case "aws": {
64
- const mod = await import("./aws-KQBWY43W.js");
64
+ const mod = await import("./aws-FRE2JVAZ.js");
65
65
  resolved = mod.default;
66
66
  return resolved;
67
67
  }
68
68
  case "azure": {
69
- const mod = await import("./azure-4EJFVJZL.js");
69
+ const mod = await import("./azure-PVC3AQVC.js");
70
70
  resolved = mod.default;
71
71
  return resolved;
72
72
  }
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-YTH4L2GN.js";
5
5
  import {
6
6
  buildContext
7
- } from "./chunk-JDN6PLH2.js";
7
+ } from "./chunk-T5EX55GP.js";
8
8
  import {
9
9
  getConfig
10
10
  } from "./chunk-CX5SL5HP.js";
@@ -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-AFUTH62F.js");
48
+ const { version } = await import("./package-WAJRGBBM.js");
49
49
  const config = getConfig();
50
50
  const instanceType = intent.instanceType ?? "small";
51
51
  const openclawVersion = intent.openclawVersion ?? "latest";
@@ -20,7 +20,7 @@ async function execWithFallbackSudo(session, cmd, signal) {
20
20
  async function readRemoteConfig(session, signal) {
21
21
  const os = await detectOS(session, signal);
22
22
  const configPath = configPathForOS(os);
23
- const result = await session.exec(`cat ${configPath}`, signal);
23
+ const result = os === "Darwin" ? await session.exec(`cat ${configPath}`, signal) : await execWithFallbackSudo(session, `cat ${configPath}`, signal);
24
24
  if (result.code !== 0) {
25
25
  throw new Error(`Cannot read ${configPath}: ${result.stderr}`);
26
26
  }
@@ -10,13 +10,13 @@ import {
10
10
  } from "./chunk-BOPSG2LI.js";
11
11
  import {
12
12
  buildContext
13
- } from "./chunk-JDN6PLH2.js";
13
+ } from "./chunk-T5EX55GP.js";
14
14
  import {
15
15
  atomicWriteConfig,
16
16
  deepMerge,
17
17
  readRemoteConfig,
18
18
  restartGateway
19
- } from "./chunk-UDNZUSKA.js";
19
+ } from "./chunk-ZFNPM2WG.js";
20
20
  import {
21
21
  UsageError
22
22
  } from "./chunk-KGXPLI7W.js";
package/dist/cli.js CHANGED
@@ -5,12 +5,19 @@ import {
5
5
  writeAppConfigs
6
6
  } from "./chunk-OIGTOLB3.js";
7
7
  import {
8
- validateOpenclawConfig
9
- } from "./chunk-LVIIYY27.js";
10
- import "./chunk-JDN6PLH2.js";
8
+ chalk,
9
+ failure,
10
+ info,
11
+ monitor_default,
12
+ spinner,
13
+ success,
14
+ validateOpenclawConfig,
15
+ warn
16
+ } from "./chunk-JYQZJMD3.js";
17
+ import "./chunk-T5EX55GP.js";
11
18
  import "./chunk-ZVOEQCNW.js";
12
19
  import "./chunk-4U3LTLWZ.js";
13
- import "./chunk-UDNZUSKA.js";
20
+ import "./chunk-ZFNPM2WG.js";
14
21
  import "./chunk-A2I76FTA.js";
15
22
  import {
16
23
  getConfig,
@@ -32,27 +39,6 @@ import { generateKeyPairSync } from "crypto";
32
39
  import { writeFileSync, mkdirSync, existsSync } from "fs";
33
40
  import path from "path";
34
41
  import process2 from "process";
35
-
36
- // src/output/human.ts
37
- import chalk from "chalk";
38
- import ora from "ora";
39
- function success(msg) {
40
- console.log(chalk.green("\u2713") + " " + msg);
41
- }
42
- function failure(msg) {
43
- console.error(chalk.red("\u2717") + " " + msg);
44
- }
45
- function warn(msg) {
46
- console.warn(chalk.yellow("\u26A0") + " " + msg);
47
- }
48
- function info(msg) {
49
- console.log(chalk.blue("\u2139") + " " + msg);
50
- }
51
- function spinner(text) {
52
- return ora(text).start();
53
- }
54
-
55
- // src/cli/commands/init.ts
56
42
  var SUPPORTED_PROVIDERS = ["gcp", "aws", "azure", "local"];
57
43
  var PROVIDER_DEFAULTS = {
58
44
  gcp: { region: "us-central1", credEnv: "GOOGLE_APPLICATION_CREDENTIALS", stateScheme: "gs://" },
@@ -217,7 +203,7 @@ var up_default = defineCommand2({
217
203
  config: { type: "string", description: "Path to openclaw config overlay JSON (local provider only)" }
218
204
  },
219
205
  async run({ args }) {
220
- const { buildContext } = await import("./context-M2RMDHX6.js");
206
+ const { buildContext } = await import("./context-PSGEX2D7.js");
221
207
  const ctx = buildContext(args);
222
208
  const openclawVersion = typeof args["openclaw-version"] === "string" ? args["openclaw-version"] : "stable";
223
209
  if (ctx.adapter.name === "local") {
@@ -323,7 +309,7 @@ var up_default = defineCommand2({
323
309
  async function applyLocalConfigOverlay(opts) {
324
310
  const { connect } = await import("./ssh-IQXNME3D.js");
325
311
  const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
326
- const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-JQ77SRC2.js");
312
+ const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-QF5TT7GU.js");
327
313
  let overlay;
328
314
  try {
329
315
  overlay = JSON.parse(readFileSync(opts.configPath, "utf-8"));
@@ -363,7 +349,7 @@ var down_default = defineCommand3({
363
349
  "dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
364
350
  },
365
351
  async run({ args }) {
366
- const { buildContext } = await import("./context-M2RMDHX6.js");
352
+ const { buildContext } = await import("./context-PSGEX2D7.js");
367
353
  const ctx = buildContext(args);
368
354
  if (args["dry-run"]) {
369
355
  info(`Dry run \u2014 would destroy stack "${ctx.stackName}"`);
@@ -427,7 +413,7 @@ var status_default = defineCommand4({
427
413
  json: { type: "boolean", description: "Emit JSON" }
428
414
  },
429
415
  async run({ args }) {
430
- const { buildContext } = await import("./context-M2RMDHX6.js");
416
+ const { buildContext } = await import("./context-PSGEX2D7.js");
431
417
  const ctx = buildContext(args);
432
418
  if (ctx.adapter.name === "local") {
433
419
  const state = ctx.localState;
@@ -499,8 +485,8 @@ var plan_default = defineCommand5({
499
485
  out: { type: "string", description: "Write plan JSON to this absolute path (default: stdout)" }
500
486
  },
501
487
  async run({ args }) {
502
- const { buildContext } = await import("./context-M2RMDHX6.js");
503
- const { generatePlan } = await import("./generate-U7PJ5LRG.js");
488
+ const { buildContext } = await import("./context-PSGEX2D7.js");
489
+ const { generatePlan } = await import("./generate-Z7DBMANX.js");
504
490
  const ctx = buildContext(args);
505
491
  if (ctx.adapter.name === "local") {
506
492
  throw new UsageError(
@@ -590,7 +576,7 @@ var apply_default = defineCommand6({
590
576
  },
591
577
  async run({ args }) {
592
578
  const { validatePlan } = await import("./validate-T5M5EHSJ.js");
593
- const { applyPlan } = await import("./apply-DLNE2JYZ.js");
579
+ const { applyPlan } = await import("./apply-5RREMN3L.js");
594
580
  const planPath = args._?.[0];
595
581
  if (!planPath) {
596
582
  throw new UsageError("Usage: clawops apply <plan.json>");
@@ -706,7 +692,7 @@ var destroy_default = defineCommand7({
706
692
  "dry-run": { type: "boolean", description: "Show what would be destroyed without destroying" }
707
693
  },
708
694
  async run({ args }) {
709
- const { buildContext } = await import("./context-M2RMDHX6.js");
695
+ const { buildContext } = await import("./context-PSGEX2D7.js");
710
696
  const ctx = buildContext(args);
711
697
  if (ctx.adapter.name === "local") {
712
698
  throw new UsageError(
@@ -773,7 +759,7 @@ var ssh_default = defineCommand8({
773
759
  command: { type: "string", description: "Remote command to run (instead of interactive shell)" }
774
760
  },
775
761
  async run({ args }) {
776
- const { buildContext } = await import("./context-M2RMDHX6.js");
762
+ const { buildContext } = await import("./context-PSGEX2D7.js");
777
763
  const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
778
764
  const ctx = buildContext(args);
779
765
  let conn;
@@ -860,7 +846,7 @@ var tunnel_default = defineCommand9({
860
846
  "no-open": { type: "boolean", description: "Do not open browser after tunnel is ready" }
861
847
  },
862
848
  async run({ args }) {
863
- const { buildContext } = await import("./context-M2RMDHX6.js");
849
+ const { buildContext } = await import("./context-PSGEX2D7.js");
864
850
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
865
851
  const { acquireSession } = await import("./pool-FBFHATDG.js");
866
852
  const ctx = buildContext(args);
@@ -928,7 +914,7 @@ var logs_default = defineCommand10({
928
914
  since: { type: "string", description: "Show logs since duration (e.g. 5m, 1h)" }
929
915
  },
930
916
  async run({ args }) {
931
- const { buildContext } = await import("./context-M2RMDHX6.js");
917
+ const { buildContext } = await import("./context-PSGEX2D7.js");
932
918
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
933
919
  const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
934
920
  const ctx = buildContext(args);
@@ -1045,7 +1031,7 @@ var config_default = defineCommand11({
1045
1031
  "dry-run": { type: "boolean", description: "Show what would change without writing" }
1046
1032
  },
1047
1033
  async run({ args }) {
1048
- const { buildContext } = await import("./context-M2RMDHX6.js");
1034
+ const { buildContext } = await import("./context-PSGEX2D7.js");
1049
1035
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
1050
1036
  const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
1051
1037
  const [action, key, value] = args._ ?? [];
@@ -1171,7 +1157,7 @@ var agents_default = defineCommand12({
1171
1157
  json: { type: "boolean", description: "Emit JSON (for list)" }
1172
1158
  },
1173
1159
  async run({ args }) {
1174
- const { buildContext } = await import("./context-M2RMDHX6.js");
1160
+ const { buildContext } = await import("./context-PSGEX2D7.js");
1175
1161
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
1176
1162
  const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
1177
1163
  const [action, name] = args._ ?? [];
@@ -1275,7 +1261,7 @@ var gateway_default = defineCommand13({
1275
1261
  json: { type: "boolean", description: "Emit JSON (for status)" }
1276
1262
  },
1277
1263
  async run({ args }) {
1278
- const { buildContext } = await import("./context-M2RMDHX6.js");
1264
+ const { buildContext } = await import("./context-PSGEX2D7.js");
1279
1265
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
1280
1266
  const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
1281
1267
  const [action, versionArg] = args._ ?? [];
@@ -1388,7 +1374,7 @@ var backup_default = defineCommand14({
1388
1374
  yes: { type: "boolean", description: "[restore] Skip confirmation prompt" }
1389
1375
  },
1390
1376
  async run({ args }) {
1391
- const { buildContext } = await import("./context-M2RMDHX6.js");
1377
+ const { buildContext } = await import("./context-PSGEX2D7.js");
1392
1378
  const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
1393
1379
  const action = args.action;
1394
1380
  if (action !== "create" && action !== "restore") {
@@ -1500,7 +1486,7 @@ var stacks_default = defineCommand15({
1500
1486
  args: {
1501
1487
  json: { type: "boolean", description: "Emit JSON (for list)" },
1502
1488
  yes: { type: "boolean", description: "Skip confirmation prompt on delete" },
1503
- force: { type: "boolean", description: "Allow deleting the default stack" }
1489
+ force: { type: "boolean", description: "Skip safety checks (allow deleting default or still-deployed stacks)" }
1504
1490
  },
1505
1491
  async run({ args }) {
1506
1492
  const [action, name] = args._ ?? [];
@@ -1558,6 +1544,31 @@ var stacks_default = defineCommand15({
1558
1544
  `"${name}" is the default stack. Use --force to delete it (clawops will switch the default to another stack).`
1559
1545
  );
1560
1546
  }
1547
+ let isDeployed = false;
1548
+ if (!args.force) {
1549
+ try {
1550
+ const { buildContext } = await import("./context-PSGEX2D7.js");
1551
+ const ctx = buildContext({ stack: name });
1552
+ if (ctx.adapter.name === "local") {
1553
+ isDeployed = !!ctx.localState;
1554
+ } else {
1555
+ const stack = await ctx.getStack();
1556
+ const outputMap = await stack.outputs();
1557
+ const outputs = Object.fromEntries(
1558
+ Object.entries(outputMap).map(([k, v]) => [k, v.value])
1559
+ );
1560
+ isDeployed = !!outputs["publicIp"];
1561
+ }
1562
+ } catch {
1563
+ warn(`Could not verify deployment status for "${name}" \u2014 proceeding anyway.`);
1564
+ }
1565
+ }
1566
+ if (isDeployed) {
1567
+ failure(
1568
+ `Stack "${name}" is still deployed. Run \`clawops down --stack ${name}\` first to destroy cloud resources, or pass --force to remove from registry only.`
1569
+ );
1570
+ process17.exit(1);
1571
+ }
1561
1572
  warn(
1562
1573
  `This removes "${name}" from clawops config only. Cloud resources are NOT destroyed. Run \`clawops destroy --stack ` + name + "` first if you want to remove cloud resources."
1563
1574
  );
@@ -1657,9 +1668,9 @@ var doctor_default = defineCommand16({
1657
1668
  info("Credential checks skipped \u2014 no config");
1658
1669
  } else {
1659
1670
  const { getProvider } = await import("./providers-2OABPW2E.js");
1660
- await import("./aws-KQBWY43W.js");
1661
- await import("./gcp-SKURG3L6.js");
1662
- await import("./azure-4EJFVJZL.js");
1671
+ await import("./aws-FRE2JVAZ.js");
1672
+ await import("./gcp-BXDTC6EK.js");
1673
+ await import("./azure-PVC3AQVC.js");
1663
1674
  await import("./local-DXBEVZ5C.js");
1664
1675
  const checkedProviders = /* @__PURE__ */ new Set();
1665
1676
  for (const [stackName, stackCfg] of Object.entries(config.stacks)) {
@@ -1694,7 +1705,7 @@ var doctor_default = defineCommand16({
1694
1705
  info("Remote health skipped \u2014 no config");
1695
1706
  } else {
1696
1707
  try {
1697
- const { buildContext } = await import("./context-M2RMDHX6.js");
1708
+ const { buildContext } = await import("./context-PSGEX2D7.js");
1698
1709
  const { extractBaseOutputs } = await import("./outputs-DJHBY7EE.js");
1699
1710
  const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
1700
1711
  const ctx = buildContext({ stack: args.stack });
@@ -1802,7 +1813,7 @@ var serveCmd = defineCommand17({
1802
1813
  inspector: { type: "boolean", description: "Enable MCP inspector" }
1803
1814
  },
1804
1815
  async run({ args }) {
1805
- const { serveMcp } = await import("./server-I62UES5V.js");
1816
+ const { serveMcp } = await import("./server-IWLZJ4EU.js");
1806
1817
  await serveMcp({
1807
1818
  port: args.http ? Number(args.http) : void 0,
1808
1819
  bind: args.bind,
@@ -2303,7 +2314,7 @@ var setup_default = defineCommand18({
2303
2314
  default: false
2304
2315
  }]);
2305
2316
  if (applyNow) {
2306
- const { applyPlan } = await import("./apply-DLNE2JYZ.js");
2317
+ const { applyPlan } = await import("./apply-5RREMN3L.js");
2307
2318
  const plan = JSON.parse(readFileSync3(outputPath, "utf-8"));
2308
2319
  try {
2309
2320
  await applyPlan(plan, {
@@ -2327,7 +2338,7 @@ async function runLocalDeploy(opts) {
2327
2338
  const { setConfig: setConfig2, getConfig: getConfig2, getConfigDir: getConfigDir2 } = await import("./store-SDUR52Z5.js");
2328
2339
  const { localBootstrap } = await import("./bootstrap-G4UZ2FKH.js");
2329
2340
  const { connect } = await import("./ssh-IQXNME3D.js");
2330
- const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-JQ77SRC2.js");
2341
+ const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-QF5TT7GU.js");
2331
2342
  const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
2332
2343
  const knownHostsPath = path3.join(getConfigDir2(), "known_hosts");
2333
2344
  const existing = getConfig2();
@@ -3012,8 +3023,8 @@ var rotateCmd = defineCommand19({
3012
3023
  }
3013
3024
  info(`Re-applying config overlay to stack "${targetStack}"\u2026`);
3014
3025
  try {
3015
- const { buildContext } = await import("./context-M2RMDHX6.js");
3016
- const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-JQ77SRC2.js");
3026
+ const { buildContext } = await import("./context-PSGEX2D7.js");
3027
+ const { readRemoteConfig, atomicWriteConfig, restartGateway, deepMerge } = await import("./remote-config-QF5TT7GU.js");
3017
3028
  const { resolveSecrets } = await import("./secrets-SVZWNAPK.js");
3018
3029
  const { acquireSession, drainPool } = await import("./pool-FBFHATDG.js");
3019
3030
  const { localStateToConnectionInfo } = await import("./state-PRBIIN7I.js");
@@ -3142,6 +3153,7 @@ var COMMANDS = [
3142
3153
  ["doctor", "Check Node version, config, SSH key, provider credentials, and Pulumi home"],
3143
3154
  ["mcp", "MCP server operations (mcp serve | mcp install)"],
3144
3155
  ["secret", "Manage secrets (secret list | set | delete | rotate | audit)"],
3156
+ ["monitor", "Live dashboard: gateway health, container status, resource usage, log tail"],
3145
3157
  ["help", "Show this help message"]
3146
3158
  ];
3147
3159
  var GLOBAL_FLAGS = [
@@ -3245,6 +3257,7 @@ var main = defineCommand21({
3245
3257
  mcp: mcp_default,
3246
3258
  setup: setup_default,
3247
3259
  secret: secret_default,
3260
+ monitor: monitor_default,
3248
3261
  help: help_default
3249
3262
  }
3250
3263
  });
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  buildContext
4
- } from "./chunk-JDN6PLH2.js";
4
+ } from "./chunk-T5EX55GP.js";
5
5
  import "./chunk-A2I76FTA.js";
6
6
  import "./chunk-CX5SL5HP.js";
7
7
  import "./chunk-KGXPLI7W.js";
@@ -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,9 +2,9 @@
2
2
  import {
3
3
  generatePlan,
4
4
  planId
5
- } from "./chunk-ZDPBBYYV.js";
5
+ } from "./chunk-UAA6YOOP.js";
6
6
  import "./chunk-YTH4L2GN.js";
7
- import "./chunk-JDN6PLH2.js";
7
+ import "./chunk-T5EX55GP.js";
8
8
  import "./chunk-A2I76FTA.js";
9
9
  import "./chunk-CX5SL5HP.js";
10
10
  import "./chunk-KGXPLI7W.js";
@@ -2,7 +2,7 @@
2
2
 
3
3
  // package.json
4
4
  var name = "@clawops/cli";
5
- var version = "1.3.0";
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,