@nordbyte/nordrelay 0.7.0 → 0.8.0
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/.env.example +35 -0
- package/README.md +109 -49
- package/dist/activity-events.js +2 -2
- package/dist/adapter-conformance.js +61 -0
- package/dist/bot.js +18 -31
- package/dist/channel-adapter.js +33 -6
- package/dist/channel-command-catalog.js +6 -0
- package/dist/channel-command-core.js +60 -0
- package/dist/channel-command-service.js +20 -4
- package/dist/channel-mirror-registry.js +9 -2
- package/dist/channel-prompt-engine.js +177 -0
- package/dist/channel-turn-lifecycle.js +73 -0
- package/dist/config-metadata.js +67 -8
- package/dist/config.js +48 -1
- package/dist/context-key.js +32 -0
- package/dist/discord-bot.js +99 -327
- package/dist/index.js +9 -0
- package/dist/metrics.js +2 -0
- package/dist/peer-client.js +33 -1
- package/dist/peer-readiness.js +77 -0
- package/dist/peer-runtime-service.js +22 -0
- package/dist/peer-store.js +13 -0
- package/dist/relay-runtime-helpers.js +3 -1
- package/dist/relay-runtime.js +7 -0
- package/dist/settings-wizard-test.js +216 -0
- package/dist/slack-artifacts.js +165 -0
- package/dist/slack-bot.js +1461 -0
- package/dist/slack-channel-runtime.js +147 -0
- package/dist/slack-command-surface.js +46 -0
- package/dist/slack-diagnostics.js +116 -0
- package/dist/slack-rate-limit.js +139 -0
- package/dist/user-management-crypto.js +38 -0
- package/dist/user-management-normalize.js +188 -0
- package/dist/user-management-types.js +1 -0
- package/dist/user-management.js +193 -196
- package/dist/web-api-contract.js +8 -0
- package/dist/web-dashboard-access-routes.js +62 -0
- package/dist/web-dashboard-assets.js +1 -0
- package/dist/web-dashboard-pages.js +14 -4
- package/dist/web-dashboard-peer-routes.js +32 -11
- package/dist/web-dashboard.js +34 -0
- package/dist/web-state.js +2 -2
- package/dist/webui-assets/dashboard.css +193 -0
- package/dist/webui-assets/dashboard.js +544 -144
- package/package.json +3 -1
- package/plugins/nordrelay/scripts/nordrelay.mjs +101 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nordbyte/nordrelay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Remote control plane for coding agents across messaging channels.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Ricardo",
|
|
@@ -64,6 +64,8 @@
|
|
|
64
64
|
"@anthropic-ai/claude-agent-sdk": "^0.2.141",
|
|
65
65
|
"@grammyjs/auto-retry": "^2.0.2",
|
|
66
66
|
"@openai/codex-sdk": "^0.130.0",
|
|
67
|
+
"@slack/bolt": "^4.7.2",
|
|
68
|
+
"@slack/web-api": "^7.16.0",
|
|
67
69
|
"discord.js": "^14.26.4",
|
|
68
70
|
"grammy": "^1.41.1",
|
|
69
71
|
"selfsigned": "^3.0.1",
|
|
@@ -76,10 +76,16 @@ function parseArgs(argv) {
|
|
|
76
76
|
else if (arg === "--enable-discord") options.enableDiscord = true;
|
|
77
77
|
else if (arg === "--discord-token") options.discordBotToken = requireValue(copy, ++i, arg);
|
|
78
78
|
else if (arg === "--discord-client-id") options.discordClientId = requireValue(copy, ++i, arg);
|
|
79
|
+
else if (arg === "--enable-slack") options.enableSlack = true;
|
|
80
|
+
else if (arg === "--slack-bot-token") options.slackBotToken = requireValue(copy, ++i, arg);
|
|
81
|
+
else if (arg === "--slack-app-token") options.slackAppToken = requireValue(copy, ++i, arg);
|
|
82
|
+
else if (arg === "--slack-signing-secret") options.slackSigningSecret = requireValue(copy, ++i, arg);
|
|
79
83
|
else if (arg === "--admin-email") options.adminEmail = requireValue(copy, ++i, arg);
|
|
80
84
|
else if (arg === "--admin-name") options.adminName = requireValue(copy, ++i, arg);
|
|
81
85
|
else if (arg === "--admin-password") options.adminPassword = requireValue(copy, ++i, arg);
|
|
82
86
|
else if (arg === "--telegram-user-id") options.telegramUserId = requireValue(copy, ++i, arg);
|
|
87
|
+
else if (arg === "--slack-user-id") options.slackUserId = requireValue(copy, ++i, arg);
|
|
88
|
+
else if (arg === "--slack-team-id") options.slackTeamId = requireValue(copy, ++i, arg);
|
|
83
89
|
else if (arg === "--state-backend") options.stateBackend = requireValue(copy, ++i, arg);
|
|
84
90
|
else if (arg === "--enable-pi") options.enablePi = true;
|
|
85
91
|
else if (arg === "--enable-hermes") options.enableHermes = true;
|
|
@@ -684,10 +690,22 @@ async function commandInit(options) {
|
|
|
684
690
|
const discordClientId = enableDiscord === "true"
|
|
685
691
|
? options.discordClientId || process.env.DISCORD_CLIENT_ID || await ask(null, "Discord client ID", "")
|
|
686
692
|
: "";
|
|
693
|
+
const enableSlack = options.enableSlack ? "true" : await askChoice(null, "Enable Slack", "false");
|
|
694
|
+
const slackBotToken = enableSlack === "true"
|
|
695
|
+
? options.slackBotToken || process.env.SLACK_BOT_TOKEN || await ask(null, "Slack bot token", "")
|
|
696
|
+
: "";
|
|
697
|
+
const slackAppToken = enableSlack === "true"
|
|
698
|
+
? options.slackAppToken || process.env.SLACK_APP_TOKEN || await ask(null, "Slack app-level token for Socket Mode", "")
|
|
699
|
+
: "";
|
|
700
|
+
const slackSigningSecret = enableSlack === "true"
|
|
701
|
+
? options.slackSigningSecret || process.env.SLACK_SIGNING_SECRET || await ask(null, "Slack signing secret (optional for Socket Mode)", "")
|
|
702
|
+
: "";
|
|
687
703
|
const adminEmail = options.adminEmail || await ask(null, "Admin email", "");
|
|
688
704
|
const adminName = options.adminName || await ask(null, "Admin name", "Admin");
|
|
689
705
|
const adminPassword = options.adminPassword || await askSecret(null, "Admin password", "");
|
|
690
706
|
const telegramUserId = options.telegramUserId || await ask(null, "Optional Telegram user id to link", "");
|
|
707
|
+
const slackUserId = options.slackUserId || await ask(null, "Optional Slack user id to link", "");
|
|
708
|
+
const slackTeamId = slackUserId ? (options.slackTeamId || await ask(null, "Optional Slack team id for linked user", "")) : "";
|
|
691
709
|
const enableCodex = options.disableCodex ? "false" : await askChoice(null, "Enable Codex", "true");
|
|
692
710
|
const enablePi = options.enablePi ? "true" : await askChoice(null, "Enable Pi", "false");
|
|
693
711
|
const enableHermes = options.enableHermes ? "true" : await askChoice(null, "Enable Hermes", "false");
|
|
@@ -697,7 +715,9 @@ async function commandInit(options) {
|
|
|
697
715
|
|
|
698
716
|
if (enableTelegram === "true" && !telegramBotToken) throw new Error("Telegram bot token is required when Telegram is enabled.");
|
|
699
717
|
if (enableDiscord === "true" && !discordBotToken) throw new Error("Discord bot token is required when Discord is enabled.");
|
|
700
|
-
if (
|
|
718
|
+
if (enableSlack === "true" && !slackBotToken) throw new Error("Slack bot token is required when Slack is enabled.");
|
|
719
|
+
if (enableSlack === "true" && !slackAppToken) throw new Error("Slack app-level token is required for default Socket Mode.");
|
|
720
|
+
if (enableTelegram !== "true" && enableDiscord !== "true" && enableSlack !== "true") throw new Error("At least one chat adapter must be enabled.");
|
|
701
721
|
if (!adminEmail) throw new Error("Admin email is required.");
|
|
702
722
|
if (!adminPassword) throw new Error("Admin password is required.");
|
|
703
723
|
if (enableCodex !== "true" && enablePi !== "true" && enableHermes !== "true" && enableOpenClaw !== "true" && enableClaudeCode !== "true") throw new Error("At least one agent must be enabled.");
|
|
@@ -722,6 +742,13 @@ async function commandInit(options) {
|
|
|
722
742
|
"DISCORD_COMMAND_MODE=both",
|
|
723
743
|
"DISCORD_MESSAGE_CONTENT_ENABLED=true",
|
|
724
744
|
"DISCORD_AUTO_REGISTER_COMMANDS=true",
|
|
745
|
+
`SLACK_ENABLED=${enableSlack}`,
|
|
746
|
+
`SLACK_BOT_TOKEN=${slackBotToken}`,
|
|
747
|
+
`SLACK_APP_TOKEN=${slackAppToken}`,
|
|
748
|
+
`SLACK_SIGNING_SECRET=${slackSigningSecret}`,
|
|
749
|
+
"SLACK_SOCKET_MODE=true",
|
|
750
|
+
"SLACK_MESSAGE_CONTENT_ENABLED=true",
|
|
751
|
+
"SLACK_AUTO_SEND_ARTIFACTS=false",
|
|
725
752
|
`NORDRELAY_CODEX_ENABLED=${enableCodex}`,
|
|
726
753
|
`NORDRELAY_PI_ENABLED=${enablePi}`,
|
|
727
754
|
`NORDRELAY_HERMES_ENABLED=${enableHermes}`,
|
|
@@ -754,6 +781,8 @@ async function commandInit(options) {
|
|
|
754
781
|
displayName: adminName || adminEmail,
|
|
755
782
|
password: adminPassword,
|
|
756
783
|
telegramUserId: telegramUserId ? Number(telegramUserId) : undefined,
|
|
784
|
+
slackUserId: slackUserId || undefined,
|
|
785
|
+
slackTeamId: slackTeamId || undefined,
|
|
757
786
|
});
|
|
758
787
|
console.log(`Wrote ${envPath}`);
|
|
759
788
|
console.log(`Created admin user ${adminEmail}.`);
|
|
@@ -789,7 +818,7 @@ function parsePeerFlags(argv) {
|
|
|
789
818
|
const copy = [...argv];
|
|
790
819
|
const subcommand = copy[0] && !copy[0].startsWith("-") ? copy.shift() : "list";
|
|
791
820
|
const flags = { subcommand, url: undefined };
|
|
792
|
-
if (["add", "test", "revoke"].includes(subcommand) && copy[0] && !copy[0].startsWith("-")) {
|
|
821
|
+
if (["add", "test", "check", "revoke"].includes(subcommand) && copy[0] && !copy[0].startsWith("-")) {
|
|
793
822
|
flags.url = copy.shift();
|
|
794
823
|
flags.id = flags.url;
|
|
795
824
|
}
|
|
@@ -797,6 +826,7 @@ function parsePeerFlags(argv) {
|
|
|
797
826
|
const arg = copy[i];
|
|
798
827
|
if (arg === "--name") flags.name = requireValue(copy, ++i, arg);
|
|
799
828
|
else if (arg === "--code") flags.code = requireValue(copy, ++i, arg);
|
|
829
|
+
else if (arg === "--expect-fingerprint") flags.expectFingerprint = requireValue(copy, ++i, arg);
|
|
800
830
|
else if (arg === "--public-url") flags.publicUrl = requireValue(copy, ++i, arg);
|
|
801
831
|
else if (arg === "--expires" || arg === "--expires-minutes") flags.expiresMinutes = Number.parseInt(requireValue(copy, ++i, arg), 10);
|
|
802
832
|
else if (arg === "--scopes") flags.scopes = requireValue(copy, ++i, arg);
|
|
@@ -858,6 +888,15 @@ async function commandPeer(options) {
|
|
|
858
888
|
|
|
859
889
|
if (flags.subcommand === "invite") {
|
|
860
890
|
const url = process.env.NORDRELAY_PEER_PUBLIC_URL || `${process.env.NORDRELAY_PEER_TLS_ENABLED === "false" ? "http" : "https"}://${process.env.NORDRELAY_PEER_HOST || "127.0.0.1"}:${process.env.NORDRELAY_PEER_PORT || "31979"}`;
|
|
891
|
+
const peerEnabled = process.env.NORDRELAY_PEER_ENABLED === "true";
|
|
892
|
+
if (!peerEnabled) {
|
|
893
|
+
console.log("Warning: peer server is disabled. The invite can be created, but pairing will fail until NORDRELAY_PEER_ENABLED=true and NordRelay is restarted.");
|
|
894
|
+
} else {
|
|
895
|
+
const probe = await clientMod.checkPeerEndpoint(url, { timeoutMs: 2500 });
|
|
896
|
+
if (!probe.ok) {
|
|
897
|
+
console.log(`Warning: peer endpoint is not reachable from this machine: ${probe.detail}`);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
861
900
|
const created = store.createInvitation({
|
|
862
901
|
name: flags.name,
|
|
863
902
|
expiresInMs: Number.isFinite(flags.expiresMinutes) ? flags.expiresMinutes * 60 * 1000 : undefined,
|
|
@@ -896,13 +935,26 @@ async function commandPeer(options) {
|
|
|
896
935
|
return;
|
|
897
936
|
}
|
|
898
937
|
|
|
938
|
+
if (flags.subcommand === "check") {
|
|
939
|
+
const url = flags.url || await ask(null, "Peer URL", "");
|
|
940
|
+
const probe = await clientMod.checkPeerEndpoint(url, { expectedTlsFingerprint: flags.expectFingerprint });
|
|
941
|
+
console.log(`Peer endpoint: ${probe.url}`);
|
|
942
|
+
console.log(`Status: ${probe.ok ? "reachable" : "unreachable"}`);
|
|
943
|
+
if (probe.latencyMs !== undefined) console.log(`Latency: ${probe.latencyMs}ms`);
|
|
944
|
+
if (probe.statusCode !== undefined) console.log(`HTTP status: ${probe.statusCode}`);
|
|
945
|
+
if (probe.tlsFingerprint) console.log(`TLS fingerprint: ${probe.tlsFingerprint}`);
|
|
946
|
+
console.log(`Detail: ${probe.detail}`);
|
|
947
|
+
if (!probe.ok) process.exitCode = 1;
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
|
|
899
951
|
if (flags.subcommand === "revoke") {
|
|
900
952
|
const id = flags.id || await ask(null, "Peer id", "");
|
|
901
953
|
console.log(store.revokePeer(id) ? `Revoked peer ${id}.` : `Peer not found: ${id}`);
|
|
902
954
|
return;
|
|
903
955
|
}
|
|
904
956
|
|
|
905
|
-
throw new Error("Usage: nordrelay peer [identity|list|invite|add|test|revoke]");
|
|
957
|
+
throw new Error("Usage: nordrelay peer [identity|list|invite|add|test|check|revoke]");
|
|
906
958
|
}
|
|
907
959
|
|
|
908
960
|
function parseUserFlags(argv) {
|
|
@@ -917,6 +969,8 @@ function parseUserFlags(argv) {
|
|
|
917
969
|
else if (arg === "--group" || arg === "--groups") flags.groups = requireValue(copy, ++i, arg);
|
|
918
970
|
else if (arg === "--telegram-user-id") flags.telegramUserId = Number.parseInt(requireValue(copy, ++i, arg), 10);
|
|
919
971
|
else if (arg === "--discord-user-id") flags.discordUserId = requireValue(copy, ++i, arg);
|
|
972
|
+
else if (arg === "--slack-user-id") flags.slackUserId = requireValue(copy, ++i, arg);
|
|
973
|
+
else if (arg === "--slack-team-id") flags.slackTeamId = requireValue(copy, ++i, arg);
|
|
920
974
|
else if (arg === "--user-id") flags.userId = requireValue(copy, ++i, arg);
|
|
921
975
|
}
|
|
922
976
|
return flags;
|
|
@@ -948,8 +1002,8 @@ async function commandUser(options) {
|
|
|
948
1002
|
? ["admin"]
|
|
949
1003
|
: (flags.groups ? flags.groups.split(",").map((item) => item.trim()).filter(Boolean) : ["user"]);
|
|
950
1004
|
const created = flags.subcommand === "create-admin"
|
|
951
|
-
? store.createAdmin({ email, displayName: name, password, telegramUserId: flags.telegramUserId, discordUserId: flags.discordUserId })
|
|
952
|
-
: store.createUser({ email, displayName: name, password, groupIds, telegramUserId: flags.telegramUserId, discordUserId: flags.discordUserId });
|
|
1005
|
+
? store.createAdmin({ email, displayName: name, password, telegramUserId: flags.telegramUserId, discordUserId: flags.discordUserId, slackUserId: flags.slackUserId, slackTeamId: flags.slackTeamId })
|
|
1006
|
+
: store.createUser({ email, displayName: name, password, groupIds, telegramUserId: flags.telegramUserId, discordUserId: flags.discordUserId, slackUserId: flags.slackUserId, slackTeamId: flags.slackTeamId });
|
|
953
1007
|
console.log(`Created user ${created.user.email} (${created.groups.map((group) => group.name).join(", ")}).`);
|
|
954
1008
|
return;
|
|
955
1009
|
}
|
|
@@ -984,6 +1038,17 @@ async function commandUser(options) {
|
|
|
984
1038
|
return;
|
|
985
1039
|
}
|
|
986
1040
|
|
|
1041
|
+
if (flags.subcommand === "link-slack") {
|
|
1042
|
+
const email = flags.email || await ask(null, "Email", "");
|
|
1043
|
+
const slackUserId = flags.slackUserId || await ask(null, "Slack user id", "");
|
|
1044
|
+
const teamId = flags.slackTeamId || await ask(null, "Slack team id (optional)", "");
|
|
1045
|
+
const user = store.getUserByEmail(email);
|
|
1046
|
+
if (!user) throw new Error(`User not found: ${email}`);
|
|
1047
|
+
store.linkSlackUser(user.user.id, { slackUserId, teamId: teamId || undefined });
|
|
1048
|
+
console.log(`Linked Slack user ${slackUserId} to ${user.user.email}.`);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
987
1052
|
if (flags.subcommand === "link-code" || flags.subcommand === "telegram-link-code") {
|
|
988
1053
|
const email = flags.email || await ask(null, "Email", "");
|
|
989
1054
|
const user = store.getUserByEmail(email);
|
|
@@ -1004,7 +1069,17 @@ async function commandUser(options) {
|
|
|
1004
1069
|
return;
|
|
1005
1070
|
}
|
|
1006
1071
|
|
|
1007
|
-
|
|
1072
|
+
if (flags.subcommand === "slack-link-code") {
|
|
1073
|
+
const email = flags.email || await ask(null, "Email", "");
|
|
1074
|
+
const user = store.getUserByEmail(email);
|
|
1075
|
+
if (!user) throw new Error(`User not found: ${email}`);
|
|
1076
|
+
const code = store.createSlackLinkCode(user.user.id);
|
|
1077
|
+
console.log(`Slack link code for ${user.user.email}: ${code.code}`);
|
|
1078
|
+
console.log(`Expires: ${code.expiresAt}`);
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
throw new Error("Usage: nordrelay user [list|create-admin|create|reset-password|link-telegram|link-discord|link-slack|link-code|telegram-link-code|discord-link-code|slack-link-code]");
|
|
1008
1083
|
}
|
|
1009
1084
|
|
|
1010
1085
|
async function commandDoctor(options) {
|
|
@@ -1016,24 +1091,39 @@ async function commandDoctor(options) {
|
|
|
1016
1091
|
checks.push(check("Node.js >= 22", Number.parseInt(process.versions.node.split(".")[0], 10) >= 22, process.version));
|
|
1017
1092
|
const telegramRequested = process.env.TELEGRAM_ENABLED !== "false";
|
|
1018
1093
|
const discordRequested = process.env.DISCORD_ENABLED === "true";
|
|
1094
|
+
const slackRequested = process.env.SLACK_ENABLED === "true";
|
|
1095
|
+
const slackSocketMode = process.env.SLACK_SOCKET_MODE !== "false";
|
|
1019
1096
|
const telegramUsable = telegramRequested && Boolean(process.env.TELEGRAM_BOT_TOKEN);
|
|
1020
1097
|
const discordUsable = discordRequested && Boolean(process.env.DISCORD_BOT_TOKEN);
|
|
1098
|
+
const slackUsable = slackRequested && Boolean(process.env.SLACK_BOT_TOKEN) && (slackSocketMode ? Boolean(process.env.SLACK_APP_TOKEN) : Boolean(process.env.SLACK_SIGNING_SECRET));
|
|
1021
1099
|
checks.push(check(
|
|
1022
1100
|
"Telegram bot token",
|
|
1023
1101
|
!telegramRequested || telegramUsable,
|
|
1024
1102
|
telegramRequested ? (telegramUsable ? "configured" : "missing; Telegram adapter will be disabled") : "disabled",
|
|
1025
|
-
telegramRequested && !discordUsable ? "fail" : "warn",
|
|
1103
|
+
telegramRequested && !discordUsable && !slackUsable ? "fail" : "warn",
|
|
1026
1104
|
));
|
|
1027
1105
|
checks.push(check(
|
|
1028
1106
|
"Discord bot token",
|
|
1029
1107
|
!discordRequested || discordUsable,
|
|
1030
1108
|
discordRequested ? (discordUsable ? "configured" : "missing; Discord adapter will be disabled") : "disabled",
|
|
1031
|
-
discordRequested && !telegramUsable ? "fail" : "warn",
|
|
1109
|
+
discordRequested && !telegramUsable && !slackUsable ? "fail" : "warn",
|
|
1110
|
+
));
|
|
1111
|
+
checks.push(check(
|
|
1112
|
+
"Slack bot token",
|
|
1113
|
+
!slackRequested || Boolean(process.env.SLACK_BOT_TOKEN),
|
|
1114
|
+
slackRequested ? (process.env.SLACK_BOT_TOKEN ? "configured" : "missing; Slack adapter will be disabled") : "disabled",
|
|
1115
|
+
slackRequested && !telegramUsable && !discordUsable ? "fail" : "warn",
|
|
1116
|
+
));
|
|
1117
|
+
checks.push(check(
|
|
1118
|
+
slackSocketMode ? "Slack app token" : "Slack signing secret",
|
|
1119
|
+
!slackRequested || slackUsable,
|
|
1120
|
+
slackRequested ? (slackUsable ? "configured" : `missing; ${slackSocketMode ? "Socket Mode requires SLACK_APP_TOKEN" : "HTTP mode requires SLACK_SIGNING_SECRET"}`) : "disabled",
|
|
1121
|
+
slackRequested && !telegramUsable && !discordUsable ? "fail" : "warn",
|
|
1032
1122
|
));
|
|
1033
1123
|
checks.push(check(
|
|
1034
1124
|
"Usable chat adapter",
|
|
1035
|
-
telegramUsable || discordUsable,
|
|
1036
|
-
telegramUsable
|
|
1125
|
+
telegramUsable || discordUsable || slackUsable,
|
|
1126
|
+
[telegramUsable ? "Telegram" : "", discordUsable ? "Discord" : "", slackUsable ? "Slack" : ""].filter(Boolean).join(" and ") || "none",
|
|
1037
1127
|
"fail",
|
|
1038
1128
|
));
|
|
1039
1129
|
checks.push(check("Discord client ID", !discordUsable || Boolean(process.env.DISCORD_CLIENT_ID), discordUsable ? (process.env.DISCORD_CLIENT_ID ? "configured" : "missing; slash command auto-registration disabled") : "disabled", "warn"));
|
|
@@ -1042,6 +1132,7 @@ async function commandDoctor(options) {
|
|
|
1042
1132
|
checks.push(check("WebUI login", true, "required for every dashboard request"));
|
|
1043
1133
|
checks.push(check("Telegram access", true, "requires linked active users and enabled group chats"));
|
|
1044
1134
|
checks.push(check("Discord access", true, "requires linked active users and enabled channels"));
|
|
1135
|
+
checks.push(check("Slack access", true, "requires linked active users and enabled channels"));
|
|
1045
1136
|
const peerEnabled = process.env.NORDRELAY_PEER_ENABLED === "true";
|
|
1046
1137
|
const peerTlsEnabled = process.env.NORDRELAY_PEER_TLS_ENABLED !== "false";
|
|
1047
1138
|
const peerHost = process.env.NORDRELAY_PEER_HOST || "127.0.0.1";
|