@clawscarf/cli 0.1.0-alpha.1 → 0.1.0-alpha.3
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 +111 -2
- package/THIRD_PARTY_NOTICES.md +5 -0
- package/deploy/execution/network/node-ingress.cfg +1 -1
- package/package.json +1 -1
- package/recipes/team-server/recipe.json +1 -1
- package/release/README.md +64 -14
- package/release/components.json +24 -0
- package/release/operator.md +29 -3
- package/runtime/releases/0.1.0-alpha.3.json +63 -0
- package/scripts/controller.js +6 -0
- package/scripts/deployment/browser-node-compose.js +0 -1
- package/scripts/deployment/certificates.js +1 -1
- package/scripts/deployment/compose.js +3 -5
- package/scripts/deployment/configuration.js +5 -5
- package/scripts/deployment/controller-compose.js +14 -6
- package/scripts/deployment/model-gateway-compose.js +6 -0
- package/scripts/deployment/model-gateway.js +9 -1
- package/scripts/deployment/networks.js +21 -0
- package/scripts/deployment/prepare.js +11 -5
- package/scripts/installation/installer/prompts.js +5 -1
- package/scripts/installation/prerequisites.js +10 -5
- package/scripts/installation/resolve.js +6 -5
- package/scripts/installation/runtime.js +3 -3
- package/scripts/release/definition.js +30 -3
- package/runtime/releases/0.1.0-alpha.1.json +0 -37
package/README.md
CHANGED
|
@@ -1,3 +1,112 @@
|
|
|
1
|
-
# ClawScarf
|
|
1
|
+
# ClawScarf 🧣
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
### OpenClaw, ready for your team.
|
|
4
|
+
|
|
5
|
+
Give your team a shared OpenClaw server—with individual logins, your choice of AI
|
|
6
|
+
models, connected accounts, and execution protected by NVIDIA OpenShell.
|
|
7
|
+
**Self-hosted. Open source. OpenClaw’s own interface.**
|
|
8
|
+
|
|
9
|
+
[Get started](#get-started) · [Installation guide](https://github.com/clawscarf/clawscarf/blob/main/deploy/deployment/installation.md) · [Releases](https://github.com/clawscarf/clawscarf/releases) · [Contribute](https://github.com/clawscarf/clawscarf/blob/main/CONTRIBUTING.md)
|
|
10
|
+
|
|
11
|
+
## Get started
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
curl -fsSL https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.3/install.sh | sh
|
|
15
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
16
|
+
clawscarf configure
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Choose Team server. Pick a model. Sign in. Start your server.**
|
|
20
|
+
|
|
21
|
+
The terminal setup walks you through it, downloads the runtime, and opens OpenClaw
|
|
22
|
+
in your browser. Your sign-in becomes the first administrator account. No checkout,
|
|
23
|
+
Docker configuration files or separate OpenClaw installation required.
|
|
24
|
+
|
|
25
|
+
You’ll need **Docker Engine 29+ with Compose** (or Docker Desktop) and an **API key
|
|
26
|
+
for your chosen model provider**. The CLI includes its own Node runtime.
|
|
27
|
+
Available for **macOS Apple Silicon and Linux ARM64/x86-64**; Windows runs through
|
|
28
|
+
**WSL2, currently experimental**. Add `~/.local/bin` to your shell's PATH to keep
|
|
29
|
+
the command available in new terminals. The installer requires no sudo.
|
|
30
|
+
[Full platform requirements →](https://github.com/clawscarf/clawscarf/blob/main/deploy/deployment/installation.md)
|
|
31
|
+
|
|
32
|
+
<details>
|
|
33
|
+
<summary>Prefer npm?</summary>
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm install -g @clawscarf/cli@next
|
|
37
|
+
clawscarf configure
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This alternative uses your own Node installation: Node 24.16+ within 24.x or 26.1+.
|
|
41
|
+
The `next` tag installs the current alpha.
|
|
42
|
+
|
|
43
|
+
</details>
|
|
44
|
+
|
|
45
|
+
## Make it your team’s workspace
|
|
46
|
+
|
|
47
|
+
| What you want to do | Where to do it |
|
|
48
|
+
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| **Start working with an agent** | Open a chat in OpenClaw. Create and configure agents using its native settings. |
|
|
50
|
+
| **Bring in your teammates** | Open **People**, create an invitation link, and assign native OpenClaw roles. Administrators can remove access there too. |
|
|
51
|
+
| **Connect Outlook and other services** | Open **Connections**, link an account, and choose which agents can use it. Connections is included in Team server and can be turned off during setup. |
|
|
52
|
+
| **Use your preferred models** | Choose the provider, model and reasoning level in setup. Run `clawscarf configure` again to change them. |
|
|
53
|
+
|
|
54
|
+
The default recipe uses **GPT-6 Astra through OpenAI, with medium reasoning**.
|
|
55
|
+
Choose another provider or model in the menu; usage is billed by your provider.
|
|
56
|
+
Your provider API key stays outside OpenClaw, in the model gateway.
|
|
57
|
+
|
|
58
|
+
Setup opens **[http://127.0.0.1:18800](http://127.0.0.1:18800)** by default.
|
|
59
|
+
That address is local to your machine. To share the server with teammates, use a
|
|
60
|
+
reachable HTTPS address; the [installation guide](https://github.com/clawscarf/clawscarf/blob/main/deploy/deployment/installation.md#configuration-options)
|
|
61
|
+
covers the public URL and company SSO settings.
|
|
62
|
+
|
|
63
|
+
OpenShell applies filesystem and network policy to the whole OpenClaw runtime,
|
|
64
|
+
including plugins, shell commands and local tools. Recipes bring login, models and
|
|
65
|
+
optional Connections together in one setup; you keep OpenClaw’s native agents,
|
|
66
|
+
conversations, skills and settings.
|
|
67
|
+
|
|
68
|
+
## Everyday commands
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
clawscarf status # Check the server
|
|
72
|
+
clawscarf stop # Stop it, keeping your data
|
|
73
|
+
clawscarf start # Start it again
|
|
74
|
+
clawscarf configure # Change its configuration
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The default installation directory is `~/clawscarf-team`. Use
|
|
78
|
+
`--directory ~/another-team` to create or manage a separate installation.
|
|
79
|
+
Chats, files and settings survive stop/start. Closing the terminal leaves the server
|
|
80
|
+
running; Docker must stay running while it is in use.
|
|
81
|
+
|
|
82
|
+
For automation, the same configuration choices are available as command-line flags
|
|
83
|
+
with `--non-interactive`, and commands support `--json`.
|
|
84
|
+
[CLI reference and examples →](https://github.com/clawscarf/clawscarf/blob/main/deploy/deployment/installation.md#configure-without-prompts)
|
|
85
|
+
|
|
86
|
+
## Your server, your team
|
|
87
|
+
|
|
88
|
+
An installation serves **one trusted team**: separate logins and roles, shared
|
|
89
|
+
execution files and browser accounts. Native roles control application permissions;
|
|
90
|
+
people allowed to run code share the Gateway’s authority. Use separate installations
|
|
91
|
+
for teams that must be isolated from one another.
|
|
92
|
+
|
|
93
|
+
Default login and optional Connections use **ClawScarf Cloud**. You can use your own
|
|
94
|
+
OIDC provider and disable Connections to run without those hosted services. Original
|
|
95
|
+
model-provider keys and Connections management credentials stay outside OpenClaw.
|
|
96
|
+
|
|
97
|
+
ClawScarf is in alpha. Browser automation is off by default while an upstream routing
|
|
98
|
+
issue remains; retained data still needs backups. See the [security boundaries](https://github.com/clawscarf/clawscarf/blob/main/deploy/openshell/README.md),
|
|
99
|
+
[browser support](https://github.com/clawscarf/clawscarf/blob/main/deploy/execution/browser-node/README.md) and [open work](https://github.com/clawscarf/clawscarf/blob/main/TODO.md)
|
|
100
|
+
for details.
|
|
101
|
+
|
|
102
|
+
## Explore and contribute
|
|
103
|
+
|
|
104
|
+
- **Using ClawScarf:** [Installation](https://github.com/clawscarf/clawscarf/blob/main/deploy/deployment/installation.md) · [People](https://github.com/clawscarf/clawscarf/blob/main/plugins/access/README.md) · [Connections](https://github.com/clawscarf/clawscarf/blob/main/plugins/connections/README.md) · [Models](https://github.com/clawscarf/clawscarf/blob/main/deploy/models/README.md)
|
|
105
|
+
- **Customizing it:** [Recipes](https://github.com/clawscarf/clawscarf/blob/main/recipes/README.md) · [Packs](https://github.com/clawscarf/clawscarf/blob/main/packs/README.md) · [Runtime architecture](https://github.com/clawscarf/clawscarf/blob/main/runtime/README.md)
|
|
106
|
+
- **Helping build it:** [Contributing](https://github.com/clawscarf/clawscarf/blob/main/CONTRIBUTING.md) · [Development setup](https://github.com/clawscarf/clawscarf/blob/main/scripts/README.md) · [Report a bug](https://github.com/clawscarf/clawscarf/issues)
|
|
107
|
+
|
|
108
|
+
Built on [OpenClaw](https://github.com/openclaw/openclaw),
|
|
109
|
+
[NVIDIA OpenShell](https://github.com/NVIDIA/OpenShell) and
|
|
110
|
+
[LiteLLM](https://github.com/BerriAI/litellm).
|
|
111
|
+
[MIT licensed](https://github.com/clawscarf/clawscarf/blob/main/LICENSE), with [third-party notices](https://github.com/clawscarf/clawscarf/blob/main/THIRD_PARTY_NOTICES.md).
|
|
112
|
+
ClawScarf is an independent project.
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -28,6 +28,11 @@ are recorded in lockfiles; upstream source is not vendored here.
|
|
|
28
28
|
The terminal installer uses MIT-licensed `@clack/prompts` 1.8.1. It is installed
|
|
29
29
|
from the operator lockfile and retains its upstream license with the dependency;
|
|
30
30
|
the installer does not vendor or modify the prompt library.
|
|
31
|
+
Standalone CLI archives include the unmodified Node.js 24.19.0 executable from
|
|
32
|
+
[official Node downloads](https://nodejs.org/dist/v24.19.0/), verified against the
|
|
33
|
+
SHA-256 pins in [release/components.json](release/components.json). Each archive
|
|
34
|
+
retains Node's complete upstream LICENSE, including its bundled third-party notices,
|
|
35
|
+
at `node/LICENSE`. JavaScript dependencies retain their licenses in `node_modules`.
|
|
31
36
|
The Gateway uses unmodified upstream commit `7bc487d39dc9e059bb9b19ea08152883022f83fe`;
|
|
32
37
|
the [image build instructions](deploy/images/README.md) own source packaging.
|
|
33
38
|
OpenClaw's upstream [license](https://github.com/openclaw/openclaw/blob/7bc487d39dc9e059bb9b19ea08152883022f83fe/LICENSE)
|
|
@@ -32,4 +32,4 @@ frontend native_node
|
|
|
32
32
|
default_backend native_gateway
|
|
33
33
|
|
|
34
34
|
backend native_gateway
|
|
35
|
-
server gateway "
|
|
35
|
+
server gateway "application:${CLAWSCARF_NODE_GATEWAY_PORT}" resolvers docker init-addr libc,none
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "team-server",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"runtime": "../../runtime/releases/0.1.0-alpha.
|
|
4
|
+
"runtime": "../../runtime/releases/0.1.0-alpha.3.json",
|
|
5
5
|
"name": "Team server",
|
|
6
6
|
"description": "A protected OpenClaw server for your team, with hosted login, managed models and optional Connections.",
|
|
7
7
|
"maturity": "supported",
|
package/release/README.md
CHANGED
|
@@ -14,9 +14,10 @@ Recipes and packs ship with the CLI; there is no separate registry or dependency
|
|
|
14
14
|
The [model catalog](../deploy/models/catalog.json) is also a CLI asset. Recipe IDs select
|
|
15
15
|
catalog entries; model protocols and limits have one definition.
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Published and development use
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
+
npm install -g @clawscarf/cli@next
|
|
20
21
|
clawscarf configure
|
|
21
22
|
# Or use staging for hosted login and Connections:
|
|
22
23
|
clawscarf configure --cloud-url https://cloud-staging.clawscarf.com
|
|
@@ -33,7 +34,13 @@ locally built Docker image IDs and checksum-pinned OpenShell tools under ignored
|
|
|
33
34
|
published, clean-machine release**. Definitions belong in the tracked directories
|
|
34
35
|
above. Compiled images, binaries and test artifacts are not source definitions.
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
Published candidates include macOS arm64 and Linux arm64/x86-64 tools, with Linux
|
|
38
|
+
container images for both architectures. Windows uses the Linux CLI inside WSL2
|
|
39
|
+
(experimental);
|
|
40
|
+
Docker Desktop must expose its Linux engine to that distribution. Intel Mac is blocked
|
|
41
|
+
by the pinned upstream OpenShell release lacking a Darwin x86-64 executable.
|
|
42
|
+
See the [upstream support matrix](https://docs.nvidia.com/openshell/reference/support-matrix)
|
|
43
|
+
for host requirements. Setup validates the host,
|
|
37
44
|
tools and ports; it pulls registry images by digest and downloads missing runtime tools
|
|
38
45
|
when their definition supplies an HTTPS URL and SHA-256. It cannot recover a missing
|
|
39
46
|
local development image ID. See the
|
|
@@ -45,7 +52,8 @@ Release candidates contain:
|
|
|
45
52
|
|
|
46
53
|
- npm `@clawscarf/cli`: compiled CLI, recipes, pack files, model catalog and runtime
|
|
47
54
|
definitions. No Docker images or large OpenShell executables in npm.
|
|
48
|
-
- GitHub Releases:
|
|
55
|
+
- GitHub Releases: standalone CLI archives with a private Node runtime and installed
|
|
56
|
+
dependencies, an installer, runtime tool archives, checksums and required notices.
|
|
49
57
|
- GHCR: runtime/companion images referenced by immutable registry digest.
|
|
50
58
|
|
|
51
59
|
Installing a newer CLI supplies newer recipes. Each recipe still selects an exact
|
|
@@ -66,15 +74,37 @@ silently replaced. Docker verifies image digests. No start command resolves late
|
|
|
66
74
|
with an exact version, such as `0.1.0-alpha.1`. It:
|
|
67
75
|
|
|
68
76
|
1. Runs checks and tests the compiled operator outside the checkout on macOS arm64.
|
|
69
|
-
2. Builds the pinned OpenClaw source and ClawScarf images on Linux arm64,
|
|
70
|
-
runtime capabilities and the Chromium sandbox,
|
|
77
|
+
2. Builds the pinned OpenClaw source and ClawScarf images on Linux arm64 and x86-64,
|
|
78
|
+
checks runtime capabilities and the Chromium sandbox, then starts a protected
|
|
79
|
+
installation with private LiteLLM networking and verifies stop/start retention.
|
|
80
|
+
It publishes image indexes containing both architectures to GHCR.
|
|
71
81
|
3. Downloads checksum-pinned OpenShell tools, records the image digests, and assembles
|
|
72
|
-
|
|
82
|
+
one runtime archive per host platform, individual executable assets, npm CLI,
|
|
83
|
+
checksums and notices.
|
|
73
84
|
4. Installs the resulting npm archive into a temporary prefix and checks its CLI/catalog.
|
|
85
|
+
5. Builds standalone CLI archives on macOS ARM64 and Linux ARM64/x86-64 using the
|
|
86
|
+
same npm payload and frozen production dependency lockfile. Downloads Node using
|
|
87
|
+
the checksums in [components.json](components.json), retains its license, and
|
|
88
|
+
tests the installer and packaged CLI with system Node deliberately unavailable.
|
|
89
|
+
Only after all three platform checks pass does it assemble `release-candidate`.
|
|
90
|
+
|
|
91
|
+
[Standalone packaging](../scripts/release/standalone.ts) adds a private Node executable
|
|
92
|
+
and a small launcher; it does not change the CLI implementation. The
|
|
93
|
+
[installer](install.sh) is versioned with each release and downloads only that
|
|
94
|
+
version's archive, verifies SHA-256, and installs under `~/.local` by default.
|
|
95
|
+
`--prefix` selects another absolute directory. It never installs a host service,
|
|
96
|
+
changes system Node, edits shell profiles or needs sudo. To upgrade the CLI, run
|
|
97
|
+
the newer release's installer; it switches the command and retains the old version.
|
|
98
|
+
Runtime upgrades and installation data are separate from CLI installation.
|
|
74
99
|
|
|
75
100
|
The [image builder](../scripts/release/build-images.sh) and
|
|
76
101
|
[candidate assembler](../scripts/release/candidate.ts) contain the build commands;
|
|
77
|
-
Actions
|
|
102
|
+
GitHub Actions orchestrates the build and supplies registry credentials. Image builds
|
|
103
|
+
and Linux installation checks use Depot ARM64/x86-64 runners; macOS checks and packaging
|
|
104
|
+
use GitHub-hosted runners. The Depot Managed Runners app connects the ClawScarf organization
|
|
105
|
+
to the RAW Labs Depot organization. The image job installs Docker 29.5.3 on its
|
|
106
|
+
disposable runner for the network features required by installations.
|
|
107
|
+
The source recipe pins the development
|
|
78
108
|
runtime. Packaging resolves that same runtime to the candidate's immutable definition;
|
|
79
109
|
it does not make recipes select latest. The first release uses the same CLI/runtime
|
|
80
110
|
version. Recipe versions remain independent; bump them when defaults, pack selection
|
|
@@ -92,20 +122,40 @@ GitHub Release and publishes the already-built npm tarball. It never rebuilds im
|
|
|
92
122
|
or packages. Prereleases use npm's `next` tag; stable versions use `latest`. Repeating
|
|
93
123
|
a GitHub upload is allowed only when the existing checksums match exactly.
|
|
94
124
|
|
|
95
|
-
Repository setup
|
|
125
|
+
Repository setup:
|
|
96
126
|
|
|
97
127
|
- GHCR image packages must be publicly readable; a private candidate digest is not
|
|
98
128
|
usable by an unauthenticated installer. The build uses GitHub's scoped job token.
|
|
99
|
-
-
|
|
100
|
-
`@clawscarf
|
|
101
|
-
|
|
102
|
-
-
|
|
129
|
+
- The GitHub `release` environment restricts deployment to `main`. The npm
|
|
130
|
+
organization `@clawscarf` owns the public `@clawscarf/cli` package.
|
|
131
|
+
- npm trusted publishing is configured for repository `clawscarf/clawscarf`, workflow
|
|
132
|
+
[publish-release.yml](../.github/workflows/publish-release.yml), environment `release`.
|
|
133
|
+
The first tested tarball was published using the owner's authenticated npm CLI because
|
|
134
|
+
npm requires a package to exist before configuring trust. Subsequent publication
|
|
135
|
+
uses the workflow's short-lived identity rather than a stored npm token; alpha.2
|
|
136
|
+
was published through that workflow successfully.
|
|
137
|
+
- Transitive notices/source review remains open in [TODO.md](../TODO.md).
|
|
103
138
|
Debian copyright files remain in the images; runtime assets include upstream notices
|
|
104
139
|
and the image build exports its network-tool sources. Those files are not a claim of
|
|
105
140
|
a completed license audit. Pinned Debian packages still depend on mirror retention.
|
|
106
141
|
|
|
107
|
-
|
|
108
|
-
|
|
142
|
+
[Candidate 35532613602](https://github.com/clawscarf/clawscarf/actions/runs/35532613602)
|
|
143
|
+
passed full checks, compiled archive installation and all eight image builds on
|
|
144
|
+
native ARM64 and x86-64 Depot runners. The same artifacts are available in the
|
|
145
|
+
[0.1.0-alpha.2 GitHub prerelease](https://github.com/clawscarf/clawscarf/releases/tag/v0.1.0-alpha.2)
|
|
146
|
+
and as `@clawscarf/cli@0.1.0-alpha.2`, tagged `next`.
|
|
147
|
+
|
|
148
|
+
Both Linux architectures passed protected runtime startup, native WebSocket forwarding,
|
|
149
|
+
authenticated TLS to the private model gateway, retained stop/start and deletion on
|
|
150
|
+
Docker 29.5.3. The final ARM64 images also passed real GPT-6 Astra inference and retained
|
|
151
|
+
restart on macOS Docker Desktop. Candidate downloads passed SHA-256 verification and
|
|
152
|
+
the CLI tarball installed independently of the repository. Disposable test containers,
|
|
153
|
+
volumes and networks were deleted afterward.
|
|
154
|
+
|
|
155
|
+
Staging hosted administrator login and the packaged interactive setup previously
|
|
156
|
+
passed on macOS with alpha.1. Hosted login and real provider inference on Linux,
|
|
157
|
+
actual Windows/WSL2 installation, production first-time signup and changed-version
|
|
158
|
+
upgrades remain unverified; see [TODO.md](../TODO.md).
|
|
109
159
|
|
|
110
160
|
## Assemble runtime artifacts
|
|
111
161
|
|
package/release/components.json
CHANGED
|
@@ -12,16 +12,40 @@
|
|
|
12
12
|
"darwin-arm64": {
|
|
13
13
|
"url": "https://github.com/NVIDIA/OpenShell/releases/download/v0.0.116/openshell-aarch64-apple-darwin.tar.gz",
|
|
14
14
|
"sha256": "e582f2374053bebac8e6aaeb4a369931b7d4bb97bd55055e2c02e85502627e22"
|
|
15
|
+
},
|
|
16
|
+
"linux-arm64": {
|
|
17
|
+
"url": "https://github.com/NVIDIA/OpenShell/releases/download/v0.0.116/openshell-aarch64-unknown-linux-musl.tar.gz",
|
|
18
|
+
"sha256": "7a949c48d1e000cd280869eea1e203e24816b9cfefc575b68a8b72b939cb3f43"
|
|
19
|
+
},
|
|
20
|
+
"linux-x64": {
|
|
21
|
+
"url": "https://github.com/NVIDIA/OpenShell/releases/download/v0.0.116/openshell-x86_64-unknown-linux-musl.tar.gz",
|
|
22
|
+
"sha256": "4fb4476d80a1875a0b83547ec3aba999cf0a2e2d75f95f2f709b622e2103520e"
|
|
15
23
|
}
|
|
16
24
|
},
|
|
17
25
|
"gateway": {
|
|
18
26
|
"darwin-arm64": {
|
|
19
27
|
"url": "https://github.com/NVIDIA/OpenShell/releases/download/v0.0.116/openshell-gateway-aarch64-apple-darwin.tar.gz",
|
|
20
28
|
"sha256": "f192d3d737c125264e13ef73458541df2ca6a9eb2fa599736a7f2587d5d2ce8d"
|
|
29
|
+
},
|
|
30
|
+
"linux-arm64": {
|
|
31
|
+
"url": "https://github.com/NVIDIA/OpenShell/releases/download/v0.0.116/openshell-gateway-aarch64-unknown-linux-gnu.tar.gz",
|
|
32
|
+
"sha256": "292c379193a339220234ffea585350901468bb8f4076e2076bc074e8ed18974b"
|
|
33
|
+
},
|
|
34
|
+
"linux-x64": {
|
|
35
|
+
"url": "https://github.com/NVIDIA/OpenShell/releases/download/v0.0.116/openshell-gateway-x86_64-unknown-linux-gnu.tar.gz",
|
|
36
|
+
"sha256": "59c6da724eae7a00c28826f9191efbdf4fbaa5c768afdc8dea6a80a949ebcc89"
|
|
21
37
|
}
|
|
22
38
|
},
|
|
23
39
|
"gatewayImage": "ghcr.io/nvidia/openshell/gateway:0.0.116@sha256:05cf77bbb022a739aed6f22daa0e7e164415f4ab273b5f84319e46d91eb8f645"
|
|
24
40
|
},
|
|
41
|
+
"cliNode": {
|
|
42
|
+
"version": "24.19.0",
|
|
43
|
+
"archives": {
|
|
44
|
+
"darwin-arm64": "8294b7aa9b03997481c06babf1e8b270c859358f27da57a11509afe537ac381d",
|
|
45
|
+
"linux-arm64": "d28c8a5bf0a808f0ed434a1dce8c54ae98f0371c0bd86ac58abc613f73e6643f",
|
|
46
|
+
"linux-x64": "f625d97cd707df4ff96254916fbc5ff014f09c09effe5a1e0ca8f6d41a8789d4"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
25
49
|
"companionNode": {
|
|
26
50
|
"version": "24.19.0",
|
|
27
51
|
"image": "node@sha256:4196d66a565c6f195728d9952f161f4adfe2ad753052a08b7ec7f1c5a6bda42b"
|
package/release/operator.md
CHANGED
|
@@ -9,10 +9,35 @@ registry images by digest. Local development image IDs cannot be downloaded. The
|
|
|
9
9
|
package is named `@clawscarf/cli` and exposes the `clawscarf` command. Development archives
|
|
10
10
|
remain private; the release candidate builder makes the package publishable. [Release contents](README.md) defines the publication model.
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Install from npm
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install -g @clawscarf/cli@next
|
|
16
|
+
clawscarf configure
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The `next` tag selects the current alpha. Use an exact version instead when needed.
|
|
20
|
+
npm installs the CLI dependencies; runtime images and tools are fetched during setup.
|
|
21
|
+
See [release contents](README.md) for supported platforms and verification.
|
|
22
|
+
|
|
23
|
+
## Standalone download
|
|
24
|
+
|
|
25
|
+
Standalone CLI archives include Node and installed dependencies. Download the archive
|
|
26
|
+
for your platform and its `SHA256SUMS` from the same GitHub Release. The release's
|
|
27
|
+
[installer](install.sh) selects the correct archive, verifies it and installs
|
|
28
|
+
`clawscarf` under `~/.local/bin`. It accepts `--prefix /absolute/directory`.
|
|
29
|
+
No system Node, npm or pnpm is required. These archives are separate from both the
|
|
30
|
+
npm package and the runtime-tool archives.
|
|
31
|
+
|
|
32
|
+
The layout is `clawscarf/` containing the launcher, `node/bin/node`, Node's LICENSE,
|
|
33
|
+
and `package/` with the CLI, dependencies and catalogs. You can also verify and
|
|
34
|
+
extract the archive yourself, then run `./clawscarf/clawscarf configure`.
|
|
35
|
+
Moving the whole directory preserves the bundled runtime and dependencies.
|
|
36
|
+
|
|
37
|
+
## Run the JavaScript archive
|
|
13
38
|
|
|
14
39
|
Verify the archive against its accompanying `SHA256SUMS`, then extract it into a new
|
|
15
|
-
directory.
|
|
40
|
+
directory. Unlike the standalone download, this requires Node 24.16 or later in the Node 24 line, or Node 26.1 or later, and
|
|
16
41
|
pnpm 10.33.0. In the extracted `package` directory:
|
|
17
42
|
|
|
18
43
|
```sh
|
|
@@ -28,7 +53,8 @@ Dependencies are installed from the included frozen lockfile. Its root importer
|
|
|
28
53
|
contains only the production dependencies referenced by the staged operator;
|
|
29
54
|
publisher and browser dependencies are omitted. Locked transitive versions are retained.
|
|
30
55
|
No TypeScript compiler, contributor source or build step is needed to run the commands.
|
|
31
|
-
|
|
56
|
+
Platform support is determined by the selected runtime definition. Published candidates
|
|
57
|
+
include macOS arm64 and Linux arm64/x86-64 tools; Windows runs the Linux CLI inside WSL2.
|
|
32
58
|
|
|
33
59
|
The installer reviews recipe settings before credentials, prepares the installation,
|
|
34
60
|
and offers **Start now**. It starts Docker services and protected OpenShell containers, then exits; closing
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
|
+
"sourceRevision": "b5e0988a9dbbf28b40974cc1a8b38e1d9466e6f2",
|
|
5
|
+
"platforms": [
|
|
6
|
+
"darwin-arm64",
|
|
7
|
+
"linux-arm64",
|
|
8
|
+
"linux-x64"
|
|
9
|
+
],
|
|
10
|
+
"images": {
|
|
11
|
+
"postgres": "postgres@sha256:742f40ea20b9ff2ff31db5458d127452988a2164df9e17441e191f3b72252193",
|
|
12
|
+
"gateway": "ghcr.io/clawscarf/clawscarf/runtime@sha256:fa6cb6ee1116a17577369166e6d73391f0194b78a69743e4f8bc137dbffb1c5c",
|
|
13
|
+
"companion": "ghcr.io/clawscarf/clawscarf/companion@sha256:cc2a9f4caf2f9a2e97d198270b8d02cfc7fc082a97c5982eb40e223fe1d2f9a8",
|
|
14
|
+
"openshellClient": "ghcr.io/clawscarf/clawscarf/openshell-client@sha256:48167a723eac58247b5e07d8266495b7b71e51e72491a1ed63c45e2f1e5590d1",
|
|
15
|
+
"relay": "ghcr.io/clawscarf/clawscarf/browser-relay@sha256:a6958a6ee43bb6ebcbf18a0aae477a76ac1c8d8c4ed914d319a40dca40571ae7",
|
|
16
|
+
"models": "ghcr.io/berriai/litellm:v1.100.1@sha256:a3715fa7ad8387941ab697259bd2881d68931657247a41984f90fae6d11c62bf",
|
|
17
|
+
"browser": {
|
|
18
|
+
"chromium": "ghcr.io/clawscarf/clawscarf/browser@sha256:b765e4698c471f26ff50d7f18d14ecf2fb903b92bb22e0299c3758a6429c2121",
|
|
19
|
+
"node": "ghcr.io/clawscarf/clawscarf/browser-node@sha256:c55268a56d45f65c05f1dd7c5f6a45c2b5cdaefb552606148c42fff0967a7654",
|
|
20
|
+
"dns": "ghcr.io/clawscarf/clawscarf/browser-dns@sha256:c9d1a557c8bb897eccd2f83693ba3bef71f8fd681e5cd4ce189942460e67e449",
|
|
21
|
+
"egress": "ghcr.io/clawscarf/clawscarf/browser-egress@sha256:b1b69e03629211ceaf84f7fd6c3bea7fbe659f81b4a4ab00fbc30588810cd970"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"tools": {
|
|
25
|
+
"openshell": {
|
|
26
|
+
"version": "0.0.116",
|
|
27
|
+
"cli": {
|
|
28
|
+
"darwin-arm64": {
|
|
29
|
+
"file": "tools/darwin-arm64/openshell",
|
|
30
|
+
"sha256": "0baeeffe0e4c184b08fde910ac9e8815fb0b8cfb808e49c0e634d7db65fb03bb",
|
|
31
|
+
"url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.3/openshell-darwin-arm64"
|
|
32
|
+
},
|
|
33
|
+
"linux-arm64": {
|
|
34
|
+
"file": "tools/linux-arm64/openshell",
|
|
35
|
+
"sha256": "84df9f059b16b66c83748606cc87d9edcda84b57effea19543f5c368aba7eb18",
|
|
36
|
+
"url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.3/openshell-linux-arm64"
|
|
37
|
+
},
|
|
38
|
+
"linux-x64": {
|
|
39
|
+
"file": "tools/linux-x64/openshell",
|
|
40
|
+
"sha256": "24e85062073d512d1951c76cd3890b7bce1ab01cc36a08f88b3b43c067a402da",
|
|
41
|
+
"url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.3/openshell-linux-x64"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"gateway": {
|
|
45
|
+
"darwin-arm64": {
|
|
46
|
+
"file": "tools/darwin-arm64/openshell-gateway",
|
|
47
|
+
"sha256": "298706259ef18350f334c222e981e91583558511d50bce22727c689d261203e6",
|
|
48
|
+
"url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.3/openshell-gateway-darwin-arm64"
|
|
49
|
+
},
|
|
50
|
+
"linux-arm64": {
|
|
51
|
+
"file": "tools/linux-arm64/openshell-gateway",
|
|
52
|
+
"sha256": "0e660428e655e7481f34ba8a4b351554400baf791863d32555c98dddb3f09ae2",
|
|
53
|
+
"url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.3/openshell-gateway-linux-arm64"
|
|
54
|
+
},
|
|
55
|
+
"linux-x64": {
|
|
56
|
+
"file": "tools/linux-x64/openshell-gateway",
|
|
57
|
+
"sha256": "780fc7ad871e2163f3fb866a2d0254ee682fc0d83e6261a84b350c8dcdd9f47b",
|
|
58
|
+
"url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.3/openshell-gateway-linux-x64"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
package/scripts/controller.js
CHANGED
|
@@ -11,6 +11,7 @@ const settingsSchema = z.strictObject({
|
|
|
11
11
|
port: z.number().int().min(1024).max(65535),
|
|
12
12
|
gateway: z.string().min(1),
|
|
13
13
|
cli: z.string().min(1),
|
|
14
|
+
hostGatewayIp: z.ipv4().optional(),
|
|
14
15
|
});
|
|
15
16
|
const releaseInput = JSON.parse(await readFile(new URL("../release/components.json", import.meta.url), "utf8"));
|
|
16
17
|
const release = z
|
|
@@ -75,6 +76,7 @@ gateway_id = ${JSON.stringify(settings.name)}
|
|
|
75
76
|
ttl_secs = 0
|
|
76
77
|
[openshell.drivers.docker]
|
|
77
78
|
network_name = ${JSON.stringify(settings.name)}
|
|
79
|
+
${settings.hostGatewayIp ? `host_gateway_ip = ${JSON.stringify(settings.hostGatewayIp)}` : ""}
|
|
78
80
|
sandbox_namespace = ${JSON.stringify(settings.name)}
|
|
79
81
|
supervisor_image = ${JSON.stringify(release.supervisorImage)}
|
|
80
82
|
image_pull_policy = "IfNotPresent"
|
|
@@ -91,6 +93,7 @@ command
|
|
|
91
93
|
.requiredOption("--directory <path>", "New private controller directory")
|
|
92
94
|
.requiredOption("--gateway <path>", "Verified openshell-gateway executable")
|
|
93
95
|
.requiredOption("--cli <path>", "Verified openshell executable")
|
|
96
|
+
.option("--host-gateway-ip <address>", "Controller address on the runtime bridge")
|
|
94
97
|
.option("--name <name>", "Isolated controller name", "clawscarf")
|
|
95
98
|
.option("--port <port>", "Loopback controller port", "17671")
|
|
96
99
|
.action(async (options) => {
|
|
@@ -101,6 +104,7 @@ command
|
|
|
101
104
|
port: Number(options.port),
|
|
102
105
|
gateway: resolve(options.gateway),
|
|
103
106
|
cli: resolve(options.cli),
|
|
107
|
+
hostGatewayIp: options.hostGatewayIp,
|
|
104
108
|
});
|
|
105
109
|
await verifyExecutable(settings.gateway);
|
|
106
110
|
await verifyExecutable(settings.cli);
|
|
@@ -115,6 +119,8 @@ command
|
|
|
115
119
|
"localhost",
|
|
116
120
|
"--server-san",
|
|
117
121
|
"host.openshell.internal",
|
|
122
|
+
"--server-san",
|
|
123
|
+
"controller.clawscarf.internal",
|
|
118
124
|
], { timeout: 30000 });
|
|
119
125
|
const mtls = join(directory, "config/openshell/gateways", settings.name, "mtls");
|
|
120
126
|
await mkdir(mtls, { recursive: true, mode: 0o700 });
|
|
@@ -41,7 +41,6 @@ export function browserNodeServices(state, directory, browserAddress, prepared)
|
|
|
41
41
|
image: state.input.relayImage,
|
|
42
42
|
user: String(process.getuid?.() ?? 1000),
|
|
43
43
|
networks: { machine: { ipv4_address: addresses.ingress }, default: {} },
|
|
44
|
-
extra_hosts: ["host.docker.internal:host-gateway"],
|
|
45
44
|
environment: {
|
|
46
45
|
CLAWSCARF_NODE_INGRESS_ADDRESS: addresses.ingress,
|
|
47
46
|
CLAWSCARF_NODE_GATEWAY_PORT: String(state.input.ports.native),
|
|
@@ -64,7 +64,7 @@ export async function ensureCertificates(directory) {
|
|
|
64
64
|
"-subj",
|
|
65
65
|
"/CN=ClawScarf local management",
|
|
66
66
|
"-addext",
|
|
67
|
-
"subjectAltName=DNS:localhost,DNS:host.docker.internal,IP:127.0.0.1",
|
|
67
|
+
"subjectAltName=DNS:localhost,DNS:host.docker.internal,DNS:models.clawscarf.internal,IP:127.0.0.1",
|
|
68
68
|
"-addext",
|
|
69
69
|
"basicConstraints=critical,CA:TRUE",
|
|
70
70
|
]);
|
|
@@ -6,7 +6,7 @@ import { resourceNames } from "./state.js";
|
|
|
6
6
|
import { run } from "./process.js";
|
|
7
7
|
import { modelGatewayServices } from "./model-gateway-compose.js";
|
|
8
8
|
import { postgresImage } from "./images.js";
|
|
9
|
-
export function composeConfiguration(state, directory, browserAddress, browserMachine) {
|
|
9
|
+
export function composeConfiguration(state, directory, controllerAddress, browserAddress, browserMachine, controllerSocketGroup = 0) {
|
|
10
10
|
const { input, ownerId } = state;
|
|
11
11
|
const names = resourceNames(state);
|
|
12
12
|
const privateDirectory = join(directory, "private");
|
|
@@ -23,7 +23,7 @@ export function composeConfiguration(state, directory, browserAddress, browserMa
|
|
|
23
23
|
const configuration = {
|
|
24
24
|
name: names.project,
|
|
25
25
|
services: {
|
|
26
|
-
...controllerServices(directory, state),
|
|
26
|
+
...controllerServices(directory, state, controllerAddress, controllerSocketGroup),
|
|
27
27
|
...modelGatewayServices(directory, state),
|
|
28
28
|
...(input.browser && browserAddress && browserMachine
|
|
29
29
|
? browserNodeServices(state, directory, browserAddress, browserMachine)
|
|
@@ -153,9 +153,7 @@ export function composeConfiguration(state, directory, browserAddress, browserMa
|
|
|
153
153
|
},
|
|
154
154
|
},
|
|
155
155
|
networks: {
|
|
156
|
-
|
|
157
|
-
? { runtime: { external: true, name: names.sandbox } }
|
|
158
|
-
: {}),
|
|
156
|
+
runtime: { external: true, name: names.sandbox },
|
|
159
157
|
default: { external: true, name: `${names.project}_default` },
|
|
160
158
|
...(input.browser
|
|
161
159
|
? {
|
|
@@ -187,8 +187,8 @@ export const localInput = z
|
|
|
187
187
|
export function parseLocalInput(value) {
|
|
188
188
|
return localInput.parse(value);
|
|
189
189
|
}
|
|
190
|
-
export function managementOrigin(
|
|
191
|
-
return
|
|
190
|
+
export function managementOrigin() {
|
|
191
|
+
return "https://localhost:18801";
|
|
192
192
|
}
|
|
193
193
|
function mountedPrivateFile(directory, path, name) {
|
|
194
194
|
absolutePath.parse(path);
|
|
@@ -224,10 +224,10 @@ export function generateLocalConfiguration(options) {
|
|
|
224
224
|
port: 18801,
|
|
225
225
|
},
|
|
226
226
|
runtime: {
|
|
227
|
-
origin: `http://
|
|
228
|
-
managementOrigin: managementOrigin(
|
|
227
|
+
origin: `http://application:${String(input.ports.native)}`,
|
|
228
|
+
managementOrigin: managementOrigin(),
|
|
229
229
|
widgetOrigin,
|
|
230
|
-
widgetUpstream: `http://
|
|
230
|
+
widgetUpstream: `http://widgets:${String(input.ports.nativeWidgets)}`,
|
|
231
231
|
},
|
|
232
232
|
...(input.team.certificateFile
|
|
233
233
|
? {
|
|
@@ -2,13 +2,14 @@ import { join } from "node:path";
|
|
|
2
2
|
import { openshellGatewayImage } from "./images.js";
|
|
3
3
|
import { resourceNames } from "./state.js";
|
|
4
4
|
/** OpenShell alone receives Docker authority. Forwarders receive only client credentials. */
|
|
5
|
-
export function controllerServices(directory, state) {
|
|
5
|
+
export function controllerServices(directory, state, address, socketGroup) {
|
|
6
6
|
const controller = join(directory, "controller");
|
|
7
7
|
const { input } = state;
|
|
8
8
|
const name = resourceNames(state).sandbox;
|
|
9
9
|
const port = input.ports.controller;
|
|
10
10
|
const forward = (target, port) => ({
|
|
11
11
|
image: input.openshellClientImage,
|
|
12
|
+
user: `${String(process.getuid?.() ?? 1000)}:${String(process.getgid?.() ?? 1000)}`,
|
|
12
13
|
init: true,
|
|
13
14
|
read_only: true,
|
|
14
15
|
cap_drop: ["ALL"],
|
|
@@ -21,23 +22,31 @@ export function controllerServices(directory, state) {
|
|
|
21
22
|
XDG_DATA_HOME: "/tmp/data",
|
|
22
23
|
},
|
|
23
24
|
volumes: [`${join(controller, "config")}:/controller/config:ro`],
|
|
24
|
-
extra_hosts: ["host.openshell.internal:host-gateway"],
|
|
25
25
|
command: [
|
|
26
26
|
"forward",
|
|
27
|
-
"
|
|
27
|
+
"service",
|
|
28
|
+
"--target-port",
|
|
29
|
+
String(port),
|
|
30
|
+
"--local",
|
|
28
31
|
`0.0.0.0:${String(port)}`,
|
|
29
32
|
target,
|
|
30
33
|
"--gateway",
|
|
31
34
|
name,
|
|
32
35
|
"--gateway-endpoint",
|
|
33
|
-
`https://
|
|
36
|
+
`https://controller.clawscarf.internal:${String(input.ports.controller)}`,
|
|
34
37
|
],
|
|
35
38
|
ports: [`127.0.0.1:${String(port)}:${String(port)}`],
|
|
36
39
|
});
|
|
37
40
|
return {
|
|
38
41
|
controller: {
|
|
39
42
|
image: openshellGatewayImage,
|
|
40
|
-
user:
|
|
43
|
+
user: `${String(process.getuid?.() ?? 1000)}:${String(process.getgid?.() ?? 1000)}`,
|
|
44
|
+
// Desktop exposes a root-group socket; Linux Engine uses the host socket's group.
|
|
45
|
+
group_add: [...new Set(["0", String(socketGroup)])],
|
|
46
|
+
networks: {
|
|
47
|
+
default: { aliases: ["controller.clawscarf.internal"] },
|
|
48
|
+
runtime: { ipv4_address: address },
|
|
49
|
+
},
|
|
41
50
|
command: ["--config", join(controller, "gateway.toml")],
|
|
42
51
|
environment: {
|
|
43
52
|
XDG_CONFIG_HOME: join(controller, "config"),
|
|
@@ -51,7 +60,6 @@ export function controllerServices(directory, state) {
|
|
|
51
60
|
"/var/run/docker.sock:/var/run/docker.sock",
|
|
52
61
|
],
|
|
53
62
|
ports: [`127.0.0.1:${String(port)}:${String(port)}`],
|
|
54
|
-
extra_hosts: ["host.openshell.internal:host-gateway"],
|
|
55
63
|
},
|
|
56
64
|
application: forward(name, input.ports.native),
|
|
57
65
|
widgets: forward(name, input.ports.nativeWidgets),
|
|
@@ -28,6 +28,12 @@ export function modelGatewayServices(directory, state) {
|
|
|
28
28
|
},
|
|
29
29
|
models: {
|
|
30
30
|
image: input.image,
|
|
31
|
+
user: `${String(process.getuid?.() ?? 1000)}:${String(process.getgid?.() ?? 1000)}`,
|
|
32
|
+
environment: { HOME: "/tmp" },
|
|
33
|
+
networks: {
|
|
34
|
+
default: {},
|
|
35
|
+
runtime: { aliases: ["models.clawscarf.internal"] },
|
|
36
|
+
},
|
|
31
37
|
init: true,
|
|
32
38
|
read_only: true,
|
|
33
39
|
cap_drop: ["ALL"],
|
|
@@ -43,6 +43,14 @@ export async function prepareModelGateway(directory, state, options = {}) {
|
|
|
43
43
|
return;
|
|
44
44
|
const loaded = await loadGatewayConfiguration(input.configurationFile, input.upstreamEnvironmentFile);
|
|
45
45
|
const root = join(directory, "private/models");
|
|
46
|
+
let baseUrl = "https://models.clawscarf.internal:4000/v1";
|
|
47
|
+
if (options.replaceConfiguration) {
|
|
48
|
+
const current = configurationSchema.parse(JSON.parse(await readFile(join(root, "native.json"), "utf8")));
|
|
49
|
+
if (current.mode !== "litellm")
|
|
50
|
+
throw new LocalSetupError("invalid_model_setup", "Expected an existing model gateway.");
|
|
51
|
+
// Model choices do not change the installation's retained network endpoint.
|
|
52
|
+
baseUrl = current.baseUrl;
|
|
53
|
+
}
|
|
46
54
|
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
47
55
|
for (const [name, prefix] of [
|
|
48
56
|
["master-key", "sk-"],
|
|
@@ -74,7 +82,7 @@ export async function prepareModelGateway(directory, state, options = {}) {
|
|
|
74
82
|
await publish(join(root, "models.json"), JSON.stringify(liteLlmConfiguration(loaded.configuration)));
|
|
75
83
|
await publish(join(root, "native.json"), JSON.stringify({
|
|
76
84
|
...loaded.configuration,
|
|
77
|
-
baseUrl
|
|
85
|
+
baseUrl,
|
|
78
86
|
}));
|
|
79
87
|
}
|
|
80
88
|
export async function prepareModelCredential(directory, state) {
|
|
@@ -264,6 +264,10 @@ export async function ensureLocalNetworks(directory, state, command = run) {
|
|
|
264
264
|
"create",
|
|
265
265
|
"--driver",
|
|
266
266
|
"bridge",
|
|
267
|
+
// Docker chooses a free subnet; declaring its size permits static endpoints.
|
|
268
|
+
...(intent.purpose !== "companion"
|
|
269
|
+
? ["--subnet", "0.0.0.0/24"]
|
|
270
|
+
: []),
|
|
267
271
|
...(intent.purpose === "runtime" ? ["--attachable"] : []),
|
|
268
272
|
...(["browser", "machine"].includes(intent.purpose)
|
|
269
273
|
? [
|
|
@@ -352,3 +356,20 @@ export async function observedBrowserMachineAddresses(directory, state, command
|
|
|
352
356
|
changed();
|
|
353
357
|
return { node: observed.isolated.address, ingress, dns };
|
|
354
358
|
}
|
|
359
|
+
/** Stable address on the owned runtime bridge for the containerized OpenShell controller. */
|
|
360
|
+
export async function observedControllerAddress(directory, state) {
|
|
361
|
+
await verifyLocalNetworks(directory, state);
|
|
362
|
+
const configs = z
|
|
363
|
+
.array(z.object({ Subnet: z.string(), Gateway: z.string() }))
|
|
364
|
+
.parse(JSON.parse(await run("docker", [
|
|
365
|
+
"network",
|
|
366
|
+
"inspect",
|
|
367
|
+
resourceNames(state).sandbox,
|
|
368
|
+
"--format",
|
|
369
|
+
"{{json .IPAM.Config}}",
|
|
370
|
+
])));
|
|
371
|
+
const config = configs.find((item) => isIP(item.Gateway) === 4);
|
|
372
|
+
if (!config)
|
|
373
|
+
changed();
|
|
374
|
+
return reservedAddress(config).address;
|
|
375
|
+
}
|
|
@@ -8,10 +8,10 @@ import { prepareRelay } from "./relay.js";
|
|
|
8
8
|
import { prepareRuntimePolicy } from "./policy.js";
|
|
9
9
|
import { prepareBrowser, initializeBrowserVolume } from "./browser.js";
|
|
10
10
|
import { readTeamMaterials, prepareTeamFiles } from "./team.js";
|
|
11
|
-
import { readFile, lstat } from "node:fs/promises";
|
|
11
|
+
import { readFile, lstat, stat } from "node:fs/promises";
|
|
12
12
|
import { join, resolve } from "node:path";
|
|
13
13
|
import { prepareInitialModels, withInitialModels, } from "./models.js";
|
|
14
|
-
import { ensureLocalNetworks } from "./networks.js";
|
|
14
|
+
import { ensureLocalNetworks, observedControllerAddress } from "./networks.js";
|
|
15
15
|
import { nodeEntrypoint } from "./entrypoint.js";
|
|
16
16
|
import { ensureCertificates } from "./certificates.js";
|
|
17
17
|
import { z } from "zod";
|
|
@@ -29,8 +29,9 @@ export async function prepareLocal(directoryInput, inputValue, capabilities) {
|
|
|
29
29
|
const input = parseLocalInput(inputValue);
|
|
30
30
|
const connections = await loadInitialConnections(input.connections);
|
|
31
31
|
const teamMaterials = await readTeamMaterials(input.team);
|
|
32
|
-
if (
|
|
33
|
-
|
|
32
|
+
if (!["darwin", "linux"].includes(process.platform) ||
|
|
33
|
+
!["arm64", "x64"].includes(process.arch))
|
|
34
|
+
throw new LocalSetupError("platform_unqualified", "Use macOS or Linux (including WSL2) with Linux Docker containers.");
|
|
34
35
|
for (const image of [
|
|
35
36
|
input.runtimeImage,
|
|
36
37
|
input.openshellClientImage,
|
|
@@ -71,6 +72,7 @@ export async function prepareLocal(directoryInput, inputValue, capabilities) {
|
|
|
71
72
|
const connectionsEndpoint = await prepareInitialConnections(directory, connections);
|
|
72
73
|
await prepareModelGateway(directory, state);
|
|
73
74
|
await ensureLocalNetworks(directory, state);
|
|
75
|
+
const controllerAddress = await observedControllerAddress(directory, state);
|
|
74
76
|
const browser = await prepareBrowser(directory, state);
|
|
75
77
|
const browserMachine = browser
|
|
76
78
|
? await prepareBrowserNode(directory, state, browser.token)
|
|
@@ -86,7 +88,9 @@ export async function prepareLocal(directoryInput, inputValue, capabilities) {
|
|
|
86
88
|
await ensureOwnedVolume(names.browserVolume, state.ownerId);
|
|
87
89
|
await initializeBrowserVolume(state, browser.token);
|
|
88
90
|
}
|
|
89
|
-
await ensurePrivateFile(join(directory, "compose.json"), JSON.stringify(composeConfiguration(state, directory, browser?.address, browserMachine
|
|
91
|
+
await ensurePrivateFile(join(directory, "compose.json"), JSON.stringify(composeConfiguration(state, directory, controllerAddress, browser?.address, browserMachine, process.platform === "linux"
|
|
92
|
+
? (await stat("/var/run/docker.sock")).gid
|
|
93
|
+
: 0), null, 2));
|
|
90
94
|
await prepareModelCredential(directory, state);
|
|
91
95
|
const models = await prepareInitialModels(directory, input.models);
|
|
92
96
|
await prepareRuntimePolicy(directory, models, connectionsEndpoint);
|
|
@@ -151,6 +155,8 @@ export async function prepareLocal(directoryInput, inputValue, capabilities) {
|
|
|
151
155
|
input.openshellGateway,
|
|
152
156
|
"--cli",
|
|
153
157
|
input.openshellCli,
|
|
158
|
+
"--host-gateway-ip",
|
|
159
|
+
controllerAddress,
|
|
154
160
|
"--name",
|
|
155
161
|
names.sandbox,
|
|
156
162
|
"--port",
|
|
@@ -66,7 +66,11 @@ export const terminalPrompts = {
|
|
|
66
66
|
note: clack.note,
|
|
67
67
|
async openBrowser(url) {
|
|
68
68
|
try {
|
|
69
|
-
await promisify(execFile)(process.platform === "darwin"
|
|
69
|
+
await promisify(execFile)(process.platform === "darwin"
|
|
70
|
+
? "open"
|
|
71
|
+
: process.env.WSL_DISTRO_NAME
|
|
72
|
+
? "explorer.exe"
|
|
73
|
+
: "xdg-open", [url], { timeout: 5000 });
|
|
70
74
|
}
|
|
71
75
|
catch {
|
|
72
76
|
process.stderr.write("Could not open a browser. Open the link above manually.\n");
|
|
@@ -12,8 +12,9 @@ import { acquireRuntimeTools } from "./runtime.js";
|
|
|
12
12
|
import { InstallationError } from "./errors.js";
|
|
13
13
|
/** No configuration, credentials or cloud account is needed for these checks. */
|
|
14
14
|
export async function checkHost(command = run) {
|
|
15
|
-
if (
|
|
16
|
-
|
|
15
|
+
if (!["darwin", "linux"].includes(process.platform) ||
|
|
16
|
+
!["arm64", "x64"].includes(process.arch))
|
|
17
|
+
throw new InstallationError("unsupported_platform", "Use macOS or Linux (including WSL2), with an ARM64 or x86-64 processor and Linux Docker containers. Run the Windows CLI inside WSL2.");
|
|
17
18
|
let os;
|
|
18
19
|
try {
|
|
19
20
|
os = await command("docker", ["info", "--format", "{{.OSType}}"]);
|
|
@@ -23,8 +24,8 @@ export async function checkHost(command = run) {
|
|
|
23
24
|
error.commandFailure?.reason === "spawn" &&
|
|
24
25
|
error.commandFailure.systemCode === "ENOENT";
|
|
25
26
|
throw new InstallationError("unavailable", missing
|
|
26
|
-
? "Docker is not installed or is not on PATH. Install Docker
|
|
27
|
-
: "Cannot reach Docker. Start Docker
|
|
27
|
+
? "Docker is not installed or is not on PATH. Install Docker Engine with Compose or Docker Desktop, then run configure again."
|
|
28
|
+
: "Cannot reach Docker. Start Docker and check that your user can access its socket, then try again.");
|
|
28
29
|
}
|
|
29
30
|
if (os.trim() !== "linux")
|
|
30
31
|
throw new InstallationError("unsupported_platform", "Docker must run Linux containers.");
|
|
@@ -32,8 +33,12 @@ export async function checkHost(command = run) {
|
|
|
32
33
|
await command("docker", ["compose", "version"]);
|
|
33
34
|
}
|
|
34
35
|
catch {
|
|
35
|
-
throw new InstallationError("unavailable", "Docker Compose is unavailable. Install or update Docker Desktop, then try again.");
|
|
36
|
+
throw new InstallationError("unavailable", "Docker Compose is unavailable. Install the Docker Compose plugin or update Docker Desktop, then try again.");
|
|
36
37
|
}
|
|
38
|
+
const version = (await command("docker", ["version", "--format", "{{.Server.Version}}"])).trim();
|
|
39
|
+
const major = Number(/^([0-9]+)\./.exec(version)?.[1]);
|
|
40
|
+
if (!Number.isInteger(major) || major < 29)
|
|
41
|
+
throw new InstallationError("unsupported_platform", "Docker Engine 29 or newer is required for automatic subnet allocation with fixed service addresses. Update Docker Engine or Docker Desktop, then try again.");
|
|
37
42
|
}
|
|
38
43
|
/** Stream only Docker's layer progress, never registry error payloads or credentials. */
|
|
39
44
|
export function pullImage(image, report, signal) {
|
|
@@ -3,7 +3,7 @@ import { hostedOidc, hostedConnections } from "../cloud/registration.js";
|
|
|
3
3
|
import { dirname, resolve, join } from "node:path";
|
|
4
4
|
import { loadGatewayConfiguration } from "../deployment/model-gateway.js";
|
|
5
5
|
import { createServer } from "node:net";
|
|
6
|
-
import { releaseSchema } from "../release/definition.js";
|
|
6
|
+
import { releaseSchema, releaseTools } from "../release/definition.js";
|
|
7
7
|
import { parseLocalInput, } from "../deployment/configuration.js";
|
|
8
8
|
import { loadInitialModels } from "../deployment/models.js";
|
|
9
9
|
import { loadInitialConnections, readInitialConnectionToken, } from "../deployment/connections.js";
|
|
@@ -49,10 +49,11 @@ export async function resolveInstallation(configFile, internalPorts) {
|
|
|
49
49
|
if (!release.platforms.some((platform) => platform === `${process.platform}-${process.arch}`))
|
|
50
50
|
throw new InstallationError("unsupported_platform", "This release does not support this host platform.");
|
|
51
51
|
const toolBase = dirname(releasePath);
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
await verifyReleaseTool(
|
|
52
|
+
const tools = releaseTools(release);
|
|
53
|
+
const cli = resolve(toolBase, tools.cli.file);
|
|
54
|
+
const gateway = resolve(toolBase, tools.gateway.file);
|
|
55
|
+
await verifyReleaseTool(cli, tools.cli.sha256);
|
|
56
|
+
await verifyReleaseTool(gateway, tools.gateway.sha256);
|
|
56
57
|
const [controller, management, native, nativeWidgets, database, browser, models,] = internalPorts;
|
|
57
58
|
const inputs = {};
|
|
58
59
|
async function inputFile(value, secret) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { chmod, copyFile, mkdir, open, rename, rm, writeFile, } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
|
-
import { releaseSchema } from "../release/definition.js";
|
|
4
|
+
import { releaseSchema, releaseTools } from "../release/definition.js";
|
|
5
5
|
import { readJson, verifyReleaseTool } from "./files.js";
|
|
6
6
|
import { InstallationError } from "./errors.js";
|
|
7
7
|
/** Installation-owned paths survive npm cache cleanup and later CLI updates. */
|
|
@@ -10,7 +10,7 @@ export async function retainRuntime(source, directory) {
|
|
|
10
10
|
const target = join(directory, "runtime");
|
|
11
11
|
await mkdir(join(target, "tools"), { recursive: true, mode: 0o700 });
|
|
12
12
|
for (const name of ["cli", "gateway"]) {
|
|
13
|
-
const tool = release
|
|
13
|
+
const tool = releaseTools(release)[name];
|
|
14
14
|
const original = resolve(dirname(source), tool.file);
|
|
15
15
|
tool.file = `tools/openshell${name === "gateway" ? "-gateway" : ""}`;
|
|
16
16
|
try {
|
|
@@ -34,7 +34,7 @@ export async function acquireRuntimeTools(releaseFile, options) {
|
|
|
34
34
|
const release = releaseSchema.parse(await readJson(releaseFile));
|
|
35
35
|
for (const name of ["cli", "gateway"]) {
|
|
36
36
|
options.signal?.throwIfAborted();
|
|
37
|
-
const tool = release
|
|
37
|
+
const tool = releaseTools(release)[name];
|
|
38
38
|
const file = resolve(dirname(releaseFile), tool.file);
|
|
39
39
|
try {
|
|
40
40
|
await verifyReleaseTool(file, tool.sha256);
|
|
@@ -15,12 +15,18 @@ const file = z.strictObject({
|
|
|
15
15
|
}, "Tool downloads require HTTPS without credentials.")
|
|
16
16
|
.optional(),
|
|
17
17
|
});
|
|
18
|
+
export const hostPlatformSchema = z.enum([
|
|
19
|
+
"darwin-arm64",
|
|
20
|
+
"linux-arm64",
|
|
21
|
+
"linux-x64",
|
|
22
|
+
]);
|
|
23
|
+
const tool = z.union([file, z.partialRecord(hostPlatformSchema, file)]);
|
|
18
24
|
/** Pinned runtime metadata, independent of recipe defaults and deployment state. */
|
|
19
25
|
export const releaseSchema = z.strictObject({
|
|
20
26
|
schemaVersion: z.literal(1),
|
|
21
27
|
version: z.string().regex(/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/),
|
|
22
28
|
sourceRevision: z.string().regex(/^[a-f0-9]{40}$/),
|
|
23
|
-
platforms: z.array(
|
|
29
|
+
platforms: z.array(hostPlatformSchema).min(1),
|
|
24
30
|
images: z
|
|
25
31
|
.strictObject({
|
|
26
32
|
postgres: z.literal(postgresImage),
|
|
@@ -44,8 +50,29 @@ export const releaseSchema = z.strictObject({
|
|
|
44
50
|
tools: z.strictObject({
|
|
45
51
|
openshell: z.strictObject({
|
|
46
52
|
version: z.string().min(1),
|
|
47
|
-
cli:
|
|
48
|
-
gateway:
|
|
53
|
+
cli: tool,
|
|
54
|
+
gateway: tool,
|
|
49
55
|
}),
|
|
50
56
|
}),
|
|
51
57
|
});
|
|
58
|
+
/** Single-platform development bundles and multi-platform published bundles share resolution. */
|
|
59
|
+
export function releaseTools(release, platform = `${process.platform}-${process.arch}`) {
|
|
60
|
+
const host = hostPlatformSchema.parse(platform);
|
|
61
|
+
if (!release.platforms.includes(host))
|
|
62
|
+
throw new Error(`Runtime ${release.version} does not include ${host}.`);
|
|
63
|
+
const select = (value) => {
|
|
64
|
+
if ("file" in value) {
|
|
65
|
+
if (release.platforms.length !== 1)
|
|
66
|
+
throw new Error("Multi-platform runtimes require a tool for each platform.");
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
const selected = value[host];
|
|
70
|
+
if (!selected)
|
|
71
|
+
throw new Error(`Missing runtime tool for ${host}.`);
|
|
72
|
+
return selected;
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
cli: select(release.tools.openshell.cli),
|
|
76
|
+
gateway: select(release.tools.openshell.gateway),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"schemaVersion": 1,
|
|
3
|
-
"version": "0.1.0-alpha.1",
|
|
4
|
-
"sourceRevision": "a53e95251321e61d8ffd6d06a6984f651c0b0db4",
|
|
5
|
-
"platforms": [
|
|
6
|
-
"darwin-arm64"
|
|
7
|
-
],
|
|
8
|
-
"images": {
|
|
9
|
-
"postgres": "postgres@sha256:742f40ea20b9ff2ff31db5458d127452988a2164df9e17441e191f3b72252193",
|
|
10
|
-
"gateway": "ghcr.io/clawscarf/clawscarf/runtime@sha256:eaa25c8e487970176b95de657df8bf4faff605f9f265ca760197800d9a1e5fcf",
|
|
11
|
-
"companion": "ghcr.io/clawscarf/clawscarf/companion@sha256:97cfeb78ab56ba625b30aafe2249633d9b69f7f3c75de7116453385010e6281f",
|
|
12
|
-
"openshellClient": "ghcr.io/clawscarf/clawscarf/openshell-client@sha256:669f37f654926fc7714783cc8b1b76bae9666895c2a4da959580037d281ebf56",
|
|
13
|
-
"relay": "ghcr.io/clawscarf/clawscarf/browser-relay@sha256:5ee13e9496e3b76e076e91c59a861a10ad7a31a13b145384fba314d7f0b686b5",
|
|
14
|
-
"models": "ghcr.io/berriai/litellm:v1.100.1@sha256:a3715fa7ad8387941ab697259bd2881d68931657247a41984f90fae6d11c62bf",
|
|
15
|
-
"browser": {
|
|
16
|
-
"chromium": "ghcr.io/clawscarf/clawscarf/browser@sha256:28ca9e4007d3b7c1bb9cb8f4bbaf275d1bc36ee2744079326b151b1f9eec78fd",
|
|
17
|
-
"node": "ghcr.io/clawscarf/clawscarf/browser-node@sha256:2837d8b75df69fb39c7f7ca8fa68249d9de6e20d4e028213792de5f8f9ef1bd7",
|
|
18
|
-
"dns": "ghcr.io/clawscarf/clawscarf/browser-dns@sha256:9f14cb85df522014c2bfc068e2b5788568f26b6e8d948cebe1250aff7630847b",
|
|
19
|
-
"egress": "ghcr.io/clawscarf/clawscarf/browser-egress@sha256:bd819dffa4e1f60fbeed3b459a26b3b274300a4e5836bb90cbd080f6681d594c"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"tools": {
|
|
23
|
-
"openshell": {
|
|
24
|
-
"version": "0.0.116",
|
|
25
|
-
"cli": {
|
|
26
|
-
"file": "tools/openshell",
|
|
27
|
-
"sha256": "0baeeffe0e4c184b08fde910ac9e8815fb0b8cfb808e49c0e634d7db65fb03bb",
|
|
28
|
-
"url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.1/openshell-darwin-arm64"
|
|
29
|
-
},
|
|
30
|
-
"gateway": {
|
|
31
|
-
"file": "tools/openshell-gateway",
|
|
32
|
-
"sha256": "298706259ef18350f334c222e981e91583558511d50bce22727c689d261203e6",
|
|
33
|
-
"url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.1/openshell-gateway-darwin-arm64"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|