Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
ee5a4af
feat(gui): add gateway lifecycle control panel and dedicated gateway …
mafueee Mar 25, 2026
21f24e5
feat(gui): add GatewayPage component
mafueee Mar 25, 2026
0831b84
test(gui): add GatewayPage test suite
mafueee Mar 25, 2026
8bac581
feat(gui): integrate gateway controls into App layout
mafueee Mar 25, 2026
dd50654
feat(gui): add GatewayStatusDetailed type for gateway page
mafueee Mar 25, 2026
ce664c8
fix: resolve gateway 'Not Installed' status — Docker API version + Co…
mafueee Mar 25, 2026
84bcb9d
docs: update README with dockerGateway.js component and config path d…
mafueee Mar 25, 2026
c6a978f
fix: correct config paths in grpcClient.js for gRPC mTLS connection
mafueee Mar 25, 2026
c8f9993
fix: switch onboard SSE from POST to GET to bypass Express middleware…
mafueee Mar 26, 2026
5738340
fix: add waitForReady:false and fast reconnect to gRPC channel
mafueee Mar 26, 2026
a3a6b5f
fix: add API key validation and corrupted-key detection to agent chat
mafueee Mar 26, 2026
f1650a0
fix: add recovery links to ChatInterface for API key, OpenClaw, and S…
mafueee Mar 26, 2026
9ca6a41
docs: update README Agent Chat description with API key validation
mafueee Mar 26, 2026
d804c21
docs: restore full README with updated Agent Chat description
mafueee Mar 26, 2026
8e895be
fix: persist API key across server restarts
mafueee Mar 26, 2026
53e5d64
revert(onboard): restore original OnboardWizard input style
mafueee Mar 26, 2026
c2cfd46
fix(onboard): restore all providers while keeping original input style
mafueee Mar 26, 2026
8721aa6
fix: map provider keys to gateway-supported gRPC types
mafueee Mar 26, 2026
415a11c
fix: use mapProviderToGrpcType in claw deployment routes
mafueee Mar 26, 2026
d55dbfa
fix: add mapProviderToGrpcType helper to grpcClient.js
mafueee Mar 26, 2026
b38c404
fix: use mapProviderToGrpcType in inference config and onboard routes
mafueee Mar 26, 2026
7dfff35
docs: update README with provider type mapping description
mafueee Mar 26, 2026
e693af4
fix: update chat test for direct LLM proxy approach
mafueee Mar 26, 2026
ff6fd15
fix: update README for direct LLM API chat proxy
mafueee Mar 26, 2026
aa092a8
fix: replace openclaw sandbox exec with direct LLM API proxy for chat
mafueee Mar 26, 2026
86e1e85
fix: sync provider definitions and chat proxy updates
mafueee Mar 26, 2026
9390e19
feat(chat): route agent chat through sandbox via ExecSandbox gRPC
mafueee Mar 26, 2026
d09882a
docs(readme): update Agent Chat docs to reflect ExecSandbox routing
mafueee Mar 26, 2026
454f3f3
docs(readme): restore full README with ExecSandbox chat routing docs
mafueee Mar 26, 2026
b598f09
fix: align claw lifecycle with OpenShell architecture - status sync a…
mafueee Mar 26, 2026
7b491fa
fix: update README for new chat architecture
mafueee Mar 26, 2026
3f49fd5
fix: restore full README with updated chat architecture docs
mafueee Mar 26, 2026
80d0707
fix: route chat through sandbox inference.local via ExecSandbox - REA…
mafueee Mar 26, 2026
4454a39
feat: make chat claw-centric with sandboxed/bypassed indicators
mafueee Mar 26, 2026
faa489b
chore: update API client types with sandboxed/warning fields
mafueee Mar 26, 2026
5312413
feat: claw-centric ChatInterface with sandboxed/bypassed badges and u…
mafueee Mar 26, 2026
e99d4b1
feat: add Chat tab to ClawDetail with embedded ChatInterface
mafueee Mar 26, 2026
5a6eea3
docs: update README with claw-centric chat and six-tab ClawDetail
mafueee Mar 26, 2026
f6ae342
feat: update README with workspace file injection documentation
mafueee Mar 26, 2026
94ddd5d
feat: implement bidirectional gateway proxy and feature parity compon…
mafueee Mar 27, 2026
cd0a681
feat: add React feature parity components and update App router + README
mafueee Mar 27, 2026
5468982
fix(extensions): gateway-native channel registration, non-fatal pip w…
mafueee Mar 27, 2026
fbf95ca
fix(extensions): correct SettingValue proto serialization for gateway…
mafueee Mar 27, 2026
0e0667b
fix(extensions): implement persistent Discord channel injection via e…
mafueee Mar 27, 2026
36b9ad7
docs: update Extensions section — document sync-channel and doctor --…
mafueee Mar 27, 2026
d46428c
Apply Discord visibility fix and walkthrough
mafueee Mar 27, 2026
4ac19d9
fix: Resolve Discord extension reporting and bot token injection via …
mafueee Mar 27, 2026
82341f9
docs: update README to document gateway restart for openclaw extensions
mafueee Mar 27, 2026
5d12b06
fix: Resolve Python PEP-668 externally-managed-environment pip errors…
mafueee Mar 27, 2026
c38a8db
fix: Prevent agent timeout loops by requiring exact channel IDs for m…
mafueee Mar 28, 2026
9040383
Show agent thinking process in chat
mafueee Mar 28, 2026
0f028c4
Update extension registry docs and entrypoint script
mafueee Mar 28, 2026
c5eaa0c
Update README and configureChannelInSandbox for gRPC
mafueee Mar 28, 2026
3f25dd3
Update GUI chat and claw interfaces for gRPC / features
mafueee Mar 28, 2026
11ada83
Update client and claws route for frontend components
mafueee Mar 28, 2026
8ee4c6f
feat(gui): implement workspace component
mafueee Mar 28, 2026
6d6d149
feat(gui): integrate workspace component into claw detail
mafueee Mar 28, 2026
33c80e1
feat(gui): update api client with workspace endpoints
mafueee Mar 28, 2026
3d90f2d
feat(api): complete backend workspace endpoints and update readme
mafueee Mar 28, 2026
4de44cb
fix(discord): inject DISCORD_BOT_TOKEN into openclaw gateway daemon e…
mafueee Mar 28, 2026
f9a08ea
Safe diff push with Discord channel fix
mafueee Mar 28, 2026
702cd71
Fix OPENCLAW_CONFIG_PATH syntax error on non-Discord configured sandb…
mafueee Mar 28, 2026
7a50bf9
Fix Discord agent integration and gateway env injection
mafueee Mar 28, 2026
4213f36
Update README documentation with Discord agent setup schema details
mafueee Mar 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
602 changes: 336 additions & 266 deletions README.md

