@clawops/cli 2.0.0 → 2.0.2

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 (75) hide show
  1. package/README.md +99 -12
  2. package/dist/apply-WOUTUUQH.js +27 -0
  3. package/dist/automation-UKRM3BD2.js +44 -0
  4. package/dist/bedrock-XVASJX2K.js +61 -0
  5. package/dist/{bootstrap-DMRPWJGN.js → bootstrap-HNGKY6WW.js} +11 -8
  6. package/dist/{chunk-N44K3LHE.js → chunk-2I7KCMXZ.js} +6 -4
  7. package/dist/{chunk-3WYA2QDM.js → chunk-2Z7J3ERQ.js} +1 -1
  8. package/dist/chunk-3OLUJPCB.js +36 -0
  9. package/dist/{chunk-GYV4KNW7.js → chunk-5TXCUOZV.js} +133 -40
  10. package/dist/chunk-AEEMRFZV.js +24 -0
  11. package/dist/{chunk-YR3O2I3S.js → chunk-BSLIYB5Q.js} +6 -1
  12. package/dist/{chunk-AHAAEXWC.js → chunk-E4NJSJ54.js} +16 -93
  13. package/dist/chunk-FEK7VJAY.js +16 -0
  14. package/dist/{chunk-6TIYVHPX.js → chunk-GIEVAGEK.js} +7 -33
  15. package/dist/chunk-HSPHXG6W.js +67 -0
  16. package/dist/chunk-LEVIW2PV.js +890 -0
  17. package/dist/chunk-MC5ATCXS.js +108 -0
  18. package/dist/chunk-N4WZLWJ4.js +16 -0
  19. package/dist/{chunk-SBLQH2TJ.js → chunk-NUL5F6YV.js} +1 -1
  20. package/dist/chunk-PSOSBJVO.js +51 -0
  21. package/dist/{chunk-AX74VXTV.js → chunk-RATEIAFU.js} +58 -49
  22. package/dist/chunk-RHOWYIWT.js +52 -0
  23. package/dist/{chunk-WHJMKDV7.js → chunk-SDZKCICH.js} +65 -12
  24. package/dist/chunk-V25QRZRB.js +45 -0
  25. package/dist/chunk-VOEJYRIK.js +33 -0
  26. package/dist/{providers-2OABPW2E.js → chunk-VUFMZEIM.js} +3 -2
  27. package/dist/chunk-WTZ2CT35.js +69 -0
  28. package/dist/{chunk-UFRO4H5W.js → chunk-ZMMRIWFS.js} +56 -40
  29. package/dist/cli-NH7VUNOU.js +11 -0
  30. package/dist/cli-auth-GD5SK7JU.js +13 -0
  31. package/dist/cli.js +518 -206
  32. package/dist/context-M4JBBIAV.js +20 -0
  33. package/dist/docker-3PDO4TS7.js +12 -0
  34. package/dist/{firewall-T3NTPZJD.js → firewall-5VKZ4WBH.js} +22 -1
  35. package/dist/generate-J6MIJGII.js +29 -0
  36. package/dist/{harden-PCDK3UQ5.js → harden-72JMRT4E.js} +8 -5
  37. package/dist/index.d.ts +56 -0
  38. package/dist/known-hosts-file-I3Q3NBYQ.js +22 -0
  39. package/dist/{logs-P7YCKVF4.js → logs-5A7YYA53.js} +1 -1
  40. package/dist/{mcp-wire-BFEY2Y6H.js → mcp-wire-NTGL5ITF.js} +25 -15
  41. package/dist/models-AGTQWYDL.js +32 -0
  42. package/dist/network-args-PYJRJBYO.js +64 -0
  43. package/dist/{package-3HUW57XH.js → package-3A6BOALU.js} +11 -3
  44. package/dist/passphrase-MJBE45N7.js +15 -0
  45. package/dist/{pool-EX4ZTA5P.js → pool-PIIKJ4L2.js} +3 -2
  46. package/dist/preflight-EF52S3WW.js +162 -0
  47. package/dist/preflight-MWWM5JT3.js +181 -0
  48. package/dist/preflight-TFD4N5ZU.js +141 -0
  49. package/dist/providers-FW6H6OPF.js +11 -0
  50. package/dist/ready-KDNRBNSU.js +95 -0
  51. package/dist/{remote-config-Z6IIZR3D.js → remote-config-3HZLUWEA.js} +3 -2
  52. package/dist/{runtime-LZ7NA53V.js → runtime-X3KW5TCP.js} +4 -2
  53. package/dist/server-RWM2JI3D.js +46 -0
  54. package/dist/ssh-VAI5DU5M.js +11 -0
  55. package/dist/ssh-key-DUO6OKOI.js +7 -0
  56. package/dist/stack-config-R4SPSUQZ.js +12 -0
  57. package/dist/{validate-BOQRWX4F.js → validate-YVI3BJ55.js} +1 -1
  58. package/dist/wait-WO72TRRQ.js +79 -0
  59. package/package.json +11 -3
  60. package/spec/mcp-tools.yaml +21 -2
  61. package/spec/models.yaml +14 -3
  62. package/spec/providers.schema.json +163 -28
  63. package/dist/apply-AM5HERE7.js +0 -19
  64. package/dist/automation-RULUSJBW.js +0 -30
  65. package/dist/aws-AMON7DA7.js +0 -265
  66. package/dist/azure-T4G6OBJQ.js +0 -273
  67. package/dist/chunk-CKSPROD5.js +0 -129
  68. package/dist/chunk-LU2V3LXA.js +0 -114
  69. package/dist/context-KHKWJJMC.js +0 -10
  70. package/dist/gcp-2JBTD7ZM.js +0 -204
  71. package/dist/generate-2XV2GSP2.js +0 -15
  72. package/dist/local-DXBEVZ5C.js +0 -47
  73. package/dist/server-A5AB67GQ.js +0 -36
  74. package/dist/ssh-B3NZAU4I.js +0 -8
  75. package/dist/{version-guard-GPSTCPT7.js → version-guard-XVYW5WFJ.js} +1 -1
