@clawscarf/cli 0.1.0-alpha.5 → 0.1.0-alpha.7
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 +8 -0
- package/package.json +2 -1
- package/pnpm-lock.yaml +37 -0
- package/recipes/README.md +1 -1
- package/recipes/team-server/recipe.json +3 -2
- package/release/README.md +19 -0
- package/release/components.json +1 -1
- package/release/telemetry.json +4 -0
- package/runtime/configuration.js +2 -1
- package/runtime/releases/{0.1.0-alpha.5.json → 0.1.0-alpha.7.json} +16 -16
- package/scripts/clawscarf.js +9 -1
- package/scripts/deployment/configuration.js +1 -0
- package/scripts/deployment/connection-settings.js +4 -1
- package/scripts/deployment/launch.js +3 -3
- package/scripts/deployment/network-policy.js +120 -0
- package/scripts/deployment/policy.js +5 -3
- package/scripts/deployment/prepare.js +1 -1
- package/scripts/deployment/public-web.js +176 -0
- package/scripts/installation/command.js +6 -3
- package/scripts/installation/configuration.js +1 -0
- package/scripts/installation/configure.js +1 -0
- package/scripts/installation/installer/collect.js +15 -0
- package/scripts/installation/installer/menu.js +2 -1
- package/scripts/installation/installer/run.js +19 -3
- package/scripts/installation/installer/settings.js +4 -1
- package/scripts/installation/installer/summary.js +1 -0
- package/scripts/installation/options.js +7 -1
- package/scripts/installation/recipes/definition.js +1 -0
- package/scripts/installation/reconfigure.js +10 -2
- package/scripts/installation/resolve.js +1 -0
- package/scripts/installation/setup.js +1 -0
- package/scripts/telemetry.js +282 -0
- package/scripts/deployment/connection-policy.js +0 -80
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// Public unicast ranges, excluding private and special-use destinations.
|
|
2
|
+
// Keep aligned with the browser egress boundary in deploy/execution/network/squid.conf.
|
|
3
|
+
export const publicWebAddresses = [
|
|
4
|
+
"1.0.0.0/8",
|
|
5
|
+
"2.0.0.0/7",
|
|
6
|
+
"4.0.0.0/6",
|
|
7
|
+
"8.0.0.0/7",
|
|
8
|
+
"11.0.0.0/8",
|
|
9
|
+
"12.0.0.0/6",
|
|
10
|
+
"16.0.0.0/4",
|
|
11
|
+
"32.0.0.0/3",
|
|
12
|
+
"64.0.0.0/3",
|
|
13
|
+
"96.0.0.0/6",
|
|
14
|
+
"100.0.0.0/10",
|
|
15
|
+
"100.128.0.0/9",
|
|
16
|
+
"101.0.0.0/8",
|
|
17
|
+
"102.0.0.0/7",
|
|
18
|
+
"104.0.0.0/5",
|
|
19
|
+
"112.0.0.0/5",
|
|
20
|
+
"120.0.0.0/6",
|
|
21
|
+
"124.0.0.0/7",
|
|
22
|
+
"126.0.0.0/8",
|
|
23
|
+
"128.0.0.0/3",
|
|
24
|
+
"160.0.0.0/5",
|
|
25
|
+
"168.0.0.0/8",
|
|
26
|
+
"169.0.0.0/9",
|
|
27
|
+
"169.128.0.0/10",
|
|
28
|
+
"169.192.0.0/11",
|
|
29
|
+
"169.224.0.0/12",
|
|
30
|
+
"169.240.0.0/13",
|
|
31
|
+
"169.248.0.0/14",
|
|
32
|
+
"169.252.0.0/15",
|
|
33
|
+
"169.255.0.0/16",
|
|
34
|
+
"170.0.0.0/7",
|
|
35
|
+
"172.0.0.0/12",
|
|
36
|
+
"172.32.0.0/11",
|
|
37
|
+
"172.64.0.0/10",
|
|
38
|
+
"172.128.0.0/9",
|
|
39
|
+
"173.0.0.0/8",
|
|
40
|
+
"174.0.0.0/7",
|
|
41
|
+
"176.0.0.0/4",
|
|
42
|
+
"192.0.1.0/24",
|
|
43
|
+
"192.0.3.0/24",
|
|
44
|
+
"192.0.4.0/22",
|
|
45
|
+
"192.0.8.0/21",
|
|
46
|
+
"192.0.16.0/20",
|
|
47
|
+
"192.0.32.0/19",
|
|
48
|
+
"192.0.64.0/18",
|
|
49
|
+
"192.0.128.0/17",
|
|
50
|
+
"192.1.0.0/16",
|
|
51
|
+
"192.2.0.0/15",
|
|
52
|
+
"192.4.0.0/14",
|
|
53
|
+
"192.8.0.0/13",
|
|
54
|
+
"192.16.0.0/12",
|
|
55
|
+
"192.32.0.0/11",
|
|
56
|
+
"192.64.0.0/12",
|
|
57
|
+
"192.80.0.0/13",
|
|
58
|
+
"192.88.0.0/18",
|
|
59
|
+
"192.88.64.0/19",
|
|
60
|
+
"192.88.96.0/23",
|
|
61
|
+
"192.88.98.0/24",
|
|
62
|
+
"192.88.100.0/22",
|
|
63
|
+
"192.88.104.0/21",
|
|
64
|
+
"192.88.112.0/20",
|
|
65
|
+
"192.88.128.0/17",
|
|
66
|
+
"192.89.0.0/16",
|
|
67
|
+
"192.90.0.0/15",
|
|
68
|
+
"192.92.0.0/14",
|
|
69
|
+
"192.96.0.0/11",
|
|
70
|
+
"192.128.0.0/11",
|
|
71
|
+
"192.160.0.0/13",
|
|
72
|
+
"192.169.0.0/16",
|
|
73
|
+
"192.170.0.0/15",
|
|
74
|
+
"192.172.0.0/14",
|
|
75
|
+
"192.176.0.0/12",
|
|
76
|
+
"192.192.0.0/10",
|
|
77
|
+
"193.0.0.0/8",
|
|
78
|
+
"194.0.0.0/7",
|
|
79
|
+
"196.0.0.0/7",
|
|
80
|
+
"198.0.0.0/12",
|
|
81
|
+
"198.16.0.0/15",
|
|
82
|
+
"198.20.0.0/14",
|
|
83
|
+
"198.24.0.0/13",
|
|
84
|
+
"198.32.0.0/12",
|
|
85
|
+
"198.48.0.0/15",
|
|
86
|
+
"198.50.0.0/16",
|
|
87
|
+
"198.51.0.0/18",
|
|
88
|
+
"198.51.64.0/19",
|
|
89
|
+
"198.51.96.0/22",
|
|
90
|
+
"198.51.101.0/24",
|
|
91
|
+
"198.51.102.0/23",
|
|
92
|
+
"198.51.104.0/21",
|
|
93
|
+
"198.51.112.0/20",
|
|
94
|
+
"198.51.128.0/17",
|
|
95
|
+
"198.52.0.0/14",
|
|
96
|
+
"198.56.0.0/13",
|
|
97
|
+
"198.64.0.0/10",
|
|
98
|
+
"198.128.0.0/9",
|
|
99
|
+
"199.0.0.0/8",
|
|
100
|
+
"200.0.0.0/7",
|
|
101
|
+
"202.0.0.0/8",
|
|
102
|
+
"203.0.0.0/18",
|
|
103
|
+
"203.0.64.0/19",
|
|
104
|
+
"203.0.96.0/20",
|
|
105
|
+
"203.0.112.0/24",
|
|
106
|
+
"203.0.114.0/23",
|
|
107
|
+
"203.0.116.0/22",
|
|
108
|
+
"203.0.120.0/21",
|
|
109
|
+
"203.0.128.0/17",
|
|
110
|
+
"203.1.0.0/16",
|
|
111
|
+
"203.2.0.0/15",
|
|
112
|
+
"203.4.0.0/14",
|
|
113
|
+
"203.8.0.0/13",
|
|
114
|
+
"203.16.0.0/12",
|
|
115
|
+
"203.32.0.0/11",
|
|
116
|
+
"203.64.0.0/10",
|
|
117
|
+
"203.128.0.0/9",
|
|
118
|
+
"204.0.0.0/6",
|
|
119
|
+
"208.0.0.0/4",
|
|
120
|
+
"2000::/16",
|
|
121
|
+
"2001:200::/23",
|
|
122
|
+
"2001:400::/22",
|
|
123
|
+
"2001:800::/22",
|
|
124
|
+
"2001:c00::/24",
|
|
125
|
+
"2001:d00::/25",
|
|
126
|
+
"2001:d80::/27",
|
|
127
|
+
"2001:da0::/28",
|
|
128
|
+
"2001:db0::/29",
|
|
129
|
+
"2001:db9::/32",
|
|
130
|
+
"2001:dba::/31",
|
|
131
|
+
"2001:dbc::/30",
|
|
132
|
+
"2001:dc0::/26",
|
|
133
|
+
"2001:e00::/23",
|
|
134
|
+
"2001:1000::/20",
|
|
135
|
+
"2001:2000::/19",
|
|
136
|
+
"2001:4000::/18",
|
|
137
|
+
"2001:8000::/17",
|
|
138
|
+
"2003::/16",
|
|
139
|
+
"2004::/14",
|
|
140
|
+
"2008::/13",
|
|
141
|
+
"2010::/12",
|
|
142
|
+
"2020::/11",
|
|
143
|
+
"2040::/10",
|
|
144
|
+
"2080::/9",
|
|
145
|
+
"2100::/8",
|
|
146
|
+
"2200::/7",
|
|
147
|
+
"2400::/6",
|
|
148
|
+
"2800::/5",
|
|
149
|
+
"3000::/5",
|
|
150
|
+
"3800::/6",
|
|
151
|
+
"3c00::/7",
|
|
152
|
+
"3e00::/8",
|
|
153
|
+
"3f00::/9",
|
|
154
|
+
"3f80::/10",
|
|
155
|
+
"3fc0::/11",
|
|
156
|
+
"3fe0::/12",
|
|
157
|
+
"3ff0::/13",
|
|
158
|
+
"3ff8::/14",
|
|
159
|
+
"3ffc::/15",
|
|
160
|
+
"3ffe::/16",
|
|
161
|
+
"3fff:1000::/20",
|
|
162
|
+
"3fff:2000::/19",
|
|
163
|
+
"3fff:4000::/18",
|
|
164
|
+
"3fff:8000::/17",
|
|
165
|
+
];
|
|
166
|
+
export function publicWebPolicy(enabled) {
|
|
167
|
+
return enabled
|
|
168
|
+
? {
|
|
169
|
+
name: "Public web",
|
|
170
|
+
endpoints: [
|
|
171
|
+
{ ports: [80, 443], allowed_ips: publicWebAddresses, tls: "skip" },
|
|
172
|
+
],
|
|
173
|
+
binaries: [{ path: "/**" }],
|
|
174
|
+
}
|
|
175
|
+
: null;
|
|
176
|
+
}
|
|
@@ -20,12 +20,13 @@ import { runConfiguration } from "./installer/run.js";
|
|
|
20
20
|
import { installationOptions } from "./options.js";
|
|
21
21
|
import { InstallationError } from "./errors.js";
|
|
22
22
|
import { administratorSetup } from "./administrator.js";
|
|
23
|
-
export function installationCommand() {
|
|
23
|
+
export function installationCommand(observation) {
|
|
24
24
|
const program = new Command("clawscarf")
|
|
25
25
|
.description("Install and operate a protected OpenClaw team server.")
|
|
26
26
|
.option("--json", "Print machine-readable results; diagnostics go to stderr");
|
|
27
27
|
program.addCommand(peopleCommand(program));
|
|
28
28
|
const output = (value, human) => {
|
|
29
|
+
observation?.result(value);
|
|
29
30
|
writeResult(program, value, human);
|
|
30
31
|
};
|
|
31
32
|
installationOptions(program.command("configure"))
|
|
@@ -43,9 +44,11 @@ export function installationCommand() {
|
|
|
43
44
|
const result = await runConfiguration({
|
|
44
45
|
...options,
|
|
45
46
|
...program.opts(),
|
|
46
|
-
});
|
|
47
|
+
}, (mode) => observation?.configurationMode(mode));
|
|
47
48
|
if (options.nonInteractive)
|
|
48
49
|
output(result);
|
|
50
|
+
else
|
|
51
|
+
observation?.result(result);
|
|
49
52
|
});
|
|
50
53
|
program
|
|
51
54
|
.command("recipes")
|
|
@@ -91,7 +94,7 @@ export function installationCommand() {
|
|
|
91
94
|
});
|
|
92
95
|
const directory = await resolveLocation(options);
|
|
93
96
|
const result = await progress("Deleting installation", (signal) => deleteInstallation(directory, signal), program.opts());
|
|
94
|
-
output(result, "Installation deleted from Docker.
|
|
97
|
+
output(result, "Installation deleted from Docker. The local folder still contains configuration and credentials. To install again, move or remove that folder, or configure a new --directory.");
|
|
95
98
|
return;
|
|
96
99
|
}
|
|
97
100
|
if (options.confirmDelete !== undefined || options.acceptDataLoss)
|
|
@@ -65,6 +65,7 @@ export const installationSchema = z
|
|
|
65
65
|
}),
|
|
66
66
|
]),
|
|
67
67
|
resources: z.strictObject({ runtime: resources }),
|
|
68
|
+
publicWeb: z.boolean().default(false),
|
|
68
69
|
browser: z.strictObject({ enabled: z.boolean() }),
|
|
69
70
|
models: z.discriminatedUnion("mode", [
|
|
70
71
|
externalLiteLlmSchema,
|
|
@@ -117,6 +117,7 @@ export async function configurationChanges(before, after) {
|
|
|
117
117
|
return {
|
|
118
118
|
models: !isDeepStrictEqual(await models(before), await models(after)),
|
|
119
119
|
connections: !isDeepStrictEqual(before.connections, after.connections),
|
|
120
|
+
publicWeb: before.publicWeb !== after.publicWeb,
|
|
120
121
|
packs: !isDeepStrictEqual(await packs(before), await packs(after)),
|
|
121
122
|
};
|
|
122
123
|
}
|
|
@@ -175,6 +175,21 @@ export async function collectInstallation(ui, options, retained) {
|
|
|
175
175
|
case "resources":
|
|
176
176
|
config.resources = await collectResources(ui, config.resources);
|
|
177
177
|
break;
|
|
178
|
+
case "public-web":
|
|
179
|
+
config.publicWeb =
|
|
180
|
+
(await ui.select("Public web access", [
|
|
181
|
+
{
|
|
182
|
+
value: "on",
|
|
183
|
+
label: "On",
|
|
184
|
+
hint: "Public HTTP(S); agents can send team data to public services",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
value: "off",
|
|
188
|
+
label: "Off",
|
|
189
|
+
hint: "Only configured services and explicit policies",
|
|
190
|
+
},
|
|
191
|
+
], config.publicWeb ? "on" : "off")) === "on";
|
|
192
|
+
break;
|
|
178
193
|
case "browser":
|
|
179
194
|
ui.note("Explicit browser node use works. Ordinary model-selected browsing is not qualified because of an upstream routing issue.", "Experimental browser");
|
|
180
195
|
config.browser.enabled =
|
|
@@ -25,6 +25,7 @@ export function installationMenu(config, directory, browserAvailable, packIssues
|
|
|
25
25
|
row("exposure", "Network", config.exposure.mode === "local"
|
|
26
26
|
? "This computer"
|
|
27
27
|
: config.exposure.applicationOrigin),
|
|
28
|
+
row("public-web", "Public web", config.publicWeb ? "On" : "Off", "HTTP(S) for agents and tools; private destinations blocked"),
|
|
28
29
|
row("models", "Models", modelSummary ?? "Choose a model"),
|
|
29
30
|
row("connections", "Connections", config.connections.mode === "disabled" ? "Off" : "On"),
|
|
30
31
|
row("packs", "Packs", packIssues.length
|
|
@@ -42,7 +43,7 @@ export function installationMenu(config, directory, browserAvailable, packIssues
|
|
|
42
43
|
];
|
|
43
44
|
return existing
|
|
44
45
|
? [
|
|
45
|
-
...choices.filter(({ value }) => ["review", "models", "connections", "packs"].includes(value)),
|
|
46
|
+
...choices.filter(({ value }) => ["review", "models", "connections", "packs", "public-web"].includes(value)),
|
|
46
47
|
{ value: "model-credentials", label: "Change LLM API keys" },
|
|
47
48
|
]
|
|
48
49
|
: choices;
|
|
@@ -161,7 +161,7 @@ function quote(value) {
|
|
|
161
161
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
162
162
|
}
|
|
163
163
|
/** The command selects the workflow; users never manipulate preparation files. */
|
|
164
|
-
export async function runConfiguration(options) {
|
|
164
|
+
export async function runConfiguration(options, observeMode) {
|
|
165
165
|
options = {
|
|
166
166
|
...options,
|
|
167
167
|
directory: options.directory ?? defaultInstallationDirectory,
|
|
@@ -193,6 +193,19 @@ export async function runConfiguration(options) {
|
|
|
193
193
|
throw error;
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
|
+
if (existing && state) {
|
|
197
|
+
try {
|
|
198
|
+
await access(join(state, "prepared.json"));
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
if (!(error instanceof Error &&
|
|
202
|
+
"code" in error &&
|
|
203
|
+
error.code === "ENOENT"))
|
|
204
|
+
throw error;
|
|
205
|
+
throw new InstallationError("invalid_configuration", `This folder retains settings but the installation is no longer prepared. If deletion failed, finish it with clawscarf stop --directory ${quote(options.directory ?? defaultInstallationDirectory)} --delete. After successful deletion, move or remove the installation folder before configuring it again, or choose a new --directory.`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
observeMode?.(existing ? "edit" : "new");
|
|
196
209
|
let result = existing && state
|
|
197
210
|
? await editInstallationSettings(state, ui, options)
|
|
198
211
|
: await installFromAnswers(options, ui, operations, (message, work) => progress(message, work, options));
|
|
@@ -218,14 +231,17 @@ export async function runConfiguration(options) {
|
|
|
218
231
|
: undefined;
|
|
219
232
|
if (result.state === "cancelled")
|
|
220
233
|
clack.cancel("Cancelled.");
|
|
221
|
-
else
|
|
234
|
+
else {
|
|
235
|
+
if ("ready" in result && result.ready)
|
|
236
|
+
ui.note(`clawscarf status --directory ${quote(options.directory ?? defaultInstallationDirectory)}\nclawscarf stop --directory ${quote(options.directory ?? defaultInstallationDirectory)}`, "Manage this installation");
|
|
222
237
|
clack.outro("ready" in result
|
|
223
238
|
? result.ready
|
|
224
|
-
? `ClawScarf is ready.${applicationUrl ? ` Visit ${terminalLink(applicationUrl)}` : ""}
|
|
239
|
+
? `ClawScarf is ready.${applicationUrl ? ` Visit ${terminalLink(applicationUrl)}` : ""}`
|
|
225
240
|
: "ClawScarf needs attention. Check status for details."
|
|
226
241
|
: result.state === "unchanged"
|
|
227
242
|
? "No changes. Server left as it was."
|
|
228
243
|
: "Configuration saved. Server stopped.");
|
|
244
|
+
}
|
|
229
245
|
if ("ready" in result && result.ready && applicationUrl)
|
|
230
246
|
await ui.openBrowser(applicationUrl);
|
|
231
247
|
}
|
|
@@ -21,7 +21,7 @@ import { withInstallationLock, writePrivate } from "../../deployment/state.js";
|
|
|
21
21
|
export async function editInstallationSettings(directory, ui, options = {}, prerequisites = { host: checkHost, check: checkInstallationPrerequisites }) {
|
|
22
22
|
directory = resolve(directory);
|
|
23
23
|
if (options.recipe || options.cloudUrl)
|
|
24
|
-
throw new InstallationError("change_unsupported", "Existing installations keep their recipe, software release and login service. Change models, keys, Connections or packs instead.");
|
|
24
|
+
throw new InstallationError("change_unsupported", "Existing installations keep their recipe, software release and login service. Change models, keys, Connections, public web or packs instead.");
|
|
25
25
|
if (options.nonInteractive && !options.yes)
|
|
26
26
|
throw new InstallationError("invalid_configuration", "Changing an existing installation noninteractively requires --yes. The server may restart and deselected packs may be removed.");
|
|
27
27
|
const task = (message, work) => progress(message, work, options);
|
|
@@ -114,6 +114,9 @@ export async function editInstallationSettings(directory, ui, options = {}, prer
|
|
|
114
114
|
`Connections: ${plan.changes.connections.to === "disabled" ? "Off" : "On"}`,
|
|
115
115
|
]
|
|
116
116
|
: []),
|
|
117
|
+
...(plan.scopes.publicWeb
|
|
118
|
+
? [`Public web: ${plan.changes.publicWeb.to ? "On" : "Off"}`]
|
|
119
|
+
: []),
|
|
117
120
|
...(plan.scopes.packs
|
|
118
121
|
? [`Packs: ${plan.changes.packs.selected.join(", ") || "None"}`]
|
|
119
122
|
: []),
|
|
@@ -39,6 +39,7 @@ export async function installationSummary(config, inputs) {
|
|
|
39
39
|
`Administrator: ${config.access.administratorName} · ${config.access.mode === "hosted" ? "ClawScarf login" : "Custom OIDC"}`,
|
|
40
40
|
`Models: ${model}`,
|
|
41
41
|
`Connections: ${config.connections.mode === "disabled" ? "Off" : "On"}`,
|
|
42
|
+
`Public web: ${config.publicWeb ? "On" : "Off"} (private destinations blocked)`,
|
|
42
43
|
`Browser: ${config.browser.enabled ? "Experimental" : "Off"} · Packs: ${config.packs.flatMap((pack) => pack.members).join(", ") || "None"}`,
|
|
43
44
|
].join("\n");
|
|
44
45
|
}
|
|
@@ -36,6 +36,7 @@ export const selectionSchema = z.object({
|
|
|
36
36
|
connections: z.boolean().optional(),
|
|
37
37
|
connectionsCloudUrl: cloudUrlSchema.optional(),
|
|
38
38
|
browser: z.boolean().optional(),
|
|
39
|
+
publicWeb: z.boolean().optional(),
|
|
39
40
|
model: text.optional(),
|
|
40
41
|
provider: text.optional(),
|
|
41
42
|
reasoning: z.enum(["low", "medium", "high"]).optional(),
|
|
@@ -79,6 +80,8 @@ export function installationOptions(command) {
|
|
|
79
80
|
.option("--connections-cloud-url <url>", "Use a separate Connections service")
|
|
80
81
|
.option("--browser", "Enable experimental browser capability")
|
|
81
82
|
.option("--no-browser", "Disable browser capability")
|
|
83
|
+
.option("--public-web", "Allow public HTTP(S) from agents and tools")
|
|
84
|
+
.option("--no-public-web", "Restrict runtime egress to configured services")
|
|
82
85
|
.option("--model <id>", "Default model from the model catalog")
|
|
83
86
|
.option("--provider <id>", "Model provider, for example openai or openrouter")
|
|
84
87
|
.addOption(new Option("--reasoning <level>", "Model reasoning level").choices([
|
|
@@ -116,6 +119,7 @@ export async function selectedDraft(context, recipe, selections, inputs, retaine
|
|
|
116
119
|
"modelGatewayKeyFile",
|
|
117
120
|
"modelGatewayCaFile",
|
|
118
121
|
"connections",
|
|
122
|
+
"publicWeb",
|
|
119
123
|
"connectionsCloudUrl",
|
|
120
124
|
"pack",
|
|
121
125
|
"packs",
|
|
@@ -123,7 +127,7 @@ export async function selectedDraft(context, recipe, selections, inputs, retaine
|
|
|
123
127
|
"packPython",
|
|
124
128
|
]);
|
|
125
129
|
if (Object.entries(o).some(([key, value]) => value !== undefined && !mutable.has(key)))
|
|
126
|
-
invalid("Existing installations support changes to models, keys, Connections and packs only.");
|
|
130
|
+
invalid("Existing installations support changes to models, keys, Connections, public web and packs only.");
|
|
127
131
|
}
|
|
128
132
|
const config = structuredClone(retained ?? recipeConfiguration(context, recipe));
|
|
129
133
|
if (o.name !== undefined)
|
|
@@ -136,6 +140,8 @@ export async function selectedDraft(context, recipe, selections, inputs, retaine
|
|
|
136
140
|
config.resources.runtime.memory = o.memory;
|
|
137
141
|
if (o.browser !== undefined)
|
|
138
142
|
config.browser.enabled = o.browser;
|
|
143
|
+
if (o.publicWeb !== undefined)
|
|
144
|
+
config.publicWeb = o.publicWeb;
|
|
139
145
|
if (o.connections !== undefined)
|
|
140
146
|
config.connections.mode = o.connections ? "hosted" : "disabled";
|
|
141
147
|
if (o.connectionsCloudUrl)
|
|
@@ -11,6 +11,7 @@ export const recipeSchema = z.strictObject({
|
|
|
11
11
|
defaults: z.strictObject({
|
|
12
12
|
resources: installationSchema.shape.resources,
|
|
13
13
|
browser: installationSchema.shape.browser,
|
|
14
|
+
publicWeb: z.boolean().default(false),
|
|
14
15
|
connections: z.strictObject({ enabled: z.boolean() }).optional(),
|
|
15
16
|
}),
|
|
16
17
|
models: z.strictObject({
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { publicWebPolicy } from "../deployment/public-web.js";
|
|
2
|
+
import { stageNetworkPolicyChange } from "../deployment/network-policy.js";
|
|
1
3
|
import { applyConnectionSettings } from "../deployment/connection-settings.js";
|
|
2
4
|
import { readFile } from "node:fs/promises";
|
|
3
5
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -41,8 +43,8 @@ export async function planSettingsChange(configFile, reapply) {
|
|
|
41
43
|
const previousPacks = selectionSchema.parse(await readJson(join(directory, "packs.json")));
|
|
42
44
|
if (!desired.packSelection.python && previousPacks.python)
|
|
43
45
|
desired.packSelection.python = previousPacks.python;
|
|
44
|
-
const { models: _oldModels, modelGateway: oldGateway, connections: oldConnections, ...oldFixed } = old;
|
|
45
|
-
const { models: _nextModels, modelGateway: nextGateway, connections: nextConnections, ...nextFixed } = next;
|
|
46
|
+
const { models: _oldModels, modelGateway: oldGateway, connections: oldConnections, publicWeb: oldPublicWeb, ...oldFixed } = old;
|
|
47
|
+
const { models: _nextModels, modelGateway: nextGateway, connections: nextConnections, publicWeb: nextPublicWeb, ...nextFixed } = next;
|
|
46
48
|
if (!isDeepStrictEqual(oldFixed, nextFixed) ||
|
|
47
49
|
!isDeepStrictEqual(await readJson(join(directory, "release.json")), desired.release))
|
|
48
50
|
unsupported("Settings changes cannot replace the release, identity, addresses, access, resources or execution protection.");
|
|
@@ -92,6 +94,7 @@ export async function planSettingsChange(configFile, reapply) {
|
|
|
92
94
|
scopes,
|
|
93
95
|
reapply,
|
|
94
96
|
changes: {
|
|
97
|
+
publicWeb: { from: oldPublicWeb, to: nextPublicWeb },
|
|
95
98
|
models: modelConfiguration
|
|
96
99
|
? {
|
|
97
100
|
enabled: modelConfiguration.models
|
|
@@ -235,6 +238,11 @@ export async function reconfigureInstallation(configFile, expectedFingerprint, r
|
|
|
235
238
|
if (scopes.connections &&
|
|
236
239
|
(state.input.connections || desired.input.connections))
|
|
237
240
|
await applyConnectionSettings(directory, { ...state, input: desired.input }, checked.previousEndpoint, desired.connectorCredentialFile);
|
|
241
|
+
stage = "public web policy";
|
|
242
|
+
if (scopes.publicWeb)
|
|
243
|
+
await stageNetworkPolicyChange(directory, state, {
|
|
244
|
+
public_web: publicWebPolicy(desired.input.publicWeb),
|
|
245
|
+
});
|
|
238
246
|
stage = "accepted settings";
|
|
239
247
|
if (scopes.models)
|
|
240
248
|
await writePrivate(join(directory, "private/model-bootstrap.json"), JSON.stringify(models));
|
|
@@ -73,6 +73,7 @@ export async function resolveInstallation(configFile, internalPorts) {
|
|
|
73
73
|
name: config.name,
|
|
74
74
|
administratorName: access.administratorName,
|
|
75
75
|
runtimeImage: release.images.gateway,
|
|
76
|
+
publicWeb: config.publicWeb,
|
|
76
77
|
companionImage: release.images.companion,
|
|
77
78
|
...(config.browser.enabled ? { relayImage: release.images.relay } : {}),
|
|
78
79
|
openshellCli: cli,
|
|
@@ -57,6 +57,7 @@ export function recipeConfiguration(context, recipeId) {
|
|
|
57
57
|
},
|
|
58
58
|
resources: structuredClone(recipe.defaults.resources),
|
|
59
59
|
browser: structuredClone(recipe.defaults.browser),
|
|
60
|
+
publicWeb: recipe.defaults.publicWeb,
|
|
60
61
|
connections: {
|
|
61
62
|
mode: recipe.defaults.connections?.enabled ? "hosted" : "disabled",
|
|
62
63
|
cloudUrl: context.cloudUrl,
|