@clawscarf/cli 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/THIRD_PARTY_NOTICES.md +102 -0
  4. package/deploy/execution/browser/LICENSE.playwright +202 -0
  5. package/deploy/execution/browser/seccomp.json +640 -0
  6. package/deploy/execution/browser-node/configuration.js +85 -0
  7. package/deploy/execution/browser-node/operator.js +167 -0
  8. package/deploy/execution/network/node-ingress.cfg +35 -0
  9. package/deploy/models/catalog.json +275 -0
  10. package/deploy/openshell/policy.yaml +24 -0
  11. package/generated/http/LICENSE.md +21 -0
  12. package/generated/http/client/client.gen.d.ts +2 -0
  13. package/generated/http/client/client.gen.js +216 -0
  14. package/generated/http/client/index.d.ts +10 -0
  15. package/generated/http/client/index.js +6 -0
  16. package/generated/http/client/types.gen.d.ts +120 -0
  17. package/generated/http/client/types.gen.js +2 -0
  18. package/generated/http/client/utils.gen.d.ts +37 -0
  19. package/generated/http/client/utils.gen.js +228 -0
  20. package/generated/http/core/auth.gen.d.ts +25 -0
  21. package/generated/http/core/auth.gen.js +14 -0
  22. package/generated/http/core/bodySerializer.gen.d.ts +25 -0
  23. package/generated/http/core/bodySerializer.gen.js +57 -0
  24. package/generated/http/core/params.gen.d.ts +43 -0
  25. package/generated/http/core/params.gen.js +109 -0
  26. package/generated/http/core/pathSerializer.gen.d.ts +33 -0
  27. package/generated/http/core/pathSerializer.gen.js +106 -0
  28. package/generated/http/core/queryKeySerializer.gen.d.ts +18 -0
  29. package/generated/http/core/queryKeySerializer.gen.js +92 -0
  30. package/generated/http/core/serverSentEvents.gen.d.ts +71 -0
  31. package/generated/http/core/serverSentEvents.gen.js +132 -0
  32. package/generated/http/core/types.gen.d.ts +83 -0
  33. package/generated/http/core/types.gen.js +2 -0
  34. package/generated/http/core/utils.gen.d.ts +19 -0
  35. package/generated/http/core/utils.gen.js +87 -0
  36. package/package.json +37 -0
  37. package/packs/README.md +130 -0
  38. package/packs/research-team/pack.json +20 -0
  39. package/packs/research-team/researcher/CLAW.md +19 -0
  40. package/packs/research-team/researcher/package.json +8 -0
  41. package/packs/research-team/researcher/profiles/openclaw.yml +6 -0
  42. package/packs/research-team/reviewer/CLAW.md +19 -0
  43. package/packs/research-team/reviewer/package.json +8 -0
  44. package/packs/research-team/reviewer/profiles/openclaw.yml +6 -0
  45. package/pnpm-lock.yaml +7158 -0
  46. package/recipes/README.md +37 -0
  47. package/recipes/team-server/recipe.json +28 -0
  48. package/release/README.md +137 -0
  49. package/release/components.json +29 -0
  50. package/release/operator.md +63 -0
  51. package/runtime/configuration.js +135 -0
  52. package/runtime/connections-configuration.js +70 -0
  53. package/runtime/model-contract.js +92 -0
  54. package/runtime/releases/0.1.0-alpha.1.json +37 -0
  55. package/scripts/clawscarf.js +92 -0
  56. package/scripts/cloud/login.js +134 -0
  57. package/scripts/cloud/registration.js +232 -0
  58. package/scripts/cloud/url.js +9 -0
  59. package/scripts/connections.js +193 -0
  60. package/scripts/controller.js +143 -0
  61. package/scripts/deployment/browser-node-compose.js +81 -0
  62. package/scripts/deployment/browser-node-helper.js +9 -0
  63. package/scripts/deployment/browser-node-pairing.js +165 -0
  64. package/scripts/deployment/browser-node.js +154 -0
  65. package/scripts/deployment/browser.js +143 -0
  66. package/scripts/deployment/certificates.js +76 -0
  67. package/scripts/deployment/compose.js +191 -0
  68. package/scripts/deployment/configuration.js +275 -0
  69. package/scripts/deployment/connection-policy.js +80 -0
  70. package/scripts/deployment/connection-settings.js +60 -0
  71. package/scripts/deployment/connections-runtime.js +31 -0
  72. package/scripts/deployment/connections.js +213 -0
  73. package/scripts/deployment/controller-compose.js +59 -0
  74. package/scripts/deployment/database.js +199 -0
  75. package/scripts/deployment/entrypoint.js +7 -0
  76. package/scripts/deployment/images.js +17 -0
  77. package/scripts/deployment/initial-services.js +53 -0
  78. package/scripts/deployment/launch.js +177 -0
  79. package/scripts/deployment/logs.js +15 -0
  80. package/scripts/deployment/model-gateway-compose.js +74 -0
  81. package/scripts/deployment/model-gateway.js +125 -0
  82. package/scripts/deployment/models.js +82 -0
  83. package/scripts/deployment/networks.js +354 -0
  84. package/scripts/deployment/policy.js +56 -0
  85. package/scripts/deployment/preflight.js +124 -0
  86. package/scripts/deployment/prepare.js +205 -0
  87. package/scripts/deployment/process.js +70 -0
  88. package/scripts/deployment/relay.js +58 -0
  89. package/scripts/deployment/runtime-binding.js +100 -0
  90. package/scripts/deployment/runtime.js +232 -0
  91. package/scripts/deployment/state.js +154 -0
  92. package/scripts/deployment/team.js +76 -0
  93. package/scripts/deployment/upgrade-rpc.py +148 -0
  94. package/scripts/deployment/upgrade-state.js +47 -0
  95. package/scripts/deployment/upgrade.js +298 -0
  96. package/scripts/deployment/volumes.js +24 -0
  97. package/scripts/errors.js +9 -0
  98. package/scripts/installation/administrator.js +23 -0
  99. package/scripts/installation/command.js +176 -0
  100. package/scripts/installation/configuration.js +104 -0
  101. package/scripts/installation/configure.js +122 -0
  102. package/scripts/installation/delete.js +111 -0
  103. package/scripts/installation/doctor.js +8 -0
  104. package/scripts/installation/errors.js +8 -0
  105. package/scripts/installation/files.js +51 -0
  106. package/scripts/installation/installer/cloud.js +22 -0
  107. package/scripts/installation/installer/collect.js +245 -0
  108. package/scripts/installation/installer/inputs.js +59 -0
  109. package/scripts/installation/installer/menu.js +49 -0
  110. package/scripts/installation/installer/prompts.js +139 -0
  111. package/scripts/installation/installer/run.js +251 -0
  112. package/scripts/installation/installer/secrets.js +16 -0
  113. package/scripts/installation/installer/sections/access.js +69 -0
  114. package/scripts/installation/installer/sections/certificates.js +18 -0
  115. package/scripts/installation/installer/sections/connections.js +4 -0
  116. package/scripts/installation/installer/sections/external-models.js +35 -0
  117. package/scripts/installation/installer/sections/models.js +119 -0
  118. package/scripts/installation/installer/sections/packs.js +82 -0
  119. package/scripts/installation/installer/sections/resources.js +10 -0
  120. package/scripts/installation/installer/settings.js +192 -0
  121. package/scripts/installation/installer/summary.js +44 -0
  122. package/scripts/installation/lifecycle.js +127 -0
  123. package/scripts/installation/location.js +3 -0
  124. package/scripts/installation/models.js +42 -0
  125. package/scripts/installation/options.js +360 -0
  126. package/scripts/installation/packs.js +150 -0
  127. package/scripts/installation/plan.js +121 -0
  128. package/scripts/installation/prerequisites.js +170 -0
  129. package/scripts/installation/recipes/catalog.js +55 -0
  130. package/scripts/installation/recipes/definition.js +32 -0
  131. package/scripts/installation/reconfigure.js +255 -0
  132. package/scripts/installation/requirements.js +23 -0
  133. package/scripts/installation/resolve.js +217 -0
  134. package/scripts/installation/runtime.js +87 -0
  135. package/scripts/installation/save.js +95 -0
  136. package/scripts/installation/setup.js +91 -0
  137. package/scripts/models/catalog.js +9 -0
  138. package/scripts/models/configuration.js +184 -0
  139. package/scripts/models/credentials.js +132 -0
  140. package/scripts/models/runtime.js +87 -0
  141. package/scripts/output.js +34 -0
  142. package/scripts/packs/connections.js +95 -0
  143. package/scripts/packs/lifecycle.js +125 -0
  144. package/scripts/packs/model.js +66 -0
  145. package/scripts/packs/native.js +69 -0
  146. package/scripts/packs/openshell.js +148 -0
  147. package/scripts/packs/policy.js +189 -0
  148. package/scripts/packs/requirements.in +1 -0
  149. package/scripts/packs/requirements.js +11 -0
  150. package/scripts/packs/requirements.txt +116 -0
  151. package/scripts/packs/source.js +90 -0
  152. package/scripts/packs/transport.py +68 -0
  153. package/scripts/people.js +52 -0
  154. package/scripts/release/create.js +62 -0
  155. package/scripts/release/definition.js +51 -0
  156. package/scripts/session.js +27 -0
  157. package/services/access/generated/client.gen.d.ts +12 -0
  158. package/services/access/generated/client.gen.js +3 -0
  159. package/services/access/generated/fastify.gen.d.ts +49 -0
  160. package/services/access/generated/fastify.gen.js +2 -0
  161. package/services/access/generated/index.d.ts +2 -0
  162. package/services/access/generated/index.js +2 -0
  163. package/services/access/generated/sdk.gen.d.ts +28 -0
  164. package/services/access/generated/sdk.gen.js +107 -0
  165. package/services/access/generated/types.gen.d.ts +349 -0
  166. package/services/access/generated/types.gen.js +2 -0
  167. package/services/access/migrations/001_access.sql +39 -0
  168. package/services/access/migrations/002_administrator_setup.sql +16 -0
  169. package/services/access/migrations/003_local_login_completion.sql +5 -0
  170. package/services/access/migrations/004_invitations.sql +21 -0
  171. package/services/access/repo/postgres.js +341 -0
  172. package/services/access/repo/session-logout.js +28 -0
  173. package/services/access/types/credential.js +2 -0
  174. package/services/access/types/errors.js +7 -0
  175. package/services/access/types/ingress.js +1 -0
  176. package/services/access/types/model.js +1 -0
  177. package/services/access/types/native-errors.js +7 -0
  178. package/services/access/types/native.js +1 -0
  179. package/services/cloud/generated/client.gen.d.ts +12 -0
  180. package/services/cloud/generated/client.gen.js +3 -0
  181. package/services/cloud/generated/index.d.ts +2 -0
  182. package/services/cloud/generated/index.js +2 -0
  183. package/services/cloud/generated/sdk.gen.d.ts +145 -0
  184. package/services/cloud/generated/sdk.gen.js +488 -0
  185. package/services/cloud/generated/types.gen.d.ts +2034 -0
  186. package/services/cloud/generated/types.gen.js +2 -0
  187. package/services/connections/cloud/generated/client.gen.d.ts +12 -0
  188. package/services/connections/cloud/generated/client.gen.js +3 -0
  189. package/services/connections/cloud/generated/fastify.gen.d.ts +68 -0
  190. package/services/connections/cloud/generated/fastify.gen.js +2 -0
  191. package/services/connections/cloud/generated/index.d.ts +2 -0
  192. package/services/connections/cloud/generated/index.js +2 -0
  193. package/services/connections/cloud/generated/sdk.gen.d.ts +95 -0
  194. package/services/connections/cloud/generated/sdk.gen.js +210 -0
  195. package/services/connections/cloud/generated/types.gen.d.ts +1044 -0
  196. package/services/connections/cloud/generated/types.gen.js +2 -0
  197. package/services/connections/migrations/001_connections.sql +220 -0