package/README.md CHANGED
@@ -12,6 +12,83 @@ and Cursor drive them through typed MCP tools with explicit safety controls.
12
12
 
13
13
  ---
14
14
 
15
+ ## What's new in 2.0.1
16
+
17
+ A patch release, and a large one: in 2.0.0 no cloud deploy succeeded by any path. Every item
18
+ below is a fix or an addition in 2.0.1. The reasoning behind each one is in its commit message,
19
+ and the decisions that came out of them are in [`docs/decisions/`](docs/decisions/).
20
+
21
+ ### Deploying to a cloud
22
+
23
+ - `clawops plan` → `clawops apply` provisions a cloud stack and deploys OpenClaw onto it.
24
+ - `clawops up` deploys to AWS, GCP and Azure, running the same path as `plan` → `apply`.
25
+ - clawops installs the Pulumi CLI it needs into `~/.clawops/.pulumi-cli`, or uses a compatible
26
+ one already on `$PATH` ([ADR 0010](docs/decisions/0010-pulumi-cli-bootstrap.md)).
27
+ - clawops creates and stores the passphrase its state backend requires
28
+ ([ADR 0011](docs/decisions/0011-state-passphrase.md)).
29
+ - `clawops plan` takes `--ssh-cidr`, `--gateway-cidr` and `--publish-gateway`, and `apply`
30
+ passes them to the cloud firewall. `auto` resolves this machine's address.
31
+ - `clawops plan` stops, and names the cause, when it cannot open the state backend.
32
+ - `--instance-type` takes a clawops alias (`micro`–`gpu`) or a machine type your cloud names
33
+ itself, and the plan records the concrete type.
34
+ - Deploys pin the account they were planned against: `gcp:project` on GCP,
35
+ `azure-native:subscriptionId` on Azure.
36
+
37
+ ### Checking the account before you spend
38
+
39
+ - `clawops doctor --provider <cloud>` checks one cloud's credentials and account setup, with or
40
+ without a stack. `--instance-type` points the size check at the size you are deploying.
41
+ - **AWS** — the account the credentials resolve to, the state bucket, and whether the instance
42
+ type is offered in the region.
43
+ - **GCP** — the project, the APIs a deploy needs, and the state bucket.
44
+ - **Azure** — the subscription, the resource providers, the VM size, and the azblob credentials
45
+ Pulumi authenticates with.
46
+ - `clawops setup` runs the same checks and offers to fix what it safely can — enabling an API,
47
+ creating a state bucket with versioning on and public access blocked — naming the change
48
+ before making it.
49
+ - A check clawops could not perform reports as a warning naming the error, rather than as a
50
+ pass or a failure.
51
+ - Azure accepts your `az login`; a service principal is no longer required.
52
+
53
+ ### Naming, config and setup
54
+
55
+ - clawops names the state backend after the account it is deploying into, instead of asking you
56
+ for a name or writing a placeholder
57
+ ([ADR 0012](docs/decisions/0012-state-bucket-naming.md)).
58
+ - A name you type instead is checked against the rules of the cloud that has to accept it.
59
+ - `clawops init` keeps the stacks already in your config.
60
+ - `clawops init` generates an SSH key that clawops can read. If you ran `init` before this
61
+ release, `clawops doctor` will tell you whether yours is usable.
62
+ - `gcloud config set project` is honoured.
63
+ - The setup wizard writes model configuration that OpenClaw accepts, and installs the plugin
64
+ your chosen provider needs.
65
+ - Amazon Bedrock works: the right transport, and an inference profile resolved against your
66
+ deployment region and recorded in the plan. Needs `bedrock:ListInferenceProfiles`.
67
+
68
+ ### While a deploy is running
69
+
70
+ - `apply` waits for SSH, then waits for the gateway to answer, before reporting success.
71
+ - `apply` reports progress as it goes instead of going quiet for minutes.
72
+ - A deploy that times out prints what the host was doing, from its bootstrap log.
73
+ - A host still installing Docker is treated as still booting rather than as a failed deploy.
74
+
75
+ ### Day-two commands
76
+
77
+ - `clawops logs` reads from the gateway on AWS.
78
+ - `doctor --stack`, `ssh`, `logs`, `gateway`, `config` and `agents` work against a freshly
79
+ deployed stack.
80
+ - `clawops doctor` validates cloud credentials.
81
+ - clawops tells a refused Docker socket from a missing container, and says which it found.
82
+ - `clawops destroy` forgets the instance's host key, so redeploying onto an address the cloud
83
+ has recycled no longer fails verification.
84
+
85
+ ### Documentation
86
+
87
+ - The GCP guide names the credential source clawops actually reads, and describes 2.0
88
+ firewall behaviour.
89
+ - The smoke-test plan covers 2.0, and `pnpm test:cloud aws|gcp|azure` runs it against a real
90
+ deployment and destroys it afterwards.
91
+
15
92
  ## What's new in 2.0
