Skip to content
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
880fd51
fix(sandbox): repair Shields removal regressions
ericksoa Sep 3, 2026
18fb464
test(onboard): cover stable create argument positions
ericksoa Sep 3, 2026
6752d85
fix(hermes): refresh validator integrity digest
ericksoa Sep 3, 2026
cee4d82
fix(onboard): preserve optional create callback contract
ericksoa Sep 3, 2026
825d82a
fix(pi): refresh Shields removal qualification receipts
ericksoa Sep 3, 2026
e3ff72f
chore(ci): retrigger exact-head qualification
ericksoa Sep 3, 2026
a3b7c98
docs(pi): clarify qualification receipt ordering
ericksoa Sep 3, 2026
0061eec
merge: integrate current main for E2E
ericksoa Sep 3, 2026
376c7c0
test(e2e): assert accepted messaging placeholders
ericksoa Sep 3, 2026
e7d2948
ci(e2e): add direct regression runner
ericksoa Sep 3, 2026
dc2c549
fix(ci): pass simple E2E catalog explicitly
ericksoa Sep 3, 2026
81d8844
fix(ci): isolate simple E2E catalog from CLI packaging
ericksoa Sep 3, 2026
39252de
fix(ci): correlate direct E2E results
ericksoa Sep 3, 2026
a30c820
ci: run affected E2Es directly
ericksoa Sep 3, 2026
1fe29d4
ci: install OpenShell for MCP regression
ericksoa Sep 3, 2026
6d1102d
fix(e2e): restore Slack runtime dependency and live output
ericksoa Sep 3, 2026
0345383
ci: add maintainer simple E2E runner
ericksoa Sep 3, 2026
bb8551a
ci: build only required E2E bases
ericksoa Sep 3, 2026
9f15ff5
ci: reuse exact simple E2E images
ericksoa Sep 3, 2026
9dc17a7
ci: separate image and candidate revisions
ericksoa Sep 3, 2026
978d703
test(e2e): expose Hermes MCP hash drift
ericksoa Sep 3, 2026
3faa497
fix(openclaw): bundle fast-uri with Slack runtime
ericksoa Sep 3, 2026
00c2edb
fix(e2e): validate mutable Hermes MCP integrity
ericksoa Sep 3, 2026
616f7b1
ci: honor simple E2E selectors
ericksoa Sep 4, 2026
cfb89c3
ci: align simple E2E selector defaults
ericksoa Sep 4, 2026
e3e3123
Merge branch 'main' into fix/shields-removal-e2e-regressions
prekshivyas Sep 4, 2026
7088a7c
fix: let MCP restart repair stale adapters
ericksoa Sep 4, 2026
205e350
fix: install fast-uri in OpenClaw runtime
ericksoa Sep 4, 2026
5e40406
ci: reuse exact OpenClaw E2E bases
ericksoa Sep 4, 2026
2ac0334
fix: restore Hermes cron backup access
ericksoa Sep 4, 2026
513f349
ci: keep runtime check out of base inputs
ericksoa Sep 4, 2026
6ca7518
fix: bundle fast-uri for external Slack runtime
ericksoa Sep 4, 2026
975b0bc
fix: keep Hermes cron state sandbox-owned
ericksoa Sep 4, 2026
38c9efb
fix: expose locked fast-uri to global OpenClaw
ericksoa Sep 4, 2026
dd7ba63
fix: move managed gateway lease into readable trust root
ericksoa Sep 4, 2026
cf4d65d
fix: restore managed WeChat extension link
ericksoa Sep 4, 2026
495eb55
merge: resolve conflicts with main
github-actions[bot] Sep 4, 2026
fc76af1
fix: preserve reviewed WeChat extension link in backups
ericksoa Sep 4, 2026
deb8a07
test: align MCP restart runtime selection
ericksoa Sep 4, 2026
3bc82b4
test(e2e): route WeChat mock through policy transport
ericksoa Sep 4, 2026
8c47ef2
fix(rebuild): retain local supervisor authority
ericksoa Sep 4, 2026
7a47ec2
ci(e2e): honor explicit image cohort reuse
ericksoa Sep 4, 2026
33780fd
ci(e2e): pass the selected base into upgrades
ericksoa Sep 4, 2026
996d41b
ci(e2e): build required base architectures
ericksoa Sep 4, 2026
2d6cfaa
ci(e2e): classify OpenClaw remediation as base input
ericksoa Sep 4, 2026
17627ac
merge: resolve conflicts with main
github-actions[bot] Sep 4, 2026
78234d7
merge: resolve conflicts with main
ericksoa Sep 4, 2026
92fa4e4
test(e2e): align regression mock coverage
ericksoa Sep 4, 2026
2aa547d
merge: take current main audit fix
ericksoa Sep 4, 2026
c3d6453
test: satisfy static validation
ericksoa Sep 4, 2026
9fdc60d
fix(images): verify receipts with audited npm
ericksoa Sep 4, 2026
25914d6
fix(hermes): preserve cron ownership in managed images
ericksoa Sep 4, 2026
a9fa8ab
fix(hermes): select lazy path by runtime identity
ericksoa Sep 4, 2026
c00cec9
fix(hermes): select lazy path by runtime identity
ericksoa Sep 4, 2026
089ab1d
merge: resolve conflicts with main
github-actions[bot] Sep 4, 2026
76029ed
merge: resolve conflicts with main
github-actions[bot] Sep 4, 2026
ff1caff
fix(sandbox): focus Shields migration repairs
prekshivyas Sep 4, 2026
5dc5b88
Merge branch 'main' into fix/shields-removal-e2e-regressions
prekshivyas Sep 4, 2026
7273f6e
Merge remote-tracking branch 'refs/remotes/origin/fix/shields-removal…
prekshivyas Sep 4, 2026
0c33508
fix(sandbox): remove unrelated Hermes base repair
prekshivyas Sep 4, 2026
e80bf46
test(onboard): clarify Shields repair evidence
prekshivyas Sep 4, 2026
9f697dc
fix(sandbox): retain latest main Hermes base repair
prekshivyas Sep 4, 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
18 changes: 7 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,7 @@ RUN --network=none install -d -o root -g root -m 0755 /out/wechat-npm-cache \
&& chown -R root:root /out/wechat-npm-cache \
&& chmod -R a+rX,go-w /out/wechat-npm-cache