Large diffs are not rendered by default.

130 changes: 130 additions & 0 deletions discord-fix-patch.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
diff --git a/README.md b/README.md
index 3d2f74d..0347944 100644
--- a/README.md
+++ b/README.md
@@ -517,6 +517,7 @@ We've extended the original into a full management platform by:
- Implementing an **extensions catalog** for installing integrations (Discord, Telegram, Slack, etc.) with network policies, credentials, and in-sandbox packages
- Implementing a **bidirectional WebSocket proxy** tunnelling the React frontend to the OpenClaw gateway daemon via gRPC `ExecSandbox` stdin/stdout for real-time agent tool-use approvals
- Building **feature parity** with the upstream NemoClaw reference implementation: exec approvals, skills viewer, plugin manager, memory search, and cron scheduler — all backed by the sandboxed OpenClaw gateway API
+- **Resolving upstream OpenClaw 2026.3.11 bugs** with strict configuration schema validation, bypassing buggy CLI operations to explicitly inject Discord and other extension properties correctly into the agent runtime configuration.

The core sandbox security model — Landlock, seccomp, network namespace isolation, and policy-enforced egress — remains as NVIDIA designed it.

diff --git a/gui/server/index.js b/gui/server/index.js
index ee1bef3..6096e2f 100644
--- a/gui/server/index.js
+++ b/gui/server/index.js
@@ -1573,6 +1573,8 @@ app.post('/api/chat/message', async (req, res) => {
// that don't inherit the gRPC-injected top-level environment.
// Also source the persistent .channel-env file written during install.
const envExports = [
+ // Point OpenClaw at the writable config we created during extension sync
+ 'export OPENCLAW_CONFIG_PATH=/sandbox/.openclaw-data/openclaw.json',
// Source the persistent env file written by the install/sync-channel route
'[ -f /sandbox/.openclaw-data/.channel-env ] && . /sandbox/.openclaw-data/.channel-env',
// Export each extension credential explicitly
diff --git a/gui/server/routes/extensions.js b/gui/server/routes/extensions.js
index 8b62911..fccfda2 100644
--- a/gui/server/routes/extensions.js
+++ b/gui/server/routes/extensions.js
@@ -141,7 +141,7 @@ function collectExecResult(stream) {
* @param {string} token - bot token to inject
* @returns {Promise<{ok: boolean, message: string}>}
*/
-async function configureChannelInSandbox(sandboxId, channelKey, token) {
+async function configureChannelInSandbox(sandboxId, sandboxName, channelKey, token) {
// Map channel key to its env var name (openclaw env fallback convention)
const envVarMap = {
discord: 'DISCORD_BOT_TOKEN',
@@ -153,7 +153,7 @@ async function configureChannelInSandbox(sandboxId, channelKey, token) {
// Step 1: Write a persistent env file inside the sandbox data volume.
// Token is passed as argv[1] to Python to bypass all shell quoting hazards.
const pyScript = [
- 'import os, sys, json',
+ 'import os, sys, json, shutil',
"env_dir = '/sandbox/.openclaw-data'",
'os.makedirs(env_dir, exist_ok=True)',
"env_file = os.path.join(env_dir, '.channel-env')",
@@ -162,15 +162,23 @@ async function configureChannelInSandbox(sandboxId, channelKey, token) {
` f.write("export ${envVar}=" + repr(tok) + "\\n")`,
'os.chmod(env_file, 0o600)',
'',
- '# Directly enable the channel in openclaw.json to bypass doctor CLI bugs',
- "config_file = '/sandbox/.openclaw/openclaw.json'",
- 'if os.path.exists(config_file):',
+ '# Clone read-only openclaw.json to a writable volume so doctor can fix it',
+ "orig_config = '/sandbox/.openclaw/openclaw.json'",
+ "writable_config = os.path.join(env_dir, 'openclaw.json')",
+ 'if os.path.exists(orig_config):',
' try:',
- ' with open(config_file, "r") as f: data = json.load(f)',
+ ' with open(orig_config, "r") as f: data = json.load(f)',
+ ' ',
+ ' # Ensure channel is explicitly enabled in config',
+ ` ch = "${channelKey}"`,
' if "channels" not in data: data["channels"] = {}',
- ' if "defaults" not in data["channels"]: data["channels"]["defaults"] = {}',
- ` data["channels"]["defaults"]["${channelKey}"] = True`,
- ' with open(config_file, "w") as f: json.dump(data, f, indent=2)',
+ ' if ch not in data["channels"]: data["channels"][ch] = {}',
+ ' data["channels"][ch]["enabled"] = True',
+ ' ',
+ ' # Remove plugins field if it exists, as stock plugins do not need it and it trips the validator',
+ ' if "plugins" in data: del data["plugins"]',
+ ' ',
+ ' with open(writable_config, "w") as f: json.dump(data, f, indent=2)',
' except Exception as e: print("Config warning:", e)',
'',
'print("ENV_WRITTEN")',
@@ -198,15 +206,15 @@ async function configureChannelInSandbox(sandboxId, channelKey, token) {
// so it survives in the daemon's env regardless of sourcing.
const restartCmd = [
// Source the env file so this shell has the token
- '. /sandbox/.openclaw-data/.channel-env 2>/dev/null || true',
- // Kill any existing gateway
- 'pkill -f "openclaw gateway" 2>/dev/null || pkill -f "nemoclaw-gateway" 2>/dev/null || true',
+ '[ -f /sandbox/.openclaw-data/.channel-env ] && . /sandbox/.openclaw-data/.channel-env || true',
+ // Kill any existing gateway, accounting for process name truncation
+ 'pkill -f "gateway run" 2>/dev/null || pkill -f "openclaw-gatewa" 2>/dev/null || pkill -f "openclaw gateway" 2>/dev/null || true',
'sleep 1',
// Find the openclaw binary wherever it lives
'OPENCLAW_BIN=$(which openclaw 2>/dev/null || ls /usr/local/bin/openclaw /usr/bin/openclaw /root/.local/bin/openclaw /home/user/.local/bin/openclaw 2>/dev/null | head -1)',
// Start the gateway with the token explicitly in its env using the `env` trick
// nohup ensures it keeps running after this shell exits
- `[ -n "$OPENCLAW_BIN" ] && nohup env ${envVar}=$${envVar} "$OPENCLAW_BIN" gateway run --allow-unconfigured --auth none > /tmp/gateway.log 2>&1 & echo "GATEWAY_STARTED:$!" || echo "GATEWAY_BIN_NOT_FOUND"`,
+ `[ -n "$OPENCLAW_BIN" ] && export OPENCLAW_CONFIG_PATH=/sandbox/.openclaw-data/openclaw.json && export ${envVar}=$${envVar} && nohup "$OPENCLAW_BIN" gateway run --allow-unconfigured --auth none > /tmp/gateway.log 2>&1 & echo "GATEWAY_STARTED:$!" || echo "GATEWAY_BIN_NOT_FOUND"`,
].join('; ');

const restartStream = grpcClient.execSandbox(sandboxId, [
@@ -233,11 +241,11 @@ async function configureChannelInSandbox(sandboxId, channelKey, token) {
// updateConfig uses settingKey/settingValue to set sandbox-scoped settings.
try {
if (typeof grpcClient.updateConfig === 'function') {
- await grpcClient.updateConfig(sandboxId, {
+ await grpcClient.updateConfig(sandboxName, {
settingKey: envVar,
settingValue: { stringValue: token },
});
- await grpcClient.updateConfig(sandboxId, {
+ await grpcClient.updateConfig(sandboxName, {
settingKey: `channels.defaults.${channelKey}`,
settingValue: { boolValue: true },
});
@@ -398,7 +406,7 @@ router.post('/api/extensions/install', async (req, res) => {
sandboxId = resp.sandbox?.id || sandboxName;
} catch { /* fall back to name */ }

- const cfgResult = await configureChannelInSandbox(sandboxId, ext.channelName, credential);
+ const cfgResult = await configureChannelInSandbox(sandboxId, sandboxName, ext.channelName, credential);
steps.push({
step: 'channel',
status: cfgResult.ok ? 'complete' : 'warning',
@@ -650,7 +658,7 @@ router.post('/api/extensions/sync-channel', async (req, res) => {
sandboxId = resp.sandbox?.id || sandboxName;
} catch { /* use name as fallback */ }

- const result = await configureChannelInSandbox(sandboxId, ext.channelName, credential);
+ const result = await configureChannelInSandbox(sandboxId, sandboxName, ext.channelName, credential);

res.json({
ok: result.ok,
Loading