16
93
 
17
94
  clawops 2.x targets **OpenClaw >= 2026.9.2**. The 1.x line continues for OpenClaw
@@ -26,9 +103,6 @@ Pin the tag in CI — `latest` moves to 2.x, so an unpinned pipeline will change
26
103
  [`CHANGELOG.md`](CHANGELOG.md) carries the full history; this section covers what changed
27
104
  about *how clawops behaves*.
28
105
 
29
- > **Release in progress.** 2.0 has not shipped yet. This section is written as the work
30
- > lands so the flows stay documented while they are being changed, not reconstructed after.
31
-
32
106
  ### Your deployment keeps its state
33
107
 
34
108
  OpenClaw 2.0 stores sessions, transcripts and credentials in SQLite. clawops mounted no
@@ -355,6 +429,9 @@ was invisible.
355
429
  whole internet, on the path most first-time users take. It offers your own IP as a `/32` now,
356
430
  and when that cannot be detected it offers no default and requires an answer.
357
431
 
432
+ **`clawops plan` could not express any of it, and `apply` never passed any of it to Pulumi.**
433
+ Both are fixed in 2.0.1 — see the list at the top of this section.
434
+
358
435
  ### The gateway port comes from the plan
359
436
 
360
437
  ```jsonc
@@ -377,7 +454,7 @@ Local deployments use `clawops up --gateway-port 9443`.
377
454
 
378
455
  ```mermaid
379
456
  flowchart TD
380
- A["clawops doctor"] --> B["local: Node, Pulumi home,<br/>config, SSH key, credentials"]
457
+ A["clawops doctor"] --> B["local: Node, Pulumi CLI + home,<br/>config, SSH key, credentials"]
381
458
  B --> C{"--stack given?"}
382
459
  C -- no --> Z["report"]
383
460
  C -- yes --> D["container state"]