@@ -0,0 +1,148 @@
1
+ import { OperatorError } from "../errors.js";
2
+ import { execFile } from "node:child_process";
3
+ import { mkdtemp, readFile, rm } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { promisify } from "node:util";
8
+ import { z } from "zod";
9
+ import { verifyNetworkPolicy } from "./policy.js";
10
+ import { NativeClaws } from "./native.js";
11
+ const execute = promisify(execFile);
12
+ const snapshotSchema = z.object({
13
+ id: z.uuid(),
14
+ name: z.string(),
15
+ phase: z.literal("Ready"),
16
+ });
17
+ export class OpenShellClaws extends NativeClaws {
18
+ options;
19
+ constructor(options) {
20
+ super("/app/clawscarf/bin/openclaw");
21
+ this.options = options;
22
+ }
23
+ async shell(args) {
24
+ const child = execute(this.options.executable, [...args, "--gateway", this.options.gateway], {
25
+ maxBuffer: 4 * 1024 * 1024,
26
+ timeout: 120000,
27
+ env: this.options.env ?? process.env,
28
+ });
29
+ child.child.stdin?.end();
30
+ const { stdout } = await child;
31
+ return stdout;
32
+ }
33
+ boundTarget;
34
+ async exec(command, stdin) {
35
+ if (!this.boundTarget)
36
+ await this.target();
37
+ if (!this.boundTarget)
38
+ throw new OperatorError("An observed target is required.");
39
+ const child = execute(this.options.python, [fileURLToPath(new URL("./transport.py", import.meta.url))], {
40
+ maxBuffer: 4 * 1024 * 1024,
41
+ timeout: 120000,
42
+ env: this.options.env ?? process.env,
43
+ });
44
+ child.child.stdin?.end(JSON.stringify({
45
+ gateway: this.options.gateway,
46
+ sandboxId: this.boundTarget.sandboxId,
47
+ command,
48
+ stdin: stdin?.toString("base64") ?? "",
49
+ }));
50
+ return (await child).stdout;
51
+ }
52
+ async target() {
53
+ const gateway = snapshotSchema.parse(JSON.parse(await this.shell([
54
+ "sandbox",
55
+ "get",
56
+ this.options.sandbox,
57
+ "--output",
58
+ "json",
59
+ ])));
60
+ if (gateway.name !== this.options.sandbox)
61
+ throw new OperatorError("Unexpected sandbox identity.");
62
+ const target = {
63
+ kind: "openshell",
64
+ gateway: this.options.gateway,
65
+ sandbox: gateway.name,
66
+ sandboxId: gateway.id,
67
+ };
68
+ if (this.boundTarget && this.boundTarget.sandboxId !== target.sandboxId)
69
+ throw new OperatorError("Sandbox identity changed; create a new pack preview.");
70
+ this.boundTarget = target;
71
+ return target;
72
+ }
73
+ async version() {
74
+ if ((this.options.env ?? process.env).OPENCLAW_EXPERIMENTAL_CLAWS !== "1")
75
+ throw new OperatorError("Set OPENCLAW_EXPERIMENTAL_CLAWS=1 to acknowledge the experimental native Claws contract.");
76
+ const stdout = await this.exec([
77
+ "/app/clawscarf/bin/openclaw",
78
+ "--version",
79
+ ]);
80
+ if (!/\b2026\.9\.4\b/.test(stdout))
81
+ throw new OperatorError("Packs require the pinned OpenClaw 2026.9.4 release.");
82
+ }
83
+ async run(args) {
84
+ return JSON.parse(await this.exec(["/app/clawscarf/bin/openclaw", ...args]));
85
+ }
86
+ async source(root, digest, existing) {
87
+ const targetRoot = existing ??
88
+ (await this.exec([
89
+ "/usr/local/bin/node",
90
+ "-e",
91
+ "process.stdout.write(require('node:fs').mkdtempSync('/home/node/.clawscarf-pack-'))",
92
+ ])).trim();
93
+ if (!/^\/home\/node\/\.clawscarf-pack-[A-Za-z0-9]+$/.test(targetRoot))
94
+ throw new OperatorError("Invalid pack staging directory.");
95
+ if (!existing) {
96
+ const directory = await mkdtemp(join(tmpdir(), "clawscarf-pack-upload-"));
97
+ try {
98
+ const archive = join(directory, "source.tar");
99
+ await execute("tar", ["-cf", archive, "-C", root, "."], {
100
+ env: { ...process.env, COPYFILE_DISABLE: "1" },
101
+ });
102
+ await this.exec(["tar", "-xf", "-", "-C", targetRoot], await readFile(archive));
103
+ }
104
+ finally {
105
+ await rm(directory, { recursive: true, force: true });
106
+ }
107
+ }
108
+ const observed = z
109
+ .object({ digest: z.string() })
110
+ .parse(JSON.parse(await this.exec([
111
+ "/app/clawscarf/bin/packs",
112
+ "inspect",
113
+ targetRoot,
114
+ "--json",
115
+ ])));
116
+ if (observed.digest !== digest)
117
+ throw new OperatorError("Target pack differs from reviewed source; create a new preview.");
118
+ return targetRoot;
119
+ }
120
+ async network(requirements) {
121
+ if (!requirements.length)
122
+ return null;
123
+ const target = await this.target();
124
+ if (target.kind !== "openshell")
125
+ throw new OperatorError("An OpenShell target is required.");
126
+ for (const requirement of requirements) {
127
+ await this.exec([
128
+ "/usr/local/bin/node",
129
+ "-e",
130
+ "const fs=require('node:fs');fs.accessSync(process.argv[1],fs.constants.X_OK);if(fs.realpathSync(process.argv[1])!==process.argv[1])process.exit(1)",
131
+ requirement.binary,
132
+ ]);
133
+ }
134
+ return verifyNetworkPolicy(requirements, {
135
+ sandbox: target.sandbox,
136
+ sandboxId: target.sandboxId,
137
+ run: (args) => this.shell(args),
138
+ });
139
+ }
140
+ async binary(name) {
141
+ await this.exec([
142
+ "/usr/local/bin/node",
143
+ "-e",
144
+ "const fs=require('node:fs'),path=require('node:path');if(!process.env.PATH.split(path.delimiter).some(p=>{try{fs.accessSync(path.join(p,process.argv[1]),fs.constants.X_OK);return true}catch{return false}}))process.exit(1)",
145
+ name,
146
+ ]);
147
+ }
148
+ }
@@ -0,0 +1,189 @@
1
+ import { isAbsolute, normalize } from "node:path/posix";
2
+ import { z } from "zod";
3
+ const host = z
4
+ .string()
5
+ .max(253)
6
+ .regex(/^(?=.{1,253}$)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/)
7
+ .refine((value) => !/^[0-9.]+$/.test(value) &&
8
+ value.split(".").every((label) => label.length <= 63), "Use an exact DNS hostname, not an IP address.");
9
+ const binary = z
10
+ .string()
11
+ .refine((value) => isAbsolute(value) &&
12
+ normalize(value) === value &&
13
+ !/[\s*?[\]{}]/.test(value) &&
14
+ Array.from(value).every((character) => character.charCodeAt(0) >= 32), "Use an exact canonical executable path inside the runtime.");
15
+ export const networkRequirementSchema = z.strictObject({
16
+ binary,
17
+ host,
18
+ port: z.number().int().min(1).max(65535),
19
+ protocol: z.literal("tcp"),
20
+ });
21
+ const hash = z.string().regex(/^[a-f0-9]{64}$/);
22
+ const revision = z
23
+ .string()
24
+ .regex(/^\d+$/)
25
+ .refine((value) => BigInt(value) <= 18446744073709551615n);
26
+ export const policyProofSchema = z.strictObject({
27
+ sandboxId: z.string().min(1),
28
+ version: z.number().int().positive(),
29
+ hash,
30
+ configRevision: revision,
31
+ });
32
+ export class PolicyVerificationError extends Error {
33
+ code;
34
+ constructor(code, message) {
35
+ super(message);
36
+ this.code = code;
37
+ this.name = "PolicyVerificationError";
38
+ }
39
+ }
40
+ const sandboxSchema = z.object({
41
+ id: z.string(),
42
+ name: z.string(),
43
+ phase: z.literal("Ready"),
44
+ current_policy_version: z.number().int().positive(),
45
+ });
46
+ const effectiveSchema = z.object({
47
+ sandbox: z.string(),
48
+ version: z.number().int().positive(),
49
+ hash,
50
+ config_revision: revision,
51
+ status: z.literal("effective"),
52
+ policy_source: z.string(),
53
+ policy: z.object({
54
+ network_policies: z.record(z.string(), z.unknown()).default({}),
55
+ network_middlewares: z.record(z.string(), z.unknown()).default({}),
56
+ }),
57
+ });
58
+ const loadedSchema = z.object({
59
+ sandbox: z.string(),
60
+ version: z.number().int().positive(),
61
+ active_version: z.number().int().positive(),
62
+ hash,
63
+ status: z.string(),
64
+ loaded_at_ms: z.number().int().positive().optional(),
65
+ load_error: z.string().optional(),
66
+ });
67
+ const entrySchema = z.strictObject({
68
+ name: z.string().optional(),
69
+ endpoints: z.array(z.record(z.string(), z.unknown())),
70
+ binaries: z.array(z.strictObject({ path: z.string() })),
71
+ });
72
+ const endpointSchema = z.strictObject({
73
+ host,
74
+ port: z.number().int().min(1).max(65535),
75
+ protocol: z.literal("tcp"),
76
+ enforcement: z.literal("enforce").optional(),
77
+ });
78
+ /** JSON.parse's source context preserves the CLI's uint64 fingerprint exactly. */
79
+ function parse(raw) {
80
+ return JSON.parse(raw, (key, value, context) => {
81
+ if (key !== "config_revision")
82
+ return value;
83
+ if (typeof value !== "number" ||
84
+ !context?.source ||
85
+ !/^\d+$/.test(context.source))
86
+ throw new PolicyVerificationError("policy_unverified", "OpenShell returned an invalid config revision.");
87
+ return context.source;
88
+ });
89
+ }
90
+ function verifyCoverage(policy, requirements) {
91
+ if (Object.keys(policy.network_middlewares).length)
92
+ throw new PolicyVerificationError("policy_unsupported", "This pack verifier cannot qualify middleware-dependent network policy. Review the native policy explicitly.");
93
+ const entries = Object.values(policy.network_policies).map((value) => {
94
+ const result = entrySchema.safeParse(value);
95
+ if (!result.success)
96
+ throw new PolicyVerificationError("policy_unsupported", "Unsupported network rule shape; this verifier supports exact executable and unconditional TCP grants only.");
97
+ return result.data;
98
+ });
99
+ for (const requirement of requirements) {
100
+ let permitted = false;
101
+ for (const entry of entries) {
102
+ for (const endpoint of entry.endpoints) {
103
+ if (typeof endpoint.host !== "string" ||
104
+ /[*?[\]{}]/.test(endpoint.host))
105
+ throw new PolicyVerificationError("policy_unsupported", "Hostless and wildcard endpoint rules require native policy review; this verifier does not infer their coverage.");
106
+ if (endpoint.host !== requirement.host)
107
+ continue;
108
+ if (entry.binaries.some((value) => /[*?[\]{}]/.test(value.path)))
109
+ throw new PolicyVerificationError("policy_unsupported", "Wildcard executable rules require native policy review. Use a canonical executable requirement.");
110
+ if (!entry.binaries.some((value) => value.path === requirement.binary))
111
+ continue;
112
+ if (endpoint.protocol === undefined)
113
+ throw new PolicyVerificationError("policy_unsupported", `Policy for ${requirement.host} permits explicit-proxy passthrough, not native TCP. This TCP requirement needs an explicit protocol: tcp grant; review the required transport without changing policy automatically.`);
114
+ const result = endpointSchema.safeParse(endpoint);
115
+ if (!result.success)
116
+ throw new PolicyVerificationError("policy_unsupported", `Cannot qualify conditional, audited or non-TCP policy for ${requirement.host}; this verifier does not evaluate L7 rules.`);
117
+ if (result.data.port === requirement.port)
118
+ permitted = true;
119
+ }
120
+ }
121
+ if (!permitted)
122
+ throw new PolicyVerificationError("network_not_permitted", `No exact loaded TCP grant for ${requirement.binary} to ${requirement.host}:${String(requirement.port)}.`);
123
+ }
124
+ }
125
+ /** Observes loaded policy coverage, never changes policy or claims a successful connection. */
126
+ export async function verifyNetworkPolicy(requirements, target) {
127
+ if (!requirements.length)
128
+ return null;
129
+ const parsed = z.array(networkRequirementSchema).parse(requirements);
130
+ const readSandbox = async () => sandboxSchema.parse(parse(await target.run([
131
+ "sandbox",
132
+ "get",
133
+ target.sandbox,
134
+ "--output",
135
+ "json",
136
+ ])));
137
+ const readEffective = async () => effectiveSchema.parse(parse(await target.run([
138
+ "policy",
139
+ "get",
140
+ target.sandbox,
141
+ "--full",
142
+ "--output",
143
+ "json",
144
+ ])));
145
+ const identity = await readSandbox();
146
+ if (identity.id !== target.sandboxId || identity.name !== target.sandbox)
147
+ throw new PolicyVerificationError("policy_changed", "The target sandbox was replaced; create a new pack preview.");
148
+ const effective = await readEffective();
149
+ if (effective.sandbox !== target.sandbox ||
150
+ effective.policy_source !== "sandbox")
151
+ throw new PolicyVerificationError("policy_unsupported", "Global policy requires a per-sandbox effective-load acknowledgment this verifier cannot obtain. Use explicit native review.");
152
+ const loaded = loadedSchema.parse(parse(await target.run([
153
+ "policy",
154
+ "get",
155
+ target.sandbox,
156
+ "--rev",
157
+ String(effective.version),
158
+ "--output",
159
+ "json",
160
+ ])));
161
+ if (loaded.sandbox !== target.sandbox ||
162
+ loaded.status !== "loaded" ||
163
+ !loaded.loaded_at_ms ||
164
+ loaded.load_error ||
165
+ loaded.version !== effective.version ||
166
+ loaded.active_version !== effective.version ||
167
+ identity.current_policy_version !== effective.version)
168
+ throw new PolicyVerificationError("policy_unverified", "The requested policy is not confirmed loaded. Wait for native policy activation, then retry the preview.");
169
+ if (loaded.hash !== effective.hash)
170
+ throw new PolicyVerificationError("policy_unsupported", "Effective policy differs from the acknowledged revision, including possible provider composition. Native review is required.");
171
+ verifyCoverage(effective.policy, parsed);
172
+ const after = await readEffective();
173
+ const finalIdentity = await readSandbox();
174
+ if (finalIdentity.id !== identity.id ||
175
+ finalIdentity.name !== identity.name ||
176
+ finalIdentity.current_policy_version !== loaded.version ||
177
+ after.hash !== effective.hash ||
178
+ after.version !== effective.version ||
179
+ after.config_revision !== effective.config_revision ||
180
+ after.policy_source !== effective.policy_source ||
181
+ after.sandbox !== effective.sandbox)
182
+ throw new PolicyVerificationError("policy_changed", "Runtime policy changed during verification; create a new pack preview.");
183
+ return {
184
+ sandboxId: identity.id,
185
+ version: loaded.version,
186
+ hash: effective.hash,
187
+ configRevision: effective.config_revision,
188
+ };
189
+ }
@@ -0,0 +1 @@
1
+ openshell==0.0.116
@@ -0,0 +1,11 @@
1
+ import { verifyConnections } from "./connections.js";
2
+ export async function requirements(pack, member, native, bindingsFile, sourceRoot = "") {
3
+ for (const binary of pack.execution.binaries)
4
+ await native.binary(binary);
5
+ const network = await native.network(pack.execution.network);
6
+ const model = member.requirements.model === "configured-default"
7
+ ? await native.defaultModel()
8
+ : null;
9
+ const connections = await verifyConnections(member, native, sourceRoot, bindingsFile);
10
+ return { model, connections, network };
11
+ }
@@ -0,0 +1,116 @@
1
+ # This file was autogenerated by uv via the following command:
2
+ # uv pip compile scripts/packs/requirements.in --generate-hashes --output-file scripts/packs/requirements.txt --python-version 3.12
3
+ anyio==4.15.1 \
4
+ --hash=sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101 \
5
+ --hash=sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94
6
+ # via httpx
7
+ certifi==2026.7.22 \
8
+ --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \
9
+ --hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55
10
+ # via
11
+ # httpcore
12
+ # httpx
13
+ cloudpickle==3.1.2 \
14
+ --hash=sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414 \
15
+ --hash=sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a
16
+ # via openshell
17
+ grpcio==1.84.0 \
18
+ --hash=sha256:026d757df86c5b7a41de8200b9a2cda454aaa5004cb0c7e3374c66eb82f61499 \
19
+ --hash=sha256:06619ba1515e5ee69fb2a514e95dd8be05ce74cb3928d5b34f87f87c86fe3c27 \
20
+ --hash=sha256:08735e3d08d24ab3132cf87e2e5dea8746cabcc7d676c2b0b7362f195feef9d9 \
21
+ --hash=sha256:0d532ade4486dad9b302ffa4d4683d67561051c26d17c4023322845e9fa10140 \
22
+ --hash=sha256:158c1c11cfb61b4849c3caf4d52de6f5ecd376e14446feb4a90dc95a90d616f5 \
23
+ --hash=sha256:15bb76489e337fc492685c9758e2fd4d4ab516b901ad830dc5a91987decf00be \
24
+ --hash=sha256:19aaf172fc2edbefccce3f6e92c5150975dbe56c45744e9e87cf72ebdf85bfbe \
25
+ --hash=sha256:209414080da8c20af94df1395b635da52dd57b5edc9e917e1deca0dc1c4bb55e \
26
+ --hash=sha256:210e4c32f907045eb8158273e60c6ab69a3947697df6245dbda381f26c59485b \
27
+ --hash=sha256:23e6e8e8a75cff88e0a793bfd3becea03a13e2763ae90c1ff573bc19ca5b429a \
28
+ --hash=sha256:27b8b36200a9fbee6e120246f4a8a41657549107ef19fb2c819c4b2fd524f39a \
29
+ --hash=sha256:28d2609691da93051e998495108bbddd2a9f7a561253bae94828d81290f30c15 \
30
+ --hash=sha256:2c024da73b296f040b8360e60bd73a659b230093684a438da0e1260f34cc724e \
31
+ --hash=sha256:393d8a78bff6731ecc5ad2151a821f8fbc1709b137ebb9c25a4ef399fbdcc914 \
32
+ --hash=sha256:3d6a82c4fc6c85f2fb7572c86bdb86f84c97b6580e5f6599f711800bac48a5d8 \
33
+ --hash=sha256:3de427b05f244ba2c2a9bdc67e7a6731c8340811524ecc4435466549f8af1d17 \
34
+ --hash=sha256:406583b4e8fb2282ebd392e12b963e601c1f82e07125a8c2cb5b144e7e024796 \
35
+ --hash=sha256:4119efa6519871719ad81f33bc95ab87857dcb1c5801f30a6e592f2c41164169 \
36
+ --hash=sha256:42959bd50dd660ffc3f2a9bec15a6da4f9aaa0dda555d59ff2d2e80b908456a8 \
37
+ --hash=sha256:455ed6083353b8e938f1d58c765eab2fbb165731e5b507be30fee344915a2a11 \
38
+ --hash=sha256:465eef3d17e59ad22a556fc0138f7c7c799df426734344daec42c797d49fda99 \
39
+ --hash=sha256:47ecf0d9b81d981f07b61bd89eced9d2582f5eaacc3aaa36ad27f81aef70a27f \
40
+ --hash=sha256:49717e857899f4136d7657bf5aded61ac479110a075438290923a4d86af7cd02 \
41
+ --hash=sha256:4aaeceeb7fa7d824c322d1ec3208c8495c88478a927295553235435fc49043ad \
42
+ --hash=sha256:57dc36a5ab0e676f5f6e171de2917fd0aef73f32a9aaf23956bfe19997a30bd1 \
43
+ --hash=sha256:5933a052946873d01a42119a05420d669bdca436aeba2d1851988ccb12b421c0 \
44
+ --hash=sha256:5deda5b4bf62769eb98c119cca43d40e1231e34846b19db5cdea821d446a2253 \
45
+ --hash=sha256:61386101ecaa096b694d0dd278caf99a56aeec78440cc17e918eef0b50f2d567 \
46
+ --hash=sha256:659728f20fc7a0933ed7b1945435e31014b97ab8a5a7edcbaa70da4794aeb191 \
47
+ --hash=sha256:70bb4ce8be0c5606bec259cbd7152374470396413b7863a658a08c849e6b29ff \
48
+ --hash=sha256:71fd60e6e426d293d0a2f685115ad0a0845117602cf13605a4be7524fb5f7bba \
49
+ --hash=sha256:756ea5c2da00fa65c930284892d2a9706828704ca3ba40b4c51c4834eb39fcfd \
50
+ --hash=sha256:800b7e00d92553313c0463c200087930aa78678ec1d528193aeb50906f55989b \
51
+ --hash=sha256:82da34ae4f639c73ac46e521e00c0a49bf86f717b9fb1f405f133e98731e38dc \
52
+ --hash=sha256:8e1a45d174b6b8589f51dce1cea804aa6c1f72c9c80cba91ae2caabeb6d90540 \
53
+ --hash=sha256:8e3f508d0e9e6236ba2f08d56e33355e434e785e813149a1b8477d3edf69779d \
54
+ --hash=sha256:986e9751d416d7a6eaa2fecdac38da63153d63a4b340ba7d624889c490451500 \
55
+ --hash=sha256:9b73836ba0e16fcbb57c31cf6cbc2907c8d8c790b83679df454b74bd15e0be04 \
56
+ --hash=sha256:9bab4cf571653a8afffb83ce21aa27b51dfe629b526b7b6adec35491fe1fc2ea \
57
+ --hash=sha256:a71d24f40b0cc6798feaa978c7411dc1135b7018e9fc0442db611c139bf58344 \
58
+ --hash=sha256:a9383401d9f116f98cacd4eba6c505a6edb80ba65badfc8e8ed8ae64983bcc44 \
59
+ --hash=sha256:b44f0a0fc7bc6677d38cc80bca1a32814ce6c8f200fb8b3c1a61c9d77eaefbf3 \
60
+ --hash=sha256:b5c6f20d657ae09ae4e30d9d3a21edd13f1219d58cc6f999b9d1bb63be9c1baa \
61
+ --hash=sha256:b61692f0069b3eee2fc8a3a1b7f6c044df9e03fede6ce69b3ca832e1c39f26c5 \
62
+ --hash=sha256:b8c62888c3e49debf37ad9773e3c02f77b0c1e811f8fb0962f2b6c3bbab5b97a \
63
+ --hash=sha256:bd8ea8eb3817b226057cc1c0e7ec4b378dcda52043b972b6ff12b1152178967d \
64
+ --hash=sha256:c5559b492007dc09b4de9b95dab05f0b5e53547aad230cf07e46c7dd017a3be5 \
65
+ --hash=sha256:d0fdd25faece8a1f95e8a3a8006e29701b5cf8dadb4a8132e68f3134637004a5 \
66
+ --hash=sha256:e094dd21f077af8194923fc263cad872eaa1802bb0156fd7e5ae18e99cd86715 \
67
+ --hash=sha256:e41c3993eee896c617dbd8a505085d28b6e84a0445ed9a1f40f95808473cf678 \
68
+ --hash=sha256:e88d304f094f4937bc27ec6a435e218a084168f11ec630c8d5d39b431d08d81d \
69
+ --hash=sha256:e90e3bdf7b5eac005fef631adae9cafde16f922def207b80a7c46b253c18ad20 \
70
+ --hash=sha256:ed2c1493c44d0932f1e55fdb5d1ead658c68288ec5d51b8c4928422d98633ef9 \
71
+ --hash=sha256:edb6f87fc60ff438557291501b3e16c7a77c3b01a52d782cf276dccc7c5dd89c \
72
+ --hash=sha256:efb29f8633bf6630dc89de4fe0353ac3d7e4b70ef7b6e29fb40f00e68c127fa5 \
73
+ --hash=sha256:f6c972474ce691aca74e58d17625450cef153dc4760364cadeb167983ea6d589 \
74
+ --hash=sha256:f6d178ba6dc8e82976c184b65fddde172d054c17237993a3e083efe4f134d55b \
75
+ --hash=sha256:f9a456bdbed52a01c9ab8423bdebab04a5363c78676edc55ab9b58bd13bdf9e1 \
76
+ --hash=sha256:fbdbcd06986ede3ce584083b1dc2afe6808e8943e5cf50ad11183c03aceda25a \
77
+ --hash=sha256:fc66cb50c93554b86db0b6625ab5c6e9051dbf8847c08d93c84918e02e413fb7 \
78
+ --hash=sha256:fff5ef3fe1bba7d6147e5f19e01e5e122ac2c076486887ddcb8d42e663400fbe
79
+ # via openshell
80
+ h11==0.16.0 \
81
+ --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \
82
+ --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86
83
+ # via httpcore
84
+ httpcore==1.0.9 \
85
+ --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \
86
+ --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8
87
+ # via httpx
88
+ httpx==0.28.1 \
89
+ --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \
90
+ --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
91
+ # via openshell
92
+ idna==3.19 \
93
+ --hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \
94
+ --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4
95
+ # via
96
+ # anyio
97
+ # httpx
98
+ openshell==0.0.116 \
99
+ --hash=sha256:5a31eb4e38d7b5d746956404145b7335557f9060ac7a987c65ea5af4d708b3fc
100
+ # via -r scripts/packs/requirements.in
101
+ protobuf==7.36.1 \
102
+ --hash=sha256:0b53ce95272aad50ad25d7ff03373743209822e8ba42ea7fad27d2bee1547d00 \
103
+ --hash=sha256:39c518c05586c016d7874ff6079ee115bcec1ea5fbb1d177fbf7867ef4c67e44 \
104
+ --hash=sha256:3cf2ee25d006cee57294a1196ea43b37feb78e0dcd1e8af5c1aeddb777655aca \
105
+ --hash=sha256:43d3d37b1eb24c113b9b7d02008cac44e423f00b611b7781ae998d7623972969 \
106
+ --hash=sha256:51139351435d9b43d88a55eaa49fb6f737fbb478fb0cbf2cf694d1a04a9d3363 \
107
+ --hash=sha256:7d951e46b3f963d6c264c367c437921de9d5aedd9c3f9612b9077736b4e3ad5c \
108
+ --hash=sha256:97198b77e369a0abd8e262b8f6c7266c55ddb796a3a12c76d7b8881188ed83aa \
109
+ --hash=sha256:d0f6470f0ce2b84e3feaea2d4b816378b37ba4d4aa08a274305373de93e2d524
110
+ # via openshell
111
+ typing-extensions==4.16.0 \
112
+ --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \
113
+ --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5
114
+ # via
115
+ # anyio
116
+ # grpcio
@@ -0,0 +1,90 @@
1
+ import { OperatorError } from "../errors.js";
2
+ import { createHash } from "node:crypto";
3
+ import { lstat, readFile, readdir, realpath, cp, mkdtemp, writeFile, rm, } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { z } from "zod";
6
+ import { join, relative, resolve, sep } from "node:path";
7
+ import { packSchema } from "./model.js";
8
+ export async function openPack(directory) {
9
+ const root = await realpath(directory);
10
+ const manifest = packSchema.parse(JSON.parse(await readFile(join(root, "pack.json"), "utf8")));
11
+ if (new Set(manifest.members.map((item) => item.id)).size !==
12
+ manifest.members.length)
13
+ throw new OperatorError("Pack member IDs must be unique.");
14
+ for (const member of manifest.members) {
15
+ if (new Set(member.requirements.connections.map((item) => item.slot)).size !==
16
+ member.requirements.connections.length)
17
+ throw new OperatorError("Connection slots must be unique within a member.");
18
+ const source = await realpath(resolve(root, member.source));
19
+ if (!source.startsWith(root + sep))
20
+ throw new OperatorError("Claw source must remain inside its pack.");
21
+ }
22
+ const digest = createHash("sha256");
23
+ let size = 0;
24
+ async function visit(path) {
25
+ const metadata = await lstat(path);
26
+ if (metadata.isSymbolicLink() ||
27
+ (metadata.nlink > 1 && !metadata.isDirectory()))
28
+ throw new OperatorError("Pack sources cannot use linked files.");
29
+ if (metadata.isDirectory()) {
30
+ for (const item of (await readdir(path)).sort())
31
+ await visit(join(path, item));
32
+ return;
33
+ }
34
+ if (!metadata.isFile() || (size += metadata.size) > 8 * 1024 * 1024)
35
+ throw new OperatorError("Pack source exceeds the 8 MiB limit or is not a regular file.");
36
+ digest.update(JSON.stringify({
37
+ path: relative(root, path).split(sep).join("/"),
38
+ executable: metadata.mode & 0o111,
39
+ sha256: createHash("sha256")
40
+ .update(await readFile(path))
41
+ .digest("hex"),
42
+ }) + "\n");
43
+ }
44
+ await visit(root);
45
+ return { root, manifest, digest: "sha256:" + digest.digest("hex") };
46
+ }
47
+ export async function prepareSource(source, member, connections, native, existing) {
48
+ if (!connections.length)
49
+ return native.source(source.root, source.digest, existing);
50
+ if ((await native.target()).kind !== "openshell")
51
+ throw new OperatorError("Connection file installation requires operator-side OpenShell execution.");
52
+ if (!member.connectionFile)
53
+ throw new OperatorError("Connection file is not declared.");
54
+ const directory = await mkdtemp(join(tmpdir(), "clawscarf-pack-bound-"));
55
+ try {
56
+ await cp(source.root, directory, { recursive: true });
57
+ await writeFile(join(directory, member.source, member.connectionFile), JSON.stringify({
58
+ schemaVersion: 1,
59
+ connections: connections.map(({ slot, connectionId, name, connectorId }) => ({
60
+ slot,
61
+ connectionId,
62
+ name,
63
+ connectorId,
64
+ })),
65
+ }, null, 2) + "\n", { mode: 0o600 });
66
+ const prepared = await openPack(directory);
67
+ const target = await native.source(directory, prepared.digest, existing);
68
+ const inspected = z
69
+ .object({
70
+ valid: z.literal(true),
71
+ manifest: z.object({
72
+ workspace: z.object({
73
+ files: z.array(z.object({ source: z.string(), path: z.string() })),
74
+ }),
75
+ }),
76
+ })
77
+ .parse(await native.run([
78
+ "claws",
79
+ "inspect",
80
+ join(target, member.source),
81
+ "--json",
82
+ ]));
83
+ if (!inspected.manifest.workspace.files.some((file) => file.source === member.connectionFile))
84
+ throw new OperatorError("connectionFile must be declared in the native Claw workspace.files; its ownership cannot be inferred.");
85
+ return target;
86
+ }
87
+ finally {
88
+ await rm(directory, { recursive: true, force: true });
89
+ }
90
+ }
@@ -0,0 +1,68 @@
1
+ """Operator-only bridge to the pinned official OpenShell SDK; no runtime secrets."""
2
+ import base64
3
+ import json
4
+ import re
5
+ import sys
6
+ from uuid import UUID
7
+
8
+ from openshell.sandbox import SandboxClient
9
+
10
+ MAX_INPUT = 16 * 1024 * 1024
11
+ MAX_OUTPUT = 4 * 1024 * 1024
12
+
13
+
14
+ def parse_request():
15
+ raw = sys.stdin.buffer.read(MAX_INPUT + 1)
16
+ if len(raw) > MAX_INPUT:
17
+ raise ValueError("Operator payload too large")
18
+ value = json.loads(raw)
19
+ if not isinstance(value, dict) or set(value) != {
20
+ "gateway", "sandboxId", "command", "stdin"
21
+ }:
22
+ raise ValueError("Invalid operator request")
23
+ gateway = value["gateway"]
24
+ sandbox_id = value["sandboxId"]
25
+ command = value["command"]
26
+ encoded = value["stdin"]
27
+ if not isinstance(gateway, str) or not re.fullmatch(r"[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}", gateway):
28
+ raise ValueError("Invalid gateway name")
29
+ if not isinstance(sandbox_id, str) or str(UUID(sandbox_id)) != sandbox_id:
30
+ raise ValueError("Invalid sandbox UUID")
31
+ if not isinstance(command, list) or not 1 <= len(command) <= 128:
32
+ raise ValueError("Invalid command")
33
+ if not all(isinstance(arg, str) and len(arg) <= 8192 and "\0" not in arg for arg in command):
34
+ raise ValueError("Invalid command argument")
35
+ if not isinstance(encoded, str):
36
+ raise ValueError("Invalid stdin")
37
+ return gateway, sandbox_id, command, base64.b64decode(encoded, validate=True)
38
+
39
+
40
+ def main():
41
+ try:
42
+ gateway, sandbox_id, command, stdin = parse_request()
43
+ except (ValueError, TypeError):
44
+ sys.stderr.write("Invalid operator execution request.\n")
45
+ return 2
46
+ try:
47
+ with SandboxClient.from_active_cluster(cluster=gateway, timeout=120) as client:
48
+ result = client.exec(
49
+ sandbox_id, command,
50
+ env={"OPENCLAW_EXPERIMENTAL_CLAWS": "1"},
51
+ stdin=stdin,
52
+ timeout_seconds=110,
53
+ )
54
+ except Exception:
55
+ sys.stderr.write("OpenShell execution has no confirmed completion; inspect native state before retrying.\n")
56
+ return 3
57
+ if result.exit_code != 0:
58
+ sys.stderr.write(f"Native command exited with status {result.exit_code}; inspect native state before retrying.\n")
59
+ return 1
60
+ if len(result.stdout.encode("utf8")) > MAX_OUTPUT:
61
+ sys.stderr.write("Native response exceeded the operator output limit; inspect native state before retrying.\n")
62
+ return 3
63
+ sys.stdout.write(result.stdout)
64
+ return 0
65
+
66
+
67
+ if __name__ == "__main__":
68
+ sys.exit(main())