@sealant/mend 0.36.0-next.663 → 0.36.0-next.665
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/dist/{chunk-VOP6USOZ.js → chunk-K5WNRH4V.js} +13 -0
- package/dist/{dashboard-CEJN6IOO.js → dashboard-DQB5RNXJ.js} +1 -1
- package/dist/main.js +173 -25
- package/man/mend-accounts.1 +1 -1
- package/man/mend-adopt.1 +1 -1
- package/man/mend-attach.1 +1 -1
- package/man/mend-codex.1 +1 -1
- package/man/mend-completions.1 +1 -1
- package/man/mend-connect-pi.1 +1 -1
- package/man/mend-connect.1 +1 -1
- package/man/mend-continue.1 +1 -1
- package/man/mend-doctor.1 +1 -1
- package/man/mend-dotfiles-repo.1 +1 -1
- package/man/mend-dotfiles-sync.1 +1 -1
- package/man/mend-dotfiles.1 +1 -1
- package/man/mend-env-cluster.1 +1 -1
- package/man/mend-env-load.1 +1 -1
- package/man/mend-env-show.1 +1 -1
- package/man/mend-folder-create.1 +1 -1
- package/man/mend-folder-list.1 +1 -1
- package/man/mend-folder-push.1 +1 -1
- package/man/mend-folder-rm.1 +1 -1
- package/man/mend-git-author.1 +1 -1
- package/man/mend-help.1 +1 -1
- package/man/mend-invite.1 +1 -1
- package/man/mend-keys-init.1 +1 -1
- package/man/mend-keys-mode.1 +1 -1
- package/man/mend-keys-share.1 +1 -1
- package/man/mend-keys-show.1 +1 -1
- package/man/mend-land.1 +1 -1
- package/man/mend-login.1 +1 -1
- package/man/mend-logout.1 +1 -1
- package/man/mend-logs.1 +1 -1
- package/man/mend-man.1 +1 -1
- package/man/mend-members.1 +1 -1
- package/man/mend-memory-import.1 +1 -1
- package/man/mend-memory-rm.1 +1 -1
- package/man/mend-memory-show.1 +1 -1
- package/man/mend-memory.1 +1 -1
- package/man/mend-models.1 +1 -1
- package/man/mend-operator-exposure.1 +2 -2
- package/man/mend-operator-gate.1 +1 -1
- package/man/mend-operator-grant-owner.1 +1 -1
- package/man/mend-operator-org-create.1 +1 -1
- package/man/mend-operator-org-invite-owner.1 +1 -1
- package/man/mend-operator-org-list.1 +1 -1
- package/man/mend-operator-org-rename.1 +1 -1
- package/man/mend-operator-reset-link.1 +1 -1
- package/man/mend-pair.1 +1 -1
- package/man/mend-projects.1 +1 -1
- package/man/mend-pull.1 +1 -1
- package/man/mend-refresh.1 +1 -1
- package/man/mend-rejoin.1 +1 -1
- package/man/mend-resume.1 +1 -1
- package/man/mend-run.1 +1 -1
- package/man/mend-secrets-add.1 +1 -1
- package/man/mend-secrets-rm.1 +1 -1
- package/man/mend-secrets.1 +1 -1
- package/man/mend-server-logs.1 +1 -1
- package/man/mend-server-restart.1 +1 -1
- package/man/mend-server-setup.1 +12 -2
- package/man/mend-server-start.1 +1 -1
- package/man/mend-server-status.1 +1 -1
- package/man/mend-server-stop.1 +1 -1
- package/man/mend-server-upgrade.1 +1 -1
- package/man/mend-server.1 +1 -1
- package/man/mend-service-add.1 +1 -1
- package/man/mend-service-connect.1 +1 -1
- package/man/mend-service-init.1 +1 -1
- package/man/mend-service-list.1 +1 -1
- package/man/mend-service-logs.1 +1 -1
- package/man/mend-service-restart.1 +1 -1
- package/man/mend-service-run.1 +1 -1
- package/man/mend-service-stop.1 +1 -1
- package/man/mend-session-share.1 +1 -1
- package/man/mend-sessions.1 +1 -1
- package/man/mend-shell.1 +1 -1
- package/man/mend-skills-push.1 +1 -1
- package/man/mend-skills.1 +1 -1
- package/man/mend-snake.1 +1 -1
- package/man/mend-ssh-setup.1 +1 -1
- package/man/mend-ssh.1 +1 -1
- package/man/mend-stop.1 +1 -1
- package/man/mend-ui.1 +1 -1
- package/man/mend-uninstall.1 +1 -1
- package/man/mend-version.1 +1 -1
- package/man/mend-wait.1 +1 -1
- package/man/mend-workspace-replace.1 +1 -1
- package/man/mend-worktrees-rm.1 +1 -1
- package/man/mend-worktrees.1 +1 -1
- package/man/mend.1 +1 -1
- package/package.json +2 -2
|
@@ -28227,6 +28227,19 @@ var Session = class extends Schema_exports.Class("Session")({
|
|
|
28227
28227
|
Schema_exports.withDecodingDefaultKey(Effect_exports.succeed(null)),
|
|
28228
28228
|
Schema_exports.withConstructorDefault(Effect_exports.succeed(null))
|
|
28229
28229
|
),
|
|
28230
|
+
/**
|
|
28231
|
+
* Who launched the session's executor: the owner of the session whose own launch made it
|
|
28232
|
+
* (`SessionsRepo.executorSessionOf`), which a joined session is not. Remote-SSH into the workspace
|
|
28233
|
+
* admits that person only (docs/adr/0016), in either harness layout, so an editor asks before it
|
|
28234
|
+
* opens. Filled on every API read that hands sessions out (the session list, retained additions
|
|
28235
|
+
* included, the session view and the project view): the view reads in their own statement, the
|
|
28236
|
+
* plain reads from one batched read (`SessionsRepo.launchersOf`). Null with no executor, on the
|
|
28237
|
+
* repository's other reads, and from older servers.
|
|
28238
|
+
*/
|
|
28239
|
+
workspaceLauncherUserId: Schema_exports.NullOr(Schema_exports.String).pipe(
|
|
28240
|
+
Schema_exports.withDecodingDefaultKey(Effect_exports.succeed(null)),
|
|
28241
|
+
Schema_exports.withConstructorDefault(Effect_exports.succeed(null))
|
|
28242
|
+
),
|
|
28230
28243
|
/**
|
|
28231
28244
|
* Whether the harness left a conversation behind — a transcript Mend captured at settle or
|
|
28232
28245
|
* found in the live harness home. False is a dead end: nothing to resume, nothing to hand
|
package/dist/main.js
CHANGED
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
workspaceRetirementLine,
|
|
69
69
|
worktreeJoinLine,
|
|
70
70
|
worktreeListsPeople
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-K5WNRH4V.js";
|
|
72
72
|
|
|
73
73
|
// src/main.ts
|
|
74
74
|
import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
|
|
@@ -861,6 +861,9 @@ var isTenancy = (value) => TENANCIES.some((tenancy) => tenancy === value);
|
|
|
861
861
|
var EDGE_IMAGE = "caddy:2.10-alpine";
|
|
862
862
|
var EDGE_COMPOSE_FILE = "compose.edge.yaml";
|
|
863
863
|
var POSTURE_COMPOSE_FILE = "compose.posture.yaml";
|
|
864
|
+
var DECLARABLE_ITEMS = ["core-private", "edge-tls", "workspace-ssh"];
|
|
865
|
+
var isDeclarableItem = (value) => DECLARABLE_ITEMS.some((item) => item === value);
|
|
866
|
+
var publishedAddress = (address, port) => `${net2.isIP(address) === 6 ? `[${address}]` : address}:${port}`;
|
|
864
867
|
var parseEdgeHost = (value) => {
|
|
865
868
|
const host = value.trim().toLowerCase().replace(/\.$/, "");
|
|
866
869
|
const labels = host.split(".");
|
|
@@ -876,7 +879,11 @@ var postureEnvironment = (posture) => {
|
|
|
876
879
|
...posture.exposure === void 0 ? [] : [["MEND_EXPOSURE", posture.exposure]],
|
|
877
880
|
...posture.tenancy === void 0 ? [] : [["MEND_TENANCY", posture.tenancy]],
|
|
878
881
|
...gate ? [["MEND_SOURCE_POLICY", "tenant"], ["MEND_CAPTURE_REQUIRE_SIZES", "true"]] : [],
|
|
879
|
-
...posture.exposure === "public" ? [["MEND_URL_BEARERS", "refuse"]] : []
|
|
882
|
+
...posture.exposure === "public" ? [["MEND_URL_BEARERS", "refuse"]] : [],
|
|
883
|
+
// The gate's workspace-ssh item reads where SSH is published apart from the web port; the
|
|
884
|
+
// container cannot see what its host publishes.
|
|
885
|
+
...posture.sshBind === void 0 || posture.sshPort === void 0 ? [] : [["MEND_SSH_PUBLISHED", publishedAddress(posture.sshBind, posture.sshPort)]],
|
|
886
|
+
...posture.declared === void 0 || posture.declared.length === 0 ? [] : [["MEND_EXPOSURE_DECLARED", posture.declared.join(",")]]
|
|
880
887
|
];
|
|
881
888
|
};
|
|
882
889
|
var postureEnvLines = (posture) => [
|
|
@@ -927,7 +934,11 @@ var healthPosture = (body) => {
|
|
|
927
934
|
var declaredPostureLines = (posture) => [
|
|
928
935
|
...posture.edgeHost === void 0 ? [] : [`edge \xB7 ${posture.edgeHost} \xB7 ${EDGE_IMAGE} on 80 and 443 \xB7 Mend's own port on loopback`],
|
|
929
936
|
`exposure \xB7 declared ${posture.exposure ?? "private"}${posture.exposure === void 0 ? " \xB7 the default, not set on this install" : ""}`,
|
|
930
|
-
`tenancy \xB7 declared ${posture.tenancy ?? "single"}${posture.tenancy === void 0 ? " \xB7 the default, not set on this install" : ""}
|
|
937
|
+
`tenancy \xB7 declared ${posture.tenancy ?? "single"}${posture.tenancy === void 0 ? " \xB7 the default, not set on this install" : ""}`,
|
|
938
|
+
...posture.sshBind === void 0 || posture.sshPort === void 0 ? [] : [
|
|
939
|
+
`workspace ssh \xB7 published on ${publishedAddress(posture.sshBind, posture.sshPort)} apart from the web port`
|
|
940
|
+
],
|
|
941
|
+
...posture.declared === void 0 || posture.declared.length === 0 ? [] : [`stated verified from outside \xB7 ${posture.declared.join(", ")}`]
|
|
931
942
|
];
|
|
932
943
|
var observedEdgeLine = (host, observed) => [
|
|
933
944
|
`edge \xB7 ${host}`,
|
|
@@ -2710,7 +2721,7 @@ var COMMANDS = [
|
|
|
2710
2721
|
summary: "how this instance is exposed, as declared and as observed",
|
|
2711
2722
|
synopsis: [],
|
|
2712
2723
|
description: [
|
|
2713
|
-
"Operator only. States MEND_EXPOSURE as declared (loopback, private or public) and one line per item of the public exposure gate, with how it was established: observed (this server read it), carried (this build contains it, and the server cannot see it in effect), declared (you stated it and the server cannot check it), or open. MEND_EXPOSURE=public refuses to start while an item the server can observe
|
|
2724
|
+
"Operator only. States MEND_EXPOSURE as declared (loopback, private or public) and one line per item of the public exposure gate, with how it was established: observed (this server read it), carried (this build contains it, and the server cannot see it in effect), declared (you stated it and the server cannot check it), or open. MEND_EXPOSURE=public refuses to start while an item that blocks a start is open: every item the server can observe, and workspace-ssh until you declare it. Items it cannot observe say what would verify them; once you have verified core-private, edge-tls or workspace-ssh (SSH published apart from the web port) from outside, name it in MEND_EXPOSURE_DECLARED, or with mend server setup --declare. The report is what was observed; it is not a statement that the instance is fit to expose."
|
|
2714
2725
|
],
|
|
2715
2726
|
see: ["operator gate", "doctor"]
|
|
2716
2727
|
},
|
|
@@ -2796,7 +2807,7 @@ var COMMANDS = [
|
|
|
2796
2807
|
section: "this machine",
|
|
2797
2808
|
summary: "install or repair the local Mend server",
|
|
2798
2809
|
synopsis: [
|
|
2799
|
-
"[--context <name>] [--version <version|latest>] [--bind <ip>] [--url <origin>] [--origin <origin>...] [--port <n>] [--ssh-port <n>] [--edge <host> | --no-edge] [--exposure <loopback|private|public>] [--tenancy <single|multi>] [--docker-socket <path>] [--assets-dir <dir>] [--offline]"
|
|
2810
|
+
"[--context <name>] [--version <version|latest>] [--bind <ip>] [--ssh-bind <ip>] [--url <origin>] [--origin <origin>...] [--port <n>] [--ssh-port <n>] [--edge <host> | --no-edge] [--exposure <loopback|private|public>] [--tenancy <single|multi>] [--declare <item>...] [--docker-socket <path>] [--assets-dir <dir>] [--offline]"
|
|
2800
2811
|
],
|
|
2801
2812
|
description: [
|
|
2802
2813
|
"Checks a local Unix-socket Docker context and the Compose plugin, downloads the compose and Postgres initialization assets for one Mend release, preserves existing data and secrets, and starts the server. Re-running repairs the same pinned version. A changed --version is refused; use mend server upgrade. Updating this CLI never updates an existing server pin.",
|
|
@@ -2816,6 +2827,10 @@ var COMMANDS = [
|
|
|
2816
2827
|
text: "exact Mend server version, or latest; omitted keeps the current pin"
|
|
2817
2828
|
},
|
|
2818
2829
|
{ flag: "--bind <ip>", text: "published listen address. Default: 127.0.0.1" },
|
|
2830
|
+
{
|
|
2831
|
+
flag: "--ssh-bind <ip>",
|
|
2832
|
+
text: "where workspace SSH is published, when not on --bind; with --edge, Remote-SSH from another machine needs it. Kept across reruns; the --bind address takes it away"
|
|
2833
|
+
},
|
|
2819
2834
|
{ flag: "--url <origin>", text: "advertised browser URL, required with a non-loopback bind" },
|
|
2820
2835
|
{
|
|
2821
2836
|
flag: "--origin <origin>",
|
|
@@ -2839,6 +2854,10 @@ var COMMANDS = [
|
|
|
2839
2854
|
flag: "--tenancy <v>",
|
|
2840
2855
|
text: "declare single or multi; kept across reruns and upgrades. multi also sets the multi mode gate's variables"
|
|
2841
2856
|
},
|
|
2857
|
+
{
|
|
2858
|
+
flag: "--declare <item>",
|
|
2859
|
+
text: "state a gate item you verified from outside: core-private, edge-tls or workspace-ssh; repeat for more, none clears. Kept across reruns. public with --ssh-bind beyond loopback needs workspace-ssh"
|
|
2860
|
+
},
|
|
2842
2861
|
{
|
|
2843
2862
|
flag: "--assets-dir <dir>",
|
|
2844
2863
|
text: "copy compose.v2.yaml and postgres-init.sh from a release directory; fresh setup requires --version"
|
|
@@ -2859,6 +2878,10 @@ var COMMANDS = [
|
|
|
2859
2878
|
command: "mend server setup --version 0.25.0 --assets-dir ./release-assets --offline",
|
|
2860
2879
|
text: "install from local release assets and preloaded images"
|
|
2861
2880
|
},
|
|
2881
|
+
{
|
|
2882
|
+
command: "mend server setup --edge mend.example.com --ssh-bind 0.0.0.0 --exposure public --declare workspace-ssh",
|
|
2883
|
+
text: "the TLS edge for the browser and the API, and workspace SSH for Remote-SSH from other machines"
|
|
2884
|
+
},
|
|
2862
2885
|
{
|
|
2863
2886
|
command: "mend server setup --edge mend.example.com",
|
|
2864
2887
|
text: "a TLS edge for mend.example.com; its DNS points at this machine and 80 and 443 reach it"
|
|
@@ -4256,7 +4279,7 @@ var gitAuthorLine = (author) => `${author.name} <${author.email}> \xB7 ${author.
|
|
|
4256
4279
|
|
|
4257
4280
|
// src/http-client.ts
|
|
4258
4281
|
import { Agent, setGlobalDispatcher } from "undici";
|
|
4259
|
-
var cliDispatcher = (
|
|
4282
|
+
var cliDispatcher = (connect3) => new Agent({ allowH2: false, ...connect3 === void 0 ? {} : { connect: connect3 } });
|
|
4260
4283
|
var useHttp1 = () => {
|
|
4261
4284
|
setGlobalDispatcher(cliDispatcher());
|
|
4262
4285
|
};
|
|
@@ -5241,13 +5264,13 @@ var renderExposure = (report) => {
|
|
|
5241
5264
|
const mark = (item) => marks[item.established];
|
|
5242
5265
|
const open = report.items.filter((item) => item.established === "open");
|
|
5243
5266
|
const blocking = open.filter((item) => item.blocksStart).length;
|
|
5244
|
-
const
|
|
5267
|
+
const notBlocking = open.length - blocking;
|
|
5245
5268
|
return [
|
|
5246
5269
|
`exposure \xB7 declared ${report.declared} \xB7 ${EXPOSURE_WORDS[report.declared]}`,
|
|
5247
5270
|
...report.items.map(
|
|
5248
5271
|
(item) => `${mark(item)} ${item.id.padEnd(width)} ${item.established.padEnd(8)} ${item.detail}${item.fix === null ? "" : dim4(` \xB7 ${item.fix}`)}`
|
|
5249
5272
|
),
|
|
5250
|
-
open.length === 0 ? "nothing open \xB7 every item was observed here, is carried by this build, or was declared by the operator" : `${open.length} of ${report.items.length} items open \xB7 ${blocking}
|
|
5273
|
+
open.length === 0 ? "nothing open \xB7 every item was observed here, is carried by this build, or was declared by the operator" : `${open.length} of ${report.items.length} items open \xB7 ${blocking} refuse a public start${blocking === 0 ? "" : "; MEND_EXPOSURE=public refuses to start"} \xB7 ${notBlocking} do not`
|
|
5251
5274
|
];
|
|
5252
5275
|
};
|
|
5253
5276
|
var renderOrganizations = (rows) => {
|
|
@@ -6817,6 +6840,7 @@ var SETUP_FLAGS = /* @__PURE__ */ new Set([
|
|
|
6817
6840
|
"--context",
|
|
6818
6841
|
"--version",
|
|
6819
6842
|
"--bind",
|
|
6843
|
+
"--ssh-bind",
|
|
6820
6844
|
"--url",
|
|
6821
6845
|
"--origin",
|
|
6822
6846
|
"--port",
|
|
@@ -6827,7 +6851,8 @@ var SETUP_FLAGS = /* @__PURE__ */ new Set([
|
|
|
6827
6851
|
"--edge",
|
|
6828
6852
|
"--no-edge",
|
|
6829
6853
|
"--exposure",
|
|
6830
|
-
"--tenancy"
|
|
6854
|
+
"--tenancy",
|
|
6855
|
+
"--declare"
|
|
6831
6856
|
]);
|
|
6832
6857
|
var parseExposure = (value) => {
|
|
6833
6858
|
if (!isExposure(value))
|
|
@@ -6839,6 +6864,21 @@ var parseTenancy = (value) => {
|
|
|
6839
6864
|
throw setupError(`--tenancy must be one of ${TENANCIES.join(", ")}, not "${value}".`);
|
|
6840
6865
|
return value;
|
|
6841
6866
|
};
|
|
6867
|
+
var parseDeclared = (values) => {
|
|
6868
|
+
if (values.length === 1 && values[0] === "none") return [];
|
|
6869
|
+
return [
|
|
6870
|
+
...new Set(
|
|
6871
|
+
values.map((value) => {
|
|
6872
|
+
if (!isDeclarableItem(value)) {
|
|
6873
|
+
throw setupError(
|
|
6874
|
+
`--declare takes ${DECLARABLE_ITEMS.join(", ")} or none, not "${value}". Every other gate item is observed by the server, never stated.`
|
|
6875
|
+
);
|
|
6876
|
+
}
|
|
6877
|
+
return value;
|
|
6878
|
+
})
|
|
6879
|
+
)
|
|
6880
|
+
];
|
|
6881
|
+
};
|
|
6842
6882
|
var parseEdge = (value) => {
|
|
6843
6883
|
const host = parseEdgeHost(value);
|
|
6844
6884
|
if (host === null) {
|
|
@@ -6863,7 +6903,7 @@ var parseSetupOptions = (args) => {
|
|
|
6863
6903
|
const [value, valueIndex] = nextFlagValue(args, index, flag);
|
|
6864
6904
|
index = valueIndex;
|
|
6865
6905
|
const previous = values.get(flag) ?? [];
|
|
6866
|
-
if (flag !== "--origin" && previous.length > 0) {
|
|
6906
|
+
if (flag !== "--origin" && flag !== "--declare" && previous.length > 0) {
|
|
6867
6907
|
throw setupError(`${flag} may be supplied only once.`);
|
|
6868
6908
|
}
|
|
6869
6909
|
previous.push(value);
|
|
@@ -6877,12 +6917,14 @@ var parseSetupOptions = (args) => {
|
|
|
6877
6917
|
const edge = flagValue3("--edge");
|
|
6878
6918
|
const exposure = flagValue3("--exposure");
|
|
6879
6919
|
const tenancy = flagValue3("--tenancy");
|
|
6920
|
+
const declared = values.get("--declare");
|
|
6880
6921
|
if (edge !== void 0 && values.has("--no-edge"))
|
|
6881
6922
|
throw setupError("--edge and --no-edge contradict each other.");
|
|
6882
6923
|
return {
|
|
6883
6924
|
context: flagValue3("--context"),
|
|
6884
6925
|
version: version === void 0 ? void 0 : parseVersion(version),
|
|
6885
6926
|
bind: flagValue3("--bind"),
|
|
6927
|
+
sshBind: flagValue3("--ssh-bind"),
|
|
6886
6928
|
url: flagValue3("--url"),
|
|
6887
6929
|
origins: origins === void 0 ? void 0 : origins,
|
|
6888
6930
|
appPort: appPort === void 0 ? void 0 : parsePort(appPort, "--port"),
|
|
@@ -6893,7 +6935,8 @@ var parseSetupOptions = (args) => {
|
|
|
6893
6935
|
edge: edge === void 0 ? void 0 : parseEdge(edge),
|
|
6894
6936
|
noEdge: values.has("--no-edge"),
|
|
6895
6937
|
exposure: exposure === void 0 ? void 0 : parseExposure(exposure),
|
|
6896
|
-
tenancy: tenancy === void 0 ? void 0 : parseTenancy(tenancy)
|
|
6938
|
+
tenancy: tenancy === void 0 ? void 0 : parseTenancy(tenancy),
|
|
6939
|
+
declared: declared === void 0 ? void 0 : parseDeclared(declared)
|
|
6897
6940
|
};
|
|
6898
6941
|
};
|
|
6899
6942
|
var parseUrl = (input, label) => {
|
|
@@ -6927,9 +6970,13 @@ var resolveAppUrl = (existing, options, appPort, edgeHost) => {
|
|
|
6927
6970
|
}
|
|
6928
6971
|
return origin;
|
|
6929
6972
|
}
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6973
|
+
if (existing?.edgeHost !== void 0) {
|
|
6974
|
+
return parseHttpOrigin(options.url ?? `http://localhost:${appPort}`, "--url");
|
|
6975
|
+
}
|
|
6976
|
+
const saved = existing === null ? null : new URL(existing.appUrl);
|
|
6977
|
+
const savedPointsAtOldPort = saved !== null && existing !== null && options.appPort !== void 0 && saved.protocol === "http:" && saved.port !== "" && Number(saved.port) === existing.appPort;
|
|
6978
|
+
if (saved !== null && savedPointsAtOldPort) saved.port = String(appPort);
|
|
6979
|
+
const fallback = saved === null ? `http://localhost:${appPort}` : saved.origin;
|
|
6933
6980
|
return parseHttpOrigin(options.url ?? fallback, "--url");
|
|
6934
6981
|
};
|
|
6935
6982
|
var checkExposurePair = (bind, appUrl, requireExplicitUrl) => {
|
|
@@ -6944,7 +6991,7 @@ var checkExposurePair = (bind, appUrl, requireExplicitUrl) => {
|
|
|
6944
6991
|
);
|
|
6945
6992
|
}
|
|
6946
6993
|
};
|
|
6947
|
-
var checkPosture = (bind, ports, edgeHost, exposure) => {
|
|
6994
|
+
var checkPosture = (bind, sshBind, ports, edgeHost, exposure, declared) => {
|
|
6948
6995
|
if (edgeHost !== void 0 && [ports.appPort, ports.sshPort].some((p) => p === 80 || p === 443)) {
|
|
6949
6996
|
throw setupError(
|
|
6950
6997
|
"With an edge, --port and --ssh-port must not be 80 or 443: the edge publishes both on every interface."
|
|
@@ -6965,6 +7012,16 @@ var checkPosture = (bind, ports, edgeHost, exposure) => {
|
|
|
6965
7012
|
"--exposure loopback contradicts a non-loopback --bind: the port is published beyond this machine."
|
|
6966
7013
|
);
|
|
6967
7014
|
}
|
|
7015
|
+
if (exposure === "loopback" && !isLoopbackBind(sshBind)) {
|
|
7016
|
+
throw setupError(
|
|
7017
|
+
"--exposure loopback contradicts a non-loopback --ssh-bind: the SSH port is published beyond this machine."
|
|
7018
|
+
);
|
|
7019
|
+
}
|
|
7020
|
+
if (exposure === "public" && !isLoopbackBind(sshBind) && !declared.includes("workspace-ssh")) {
|
|
7021
|
+
throw setupError(
|
|
7022
|
+
`--exposure public with workspace SSH published on ${publishedAddress(sshBind, ports.sshPort)} beside the edge needs a statement Mend cannot observe: who reaches that port. Check it from each network that should not reach it, then add --declare workspace-ssh; or publish SSH on loopback.`
|
|
7023
|
+
);
|
|
7024
|
+
}
|
|
6968
7025
|
if (exposure === "public" && edgeHost === void 0) {
|
|
6969
7026
|
throw setupError(
|
|
6970
7027
|
"--exposure public needs the edge: add --edge <host>. Without it nothing sets MEND_TRUSTED_PROXIES or an https origin, and the server refuses to start as public."
|
|
@@ -6983,10 +7040,18 @@ var validateExposure = (existing, options) => {
|
|
|
6983
7040
|
"--bind must be a literal IPv4 or IPv6 address, such as 127.0.0.1 or 0.0.0.0."
|
|
6984
7041
|
);
|
|
6985
7042
|
}
|
|
7043
|
+
const requestedSshBind = options.sshBind ?? existing?.sshBind;
|
|
7044
|
+
if (requestedSshBind !== void 0 && net3.isIP(requestedSshBind) === 0) {
|
|
7045
|
+
throw setupError(
|
|
7046
|
+
"--ssh-bind must be a literal IPv4 or IPv6 address, such as 127.0.0.1 or 0.0.0.0."
|
|
7047
|
+
);
|
|
7048
|
+
}
|
|
7049
|
+
const sshBind = requestedSshBind === bind ? void 0 : requestedSshBind;
|
|
6986
7050
|
const edgeHost = options.noEdge ? void 0 : options.edge ?? existing?.edgeHost;
|
|
6987
7051
|
const exposure = options.exposure ?? existing?.exposure;
|
|
6988
7052
|
const tenancy = options.tenancy ?? existing?.tenancy;
|
|
6989
|
-
|
|
7053
|
+
const declared = options.declared ?? existing?.declared ?? [];
|
|
7054
|
+
checkPosture(bind, sshBind ?? bind, { appPort, sshPort }, edgeHost, exposure, declared);
|
|
6990
7055
|
const appUrl = resolveAppUrl(existing, options, appPort, edgeHost);
|
|
6991
7056
|
if (edgeHost === void 0)
|
|
6992
7057
|
checkExposurePair(bind, appUrl, options.url === void 0 && existing === null);
|
|
@@ -6997,13 +7062,15 @@ var validateExposure = (existing, options) => {
|
|
|
6997
7062
|
];
|
|
6998
7063
|
return {
|
|
6999
7064
|
bind,
|
|
7065
|
+
...sshBind === void 0 ? {} : { sshBind },
|
|
7000
7066
|
appUrl,
|
|
7001
7067
|
allowedOrigins,
|
|
7002
7068
|
appPort,
|
|
7003
7069
|
sshPort,
|
|
7004
7070
|
...edgeHost === void 0 ? {} : { edgeHost },
|
|
7005
7071
|
...exposure === void 0 ? {} : { exposure },
|
|
7006
|
-
...tenancy === void 0 ? {} : { tenancy }
|
|
7072
|
+
...tenancy === void 0 ? {} : { tenancy },
|
|
7073
|
+
...declared.length === 0 ? {} : { declared }
|
|
7007
7074
|
};
|
|
7008
7075
|
};
|
|
7009
7076
|
var parseServerConfig = (raw) => {
|
|
@@ -7042,6 +7109,13 @@ var parseServerConfig = (raw) => {
|
|
|
7042
7109
|
if (tenancy !== void 0 && !isTenancy(tenancy)) {
|
|
7043
7110
|
throw setupError(`Server config is corrupt: tenancy must be one of ${TENANCIES.join(", ")}.`);
|
|
7044
7111
|
}
|
|
7112
|
+
const declaredField = fields.get("declared");
|
|
7113
|
+
if (declaredField !== void 0 && (!Array.isArray(declaredField) || !declaredField.every((item) => typeof item === "string" && isDeclarableItem(item)))) {
|
|
7114
|
+
throw setupError(
|
|
7115
|
+
`Server config is corrupt: declared must be an array of ${DECLARABLE_ITEMS.join(", ")}.`
|
|
7116
|
+
);
|
|
7117
|
+
}
|
|
7118
|
+
const declared = Array.isArray(declaredField) ? declaredField.filter((item) => isDeclarableItem(String(item))) : [];
|
|
7045
7119
|
const config = {
|
|
7046
7120
|
schemaVersion: requiredInteger(fields, "schemaVersion"),
|
|
7047
7121
|
assetContract: requiredString(fields, "assetContract"),
|
|
@@ -7051,6 +7125,7 @@ var parseServerConfig = (raw) => {
|
|
|
7051
7125
|
dockerSocket: parseDockerSocketPath(requiredString(fields, "dockerSocket")),
|
|
7052
7126
|
dockerSocketSource,
|
|
7053
7127
|
bind: requiredString(fields, "bind"),
|
|
7128
|
+
...fields.has("sshBind") ? { sshBind: requiredString(fields, "sshBind") } : {},
|
|
7054
7129
|
appUrl: parseHttpOrigin(requiredString(fields, "appUrl"), "Server config appUrl"),
|
|
7055
7130
|
allowedOrigins: origins.map(
|
|
7056
7131
|
(origin) => parseHttpOrigin(origin, "Server config allowedOrigins")
|
|
@@ -7061,7 +7136,8 @@ var parseServerConfig = (raw) => {
|
|
|
7061
7136
|
...fields.has("bucket") ? { bucket: requiredBucket(fields) } : {},
|
|
7062
7137
|
...edgeHost === void 0 ? {} : { edgeHost },
|
|
7063
7138
|
...exposure === void 0 ? {} : { exposure },
|
|
7064
|
-
...tenancy === void 0 ? {} : { tenancy }
|
|
7139
|
+
...tenancy === void 0 ? {} : { tenancy },
|
|
7140
|
+
...declared.length === 0 ? {} : { declared }
|
|
7065
7141
|
};
|
|
7066
7142
|
if (config.schemaVersion !== CONFIG_SCHEMA_VERSION || config.assetContract !== ASSET_CONTRACT && !LEGACY_ASSET_CONTRACTS.has(config.assetContract)) {
|
|
7067
7143
|
throw setupError(
|
|
@@ -7076,6 +7152,7 @@ var parseServerConfig = (raw) => {
|
|
|
7076
7152
|
context: void 0,
|
|
7077
7153
|
version: void 0,
|
|
7078
7154
|
bind: config.bind,
|
|
7155
|
+
sshBind: config.sshBind,
|
|
7079
7156
|
url: config.appUrl,
|
|
7080
7157
|
origins: config.allowedOrigins,
|
|
7081
7158
|
appPort: config.appPort,
|
|
@@ -7086,7 +7163,8 @@ var parseServerConfig = (raw) => {
|
|
|
7086
7163
|
edge: config.edgeHost,
|
|
7087
7164
|
noEdge: false,
|
|
7088
7165
|
exposure: config.exposure,
|
|
7089
|
-
tenancy: config.tenancy
|
|
7166
|
+
tenancy: config.tenancy,
|
|
7167
|
+
declared: config.declared
|
|
7090
7168
|
});
|
|
7091
7169
|
return config;
|
|
7092
7170
|
};
|
|
@@ -7423,9 +7501,10 @@ var renderGarage = (secrets, config) => {
|
|
|
7423
7501
|
];
|
|
7424
7502
|
};
|
|
7425
7503
|
var namespaceOf = (config) => config.bucket === "garage" ? MEND_DOCKER_NAMESPACE_WITH_GARAGE : MEND_DOCKER_NAMESPACE;
|
|
7504
|
+
var composeHost = (address) => net3.isIP(address) === 6 ? `[${address}]` : address;
|
|
7426
7505
|
var renderSecrets = (secrets, config) => {
|
|
7427
7506
|
const sshHost = parseUrl(config.appUrl, "Server config appUrl").hostname.replace(/^\[|\]$/g, "");
|
|
7428
|
-
const composeBind =
|
|
7507
|
+
const composeBind = composeHost(config.bind);
|
|
7429
7508
|
return [
|
|
7430
7509
|
`MEND_VERSION=${config.serverVersion}`,
|
|
7431
7510
|
"MEND_IMAGE_REPOSITORY=ghcr.io/sealant-sh/mend",
|
|
@@ -7433,6 +7512,7 @@ var renderSecrets = (secrets, config) => {
|
|
|
7433
7512
|
`APP_URL=${config.appUrl}`,
|
|
7434
7513
|
`MEND_ALLOWED_ORIGINS=${JSON.stringify(config.allowedOrigins)}`,
|
|
7435
7514
|
`MEND_BIND_HOST=${composeBind}`,
|
|
7515
|
+
...config.sshBind === void 0 ? [] : [`MEND_SSH_BIND_HOST=${composeHost(config.sshBind)}`],
|
|
7436
7516
|
`MEND_PORT=${config.appPort}`,
|
|
7437
7517
|
`MEND_SSH_PORT=${config.sshPort}`,
|
|
7438
7518
|
...config.registryPort === void 0 ? [] : [`MEND_REGISTRY_PORT=${config.registryPort}`],
|
|
@@ -7632,6 +7712,13 @@ var resolveDockerSocket = (runtime, options, existing, selectedContext) => {
|
|
|
7632
7712
|
dockerSocketSource: override === void 0 ? "detected" : "override"
|
|
7633
7713
|
};
|
|
7634
7714
|
};
|
|
7715
|
+
var checkSshBindAsset = (config, compose) => {
|
|
7716
|
+
if (config.sshBind !== void 0 && !compose.includes("MEND_SSH_BIND_HOST")) {
|
|
7717
|
+
throw setupError(
|
|
7718
|
+
`Mend ${config.serverVersion}'s ${COMPOSE_ASSET} publishes workspace SSH on --bind and cannot honour --ssh-bind ${config.sshBind}. Use a release that supports --ssh-bind, or pass --ssh-bind with the --bind address.`
|
|
7719
|
+
);
|
|
7720
|
+
}
|
|
7721
|
+
};
|
|
7635
7722
|
var resolveAssets = async (runtime, serverVersion, existing, store, options) => {
|
|
7636
7723
|
if (options.assetsDir !== void 0) {
|
|
7637
7724
|
try {
|
|
@@ -7865,7 +7952,28 @@ var startCompose = async (runtime, installation) => {
|
|
|
7865
7952
|
);
|
|
7866
7953
|
if (compose.status !== 0) throw commandFailure("Mend containers did not start", compose);
|
|
7867
7954
|
};
|
|
7868
|
-
var
|
|
7955
|
+
var SSH_PROBE_BOUND_MS = 1e4;
|
|
7956
|
+
var sshPublicationLine = async (runtime, config) => {
|
|
7957
|
+
if (config.sshBind === void 0 || isLoopbackBind(config.sshBind)) return null;
|
|
7958
|
+
const published = publishedAddress(config.sshBind, config.sshPort);
|
|
7959
|
+
if (runtime.probeSsh === void 0) return null;
|
|
7960
|
+
const boundMs = runtime.sshProbeBoundMs ?? SSH_PROBE_BOUND_MS;
|
|
7961
|
+
let bound;
|
|
7962
|
+
const probes = await Promise.race([
|
|
7963
|
+
runtime.probeSsh(config.sshBind, config.sshPort),
|
|
7964
|
+
new Promise((resolve8) => {
|
|
7965
|
+
bound = setTimeout(() => resolve8(null), boundMs);
|
|
7966
|
+
})
|
|
7967
|
+
]).finally(() => clearTimeout(bound));
|
|
7968
|
+
if (probes === null) {
|
|
7969
|
+
return `Workspace SSH is published on ${published}. This machine's look at it did not finish within ${boundMs / 1e3} s. Who reaches it is up to the network and its firewall; mend operator exposure reports it as workspace-ssh.`;
|
|
7970
|
+
}
|
|
7971
|
+
const observed = probes.length === 0 ? "this machine has no address to try it at" : probes.map(
|
|
7972
|
+
(probe) => probe.banner === null ? `${publishedAddress(probe.address, config.sshPort)} did not answer` : `${publishedAddress(probe.address, config.sshPort)} answers (${probe.banner})`
|
|
7973
|
+
).join(", ");
|
|
7974
|
+
return `Workspace SSH is published on ${published}. From this machine: ${observed}. Who else reaches it is up to the network and its firewall; mend operator exposure reports it as workspace-ssh.`;
|
|
7975
|
+
};
|
|
7976
|
+
var edgeStartedLine = (config) => config.edgeHost === void 0 ? null : `The edge for ${config.edgeHost} is up on 80 and 443. Caddy asks for its certificate once ${config.edgeHost} resolves to this machine and both ports reach it from the Internet. mend server status says whether it holds one, and mend server logs shows what Caddy tried.${config.sshBind === void 0 ? ` Workspace SSH is published on ${config.bind}:${config.sshPort} only, so Remote-SSH and mend ssh from another machine cannot reach it; --ssh-bind 0.0.0.0 publishes it.` : ` Workspace SSH is published on ${config.sshBind}:${config.sshPort}.`}`;
|
|
7869
7977
|
var setupServer = async (args, runtime, store) => {
|
|
7870
7978
|
if (runtime.platform !== "linux" && runtime.platform !== "darwin") {
|
|
7871
7979
|
throw setupError(`mend server setup supports Linux and macOS, not ${runtime.platform}.`);
|
|
@@ -7904,6 +8012,7 @@ var setupServer = async (args, runtime, store) => {
|
|
|
7904
8012
|
...validateExposure(existing?.config ?? null, options)
|
|
7905
8013
|
};
|
|
7906
8014
|
const assets = await resolveAssets(runtime, serverVersion, existing, store, options);
|
|
8015
|
+
checkSshBindAsset(configWithoutBucket, assets.compose);
|
|
7907
8016
|
const bucket = composeBucket(assets.compose);
|
|
7908
8017
|
if (bucket !== void 0 && runtime.dockerDaemonFacts !== void 0) {
|
|
7909
8018
|
const info = await runtime.run("docker", [
|
|
@@ -7954,6 +8063,8 @@ var setupServer = async (args, runtime, store) => {
|
|
|
7954
8063
|
runtime.writeLine(reachableLine(config));
|
|
7955
8064
|
const edgeStarted = edgeStartedLine(config);
|
|
7956
8065
|
if (edgeStarted !== null) runtime.writeLine(edgeStarted);
|
|
8066
|
+
const sshPublication = await sshPublicationLine(runtime, config);
|
|
8067
|
+
if (sshPublication !== null) runtime.writeLine(sshPublication);
|
|
7957
8068
|
if (existing?.config.edgeHost !== void 0 && config.edgeHost === void 0) {
|
|
7958
8069
|
runtime.writeLine(
|
|
7959
8070
|
`The edge for ${existing.config.edgeHost} is gone. Its certificate volumes stay until you remove them: docker --context ${config.dockerContext} volume rm mend_mend-edge-data mend_mend-edge-config`
|
|
@@ -8276,6 +8387,7 @@ var upgradeServer = async (args, runtime, store, existing) => {
|
|
|
8276
8387
|
const previous = storeValue(store.readActive());
|
|
8277
8388
|
if (previous === null) throw setupError("The active server generation is missing.");
|
|
8278
8389
|
const assets = await resolveAssets(runtime, version, existing, store, options);
|
|
8390
|
+
checkSshBindAsset({ ...existing.config, serverVersion: version }, assets.compose);
|
|
8279
8391
|
const {
|
|
8280
8392
|
registryPort: _legacyRegistryPort,
|
|
8281
8393
|
bucket: _previousBucket,
|
|
@@ -8615,9 +8727,45 @@ var nodeServerRuntime = () => {
|
|
|
8615
8727
|
writeLine: (line) => process.stdout.write(`${redactCredentials(line)}
|
|
8616
8728
|
`),
|
|
8617
8729
|
dockerDaemonFacts: hostDockerDaemonFacts,
|
|
8618
|
-
readLogin: (configDir) => savedLogin(configDir, environment)
|
|
8730
|
+
readLogin: (configDir) => savedLogin(configDir, environment),
|
|
8731
|
+
probeSsh: probeSshFromHere
|
|
8619
8732
|
};
|
|
8620
8733
|
};
|
|
8734
|
+
var sshBannerAt = async (host, port, timeoutMs) => {
|
|
8735
|
+
const socket = net3.connect({ host, port });
|
|
8736
|
+
let deadline;
|
|
8737
|
+
try {
|
|
8738
|
+
return await Promise.race([
|
|
8739
|
+
new Promise((resolve8) => {
|
|
8740
|
+
socket.once(
|
|
8741
|
+
"data",
|
|
8742
|
+
(bytes) => resolve8(bytes.toString("utf8").split(/\r?\n/)[0]?.trim() ?? "")
|
|
8743
|
+
);
|
|
8744
|
+
socket.once("end", () => resolve8(null));
|
|
8745
|
+
socket.once("close", () => resolve8(null));
|
|
8746
|
+
socket.once("error", () => resolve8(null));
|
|
8747
|
+
}),
|
|
8748
|
+
new Promise((resolve8) => {
|
|
8749
|
+
deadline = setTimeout(() => resolve8(null), timeoutMs);
|
|
8750
|
+
})
|
|
8751
|
+
]);
|
|
8752
|
+
} finally {
|
|
8753
|
+
clearTimeout(deadline);
|
|
8754
|
+
socket.destroy();
|
|
8755
|
+
}
|
|
8756
|
+
};
|
|
8757
|
+
var probeSshFromHere = async (bind, port) => {
|
|
8758
|
+
const unspecified = bind === "0.0.0.0" || bind === "::";
|
|
8759
|
+
const addresses = unspecified ? Object.values(os10.networkInterfaces()).flatMap((entries) => entries ?? []).filter(
|
|
8760
|
+
(entry) => !entry.internal && (entry.family === "IPv4" || bind === "::" && !entry.address.startsWith("fe80"))
|
|
8761
|
+
).map((entry) => entry.address).slice(0, 4) : [bind];
|
|
8762
|
+
return Promise.all(
|
|
8763
|
+
addresses.map(async (address) => ({
|
|
8764
|
+
address,
|
|
8765
|
+
banner: await sshBannerAt(address, port, 3e3)
|
|
8766
|
+
}))
|
|
8767
|
+
);
|
|
8768
|
+
};
|
|
8621
8769
|
var savedLogin = (configDir, environment) => {
|
|
8622
8770
|
let saved = null;
|
|
8623
8771
|
try {
|
|
@@ -10844,7 +10992,7 @@ var attachTty = async (config, sessionId, harness, from, processId, options) =>
|
|
|
10844
10992
|
process.stdout.write("\x07");
|
|
10845
10993
|
}
|
|
10846
10994
|
};
|
|
10847
|
-
const
|
|
10995
|
+
const connect3 = async () => {
|
|
10848
10996
|
let url;
|
|
10849
10997
|
try {
|
|
10850
10998
|
url = await socketUrl(
|
|
@@ -10913,7 +11061,7 @@ ${dim7(`read-only \xB7 ${detachKeyEnabled ? "Ctrl+] or " : ""}Ctrl+C detaches`)}
|
|
|
10913
11061
|
`
|
|
10914
11062
|
);
|
|
10915
11063
|
}
|
|
10916
|
-
const connected = await
|
|
11064
|
+
const connected = await connect3();
|
|
10917
11065
|
stopConnecting();
|
|
10918
11066
|
if (connected === "unavailable") return "unavailable";
|
|
10919
11067
|
if (connected === "aborted") return abortReason ?? "interrupted";
|
|
@@ -13674,7 +13822,7 @@ var dashboard = async (config, options = {}) => {
|
|
|
13674
13822
|
);
|
|
13675
13823
|
process.exit(rerun.status ?? 0);
|
|
13676
13824
|
}
|
|
13677
|
-
const { runDashboard } = await import("./dashboard-
|
|
13825
|
+
const { runDashboard } = await import("./dashboard-DQB5RNXJ.js");
|
|
13678
13826
|
const agentShare = await startShareIfBridge(config);
|
|
13679
13827
|
const tunnels = options.noTunnel === true || serverIsLocal(config) ? null : createServiceTunnels({
|
|
13680
13828
|
listServices: () => fetchServices(config),
|
package/man/mend-accounts.1
CHANGED
package/man/mend-adopt.1
CHANGED
package/man/mend-attach.1
CHANGED
package/man/mend-codex.1
CHANGED
package/man/mend-completions.1
CHANGED
package/man/mend-connect-pi.1
CHANGED
package/man/mend-connect.1
CHANGED
package/man/mend-continue.1
CHANGED
package/man/mend-doctor.1
CHANGED
package/man/mend-dotfiles-repo.1
CHANGED
package/man/mend-dotfiles-sync.1
CHANGED
package/man/mend-dotfiles.1
CHANGED
package/man/mend-env-cluster.1
CHANGED
package/man/mend-env-load.1
CHANGED
package/man/mend-env-show.1
CHANGED
package/man/mend-folder-create.1
CHANGED
package/man/mend-folder-list.1
CHANGED
package/man/mend-folder-push.1
CHANGED
package/man/mend-folder-rm.1
CHANGED