@@ -444,7 +521,8 @@ every agent on the host. Use `clawops gateway restart`, or stay on `@clawops/cli
444
521
  ## What clawops does
445
522
 
446
523
  - Provisions and tears down OpenClaw infrastructure on **AWS, GCP, Azure, and local VMs** using
447
- the Pulumi Automation API (embedded — no `pulumi` binary required).
524
+ the Pulumi Automation API — you do not install Pulumi; clawops installs the CLI it needs into
525
+ `~/.clawops/.pulumi-cli` on first use.
448
526
  - Manages day-to-day operations: status, logs, SSH, tunnels, config, agents, gateway, backups.
449
527
  - Exposes every operation as a **typed MCP tool** so AI agents can drive ops safely.
450
528
  - Enforces a **plan → review → apply** discipline for cloud deployments.
@@ -515,6 +593,8 @@ and prints a direct dashboard URL:
515
593
 
516
594
  **Prerequisites:** Node.js ≥ 22, an SSH key, and either an SSH-reachable Linux/macOS host or a
517
595
  cloud account with CLI credentials configured (`aws configure`, `gcloud auth login`, or `az login`).
596
+ You do not need Pulumi — the first cloud deployment installs the CLI clawops drives into
597
+ `~/.clawops/.pulumi-cli` and says so while it does.
518
598
 
519
599
  For a full narrated walkthrough with example output, see [`docs/demo-script.md`](docs/demo-script.md).
520
600
 
@@ -547,10 +627,14 @@ clawops init --provider aws
547
627
 
548
628
  # Edit ~/.clawops/config.json — set stateUrl to your S3 bucket
549
629
 
550
- clawops plan --provider aws --stack default --out /tmp/plan.json
630
+ clawops plan --provider aws --stack default --ssh-cidr auto --out /tmp/plan.json
551
631
  clawops apply /tmp/plan.json
552
632
  ```
553
633
 
634
+ `--ssh-cidr auto` allows SSH from this machine's public IP, resolved while the plan is
635
+ generated and written into it. Without it the plan allows no ingress at all and nothing —
636
+ including clawops — will be able to connect.
637
+
554
638
  ---
555
639
 
556
640
  ## Connect an AI editor
@@ -626,12 +710,12 @@ clawops down --yes # Destroy local-provider stack
626
710
  | Command | Description |
627
711
  |---|---|
628
712
  | `setup` | First-run wizard — guided LLM, integrations, and deploy-plan generation |
629
- | `init` | Register a stack in `~/.clawops/config.json` without provisioning |
713
+ | `init` | Register a stack in `~/.clawops/config.json` without provisioning. Additive — existing stacks are kept; `--force` is needed only to overwrite one |
630
714
  | `up` | Provision or update stack (`--dry-run` for preview, `--gateway-port` for a non-default port) |
631
715
  | `down` | Destroy local-provider stack (requires `--yes`; `--dry-run` shows current outputs) |
632
716
  | `destroy` | Destroy cloud-provider stack with confirmation prompt (`--dry-run` shows current outputs) |
633
717
  | `status` | Show stack outputs: IP, gateway URL, region, provisioned time |
634
- | `plan` | Generate a deploy-plan JSON artifact (dry-run safe) |
718
+ | `plan` | Generate a deploy-plan JSON artifact (dry-run safe). `--ssh-cidr <list\|auto>` and `--gateway-cidr` decide who may connect; `--publish-gateway loopback\|all` decides what is listening |
635
719
  | `apply` | Apply a previously reviewed plan file (`--dry-run` validates and shows diff without applying) |
636
720
  | `ssh` | Interactive SSH session or run a remote command |
637
721
  | `logs` | Stream OpenClaw logs (`-f`, `--tail N`, `--since 5m`) |
@@ -641,7 +725,7 @@ clawops down --yes # Destroy local-provider stack
641
725
  | `gateway` | Restart the OpenClaw gateway service |
642
726
  | `backup` | Create and restore OpenClaw state backups (`restore` expands into a staging directory, never in place) |
643
727
  | `stacks` | List named stacks and their state |
644
- | `doctor` | Check the local machine; with `--stack`, the deployment's health too. `--json` for the report. Exits 1 on any failure |
728
+ | `doctor` | Check the local machine; with `--stack`, the deployment's health too; with `--provider`, one cloud's credentials and account setup whether or not a stack exists; with `--instance-type`, account checks ask about that size rather than the provider default. `--json` for the report. Exits 1 on any failure |
645
729
  | `secret` | Manage secrets: `list`, `set`, `delete`, `rotate`, `audit` |
646
730
  | `monitor` | Live dashboard: gateway health, container stats, log tail, stack picker |
