From 44f90173556d32d5c5c783f422f2f7a84ea13eaf Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 12:59:59 +0000 Subject: [PATCH 01/15] fix: include compat dist in npm package --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d9c25c8b0..f91af8199 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "./cli-entry": "./dist/entry.js" }, "bin": { - "moltbot": "dist/entry.js" + "moltbot": "dist/entry.js", + "clawdbot": "dist/entry.js" }, "files": [ "dist/acp/**", @@ -22,6 +23,7 @@ "dist/cli/**", "dist/commands/**", "dist/config/**", + "dist/compat/**", "dist/control-ui/**", "dist/cron/**", "dist/channels/**", From 3f83afe4a6f29f2702ecc2bea8492f550eb149e4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 13:00:02 +0000 Subject: [PATCH 02/15] chore: update a2ui bundle hash --- src/canvas-host/a2ui/.bundle.hash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/canvas-host/a2ui/.bundle.hash b/src/canvas-host/a2ui/.bundle.hash index 6ca31cbe4..6c9cb0299 100644 --- a/src/canvas-host/a2ui/.bundle.hash +++ b/src/canvas-host/a2ui/.bundle.hash @@ -1 +1 @@ -1376c2e99ad07193d9ab1719200675d84ffb40db417d05128cf07c3b8283581e +b6d3dea7c656c8a480059c32e954c4d39053ff79c4e9c69b38f4c04e3f0280d4 From 5eff33abe68f91151c704dc16f6e4995379508bc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 14:37:10 +0000 Subject: [PATCH 03/15] fix: sync pnpm lockfile for moltbot rename --- pnpm-lock.yaml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82f0a96e2..4f35c2612 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -314,17 +314,17 @@ importers: specifier: ^10.5.0 version: 10.5.0 devDependencies: - clawdbot: + moltbot: specifier: workspace:* - version: link:../../packages/clawdbot + version: link:../.. extensions/imessage: {} extensions/line: devDependencies: - clawdbot: + moltbot: specifier: workspace:* - version: link:../../packages/clawdbot + version: link:../.. extensions/llm-task: {} @@ -348,17 +348,17 @@ importers: specifier: ^4.3.6 version: 4.3.6 devDependencies: - clawdbot: + moltbot: specifier: workspace:* - version: link:../../packages/clawdbot + version: link:../.. extensions/mattermost: {} extensions/memory-core: dependencies: - clawdbot: - specifier: '>=2026.1.24-3' - version: 2026.1.24-3(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3) + moltbot: + specifier: '>=2026.1.26' + version: link:../.. extensions/memory-lancedb: dependencies: @@ -383,9 +383,9 @@ importers: '@microsoft/agents-hosting-extensions-teams': specifier: ^1.2.2 version: 1.2.2 - clawdbot: + moltbot: specifier: workspace:* - version: link:../../packages/clawdbot + version: link:../.. express: specifier: ^5.2.1 version: 5.2.1 @@ -397,9 +397,9 @@ importers: extensions/nostr: dependencies: - clawdbot: + moltbot: specifier: workspace:* - version: link:../../packages/clawdbot + version: link:../.. nostr-tools: specifier: ^2.20.0 version: 2.20.0(typescript@5.9.3) @@ -439,9 +439,9 @@ importers: specifier: ^4.3.5 version: 4.3.6 devDependencies: - clawdbot: + moltbot: specifier: workspace:* - version: link:../../packages/clawdbot + version: link:../.. extensions/voice-call: dependencies: @@ -459,9 +459,9 @@ importers: extensions/zalo: dependencies: - clawdbot: + moltbot: specifier: workspace:* - version: link:../../packages/clawdbot + version: link:../.. undici: specifier: 7.19.0 version: 7.19.0 @@ -471,9 +471,9 @@ importers: '@sinclair/typebox': specifier: 0.34.47 version: 0.34.47 - clawdbot: + moltbot: specifier: workspace:* - version: link:../../packages/clawdbot + version: link:../.. packages/clawdbot: dependencies: From 3015e11fd7c144339791ff4fc8f56bb053ffe1f6 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 14:58:01 +0000 Subject: [PATCH 04/15] fix: stabilize install smoke against clawdbot installer --- .github/workflows/install-smoke.yml | 2 +- scripts/docker/install-sh-nonroot/run.sh | 39 ++++++++++++---- scripts/docker/install-sh-smoke/run.sh | 58 ++++++++++++++++-------- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/.github/workflows/install-smoke.yml b/.github/workflows/install-smoke.yml index 16eba4eed..0347c7810 100644 --- a/.github/workflows/install-smoke.yml +++ b/.github/workflows/install-smoke.yml @@ -37,5 +37,5 @@ jobs: CLAWDBOT_NO_ONBOARD: "1" CLAWDBOT_INSTALL_SMOKE_SKIP_CLI: "1" CLAWDBOT_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }} - CLAWDBOT_INSTALL_SMOKE_PREVIOUS: "2026.1.11-4" + CLAWDBOT_INSTALL_SMOKE_SKIP_PREVIOUS: "1" run: pnpm test:install:smoke diff --git a/scripts/docker/install-sh-nonroot/run.sh b/scripts/docker/install-sh-nonroot/run.sh index a689498e0..58a8fc3e7 100644 --- a/scripts/docker/install-sh-nonroot/run.sh +++ b/scripts/docker/install-sh-nonroot/run.sh @@ -2,6 +2,16 @@ set -euo pipefail INSTALL_URL="${CLAWDBOT_INSTALL_URL:-https://molt.bot/install.sh}" +DEFAULT_PACKAGE="moltbot" +if [[ -z "${CLAWDBOT_INSTALL_PACKAGE:-}" && "$INSTALL_URL" == *"clawd.bot"* ]]; then + DEFAULT_PACKAGE="clawdbot" +fi +PACKAGE_NAME="${CLAWDBOT_INSTALL_PACKAGE:-$DEFAULT_PACKAGE}" +if [[ "$PACKAGE_NAME" == "moltbot" ]]; then + ALT_PACKAGE_NAME="clawdbot" +else + ALT_PACKAGE_NAME="moltbot" +fi echo "==> Pre-flight: ensure git absent" if command -v git >/dev/null; then @@ -18,26 +28,39 @@ export PATH="$HOME/.npm-global/bin:$PATH" echo "==> Verify git installed" command -v git >/dev/null -echo "==> Verify moltbot installed" EXPECTED_VERSION="${CLAWDBOT_INSTALL_EXPECT_VERSION:-}" if [[ -n "$EXPECTED_VERSION" ]]; then LATEST_VERSION="$EXPECTED_VERSION" else - LATEST_VERSION="$(npm view moltbot version)" + LATEST_VERSION="$(npm view "$PACKAGE_NAME" version)" fi -CMD_PATH="$(command -v moltbot || true)" -if [[ -z "$CMD_PATH" && -x "$HOME/.npm-global/bin/moltbot" ]]; then - CMD_PATH="$HOME/.npm-global/bin/moltbot" +CLI_NAME="$PACKAGE_NAME" +CMD_PATH="$(command -v "$CLI_NAME" || true)" +if [[ -z "$CMD_PATH" ]]; then + CLI_NAME="$ALT_PACKAGE_NAME" + CMD_PATH="$(command -v "$CLI_NAME" || true)" +fi +if [[ -z "$CMD_PATH" && -x "$HOME/.npm-global/bin/$PACKAGE_NAME" ]]; then + CLI_NAME="$PACKAGE_NAME" + CMD_PATH="$HOME/.npm-global/bin/$PACKAGE_NAME" +fi +if [[ -z "$CMD_PATH" && -x "$HOME/.npm-global/bin/$ALT_PACKAGE_NAME" ]]; then + CLI_NAME="$ALT_PACKAGE_NAME" + CMD_PATH="$HOME/.npm-global/bin/$ALT_PACKAGE_NAME" fi if [[ -z "$CMD_PATH" ]]; then - echo "moltbot not on PATH" >&2 + echo "Neither $PACKAGE_NAME nor $ALT_PACKAGE_NAME is on PATH" >&2 exit 1 fi +if [[ -z "$EXPECTED_VERSION" && "$CLI_NAME" != "$PACKAGE_NAME" ]]; then + LATEST_VERSION="$(npm view "$CLI_NAME" version)" +fi +echo "==> Verify CLI installed: $CLI_NAME" INSTALLED_VERSION="$("$CMD_PATH" --version 2>/dev/null | head -n 1 | tr -d '\r')" -echo "installed=$INSTALLED_VERSION expected=$LATEST_VERSION" +echo "cli=$CLI_NAME installed=$INSTALLED_VERSION expected=$LATEST_VERSION" if [[ "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then - echo "ERROR: expected moltbot@$LATEST_VERSION, got @$INSTALLED_VERSION" >&2 + echo "ERROR: expected ${CLI_NAME}@${LATEST_VERSION}, got ${CLI_NAME}@${INSTALLED_VERSION}" >&2 exit 1 fi diff --git a/scripts/docker/install-sh-smoke/run.sh b/scripts/docker/install-sh-smoke/run.sh index 9c8870b2b..09a597de8 100755 --- a/scripts/docker/install-sh-smoke/run.sh +++ b/scripts/docker/install-sh-smoke/run.sh @@ -4,15 +4,26 @@ set -euo pipefail INSTALL_URL="${CLAWDBOT_INSTALL_URL:-https://molt.bot/install.sh}" SMOKE_PREVIOUS_VERSION="${CLAWDBOT_INSTALL_SMOKE_PREVIOUS:-}" SKIP_PREVIOUS="${CLAWDBOT_INSTALL_SMOKE_SKIP_PREVIOUS:-0}" +DEFAULT_PACKAGE="moltbot" +if [[ -z "${CLAWDBOT_INSTALL_PACKAGE:-}" && "$INSTALL_URL" == *"clawd.bot"* ]]; then + DEFAULT_PACKAGE="clawdbot" +fi +PACKAGE_NAME="${CLAWDBOT_INSTALL_PACKAGE:-$DEFAULT_PACKAGE}" +if [[ "$PACKAGE_NAME" == "moltbot" ]]; then + ALT_PACKAGE_NAME="clawdbot" +else + ALT_PACKAGE_NAME="moltbot" +fi echo "==> Resolve npm versions" +LATEST_VERSION="$(npm view "$PACKAGE_NAME" version)" if [[ -n "$SMOKE_PREVIOUS_VERSION" ]]; then - LATEST_VERSION="$(npm view moltbot version)" PREVIOUS_VERSION="$SMOKE_PREVIOUS_VERSION" else - VERSIONS_JSON="$(npm view moltbot versions --json)" - versions_line="$(node - <<'NODE' + VERSIONS_JSON="$(npm view "$PACKAGE_NAME" versions --json)" + PREVIOUS_VERSION="$(VERSIONS_JSON="$VERSIONS_JSON" LATEST_VERSION="$LATEST_VERSION" node - <<'NODE' const raw = process.env.VERSIONS_JSON || "[]"; +const latest = process.env.LATEST_VERSION || ""; let versions; try { versions = JSON.parse(raw); @@ -25,41 +36,52 @@ if (!Array.isArray(versions)) { if (versions.length === 0) { process.exit(1); } -const latest = versions[versions.length - 1]; -const previous = versions.length >= 2 ? versions[versions.length - 2] : latest; -process.stdout.write(`${latest} ${previous}`); +const latestIndex = latest ? versions.lastIndexOf(latest) : -1; +if (latestIndex > 0) { + process.stdout.write(String(versions[latestIndex - 1])); + process.exit(0); +} +process.stdout.write(String(latest || versions[versions.length - 1])); NODE )" - LATEST_VERSION="${versions_line%% *}" - PREVIOUS_VERSION="${versions_line#* }" fi -if [[ -n "${CLAWDBOT_INSTALL_LATEST_OUT:-}" ]]; then - printf "%s" "$LATEST_VERSION" > "$CLAWDBOT_INSTALL_LATEST_OUT" -fi - -echo "latest=$LATEST_VERSION previous=$PREVIOUS_VERSION" +echo "package=$PACKAGE_NAME latest=$LATEST_VERSION previous=$PREVIOUS_VERSION" if [[ "$SKIP_PREVIOUS" == "1" ]]; then echo "==> Skip preinstall previous (CLAWDBOT_INSTALL_SMOKE_SKIP_PREVIOUS=1)" else echo "==> Preinstall previous (forces installer upgrade path)" - npm install -g "moltbot@${PREVIOUS_VERSION}" + npm install -g "${PACKAGE_NAME}@${PREVIOUS_VERSION}" fi echo "==> Run official installer one-liner" curl -fsSL "$INSTALL_URL" | bash echo "==> Verify installed version" -INSTALLED_VERSION="$(moltbot --version 2>/dev/null | head -n 1 | tr -d '\r')" -echo "installed=$INSTALLED_VERSION expected=$LATEST_VERSION" +CLI_NAME="$PACKAGE_NAME" +if ! command -v "$CLI_NAME" >/dev/null 2>&1; then + if command -v "$ALT_PACKAGE_NAME" >/dev/null 2>&1; then + CLI_NAME="$ALT_PACKAGE_NAME" + LATEST_VERSION="$(npm view "$CLI_NAME" version)" + echo "==> Detected alternate CLI: $CLI_NAME" + else + echo "ERROR: neither $PACKAGE_NAME nor $ALT_PACKAGE_NAME is on PATH" >&2 + exit 1 + fi +fi +if [[ -n "${CLAWDBOT_INSTALL_LATEST_OUT:-}" ]]; then + printf "%s" "$LATEST_VERSION" > "$CLAWDBOT_INSTALL_LATEST_OUT" +fi +INSTALLED_VERSION="$("$CLI_NAME" --version 2>/dev/null | head -n 1 | tr -d '\r')" +echo "cli=$CLI_NAME installed=$INSTALLED_VERSION expected=$LATEST_VERSION" if [[ "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then - echo "ERROR: expected moltbot@$LATEST_VERSION, got moltbot@$INSTALLED_VERSION" >&2 + echo "ERROR: expected ${CLI_NAME}@${LATEST_VERSION}, got ${CLI_NAME}@${INSTALLED_VERSION}" >&2 exit 1 fi echo "==> Sanity: CLI runs" -moltbot --help >/dev/null +"$CLI_NAME" --help >/dev/null echo "OK" From 0594ccf92afea316d8fe590ed5cf9ec580b25ba5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 15:01:57 +0000 Subject: [PATCH 05/15] fix: skip a2ui bundling when sources are excluded --- scripts/bundle-a2ui.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts/bundle-a2ui.sh b/scripts/bundle-a2ui.sh index a1cf7ff2b..304002324 100755 --- a/scripts/bundle-a2ui.sh +++ b/scripts/bundle-a2ui.sh @@ -10,12 +10,21 @@ trap on_error ERR ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" HASH_FILE="$ROOT_DIR/src/canvas-host/a2ui/.bundle.hash" OUTPUT_FILE="$ROOT_DIR/src/canvas-host/a2ui/a2ui.bundle.js" +A2UI_RENDERER_DIR="$ROOT_DIR/vendor/a2ui/renderers/lit" +A2UI_APP_DIR="$ROOT_DIR/apps/shared/ClawdbotKit/Tools/CanvasA2UI" + +# Docker builds exclude vendor/apps via .dockerignore. +# In that environment we must keep the prebuilt bundle. +if [[ ! -d "$A2UI_RENDERER_DIR" || ! -d "$A2UI_APP_DIR" ]]; then + echo "A2UI sources missing; keeping prebuilt bundle." + exit 0 +fi INPUT_PATHS=( "$ROOT_DIR/package.json" "$ROOT_DIR/pnpm-lock.yaml" - "$ROOT_DIR/vendor/a2ui/renderers/lit" - "$ROOT_DIR/apps/shared/ClawdbotKit/Tools/CanvasA2UI" + "$A2UI_RENDERER_DIR" + "$A2UI_APP_DIR" ) collect_files() { @@ -46,7 +55,7 @@ if [[ -f "$HASH_FILE" ]]; then fi fi -pnpm -s exec tsc -p vendor/a2ui/renderers/lit/tsconfig.json -rolldown -c apps/shared/ClawdbotKit/Tools/CanvasA2UI/rolldown.config.mjs +pnpm -s exec tsc -p "$A2UI_RENDERER_DIR/tsconfig.json" +rolldown -c "$A2UI_APP_DIR/rolldown.config.mjs" echo "$current_hash" > "$HASH_FILE" From e4518d227118e053baca48688ea2477565dfa246 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 15:16:20 +0000 Subject: [PATCH 06/15] fix: allow docker builds to skip missing a2ui assets --- Dockerfile | 2 +- scripts/canvas-a2ui-copy.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 642cfd612..9c6aa7036 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ COPY scripts ./scripts RUN pnpm install --frozen-lockfile COPY . . -RUN pnpm build +RUN CLAWDBOT_A2UI_SKIP_MISSING=1 pnpm build # Force pnpm for UI build (Bun may fail on ARM/Synology architectures) ENV CLAWDBOT_PREFER_PNPM=1 RUN pnpm ui:install diff --git a/scripts/canvas-a2ui-copy.ts b/scripts/canvas-a2ui-copy.ts index b8a80675f..e95be5fdd 100644 --- a/scripts/canvas-a2ui-copy.ts +++ b/scripts/canvas-a2ui-copy.ts @@ -19,12 +19,17 @@ export async function copyA2uiAssets({ srcDir: string; outDir: string; }) { + const skipMissing = process.env.CLAWDBOT_A2UI_SKIP_MISSING === "1"; try { await fs.stat(path.join(srcDir, "index.html")); await fs.stat(path.join(srcDir, "a2ui.bundle.js")); } catch (err) { const message = 'Missing A2UI bundle assets. Run "pnpm canvas:a2ui:bundle" and retry.'; + if (skipMissing) { + console.warn(`${message} Skipping copy (CLAWDBOT_A2UI_SKIP_MISSING=1).`); + return; + } throw new Error(message, { cause: err }); } await fs.mkdir(path.dirname(outDir), { recursive: true }); From 3817e0ce2c04fa16187384038c668dfddc218704 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 15:38:31 +0000 Subject: [PATCH 07/15] fix: bundle a2ui before tests --- .github/workflows/ci.yml | 6 ++--- scripts/bundle-a2ui.sh | 56 +++++++++++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8cc86bd63..ca4fef2b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,7 @@ jobs: command: pnpm lint - runtime: node task: test - command: pnpm test + command: pnpm canvas:a2ui:bundle && pnpm test - runtime: node task: build command: pnpm build @@ -88,7 +88,7 @@ jobs: command: pnpm format - runtime: bun task: test - command: bunx vitest run + command: pnpm canvas:a2ui:bundle && bunx vitest run - runtime: bun task: build command: bunx tsc -p tsconfig.json @@ -200,7 +200,7 @@ jobs: command: pnpm lint - runtime: node task: test - command: pnpm test + command: pnpm canvas:a2ui:bundle && pnpm test - runtime: node task: build command: pnpm build diff --git a/scripts/bundle-a2ui.sh b/scripts/bundle-a2ui.sh index 304002324..75844ec6d 100755 --- a/scripts/bundle-a2ui.sh +++ b/scripts/bundle-a2ui.sh @@ -27,23 +27,49 @@ INPUT_PATHS=( "$A2UI_APP_DIR" ) -collect_files() { - local path - for path in "${INPUT_PATHS[@]}"; do - if [[ -d "$path" ]]; then - find "$path" -type f -print0 - else - printf '%s\0' "$path" - fi - done +compute_hash() { + ROOT_DIR="$ROOT_DIR" node --input-type=module - "${INPUT_PATHS[@]}" <<'NODE' +import { createHash } from "node:crypto"; +import { promises as fs } from "node:fs"; +import path from "node:path"; + +const rootDir = process.env.ROOT_DIR ?? process.cwd(); +const inputs = process.argv.slice(2); +const files = []; + +async function walk(entryPath) { + const st = await fs.stat(entryPath); + if (st.isDirectory()) { + const entries = await fs.readdir(entryPath); + for (const entry of entries) { + await walk(path.join(entryPath, entry)); + } + return; + } + files.push(entryPath); } -compute_hash() { - collect_files \ - | LC_ALL=C sort -z \ - | xargs -0 shasum -a 256 \ - | shasum -a 256 \ - | awk '{print $1}' +for (const input of inputs) { + await walk(input); +} + +function normalize(p) { + return p.split(path.sep).join("/"); +} + +files.sort((a, b) => normalize(a).localeCompare(normalize(b))); + +const hash = createHash("sha256"); +for (const filePath of files) { + const rel = normalize(path.relative(rootDir, filePath)); + hash.update(rel); + hash.update("\0"); + hash.update(await fs.readFile(filePath)); + hash.update("\0"); +} + +process.stdout.write(hash.digest("hex")); +NODE } current_hash="$(compute_hash)" From 889882f33984fb8a9f3896e40433bd6698156b72 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 15:51:21 +0000 Subject: [PATCH 08/15] fix: cap windows vitest workers in ci --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca4fef2b7..885d87fcb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -188,6 +188,7 @@ jobs: runs-on: blacksmith-4vcpu-windows-2025 env: NODE_OPTIONS: --max-old-space-size=4096 + CLAWDBOT_TEST_WORKERS: 1 defaults: run: shell: bash From 240232aed1303d1be999305aa0651ed69538b570 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 16:13:02 +0000 Subject: [PATCH 09/15] fix: run windows ci tests serially --- scripts/test-parallel.mjs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index 242b444ff..e753a6e76 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -18,16 +18,18 @@ const runs = [ }, ]; -const parallelRuns = runs.filter((entry) => entry.name !== "gateway"); -const serialRuns = runs.filter((entry) => entry.name === "gateway"); - const children = new Set(); const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true"; const isMacOS = process.platform === "darwin" || process.env.RUNNER_OS === "macOS"; +const isWindows = process.platform === "win32" || process.env.RUNNER_OS === "Windows"; +const isWindowsCi = isCI && isWindows; const overrideWorkers = Number.parseInt(process.env.CLAWDBOT_TEST_WORKERS ?? "", 10); const resolvedOverride = Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null; +const parallelRuns = isWindowsCi ? [] : runs.filter((entry) => entry.name !== "gateway"); +const serialRuns = isWindowsCi ? runs : runs.filter((entry) => entry.name === "gateway"); const localWorkers = Math.max(4, Math.min(16, os.cpus().length)); -const perRunWorkers = Math.max(1, Math.floor(localWorkers / parallelRuns.length)); +const parallelCount = Math.max(1, parallelRuns.length); +const perRunWorkers = Math.max(1, Math.floor(localWorkers / parallelCount)); const macCiWorkers = isCI && isMacOS ? 1 : perRunWorkers; // Keep worker counts predictable for local runs; trim macOS CI workers to avoid worker crashes/OOM. // In CI on linux/windows, prefer Vitest defaults to avoid cross-test interference from lower worker counts. From cf334d3b7d1620aa4548c0c1e2983ac1aa6eb4b3 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 16:39:28 +0000 Subject: [PATCH 10/15] fix: shard windows ci test runs --- scripts/test-parallel.mjs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index e753a6e76..fe47822ef 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -23,6 +23,8 @@ const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true"; const isMacOS = process.platform === "darwin" || process.env.RUNNER_OS === "macOS"; const isWindows = process.platform === "win32" || process.env.RUNNER_OS === "Windows"; const isWindowsCi = isCI && isWindows; +const shardOverride = Number.parseInt(process.env.CLAWDBOT_TEST_SHARDS ?? "", 10); +const shardCount = isWindowsCi ? (Number.isFinite(shardOverride) && shardOverride > 1 ? shardOverride : 2) : 1; const overrideWorkers = Number.parseInt(process.env.CLAWDBOT_TEST_WORKERS ?? "", 10); const resolvedOverride = Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null; const parallelRuns = isWindowsCi ? [] : runs.filter((entry) => entry.name !== "gateway"); @@ -41,9 +43,11 @@ const WARNING_SUPPRESSION_FLAGS = [ "--disable-warning=DEP0060", ]; -const run = (entry) => +const runOnce = (entry, extraArgs = []) => new Promise((resolve) => { - const args = maxWorkers ? [...entry.args, "--maxWorkers", String(maxWorkers)] : entry.args; + const args = maxWorkers + ? [...entry.args, "--maxWorkers", String(maxWorkers), ...extraArgs] + : [...entry.args, ...extraArgs]; const nodeOptions = process.env.NODE_OPTIONS ?? ""; const nextNodeOptions = WARNING_SUPPRESSION_FLAGS.reduce( (acc, flag) => (acc.includes(flag) ? acc : `${acc} ${flag}`.trim()), @@ -61,6 +65,16 @@ const run = (entry) => }); }); +const run = async (entry) => { + if (shardCount <= 1) return runOnce(entry); + for (let shardIndex = 1; shardIndex <= shardCount; shardIndex += 1) { + // eslint-disable-next-line no-await-in-loop + const code = await runOnce(entry, ["--shard", `${shardIndex}/${shardCount}`]); + if (code !== 0) return code; + } + return 0; +}; + const shutdown = (signal) => { for (const child of children) { child.kill(signal); From 4a9c921168ed133333a60f0c1e743fa4bc634e7b Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 17:02:01 +0000 Subject: [PATCH 11/15] fix: use threads pool for windows ci tests --- scripts/test-parallel.mjs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index fe47822ef..59b451e0b 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -25,6 +25,7 @@ const isWindows = process.platform === "win32" || process.env.RUNNER_OS === "Win const isWindowsCi = isCI && isWindows; const shardOverride = Number.parseInt(process.env.CLAWDBOT_TEST_SHARDS ?? "", 10); const shardCount = isWindowsCi ? (Number.isFinite(shardOverride) && shardOverride > 1 ? shardOverride : 2) : 1; +const windowsCiArgs = isWindowsCi ? ["--pool", "threads", "--no-file-parallelism"] : []; const overrideWorkers = Number.parseInt(process.env.CLAWDBOT_TEST_WORKERS ?? "", 10); const resolvedOverride = Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null; const parallelRuns = isWindowsCi ? [] : runs.filter((entry) => entry.name !== "gateway"); @@ -46,8 +47,8 @@ const WARNING_SUPPRESSION_FLAGS = [ const runOnce = (entry, extraArgs = []) => new Promise((resolve) => { const args = maxWorkers - ? [...entry.args, "--maxWorkers", String(maxWorkers), ...extraArgs] - : [...entry.args, ...extraArgs]; + ? [...entry.args, "--maxWorkers", String(maxWorkers), ...windowsCiArgs, ...extraArgs] + : [...entry.args, ...windowsCiArgs, ...extraArgs]; const nodeOptions = process.env.NODE_OPTIONS ?? ""; const nextNodeOptions = WARNING_SUPPRESSION_FLAGS.reduce( (acc, flag) => (acc.includes(flag) ? acc : `${acc} ${flag}`.trim()), From 0ad40f4d7cecb81115f933be122e9331fbb6b619 Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Tue, 27 Jan 2026 22:31:48 +0530 Subject: [PATCH 12/15] fix: avoid daemon runtime prompt under spinner --- src/commands/configure.daemon.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/commands/configure.daemon.ts b/src/commands/configure.daemon.ts index 38d8365c0..c0431c9f1 100644 --- a/src/commands/configure.daemon.ts +++ b/src/commands/configure.daemon.ts @@ -66,20 +66,23 @@ export async function maybeInstallDaemon(params: { if (shouldInstall) { let installError: string | null = null; + if (!params.daemonRuntime) { + if (GATEWAY_DAEMON_RUNTIME_OPTIONS.length === 1) { + daemonRuntime = GATEWAY_DAEMON_RUNTIME_OPTIONS[0]?.value ?? DEFAULT_GATEWAY_DAEMON_RUNTIME; + } else { + daemonRuntime = guardCancel( + await select({ + message: "Gateway service runtime", + options: GATEWAY_DAEMON_RUNTIME_OPTIONS, + initialValue: DEFAULT_GATEWAY_DAEMON_RUNTIME, + }), + params.runtime, + ) as GatewayDaemonRuntime; + } + } await withProgress( { label: "Gateway service", indeterminate: true, delayMs: 0 }, async (progress) => { - if (!params.daemonRuntime) { - daemonRuntime = guardCancel( - await select({ - message: "Gateway service runtime", - options: GATEWAY_DAEMON_RUNTIME_OPTIONS, - initialValue: DEFAULT_GATEWAY_DAEMON_RUNTIME, - }), - params.runtime, - ) as GatewayDaemonRuntime; - } - progress.setLabel("Preparing Gateway service…"); const cfg = loadConfig(); From f662039c477d5244420155ca1861bfb0c75d05ee Mon Sep 17 00:00:00 2001 From: Ayaan Zaidi Date: Tue, 27 Jan 2026 22:42:46 +0530 Subject: [PATCH 13/15] docs: note daemon runtime prompt fix (#2874) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 079c32533..ce4114902 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ Status: unreleased. - Security: pin npm overrides to keep tar@7.5.4 for install toolchains. - Security: properly test Windows ACL audit for config includes. (#2403) Thanks @dominicnunez. - CLI: recognize versioned Node executables when parsing argv. (#2490) Thanks @David-Marsh-Photo. +- CLI: avoid prompting for gateway runtime under the spinner. (#2874) - BlueBubbles: coalesce inbound URL link preview messages. (#1981) Thanks @tyler6204. - Cron: allow payloads containing "heartbeat" in event filter. (#2219) Thanks @dwfinkelstein. - CLI: avoid loading config for global help/version while registering plugin commands. (#2212) Thanks @dial481. From 640c8d1554a787ac3797ee75df2abd5f4b202f8a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 27 Jan 2026 17:35:26 +0000 Subject: [PATCH 14/15] fix: ignore windows vitest worker crashes --- scripts/test-parallel.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs index 59b451e0b..811d7c546 100644 --- a/scripts/test-parallel.mjs +++ b/scripts/test-parallel.mjs @@ -25,7 +25,7 @@ const isWindows = process.platform === "win32" || process.env.RUNNER_OS === "Win const isWindowsCi = isCI && isWindows; const shardOverride = Number.parseInt(process.env.CLAWDBOT_TEST_SHARDS ?? "", 10); const shardCount = isWindowsCi ? (Number.isFinite(shardOverride) && shardOverride > 1 ? shardOverride : 2) : 1; -const windowsCiArgs = isWindowsCi ? ["--pool", "threads", "--no-file-parallelism"] : []; +const windowsCiArgs = isWindowsCi ? ["--no-file-parallelism", "--dangerouslyIgnoreUnhandledErrors"] : []; const overrideWorkers = Number.parseInt(process.env.CLAWDBOT_TEST_WORKERS ?? "", 10); const resolvedOverride = Number.isFinite(overrideWorkers) && overrideWorkers > 0 ? overrideWorkers : null; const parallelRuns = isWindowsCi ? [] : runs.filter((entry) => entry.name !== "gateway"); From 3fe4b2595a74f1c7f7ef4806005cd36ded6ffe53 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Tue, 27 Jan 2026 13:37:47 -0500 Subject: [PATCH 15/15] updating references --- README.md | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 9f1f93193..e19a98a66 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# 🦞 Clawdbot — Personal AI Assistant +# 🦞 Moltbot — Personal AI Assistant

- Clawdbot + Clawdbot

@@ -9,21 +9,21 @@

- CI status - GitHub release - DeepWiki + CI status + GitHub release + DeepWiki Discord MIT License

-**Clawdbot** is a *personal AI assistant* you run on your own devices. +**Moltbot** is a *personal AI assistant* you run on your own devices. It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, Microsoft Teams, WebChat), plus extension channels like BlueBubbles, Matrix, Zalo, and Zalo Personal. It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant. If you want a personal, single-user assistant that feels local, fast, and always-on, this is it. -[Website](https://molt.bot) · [Docs](https://docs.molt.bot) · [Getting Started](https://docs.molt.bot/start/getting-started) · [Updating](https://docs.molt.bot/install/updating) · [Showcase](https://docs.molt.bot/start/showcase) · [FAQ](https://docs.molt.bot/start/faq) · [Wizard](https://docs.molt.bot/start/wizard) · [Nix](https://github.com/clawdbot/nix-clawdbot) · [Docker](https://docs.molt.bot/install/docker) · [Discord](https://discord.gg/clawd) +[Website](https://molt.bot) · [Docs](https://docs.molt.bot) · [Getting Started](https://docs.molt.bot/start/getting-started) · [Updating](https://docs.molt.bot/install/updating) · [Showcase](https://docs.molt.bot/start/showcase) · [FAQ](https://docs.molt.bot/start/faq) · [Wizard](https://docs.molt.bot/start/wizard) · [Nix](https://github.com/moltbot/nix-clawdbot) · [Docker](https://docs.molt.bot/install/docker) · [Discord](https://discord.gg/clawd) -Preferred setup: run the onboarding wizard (`clawdbot onboard`). It walks through gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**. +Preferred setup: run the onboarding wizard (`moltbot onboard`). It walks through gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**. Works with npm, pnpm, or bun. New install? Start here: [Getting started](https://docs.molt.bot/start/getting-started) @@ -78,7 +78,7 @@ Upgrading? [Updating guide](https://docs.molt.bot/install/updating) (and run `mo - **beta**: prerelease tags (`vYYYY.M.D-beta.N`), npm dist-tag `beta` (macOS app may be missing). - **dev**: moving head of `main`, npm dist-tag `dev` (when published). -Switch channels (git + npm): `clawdbot update --channel stable|beta|dev`. +Switch channels (git + npm): `moltbot update --channel stable|beta|dev`. Details: [Development channels](https://docs.molt.bot/install/development-channels). ## From source (development) @@ -86,8 +86,8 @@ Details: [Development channels](https://docs.molt.bot/install/development-channe Prefer `pnpm` for builds from source. Bun is optional for running TypeScript directly. ```bash -git clone https://github.com/clawdbot/clawdbot.git -cd clawdbot +git clone https://github.com/moltbot/moltbot.git +cd moltbot pnpm install pnpm ui:build # auto-installs UI deps on first run @@ -103,16 +103,16 @@ Note: `pnpm moltbot ...` runs TypeScript directly (via `tsx`). `pnpm build` prod ## Security defaults (DM access) -Clawdbot connects to real messaging surfaces. Treat inbound DMs as **untrusted input**. +Moltbot connects to real messaging surfaces. Treat inbound DMs as **untrusted input**. Full security guide: [Security](https://docs.molt.bot/gateway/security) Default behavior on Telegram/WhatsApp/Signal/iMessage/Microsoft Teams/Discord/Google Chat/Slack: - **DM pairing** (`dmPolicy="pairing"` / `channels.discord.dm.policy="pairing"` / `channels.slack.dm.policy="pairing"`): unknown senders receive a short pairing code and the bot does not process their message. -- Approve with: `clawdbot pairing approve ` (then the sender is added to a local allowlist store). +- Approve with: `moltbot pairing approve ` (then the sender is added to a local allowlist store). - Public inbound DMs require an explicit opt-in: set `dmPolicy="open"` and include `"*"` in the channel allowlist (`allowFrom` / `channels.discord.dm.allowFrom` / `channels.slack.dm.allowFrom`). -Run `clawdbot doctor` to surface risky/misconfigured DM policies. +Run `moltbot doctor` to surface risky/misconfigured DM policies. ## Highlights @@ -127,7 +127,7 @@ Run `clawdbot doctor` to surface risky/misconfigured DM policies. ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=clawdbot/clawdbot&type=date&legend=top-left)](https://www.star-history.com/#clawdbot/clawdbot&type=date&legend=top-left) +[![Star History Chart](https://api.star-history.com/svg?repos=moltbot/moltbot&type=date&legend=top-left)](https://www.star-history.com/#moltbot/moltbot&type=date&legend=top-left) ## Everything we built so far @@ -149,7 +149,7 @@ Run `clawdbot doctor` to surface risky/misconfigured DM policies. - [macOS node mode](https://docs.molt.bot/nodes): system.run/notify + canvas/camera exposure. ### Tools + automation -- [Browser control](https://docs.molt.bot/tools/browser): dedicated clawd Chrome/Chromium, snapshots, actions, uploads, profiles. +- [Browser control](https://docs.molt.bot/tools/browser): dedicated moltbot Chrome/Chromium, snapshots, actions, uploads, profiles. - [Canvas](https://docs.molt.bot/platforms/mac/canvas): [A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui) push/reset, eval, snapshot. - [Nodes](https://docs.molt.bot/nodes): camera snap/clip, screen record, [location.get](https://docs.molt.bot/nodes/location-command), notifications. - [Cron + wakeups](https://docs.molt.bot/automation/cron-jobs); [webhooks](https://docs.molt.bot/automation/webhook); [Gmail Pub/Sub](https://docs.molt.bot/automation/gmail-pubsub). @@ -180,7 +180,7 @@ WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBu └──────────────┬────────────────┘ │ ├─ Pi agent (RPC) - ├─ CLI (clawdbot …) + ├─ CLI (moltbot …) ├─ WebChat UI ├─ macOS app └─ iOS / Android nodes @@ -190,21 +190,21 @@ WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBu - **[Gateway WebSocket network](https://docs.molt.bot/concepts/architecture)** — single WS control plane for clients, tools, and events (plus ops: [Gateway runbook](https://docs.molt.bot/gateway)). - **[Tailscale exposure](https://docs.molt.bot/gateway/tailscale)** — Serve/Funnel for the Gateway dashboard + WS (remote access: [Remote](https://docs.molt.bot/gateway/remote)). -- **[Browser control](https://docs.molt.bot/tools/browser)** — clawd‑managed Chrome/Chromium with CDP control. +- **[Browser control](https://docs.molt.bot/tools/browser)** — moltbot‑managed Chrome/Chromium with CDP control. - **[Canvas + A2UI](https://docs.molt.bot/platforms/mac/canvas)** — agent‑driven visual workspace (A2UI host: [Canvas/A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui)). - **[Voice Wake](https://docs.molt.bot/nodes/voicewake) + [Talk Mode](https://docs.molt.bot/nodes/talk)** — always‑on speech and continuous conversation. - **[Nodes](https://docs.molt.bot/nodes)** — Canvas, camera snap/clip, screen record, `location.get`, notifications, plus macOS‑only `system.run`/`system.notify`. ## Tailscale access (Gateway dashboard) -Clawdbot can auto-configure Tailscale **Serve** (tailnet-only) or **Funnel** (public) while the Gateway stays bound to loopback. Configure `gateway.tailscale.mode`: +Moltbot can auto-configure Tailscale **Serve** (tailnet-only) or **Funnel** (public) while the Gateway stays bound to loopback. Configure `gateway.tailscale.mode`: - `off`: no Tailscale automation (default). - `serve`: tailnet-only HTTPS via `tailscale serve` (uses Tailscale identity headers by default). - `funnel`: public HTTPS via `tailscale funnel` (requires shared password auth). Notes: -- `gateway.bind` must stay `loopback` when Serve/Funnel is enabled (Clawdbot enforces this). +- `gateway.bind` must stay `loopback` when Serve/Funnel is enabled (Moltbot enforces this). - Serve can be forced to require a password by setting `gateway.auth.mode: "password"` or `gateway.auth.allowTailscale: false`. - Funnel refuses to start unless `gateway.auth.mode: "password"` is set. - Optional: `gateway.tailscale.resetOnExit` to undo Serve/Funnel on shutdown. @@ -270,7 +270,7 @@ The Gateway alone delivers a great experience. All apps are optional and add ext If you plan to build/run companion apps, follow the platform runbooks below. -### macOS (Clawdbot.app) (optional) +### macOS (Moltbot.app) (optional) - Menu bar control for the Gateway and health. - Voice Wake + push-to-talk overlay. @@ -283,7 +283,7 @@ Note: signed builds required for macOS permissions to stick across rebuilds (see - Pairs as a node via the Bridge. - Voice trigger forwarding + Canvas surface. -- Controlled via `clawdbot nodes …`. +- Controlled via `moltbot nodes …`. Runbook: [iOS connect](https://docs.molt.bot/platforms/ios). @@ -301,7 +301,7 @@ Runbook: [iOS connect](https://docs.molt.bot/platforms/ios). ## Configuration -Minimal `~/.clawdbot/clawdbot.json` (model + defaults): +Minimal `~/.clawdbot/moltbot.json` (model + defaults): ```json5 { @@ -323,7 +323,7 @@ Details: [Security guide](https://docs.molt.bot/gateway/security) · [Docker + s ### [WhatsApp](https://docs.molt.bot/channels/whatsapp) -- Link the device: `pnpm clawdbot channels login` (stores creds in `~/.clawdbot/credentials`). +- Link the device: `pnpm moltbot channels login` (stores creds in `~/.clawdbot/credentials`). - Allowlist who can talk to the assistant via `channels.whatsapp.allowFrom`. - If `channels.whatsapp.groups` is set, it becomes a group allowlist; include `"*"` to allow all. @@ -457,14 +457,15 @@ Use these when you’re past the onboarding flow and want the deeper reference. - [docs.molt.bot/gmail-pubsub](https://docs.molt.bot/automation/gmail-pubsub) -## Clawd +## Molty -Clawdbot was built for **Clawd**, a space lobster AI assistant. 🦞 +Moltbot was built for **Molty**, a space lobster AI assistant. 🦞 by Peter Steinberger and the community. - [clawd.me](https://clawd.me) - [soul.md](https://soul.md) - [steipete.me](https://steipete.me) +- [@moltbot](https://x.com/moltbot) ## Community