# Fetch every locked messaging archive outside RUN instructions. BuildKit
# verifies the committed SHA-256 source pins before the selected architecture
# enters the cache stage; SHA-512 verification against package-lock.json and
# every package-materialization command then execute with networking disabled.
# Offline messaging archives.
FROM scratch AS openclaw-managed-messaging-npm-common-archives-1

ADD --chmod=0444 --checksum=sha256:d98ffa76628ea162ddf7539b7b84ab851ef889689b16d454483456ba2e166d84 https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz /abort-controller-2.1.2.tgz
Expand Down Expand Up @@ -265,6 +262,7 @@ ADD --chmod=0444 --checksum=sha256:703cdecfa6951d9ad258f615ab96895750add3cb2d95e
ADD --chmod=0444 --checksum=sha256:21d4a36175672b9e6640c39a68613af73f9a4c47a4a4da39993e8cd085564eb6 https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz /eventemitter3-5.0.4.tgz
ADD --chmod=0444 --checksum=sha256:1773a16c02b4422653479b9c4d211268f7022bdac0d817b5698535bb485dd005 https://registry.npmjs.org/express/-/express-5.2.1.tgz /express-5.2.1.tgz
ADD --chmod=0444 --checksum=sha256:1d91d0b0faa50cba223fa937c7b5a4a662968b1d78b3e59dca5c917dd5cf72b2 https://registry.npmjs.org/extend/-/extend-3.0.2.tgz /extend-3.0.2.tgz
ADD --chmod=0444 --checksum=sha256:264af0e32c4b7b7bcb9ce5b4623c82469ee3e69ba5d171920f1762d626db1064 https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.6.tgz /fast-uri-3.1.6.tgz
ADD --chmod=0444 --checksum=sha256:54481d9c62debce1c38b0239f2358eeb3b73f7bb1ba3105bd6123fd81b8b7268 https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz /fetch-1.1.1.tgz
ADD --chmod=0444 --checksum=sha256:4abf0d58a4977fce2240e08c280a2bc59f5363e9553a4f236cea6d74cce40c52 https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz /fetch-blob-3.2.0.tgz
ADD --chmod=0444 --checksum=sha256:87fdc6557e71ec47373edfbde774165e976c760845d02733f81fbfe1ad232780 https://registry.npmjs.org/file-type/-/file-type-22.0.1.tgz /file-type-22.0.1.tgz
Expand Down Expand Up @@ -920,9 +918,7 @@ RUN --network=default \
echo "ERROR: Base image has OpenClaw $CUR_VER, which is newer than reviewed target $OPENCLAW_VERSION" >&2; exit 1; \
else \
echo "INFO: Base image OpenClaw $CUR_VER lacks matching reviewed provenance; installing $OPENCLAW_VERSION"; \
# npm 10's atomic-move install can hit EROFS on overlayfs when the prior
# install spans image layers. Removing it first also prevents unreviewed
# files from surviving a same-version reinstall.
# Remove the stale overlayfs install before replacement.
rm -rf /usr/local/lib/node_modules/openclaw /usr/local/bin/openclaw; \
if [ "$OPENCLAW_VERSION" = "2026.7.1" ]; then \
node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts --verify-lock \
Expand Down Expand Up @@ -964,6 +960,7 @@ RUN --network=default \
rm -rf "$OPENCLAW_PACK_DIR"; \
fi; \
fi; \
if [ "$OPENCLAW_VERSION" = "2026.7.1" ]; then rm -rf /usr/local/lib/node_modules/fast-uri; ln -s /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/fast-uri /usr/local/lib/node_modules/fast-uri; node -e 'require.resolve("fast-uri", { paths: ["/usr/local/lib/node_modules/openclaw"] })'; fi; \
case "$OPENCLAW_VERSION" in \
2026.3.11) npm ls -g --depth=1 openclaw tar >/dev/null ;; \
esac; \
Expand All @@ -973,8 +970,7 @@ RUN --network=default \
node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts --verify-only \
--package-spec "mcporter@${MCPORTER_VERSION}" --integrity "$MCPORTER_EXPECTED_INTEGRITY" \
--tarball-url "$MCPORTER_EXPECTED_TARBALL" --label "mcporter ${MCPORTER_VERSION}"; \
# Reinstall from the committed lock when matching protected base provenance
# is unavailable; matching top-level versions can hide transitive drift.
# Reinstall the committed lock when base provenance is unavailable.
echo "INFO: Installing locked mcporter $MCPORTER_VERSION dependency graph"; \
rm -rf /usr/local/lib/node_modules/mcporter /usr/local/bin/mcporter; \
npm --prefix /usr/local/lib/nemoclaw/mcporter-runtime ci \
Expand All @@ -997,7 +993,7 @@ node --experimental-strip-types /scripts/lib/npm-audit-receipt.mts \
--package-json /usr/local/lib/nemoclaw/mcporter-runtime/package.json \
--package-lock /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json \
--raw-report "$MCPORTER_RAW_REPORT" --exceptions /scripts/npm-audit-exceptions.json \
--graph mcporter-runtime --npm-version "$(npm --version)" \
--graph mcporter-runtime --npm-version "10.9.4" \
--registry https://registry.yarnpkg.com --threshold high --legacy-npmjs true; \
else \
node --experimental-strip-types /scripts/lib/reviewed-npm-audit.mts \
Expand Down Expand Up @@ -1917,7 +1913,7 @@ RUN managed_runtime_assertion_failed() { \
&& test -f /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh \
&& test ! -L /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh \
&& test "$(stat -c '%u:%g:%a' /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh)" = '0:0:444' \
&& install -d -o root -g root -m 0755 /run/nemoclaw
&& install -d -o root -g root -m 0755 /run/nemoclaw /etc/nemoclaw

# Copy startup script and shared sandbox initialisation library.
RUN chmod 755 /usr/local/bin/nemoclaw-start /usr/local/bin/nemoclaw-codex-acp \
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep
--package-json /usr/local/lib/nemoclaw/mcporter-runtime/package.json \
--package-lock /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json \
--raw-report "$MCPORTER_RAW_REPORT" --exceptions /scripts/npm-audit-exceptions.json \
--graph mcporter-runtime --npm-version "$(npm --version)" \
--graph mcporter-runtime --npm-version "10.9.4" \
--registry https://registry.yarnpkg.com --threshold high \
--legacy-npmjs true \
--result /tmp/mcporter-npm-audit-policy.json; \
Expand Down
1 change: 0 additions & 1 deletion agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -1192,7 +1192,6 @@ RUN set -eu; \
rm -rf /root/.cache/pip /sandbox/.cache \
&& chown -R sandbox:sandbox /sandbox/.hermes \
&& chown gateway:sandbox \
/sandbox/.hermes/cron \
/sandbox/.hermes/gateway \
/sandbox/.hermes/runtime \
&& chmod 3770 /sandbox/.hermes \
Expand Down
21 changes: 18 additions & 3 deletions agents/hermes/hermes-wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
import ast
import json
import os
import pwd
import re
import stat
import subprocess
Expand All @@ -102,6 +103,7 @@
_CLI_ADAPTER_DEV_FILENAME = "hermes-cli-adapter-v1.json"
_HERMES_MAIN_DEV_FILENAME = "hermes-main.py"
_GATEWAY_LAZY_INSTALL_TARGET = "/run/nemoclaw/hermes-gateway-lazy-packages"
_SANDBOX_LAZY_INSTALL_TARGET = "/sandbox/.hermes/lazy-packages"
_MANAGED_BUNDLED_PLUGINS = "/opt/hermes/plugins"
_MANAGED_HERMES_HOME = "/sandbox/.hermes"
_MANAGED_HOME = "/sandbox"
Expand Down Expand Up @@ -355,13 +357,25 @@
return subprocess.call([python3, "-I", guard_path, "runtime-env"])


def _harden_root_separated_gateway_package_env() -> None:
def _managed_gateway_lazy_install_target() -> str:
"""Return the identity-specific reviewed lazy-install directory."""

try:
if os.geteuid() == pwd.getpwnam("gateway").pw_uid:
return _GATEWAY_LAZY_INSTALL_TARGET
except KeyError:
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
pass
return _SANDBOX_LAZY_INSTALL_TARGET


def _harden_gateway_package_env(lazy_install_target: str) -> None:
"""Remove sandbox-controlled installer inputs before gateway exec."""

for key in tuple(os.environ):
if key in _GATEWAY_PACKAGE_ENV_KEYS or key.startswith(_GATEWAY_PACKAGE_ENV_PREFIXES):
os.environ.pop(key, None)
os.environ.update(_GATEWAY_PACKAGE_ENV)
os.environ["UV_CACHE_DIR"] = f"{lazy_install_target}/.uv-cache"


_SUPPORTED_CLI_ADAPTER_VERSION = 1
Expand Down Expand Up @@ -782,11 +796,12 @@
file=sys.stderr,
)
return 1
lazy_install_target = _managed_gateway_lazy_install_target()
os.environ["HERMES_HOME"] = _MANAGED_HERMES_HOME
os.environ["HERMES_BUNDLED_PLUGINS"] = _MANAGED_BUNDLED_PLUGINS
os.environ["HERMES_LAZY_INSTALL_TARGET"] = _GATEWAY_LAZY_INSTALL_TARGET
os.environ["HERMES_LAZY_INSTALL_TARGET"] = lazy_install_target
os.environ["HOME"] = _MANAGED_HOME
_harden_root_separated_gateway_package_env()
_harden_gateway_package_env(lazy_install_target)
rc = _run_gateway_guard(guard_path)
if rc != 0:
return rc
Expand Down
6 changes: 5 additions & 1 deletion agents/hermes/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,10 @@ repair_hermes_startup_layout() {
fi

ensure_hermes_config_root_mode || return 1
# Cron definitions are sandbox-owned mutable state. Hermes can recreate this
# directory with a private umask; restore the shared sandbox-group mode before
# each managed gateway launch so the sandbox identity can back it up.
ensure_hermes_state_dir "${HERMES_DIR}/cron" 2770 || return 1
repair_hermes_log_permissions || return 1
ensure_hermes_state_dir "${HERMES_DIR}/hooks" 770 || return 1
ensure_hermes_state_dir "${HERMES_DIR}/image_cache" 770 || return 1
Expand Down Expand Up @@ -2949,7 +2953,7 @@ launch_hermes_gateway_current_user() {

HERMES_MANAGED_GATEWAY_EXIT_TIMES=()
HERMES_MANAGED_GATEWAY_EXIT_COUNT=0
readonly HERMES_MANAGED_EXPECTED_EXIT_DIR="/run/nemoclaw"
readonly HERMES_MANAGED_EXPECTED_EXIT_DIR="/etc/nemoclaw/gateway-control"
readonly HERMES_MANAGED_EXPECTED_EXIT_MARKER="managed-gateway-expected-exit"
readonly HERMES_MANAGED_CONTROLLER_PATH="/usr/local/lib/nemoclaw/managed-gateway-control.py"

Expand Down
17 changes: 17 additions & 0 deletions agents/openclaw/managed-image-messaging-runtime/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@tencent-weixin/openclaw-weixin": "2.4.3",
"agent-base": "6.0.2",
"axios": "1.18.0",
"fast-uri": "3.1.6",
"https-proxy-agent": "5.0.1",
"undici": "8.10.0"
},
Expand Down
2 changes: 1 addition & 1 deletion scripts/lib/gateway-supervisor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ EOF
return 1
}