647
731
  | `mcp serve` | Start the embedded MCP server (stdio, or HTTP with `--http <port> --token <t>`) |
@@ -661,7 +745,8 @@ For non-local providers, clawops enforces a review-before-apply discipline:
661
745
 
662
746
  ```bash
663
747
  # 1. Generate a plan — runs `pulumi preview` internally, produces JSON
664
- clawops plan --provider aws --region us-east-1 --out /tmp/plan.json
748
+ # --ssh-cidr decides who may connect. `auto` means this machine; omit it and nobody can.
749
+ clawops plan --provider aws --region us-east-1 --ssh-cidr auto --out /tmp/plan.json
665
750
 
666
751
  # 2. Review plan.json — the `diff` field shows projected changes at plan-generation time
667
752
  cat /tmp/plan.json | jq .diff
@@ -827,7 +912,7 @@ clawops
827
912
 
828
913
  Key design decisions:
829
914
 
830
- - **Pulumi Automation API (embedded):** no `pulumi` binary required; Pulumi home is sandboxed to `~/.clawops/.pulumi`; stack programs are inline TypeScript closures
915
+ - **Pulumi Automation API:** the user installs no Pulumi — clawops installs the CLI the API drives into `~/.clawops/.pulumi-cli`, pinned to the bundled SDK, without editing `$PATH` (ADR 0010); Pulumi home is sandboxed to `~/.clawops/.pulumi`; stack programs are inline TypeScript closures
831
916
  - **State in cloud blob storage:** GCS (`gs://`), S3 (`s3://`), Azure Blob — no local state files, no `pulumi.yaml`
832
917
  - **SSH via `ssh2`:** never shells out to `/usr/bin/ssh`; TOFU host verification against `~/.clawops/known_hosts`; connection pool with 5-min idle TTL
833
918
  - **Plan → apply discipline:** every non-local deployment goes through `generatePlan()` → review → `applyPlan()`; destructive changes always require human review of the plan JSON
@@ -926,13 +1011,15 @@ pnpm dev doctor # verify toolchain
926
1011
  ```bash
927
1012
  pnpm dev # run CLI from src/ via tsx
928
1013
  pnpm build # tsup → dist/
929
- pnpm test # vitest (1210 tests, ~13s)
1014
+ pnpm test # vitest (1669 tests, ~12s)
930
1015
  pnpm test:changed # vitest --changed (fast edit loop)
931
1016
  pnpm test:integration # Docker-based SSH integration tests
932
1017
  pnpm typecheck # tsc --noEmit
933
1018
  pnpm lint # eslint src/ tests/ scripts/ (--max-warnings=0)
934
1019
  pnpm gen:schemas # regenerate src/providers/types.ts + src/mcp/tools/_generated.ts
935
1020
  pnpm gen:schemas --check # CI guard: committed generated files match spec
1021
+ pnpm graph # local coupling report (--base <ref> for this branch's delta)
1022
+ pnpm verify:pack # install the packed tarball elsewhere and run it (CI gate)
936
1023
  pnpm changeset # record a release note before merging
937
1024
  ```
