@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.
- package/README.md +99 -12
- package/dist/apply-WOUTUUQH.js +27 -0
- package/dist/automation-UKRM3BD2.js +44 -0
- package/dist/bedrock-XVASJX2K.js +61 -0
- package/dist/{bootstrap-DMRPWJGN.js → bootstrap-HNGKY6WW.js} +11 -8
- package/dist/{chunk-N44K3LHE.js → chunk-2I7KCMXZ.js} +6 -4
- package/dist/{chunk-3WYA2QDM.js → chunk-2Z7J3ERQ.js} +1 -1
- package/dist/chunk-3OLUJPCB.js +36 -0
- package/dist/{chunk-GYV4KNW7.js → chunk-5TXCUOZV.js} +133 -40
- package/dist/chunk-AEEMRFZV.js +24 -0
- package/dist/{chunk-YR3O2I3S.js → chunk-BSLIYB5Q.js} +6 -1
- package/dist/{chunk-AHAAEXWC.js → chunk-E4NJSJ54.js} +16 -93
- package/dist/chunk-FEK7VJAY.js +16 -0
- package/dist/{chunk-6TIYVHPX.js → chunk-GIEVAGEK.js} +7 -33
- package/dist/chunk-HSPHXG6W.js +67 -0
- package/dist/chunk-LEVIW2PV.js +890 -0
- package/dist/chunk-MC5ATCXS.js +108 -0
- package/dist/chunk-N4WZLWJ4.js +16 -0
- package/dist/{chunk-SBLQH2TJ.js → chunk-NUL5F6YV.js} +1 -1
- package/dist/chunk-PSOSBJVO.js +51 -0
- package/dist/{chunk-AX74VXTV.js → chunk-RATEIAFU.js} +58 -49
- package/dist/chunk-RHOWYIWT.js +52 -0
- package/dist/{chunk-WHJMKDV7.js → chunk-SDZKCICH.js} +65 -12
- package/dist/chunk-V25QRZRB.js +45 -0
- package/dist/chunk-VOEJYRIK.js +33 -0
- package/dist/{providers-2OABPW2E.js → chunk-VUFMZEIM.js} +3 -2
- package/dist/chunk-WTZ2CT35.js +69 -0
- package/dist/{chunk-UFRO4H5W.js → chunk-ZMMRIWFS.js} +56 -40
- package/dist/cli-NH7VUNOU.js +11 -0
- package/dist/cli-auth-GD5SK7JU.js +13 -0
- package/dist/cli.js +518 -206
- package/dist/context-M4JBBIAV.js +20 -0
- package/dist/docker-3PDO4TS7.js +12 -0
- package/dist/{firewall-T3NTPZJD.js → firewall-5VKZ4WBH.js} +22 -1
- package/dist/generate-J6MIJGII.js +29 -0
- package/dist/{harden-PCDK3UQ5.js → harden-72JMRT4E.js} +8 -5
- package/dist/index.d.ts +56 -0
- package/dist/known-hosts-file-I3Q3NBYQ.js +22 -0
- package/dist/{logs-P7YCKVF4.js → logs-5A7YYA53.js} +1 -1
- package/dist/{mcp-wire-BFEY2Y6H.js → mcp-wire-NTGL5ITF.js} +25 -15
- package/dist/models-AGTQWYDL.js +32 -0
- package/dist/network-args-PYJRJBYO.js +64 -0
- package/dist/{package-3HUW57XH.js → package-3A6BOALU.js} +11 -3
- package/dist/passphrase-MJBE45N7.js +15 -0
- package/dist/{pool-EX4ZTA5P.js → pool-PIIKJ4L2.js} +3 -2
- package/dist/preflight-EF52S3WW.js +162 -0
- package/dist/preflight-MWWM5JT3.js +181 -0
- package/dist/preflight-TFD4N5ZU.js +141 -0
- package/dist/providers-FW6H6OPF.js +11 -0
- package/dist/ready-KDNRBNSU.js +95 -0
- package/dist/{remote-config-Z6IIZR3D.js → remote-config-3HZLUWEA.js} +3 -2
- package/dist/{runtime-LZ7NA53V.js → runtime-X3KW5TCP.js} +4 -2
- package/dist/server-RWM2JI3D.js +46 -0
- package/dist/ssh-VAI5DU5M.js +11 -0
- package/dist/ssh-key-DUO6OKOI.js +7 -0
- package/dist/stack-config-R4SPSUQZ.js +12 -0
- package/dist/{validate-BOQRWX4F.js → validate-YVI3BJ55.js} +1 -1
- package/dist/wait-WO72TRRQ.js +79 -0
- package/package.json +11 -3
- package/spec/mcp-tools.yaml +21 -2
- package/spec/models.yaml +14 -3
- package/spec/providers.schema.json +163 -28
- package/dist/apply-AM5HERE7.js +0 -19
- package/dist/automation-RULUSJBW.js +0 -30
- package/dist/aws-AMON7DA7.js +0 -265
- package/dist/azure-T4G6OBJQ.js +0 -273
- package/dist/chunk-CKSPROD5.js +0 -129
- package/dist/chunk-LU2V3LXA.js +0 -114
- package/dist/context-KHKWJJMC.js +0 -10
- package/dist/gcp-2JBTD7ZM.js +0 -204
- package/dist/generate-2XV2GSP2.js +0 -15
- package/dist/local-DXBEVZ5C.js +0 -47
- package/dist/server-A5AB67GQ.js +0 -36
- package/dist/ssh-B3NZAU4I.js +0 -8
- 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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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-
|
|
5
|
-
import "./chunk-
|
|
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-
|
|
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");
|
|
@@ -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
|
+
};
|