@clawscarf/cli 0.1.0-alpha.7 → 0.1.0-alpha.8

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 CHANGED
@@ -13,7 +13,7 @@ models, connected accounts, and execution protected by NVIDIA OpenShell.
13
13
  ## Get started
14
14
 
15
15
  ```sh
16
- curl -fsSL https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.5/install.sh | sh
16
+ curl -fsSL https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.7/install.sh | sh
17
17
  export PATH="$HOME/.local/bin:$PATH"
18
18
  clawscarf configure
19
19
  ```
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.0-alpha.7",
2
+ "version": "0.1.0-alpha.8",
3
3
  "engines": {
4
4
  "node": ">=24.16.0 <25 || >=26.1.0"
5
5
  },
package/pnpm-lock.yaml CHANGED
@@ -620,6 +620,12 @@ packages:
620
620
  integrity: sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==
621
621
  }
622
622
 
623
+ '@types/semver@7.8.0':
624
+ resolution:
625
+ {
626
+ integrity: sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==
627
+ }
628
+
623
629
  '@types/ws@8.18.1':
624
630
  resolution:
625
631
  {
@@ -2776,6 +2782,8 @@ snapshots:
2776
2782
 
2777
2783
  '@types/retry@0.12.5': {}
2778
2784
 
2785
+ '@types/semver@7.8.0': {}
2786
+
2779
2787
  '@types/ws@8.18.1':
2780
2788
  dependencies:
2781
2789
  '@types/node': 24.10.13
package/recipes/README.md CHANGED
@@ -5,13 +5,15 @@ Recipes are bundled with the CLI. Each folder contains a [recipe.json](team-serv
5
5
 
6
6
  ```text
7
7
  recipes/team-server/recipe.json
8
- runtime/releases/0.1.0-dev.json
8
+ runtime/current.json
9
9
  packs/research-team/pack.json
10
10
  ```
11
11
 
12
12
  Each recipe has an ID and a version. Accepted installations retain that provenance
13
13
  alongside their actual settings; a CLI update never reapplies new defaults. Bump the
14
- recipe version when its defaults, pack selections or runtime pin change.
14
+ recipe version when its defaults, pack selections or explicit runtime reference change.
15
+ The resolved runtime release is retained separately; automatic advancement of the
16
+ [current selection](../release/README.md#published-and-development-use) does not change recipe defaults.
15
17
 
16
18
  A recipe's `runtime` points to an exact runtime definition, relative to the recipe
17
19
  file. Runtime images and tools are fixed; its other fields are defaults that users
@@ -1,7 +1,5 @@
1
1
  {
2
2
  "id": "team-server",
3
- "version": "0.1.2",
4
- "runtime": "../../runtime/releases/0.1.0-alpha.7.json",
5
3
  "name": "Team server",
6
4
  "description": "A protected OpenClaw server for your team, with hosted login, managed models and optional Connections.",
7
5
  "maturity": "supported",
@@ -15,15 +13,16 @@
15
13
  "browser": {
16
14
  "enabled": false
17
15
  },
18
- "publicWeb": true,
19
16
  "connections": {
20
17
  "enabled": true
21
- }
18
+ },
19
+ "publicWeb": true
22
20
  },
23
21
  "models": {
24
22
  "model": "gpt-6-astra",
25
23
  "provider": "openai",
26
24
  "reasoning": "medium"
27
25
  },
28
- "packs": []
26
+ "runtime": "../../runtime/current.json",
27
+ "version": "0.1.3"
29
28
  }
package/release/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  These have separate responsibilities:
4
4
 
5
- | Definition | Owns | Location |
6
- | --------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------ |
7
- | Runtime release | Exact software images, OpenShell executables, checksums and supported platforms | [runtime/releases](../runtime/releases/0.1.0-dev.json) |
8
- | Recipe | A fixed runtime reference plus editable model, reasoning, resource, capability and pack defaults | [recipes](../recipes/README.md) |
9
- | Pack | Native agent, skill and workflow files with declared prerequisites | [packs](../packs/README.md) |
5
+ | Definition | Owns | Location |
6
+ | --------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------- |
7
+ | Runtime release | Exact software images, OpenShell executables, checksums and supported platforms | [runtime/current.json](../runtime/current.json) |
8
+ | Recipe | A fixed runtime reference plus editable model, reasoning, resource, capability and pack defaults | [recipes](../recipes/README.md) |
9
+ | Pack | Native agent, skill and workflow files with declared prerequisites | [packs](../packs/README.md) |
10
10
 
11
11
  Several recipes can use the same runtime. A runtime contains no recipes, model
12
12
  catalog or pack selections. Changing a recipe does not require rebuilding its runtime.
@@ -20,10 +20,23 @@ Use the [installation guide](../deploy/deployment/installation.md) for published
20
20
  CLI installation, platform requirements and runtime downloads. Use the
21
21
  [development command](../scripts/README.md#development-command) for this checkout.
22
22
 
23
- The tracked [development runtime](../runtime/releases/0.1.0-dev.json) is an input
24
- for a prepared development environment, not a clean-machine release. Its exact
25
- images and executable paths are data in that definition. Packaging resolves those
26
- inputs to immutable candidate artifacts; runtime startup never selects latest.
23
+ The tracked [current runtime](../runtime/current.json) selects a published release
24
+ by exact image digests and downloadable tool checksums. The development command
25
+ reads it from the checkout; it does not query a remote latest version or build images.
26
+ New installations retain their own copy. Updating this file or the CLI never changes
27
+ an existing installation's saved selection.
28
+
29
+ After publication, the workflow copies the verified release manifest into this file
30
+ and commits it to `main`. Pulling that commit updates the development command's
31
+ selection. [Promotion](../scripts/release/advance-runtime.ts) requires a newer version
32
+ and the same selection the candidate was built against; an identical repeat is a no-op.
33
+ A changed pin or concurrent Git update fails visibly instead of overwriting another
34
+ selection. The promotion job can be retried independently of publication. Repository
35
+ rules must permit the release workflow's bot to push this commit to `main`.
36
+
37
+ For experiments with locally built images/tools, create a separate runtime bundle
38
+ and custom recipe using [runtime assembly](#assemble-runtime-artifacts). The bundled
39
+ recipe uses the same published artifact format in development and installed CLIs.
27
40
 
28
41
  ## Publishing structure
29
42
 
@@ -84,9 +97,10 @@ owns source identity and reconstruction. Candidate assembly packages
84
97
  [openclaw-source.json and openclaw-patches.tgz](../scripts/release/candidate.ts)
85
98
  with checksums; each runtime archive includes both. The runtime definition's `sourceRevision`
86
99
  identifies the ClawScarf commit that owns those inputs.
87
- The source recipe pins the development
88
- runtime. Packaging resolves that same runtime to the candidate's immutable definition;
89
- it does not make recipes select latest. Published definitions must not be overwritten.
100
+ Recipes reference the current manifest within their checkout or installed package.
101
+ Candidate packaging replaces that file only in the staged package with the newly
102
+ built manifest. It leaves source files untouched; publication advances the checkout
103
+ as described above. Published packages and release assets remain immutable.
90
104
 
91
105
  The `release-candidate` Actions artifact is the reviewable output. Download it and test
92
106
  a fresh supported installation, administrator login, real inference and stop/start
@@ -99,6 +113,9 @@ build run from `main`. It checks the source commit and artifact checksums, creat
99
113
  GitHub Release and publishes the already-built npm tarball. It never rebuilds images
100
114
  or packages. Prereleases use npm's `next` tag; stable versions use `latest`. Repeating
101
115
  a GitHub upload is allowed only when the existing checksums match exactly.
116
+ A separate dependent job then advances the checkout's current runtime selection;
117
+ a failure there leaves the published release available and can be retried without
118
+ republishing npm or rebuilding artifacts.
102
119
 
103
120
  Repository setup:
104
121
 
@@ -32,6 +32,6 @@ node scripts/clawscarf.js configure --directory /absolute/new-team
32
32
 
33
33
  Keep installation data outside the extracted package. A development runtime that
34
34
  references local image IDs needs those images prepared separately; see
35
- [development releases](README.md#published-and-development-use).
35
+ [custom runtime assembly](README.md#assemble-runtime-artifacts).
36
36
  The optional pack operator also needs its [Python dependencies](../packs/README.md#requirements-and-bindings).
37
37
  Required [notices](../THIRD_PARTY_NOTICES.md) and dependency licenses accompany the payload.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.1.0-alpha.7",
4
- "sourceRevision": "681655cc3e931209cb56b9f6866c9d780055c709",
3
+ "version": "0.1.0-alpha.8",
4
+ "sourceRevision": "dc1a8fc480b3a55841e2a1c1aea58bad9811402c",
5
5
  "platforms": [
6
6
  "darwin-arm64",
7
7
  "linux-arm64",
@@ -9,16 +9,16 @@
9
9
  ],
10
10
  "images": {
11
11
  "postgres": "postgres@sha256:742f40ea20b9ff2ff31db5458d127452988a2164df9e17441e191f3b72252193",
12
- "gateway": "ghcr.io/clawscarf/clawscarf/runtime@sha256:05a2341c36ca49d4a83d24a5f3bffff74dd54944b9fc3e13b7a5113d9ecda5f0",
13
- "companion": "ghcr.io/clawscarf/clawscarf/companion@sha256:94a134f1f88eaacc785d1f0b5d7715725506f5c2e9f1d822916ada86513fb3aa",
14
- "openshellClient": "ghcr.io/clawscarf/clawscarf/openshell-client@sha256:1ed1c3db878f5dc11045442319e3ff667aaebbf98977e042ace7486aff6a5b7a",
15
- "relay": "ghcr.io/clawscarf/clawscarf/browser-relay@sha256:96101feea8be108c25eb2df1847a99b4a4cab2ca632d9895b36ea665bbd5f5ac",
12
+ "gateway": "ghcr.io/clawscarf/clawscarf/runtime@sha256:a942812602fc1bdf40421d0ddcfda89aa87b9412b0616adba9a3da4e2a8a9a91",
13
+ "companion": "ghcr.io/clawscarf/clawscarf/companion@sha256:fea91c1beda96cf811cfc952c77fd7ac17df8374704da926984ec65df8de49af",
14
+ "openshellClient": "ghcr.io/clawscarf/clawscarf/openshell-client@sha256:419510bc89b4beee1763ad4b4a86ec490546e4cc1e645d054ef05a5cf654f27d",
15
+ "relay": "ghcr.io/clawscarf/clawscarf/browser-relay@sha256:602226e11f463fd811378173caedc0bea017f556d3fe94d17f4a752aaabd7171",
16
16
  "models": "ghcr.io/berriai/litellm:v1.100.1@sha256:a3715fa7ad8387941ab697259bd2881d68931657247a41984f90fae6d11c62bf",
17
17
  "browser": {
18
- "chromium": "ghcr.io/clawscarf/clawscarf/browser@sha256:c0b9a423829a990c805fdee6af55597ec02a9dc0a8aed35590fc43a75d5fe60f",
19
- "node": "ghcr.io/clawscarf/clawscarf/browser-node@sha256:2102a3bb15cb8c828ae09c91271f67b7e963d8bacd21f848044782e7c33f9d42",
20
- "dns": "ghcr.io/clawscarf/clawscarf/browser-dns@sha256:bc877b4bc0635a1dd4b4d3dba11e1142a38d0c1ff51bb90fc58ebf42fa57562d",
21
- "egress": "ghcr.io/clawscarf/clawscarf/browser-egress@sha256:7519c3dc6c54afadf5104ae92ecc5f6af6983fb308e16e2c81b7df593a8c6f8d"
18
+ "chromium": "ghcr.io/clawscarf/clawscarf/browser@sha256:ce259d28f078ac5d3ff9afc0a79b0a8935873eda953e6c04dbd8f756c3eb17ac",
19
+ "node": "ghcr.io/clawscarf/clawscarf/browser-node@sha256:de6b364dad0010ddf17f141888569c75f72364f4c2305d96e012c0c78c34f7c2",
20
+ "dns": "ghcr.io/clawscarf/clawscarf/browser-dns@sha256:3b8e9364ba8cc6271a53135c2c82944f9a3be423b20ccef3dea930e38d08d8a8",
21
+ "egress": "ghcr.io/clawscarf/clawscarf/browser-egress@sha256:06a33c3d9a5979359c92179ae936bfc0bb991b057eb5a4dc87dcf9240b513542"
22
22
  }
23
23
  },
24
24
  "tools": {
@@ -28,34 +28,34 @@
28
28
  "darwin-arm64": {
29
29
  "file": "tools/darwin-arm64/openshell",
30
30
  "sha256": "0baeeffe0e4c184b08fde910ac9e8815fb0b8cfb808e49c0e634d7db65fb03bb",
31
- "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.7/openshell-darwin-arm64"
31
+ "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.8/openshell-darwin-arm64"
32
32
  },
33
33
  "linux-arm64": {
34
34
  "file": "tools/linux-arm64/openshell",
35
35
  "sha256": "84df9f059b16b66c83748606cc87d9edcda84b57effea19543f5c368aba7eb18",
36
- "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.7/openshell-linux-arm64"
36
+ "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.8/openshell-linux-arm64"
37
37
  },
38
38
  "linux-x64": {
39
39
  "file": "tools/linux-x64/openshell",
40
40
  "sha256": "24e85062073d512d1951c76cd3890b7bce1ab01cc36a08f88b3b43c067a402da",
41
- "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.7/openshell-linux-x64"
41
+ "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.8/openshell-linux-x64"
42
42
  }
43
43
  },
44
44
  "gateway": {
45
45
  "darwin-arm64": {
46
46
  "file": "tools/darwin-arm64/openshell-gateway",
47
47
  "sha256": "298706259ef18350f334c222e981e91583558511d50bce22727c689d261203e6",
48
- "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.7/openshell-gateway-darwin-arm64"
48
+ "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.8/openshell-gateway-darwin-arm64"
49
49
  },
50
50
  "linux-arm64": {
51
51
  "file": "tools/linux-arm64/openshell-gateway",
52
52
  "sha256": "0e660428e655e7481f34ba8a4b351554400baf791863d32555c98dddb3f09ae2",
53
- "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.7/openshell-gateway-linux-arm64"
53
+ "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.8/openshell-gateway-linux-arm64"
54
54
  },
55
55
  "linux-x64": {
56
56
  "file": "tools/linux-x64/openshell-gateway",
57
57
  "sha256": "780fc7ad871e2163f3fb866a2d0254ee682fc0d83e6261a84b350c8dcdd9f47b",
58
- "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.7/openshell-gateway-linux-x64"
58
+ "url": "https://github.com/clawscarf/clawscarf/releases/download/v0.1.0-alpha.8/openshell-gateway-linux-x64"
59
59
  }
60
60
  }
61
61
  }
@@ -4,10 +4,16 @@ import { join } from "node:path";
4
4
  import { z } from "zod";
5
5
  import { run } from "./process.js";
6
6
  import { resourceNames, writePrivate } from "./state.js";
7
+ import { composePublicWebRules } from "./public-web.js";
7
8
  import { runtimeManager } from "./runtime.js";
8
9
  const connectionRuleSchema = z.object({
9
10
  name: z.string(),
10
- endpoints: z.array(z.object({ host: z.string(), port: z.number(), tls: z.string() })),
11
+ endpoints: z.array(z.object({
12
+ host: z.string(),
13
+ port: z.number(),
14
+ tls: z.string(),
15
+ allowed_ips: z.array(z.string()).optional(),
16
+ })),
11
17
  binaries: z.array(z.object({ path: z.string() })),
12
18
  });
13
19
  const publicRuleSchema = z.object({
@@ -65,19 +71,34 @@ export async function applyNetworkPolicy(directory, state, env, verify = false,
65
71
  const update = (policy) => {
66
72
  return {
67
73
  ...policy,
68
- network_policies: Object.fromEntries([
74
+ network_policies: composePublicWebRules(Object.fromEntries([
69
75
  ...Object.entries(policy.network_policies).filter(([key]) => !Object.hasOwn(change.rules, key)),
70
76
  ...Object.entries(change.rules).filter(([, rule]) => rule !== null),
71
- ]),
77
+ ])),
72
78
  };
73
79
  };
74
- const matches = (policy) => Object.entries(change.rules).every(([key, rule]) => {
75
- const observed = policy.network_policies[key];
76
- if (rule === null)
77
- return observed === undefined;
78
- const parsed = (key === "public_web" ? publicRuleSchema : connectionRuleSchema).safeParse(observed);
79
- return parsed.success && isDeepStrictEqual(parsed.data, rule);
80
- });
80
+ const matches = (policy) => {
81
+ const expected = update(policy).network_policies;
82
+ return [
83
+ ...new Set([
84
+ ...Object.keys(change.rules),
85
+ "connections_broker",
86
+ "model_gateway",
87
+ ]),
88
+ ].every((key) => {
89
+ const observed = policy.network_policies[key];
90
+ if (!Object.hasOwn(change.rules, key))
91
+ return isDeepStrictEqual(observed, expected[key]);
92
+ if (expected[key] === undefined)
93
+ return observed === undefined;
94
+ const schema = key === "public_web" ? publicRuleSchema : connectionRuleSchema;
95
+ const actualRule = schema.safeParse(observed);
96
+ const expectedRule = schema.safeParse(expected[key]);
97
+ return (actualRule.success &&
98
+ expectedRule.success &&
99
+ isDeepStrictEqual(actualRule.data, expectedRule.data));
100
+ });
101
+ };
81
102
  const name = resourceNames(state).sandbox;
82
103
  if (!(await runtimeManager(directory, state, env, command).recorded()).receipt) {
83
104
  if (verify)
@@ -4,7 +4,7 @@ import { parseDocument } from "yaml";
4
4
  import { z } from "zod";
5
5
  import { LocalSetupError } from "./process.js";
6
6
  import { ensurePrivateFile } from "./state.js";
7
- import { publicWebPolicy } from "./public-web.js";
7
+ import { publicWebPolicy, publicWebEndpointConstraints } from "./public-web.js";
8
8
  export function initialRuntimePolicy(source, models, connections, publicWeb = false) {
9
9
  const document = parseDocument(source);
10
10
  if (document.errors.length || document.warnings.length)
@@ -27,6 +27,7 @@ export function initialRuntimePolicy(source, models, connections, publicWeb = fa
27
27
  {
28
28
  host: connections.network.host,
29
29
  port: connections.network.port,
30
+ ...publicWebEndpointConstraints(connections.network.port, publicWeb),
30
31
  tls: "skip",
31
32
  },
32
33
  ],
@@ -42,6 +43,7 @@ export function initialRuntimePolicy(source, models, connections, publicWeb = fa
42
43
  {
43
44
  host: models.network.host,
44
45
  port: models.network.port,
46
+ ...publicWebEndpointConstraints(models.network.port, publicWeb),
45
47
  tls: "skip",
46
48
  },
47
49
  ],
@@ -1,3 +1,6 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import { z } from "zod";
3
+ import { LocalSetupError } from "./process.js";
1
4
  // Public unicast ranges, excluding private and special-use destinations.
2
5
  // Keep aligned with the browser egress boundary in deploy/execution/network/squid.conf.
3
6
  export const publicWebAddresses = [
@@ -174,3 +177,46 @@ export function publicWebPolicy(enabled) {
174
177
  }
175
178
  : null;
176
179
  }
180
+ /** OpenShell requires overlapping endpoints to have identical IP constraints. */
181
+ export function publicWebEndpointConstraints(port, enabled) {
182
+ return enabled && (port === 80 || port === 443)
183
+ ? { allowed_ips: publicWebAddresses }
184
+ : {};
185
+ }
186
+ const managedRuleSchema = z.looseObject({
187
+ endpoints: z.array(z.looseObject({
188
+ port: z.number(),
189
+ allowed_ips: z.array(z.string()).optional(),
190
+ })),
191
+ });
192
+ /** Compose managed service rules with public web without broadening private access. */
193
+ export function composePublicWebRules(rules) {
194
+ const enabled = rules.public_web !== undefined;
195
+ const result = { ...rules };
196
+ for (const key of ["connections_broker", "model_gateway"]) {
197
+ const rule = result[key];
198
+ if (rule === undefined)
199
+ continue;
200
+ const parsed = managedRuleSchema.safeParse(rule);
201
+ if (!parsed.success)
202
+ throw new LocalSetupError("invalid_runtime_policy", "A managed service network rule has been edited into an unsupported shape. Review its policy before changing public web access.");
203
+ result[key] = {
204
+ ...parsed.data,
205
+ endpoints: parsed.data.endpoints.map((endpoint) => {
206
+ if (endpoint.port !== 80 && endpoint.port !== 443)
207
+ return endpoint;
208
+ const managed = isDeepStrictEqual(endpoint.allowed_ips, publicWebAddresses);
209
+ if (enabled && endpoint.allowed_ips?.length && !managed)
210
+ throw new LocalSetupError("invalid_runtime_policy", "Public web overlaps a managed service's custom IP restrictions on port 80 or 443. Keep public web off or reconcile those restrictions explicitly.");
211
+ if (!enabled && !managed)
212
+ return endpoint;
213
+ const { allowed_ips: _previous, ...rest } = endpoint;
214
+ return {
215
+ ...rest,
216
+ ...publicWebEndpointConstraints(endpoint.port, enabled),
217
+ };
218
+ }),
219
+ };
220
+ }
221
+ return result;
222
+ }
@@ -95,7 +95,7 @@ export async function progress(message, work, options = {}) {
95
95
  const report = (detail) => {
96
96
  if (spinner && detail.includes("\n")) {
97
97
  spinner.stop(message);
98
- process.stderr.write(detail + "\n");
98
+ clack.log.info(detail, { output: process.stderr });
99
99
  spinner.start(message);
100
100
  }
101
101
  else if (spinner)
@@ -55,6 +55,15 @@ export const releaseSchema = z.strictObject({
55
55
  }),
56
56
  }),
57
57
  });
58
+ /** Published manifests must work without checkout-local images or executables. */
59
+ export function assertPublishedRuntime(runtime) {
60
+ if (JSON.stringify(runtime.images).includes('"sha256:') ||
61
+ runtime.platforms.some((platform) => {
62
+ const tools = releaseTools(runtime, platform);
63
+ return !tools.cli.url || !tools.gateway.url;
64
+ }))
65
+ throw Error("Published runtimes require registry digests and downloadable tools.");
66
+ }
58
67
  /** Single-platform development bundles and multi-platform published bundles share resolution. */
59
68
  export function releaseTools(release, platform = `${process.platform}-${process.arch}`) {
60
69
  const host = hostPlatformSchema.parse(platform);