938
1025
 
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ applyPlan
4
+ } from "./chunk-ZMMRIWFS.js";
5
+ import "./chunk-BOPSG2LI.js";
6
+ import "./chunk-6ZFIFDBJ.js";
7
+ import "./chunk-2I7KCMXZ.js";
8
+ import "./chunk-HSPHXG6W.js";
9
+ import "./chunk-AEEMRFZV.js";
10
+ import "./chunk-BSLIYB5Q.js";
11
+ import "./chunk-WTZ2CT35.js";
12
+ import "./chunk-NUDIHZEH.js";
13
+ import "./chunk-JW5A7E3X.js";
14
+ import "./chunk-LEVIW2PV.js";
15
+ import "./chunk-GIEVAGEK.js";
16
+ import "./chunk-VOEJYRIK.js";
17
+ import "./chunk-EI7CQQ7D.js";
18
+ import "./chunk-FEK7VJAY.js";
19
+ import "./chunk-RHOWYIWT.js";
20
+ import "./chunk-N4WZLWJ4.js";
21
+ import "./chunk-A2I76FTA.js";
22
+ import "./chunk-CX5SL5HP.js";
23
+ import "./chunk-KGXPLI7W.js";
24
+ import "./chunk-VUFMZEIM.js";
25
+ export {
26
+ applyPlan
27
+ };
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ensurePulumiCli
4
+ } from "./chunk-PSOSBJVO.js";
5
+ import {
6
+ ensurePassphrase
7
+ } from "./chunk-V25QRZRB.js";
8
+ import {
9
+ getConfigDir
10
+ } from "./chunk-CX5SL5HP.js";
11
+ import "./chunk-KGXPLI7W.js";
12
+
13
+ // src/pulumi/automation.ts
14
+ import path from "path";
15
+ import { LocalWorkspace } from "@pulumi/pulumi/automation/index.js";
16
+ async function getOrCreateStack(opts) {
17
+ const configDir = opts.configDir ?? getConfigDir();
18
+ const pulumiCommand = await ensurePulumiCli({ configDir, onInstall: opts.onInstall });
19
+ const passphrase = ensurePassphrase(configDir);
20
+ return await LocalWorkspace.createOrSelectStack(
21
+ {
22
+ stackName: opts.stack,
23
+ projectName: "clawops",
24
+ program: opts.program
25
+ },
26
+ {
27
+ pulumiCommand,
28
+ pulumiHome: path.join(configDir, ".pulumi"),
29
+ envVars: {
30
+ PULUMI_BACKEND_URL: opts.stateUrl,
31
+ PULUMI_SKIP_UPDATE_CHECK: "1",
32
+ // A self-managed backend has no key service, so a new stack cannot be created without
33
+ // a secrets manager, and the only one available is a passphrase. Undefined when the
34
+ // operator supplies their own — overriding it would orphan their existing stacks.
35
+ // See ./passphrase.ts and ADR 0011.
36
+ ...passphrase ? { PULUMI_CONFIG_PASSPHRASE: passphrase } : {}
37
+ // R6: cloud credentials inherited from process.env, never set here
38
+ }
39
+ }
40
+ );
41
+ }
42
+ export {
43
+ getOrCreateStack
44
+ };
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/openclaw/bedrock.ts
4
+ function resolveInferenceProfile(foundationModelId, profiles, region) {
5
+ const candidates = profiles.map((p) => p.inferenceProfileId).filter((id) => id.endsWith(`.${foundationModelId}`));
6
+ if (candidates.length === 0) {
7
+ return {
8
+ ok: false,
9
+ error: `No Bedrock inference profile for "${foundationModelId}" in ${region}. Bedrock refuses bare foundation-model ids for on-demand inference, so this model cannot be used there. Check the model is enabled in this region, or choose another.`
10
+ };
11
+ }
12
+ const geo = geographyFor(region);
13
+ const regional = candidates.find((id) => id.startsWith(`${geo}.`));
14
+ if (regional) return { ok: true, profileId: regional, why: `${geo} profile for ${region}` };
15
+ const global = candidates.find((id) => id.startsWith("global."));
16
+ if (global) {
17
+ return {
18
+ ok: true,
19
+ profileId: global,
20
+ why: `no ${geo} profile for ${region}; using the global profile`
21
+ };
22
+ }
23
+ return {
24
+ ok: false,
25
+ error: `Bedrock has inference profiles for "${foundationModelId}" (${candidates.join(", ")}) but none for ${region} or global. Using one would route inference to another geography; choose a region where the model is offered.`
26
+ };
27
+ }
28
+ function geographyFor(region) {
29
+ const prefix = region.split("-")[0] ?? region;
30
+ if (prefix === "ap") return "apac";
31
+ return prefix;
32
+ }
33
+ async function fetchInferenceProfiles(region, signal) {
34
+ try {
35
+ const { BedrockClient, ListInferenceProfilesCommand } = await import("@aws-sdk/client-bedrock");
36
+ const client = new BedrockClient({ region });
37
+ const profiles = [];
38
+ let nextToken;
39
+ do {
40
+ const page = await client.send(
41
+ new ListInferenceProfilesCommand({ maxResults: 100, nextToken }),
42
+ { abortSignal: signal }
43
+ );
44
+ for (const p of page.inferenceProfileSummaries ?? []) {
45
+ if (p.inferenceProfileId) profiles.push({ inferenceProfileId: p.inferenceProfileId });
46
+ }
47
+ nextToken = page.nextToken;
48
+ } while (nextToken);
49
+ return { ok: true, profiles };
50
+ } catch (err) {
51
+ return {
52
+ ok: false,
53
+ error: `Could not list Bedrock inference profiles in ${region}: ${err instanceof Error ? err.message : String(err)}. The deploying identity needs bedrock:ListInferenceProfiles.`
54
+ };
55
+ }
56
+ }
57
+ export {
58
+ fetchInferenceProfiles,
59
+ geographyFor,
60
+ resolveInferenceProfile
61
+ };
@@ -1,8 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  acquireSession
4
- } from "./chunk-3WYA2QDM.js";
5
- import "./chunk-AHAAEXWC.js";
4
+ } from "./chunk-2Z7J3ERQ.js";
5
+ import "./chunk-E4NJSJ54.js";
6
+ import "./chunk-MC5ATCXS.js";
7
+ import {
8
+ gatewayRunArgs,
9
+ gatewayRunCommand
10
+ } from "./chunk-GIEVAGEK.js";
11
+ import {
12
+ GATEWAY_PORT
13
+ } from "./chunk-VOEJYRIK.js";
14
+ import "./chunk-EI7CQQ7D.js";
6
15
  import {
7
16
  writeLocalState
8
17
  } from "./chunk-A2I76FTA.js";