NEMOCLAW_MANAGED_EXPECTED_EXIT_DIR="/run/nemoclaw"
NEMOCLAW_MANAGED_EXPECTED_EXIT_DIR="/etc/nemoclaw/gateway-control"
NEMOCLAW_MANAGED_EXPECTED_EXIT_MARKER="managed-gateway-expected-exit"
NEMOCLAW_MANAGED_CONTROLLER_PATH="/usr/local/lib/nemoclaw/managed-gateway-control.py"

Expand Down
55 changes: 52 additions & 3 deletions scripts/lib/openclaw-npm-remediation.mts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ const REMEDIATIONS: Readonly<Record<string, Remediation>> = Object.freeze({
},
"@openclaw/slack@2026.7.1": {
expectedPatchedTreeIntegrity:
"sha512-4ThnsNS+yBlFSkTaQn2xosxrDu1s0vrxcqka5QqFj+8dCEaTa9JVLRgNniYV/QNhO53wc7a2R5oQFElzYspT2w==",
"sha512-WvkzDMT9uDyWZop858eli9vbkuna1JgWpTdAt9e1zm008uc9YJqcCoLxB4WqATThBg93j317bobMjDK1GGGMWA==",
kind: "axios",
version: "2026.7.1",
},
Expand Down Expand Up @@ -452,6 +452,7 @@ export function patchOpenClawPluginPackageGraph(
const versionAt = packageSpec.lastIndexOf("@");
const expectedName = packageSpec.slice(0, versionAt);
const expectedVersion = packageSpec.slice(versionAt + 1);
const addFastUri = packageSpec === "@openclaw/slack@2026.7.1";
requirePackageIdentity(packageJson, expectedName, expectedVersion, "OpenClaw plugin");
if (packageJson.dependencies?.axios !== undefined) {
throw new Error(`${packageSpec} already declares axios; review the remediation boundary`);
Expand All @@ -462,8 +463,19 @@ export function patchOpenClawPluginPackageGraph(
if (packageJson.bundledDependencies.includes("axios")) {
throw new Error(`${packageSpec} already bundles axios; review the remediation boundary`);
}
packageJson.dependencies = sortedObject({ ...packageJson.dependencies, axios: AXIOS_VERSION });
packageJson.bundledDependencies = [...packageJson.bundledDependencies, "axios"];
if (addFastUri && packageJson.dependencies?.["fast-uri"] !== undefined) {
throw new Error(`${packageSpec} already declares fast-uri; review the remediation boundary`);
}
packageJson.dependencies = sortedObject({
...packageJson.dependencies,
axios: AXIOS_VERSION,
...(addFastUri ? { "fast-uri": CURRENT_FAST_URI_VERSION } : {}),
});
packageJson.bundledDependencies = [
...packageJson.bundledDependencies,
"axios",
...(addFastUri ? ["fast-uri"] : []),
];

const shrinkwrap = readJson(shrinkwrapPath);
if (shrinkwrap.lockfileVersion !== 3 || !shrinkwrap.packages?.[""]) {
Expand Down Expand Up @@ -515,6 +527,22 @@ export function patchOpenClawPluginPackageGraph(
dependencies: { debug: "4" },
engines: { node: ">= 6.0.0" },
};
if (addFastUri) {
const fastUriKey = "node_modules/fast-uri";
if (shrinkwrap.packages[fastUriKey]) {
throw new Error(`${packageSpec} already has the fast-uri remediation`);
}
shrinkwrap.packages[fastUriKey] = {
version: CURRENT_FAST_URI_VERSION,
resolved: CURRENT_FAST_URI_TARBALL,
integrity: CURRENT_FAST_URI_INTEGRITY,
license: "BSD-3-Clause",
funding: [
{ type: "github", url: "https://github.com/sponsors/fastify" },
{ type: "opencollective", url: "https://opencollective.com/fastify" },
],
};
}

writeJson(packageJsonPath, packageJson);
writeJson(shrinkwrapPath, shrinkwrap);
Expand Down Expand Up @@ -1243,6 +1271,16 @@ export function buildRemediatedOpenClawPluginArchive(
remediationRoot,
env,
);
const fastUriArchive =
request.packageSpec === "@openclaw/slack@2026.7.1"
? packReplacement(
`fast-uri@${CURRENT_FAST_URI_VERSION}`,
CURRENT_FAST_URI_INTEGRITY,
CURRENT_FAST_URI_TARBALL,
remediationRoot,
env,
)
: undefined;
const axiosPackage = extractArchive(
axiosArchive.archivePath,
join(remediationRoot, "axios"),
Expand All @@ -1261,6 +1299,14 @@ export function buildRemediatedOpenClawPluginArchive(
remediationRoot,
env,
);
const fastUriPackage = fastUriArchive
? extractArchive(
fastUriArchive.archivePath,
join(remediationRoot, "fast-uri"),
remediationRoot,
env,
)
: undefined;
const axiosPackageJson = readJson(join(axiosPackage, "package.json"));
const httpsProxyAgentPackageJson = readJson(join(httpsProxyAgentPackage, "package.json"));
const agentBasePackageJson = readJson(join(agentBasePackage, "package.json"));
Expand Down Expand Up @@ -1304,6 +1350,9 @@ export function buildRemediatedOpenClawPluginArchive(
agentBasePackage,
join(axiosTarget, "node_modules", "https-proxy-agent", "node_modules", "agent-base"),
);
if (fastUriPackage) {
copyReplacementPackage(fastUriPackage, join(sourcePackage, "node_modules", "fast-uri"));
}
patchOpenClawPluginPackageGraph(sourcePackage, request.packageSpec);
} else {
const jaegerArchive = packReplacement(
Expand Down
2 changes: 1 addition & 1 deletion scripts/managed-gateway-control.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
POLL_SECONDS = 0.2
PROCESS_PROOF_GRACE_SECONDS = 1.0
PROCESS_PROOF_RETRY_SECONDS = 0.05
NEMOCLAW_RUNTIME_DIR = "/run/nemoclaw"
NEMOCLAW_RUNTIME_DIR = "/etc/nemoclaw/gateway-control"
NEMOCLAW_RUNTIME_DIR_MODE = 0o711
EXPECTED_EXIT_MARKER_NAME = "managed-gateway-expected-exit"
EXPECTED_EXIT_LOCK_NAME = "managed-gateway-expected-exit.lock"
Expand Down
43 changes: 43 additions & 0 deletions scripts/nemoclaw-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5807,6 +5807,48 @@ handle_openclaw_gateway_control_request() {
gateway_control_complete ok "$old_pid" "$GATEWAY_PID"
}

restore_openclaw_weixin_extension_link() {
local state_root=/sandbox/.openclaw
local extension="$state_root/extensions/openclaw-weixin"
local projects="$state_root/npm/projects"
local project candidate selected=""

if [ -e "$extension" ]; then
[ -d "$extension" ] || {
echo "[SECURITY] refusing non-directory OpenClaw WeChat extension path" >&2
return 1
}
return 0
fi
if [ -L "$extension" ]; then
echo "[SECURITY] refusing broken OpenClaw WeChat extension link" >&2
return 1
fi
if [ ! -d "$projects" ] || [ -L "$projects" ]; then
return 0
fi

for project in "$projects"/*; do
if [ ! -d "$project" ] || [ -L "$project" ]; then
continue
fi
candidate="$project/node_modules/@tencent-weixin/openclaw-weixin"
if [ ! -f "$candidate/package.json" ] || [ -L "$candidate/package.json" ]; then
continue
fi
node -e 'const p=require(process.argv[1]); process.exit(p.name==="@tencent-weixin/openclaw-weixin"&&p.version==="2.4.3"?0:1)' \
"$candidate/package.json" || continue
[ -z "$selected" ] || {
echo "[SECURITY] refusing ambiguous OpenClaw WeChat package projects" >&2
return 1
}
selected="$candidate"
done
[ -n "$selected" ] || return 0
mkdir -p "$state_root/extensions"
ln -s "$selected" "$extension"
}

# ── Main ─────────────────────────────────────────────────────────

# OpenClaw 2026.7.1 enforces owner-only SQLite and models-file modes on every
Expand Down Expand Up @@ -5834,6 +5876,7 @@ fi
# Migrate legacy symlink layout before anything else reads .openclaw
migrate_legacy_layout "/sandbox/.openclaw" "/sandbox/.openclaw-data" "openclaw" || exit 1
remove_openclaw_legacy_update_check_state || exit 1
restore_openclaw_weixin_extension_link || exit 1

echo 'Setting up NemoClaw...' >&2
# Best-effort: .env may not exist.
Expand Down
Loading
Loading