@@ -10,12 +19,6 @@ import "./chunk-CX5SL5HP.js";
10
19
  import {
11
20
  ProviderError
12
21
  } from "./chunk-KGXPLI7W.js";
13
- import {
14
- GATEWAY_PORT,
15
- gatewayRunArgs,
16
- gatewayRunCommand
17
- } from "./chunk-6TIYVHPX.js";
18
- import "./chunk-EI7CQQ7D.js";
19
22
 
20
23
  // src/providers/local/bootstrap.ts
21
24
  import { readFileSync, existsSync } from "fs";
@@ -4,15 +4,17 @@ import {
4
4
  } from "./chunk-NUDIHZEH.js";
5
5
  import {
6
6
  CONTAINER_UID,
7
- GATEWAY_PORT,
8
- IMAGE_INSPECT_CMD,
9
7
  PUBLISH_INSPECT_CMD,
10
8
  configPathForOS,
11
9
  gatewayRunCommand,
12
- imageForRestart,
13
10
  publishForRestart,
14
11
  stateDirForOS
15
- } from "./chunk-6TIYVHPX.js";
12
+ } from "./chunk-GIEVAGEK.js";
13
+ import {
14
+ GATEWAY_PORT,
15
+ IMAGE_INSPECT_CMD,
16
+ imageForRestart
17
+ } from "./chunk-VOEJYRIK.js";
16
18
 
17
19
  // src/plan/remote-config.ts
18
20
  var OPENCLAW_CONFIG_LINUX = configPathForOS("Linux");
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  connect
4
- } from "./chunk-AHAAEXWC.js";
4
+ } from "./chunk-E4NJSJ54.js";
5
5
  import {
6
6
  NetworkError
7
7
  } from "./chunk-KGXPLI7W.js";
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ execPrivileged
4
+ } from "./chunk-NUDIHZEH.js";
5
+
6
+ // src/openclaw/docker.ts
7
+ function looksLikeStillBooting(detail) {
8
+ const text = detail.toLowerCase();
9
+ return text.includes("command not found") || text.includes("cannot connect to the docker daemon") || text.includes("is the docker daemon running") || text.includes("no such file or directory");
10
+ }
11
+ function saysNoSuchObject(text) {
12
+ return /no such (object|container)/i.test(text);
13
+ }
14
+ async function inspectContainer(session, name, format, signal, dockerCmd = "docker") {
15
+ const result = await execPrivileged(
16
+ session,
17
+ `${dockerCmd} inspect ${name} --format '${format}'`,
18
+ signal
19
+ );
20
+ if (result.code === 0) return { kind: "ok", value: result.stdout.trim() };
21
+ const text = `${result.stderr} ${result.stdout}`.trim();
22
+ if (saysNoSuchObject(text)) return { kind: "missing" };
23
+ return { kind: "error", detail: text === "" ? `docker inspect exited ${result.code}` : text };
24
+ }
25
+ async function containerStatus(session, name = "openclaw", signal) {
26
+ const result = await inspectContainer(session, name, "{{.State.Status}}", signal);
27
+ if (result.kind === "ok") return { status: result.value || "unknown" };
28
+ if (result.kind === "missing") return { status: "not found" };
29
+ return { status: "unknown", error: result.detail };
30
+ }
31
+
32
+ export {
33
+ looksLikeStillBooting,
34
+ inspectContainer,
35
+ containerStatus
36
+ };