This file is for agentic coding tools. It's a map β read linked docs for depth.
Nexu is a desktop-first OpenClaw platform. Users create AI bots, connect them to Slack, and the local controller generates OpenClaw config for the embedded runtime.
- Monorepo: pnpm workspaces
apps/controllerβ Single-user local control plane for Nexu config, OpenClaw sync, and runtime orchestrationapps/desktopβ Electron desktop runtime shell and sidecar orchestratorapps/webβ React + Ant Design + Vitepackages/slimclawβ Repo-local Nexu-owned OpenClaw runtime contract, prepared runtime root, and staging/patch ownership for local dev and desktop packagingpackages/sharedβ Shared Zod schemaspackages/dev-utilsβ TS-first reusable utilities for local script tooling
Nexu is a desktop-first OpenClaw product. Users create AI bots via a dashboard and connect them to Slack. The system dynamically generates OpenClaw configuration and hot-loads it into the local runtime managed by the controller.
All commands use pnpm. Target a single app with pnpm --filter <package>.
pnpm install # Install
pnpm --filter @nexu/shared build # Build shared dist required by cold-start dev flows
pnpm dev start # Start the lightweight local stack: openclaw -> controller -> web -> desktop
pnpm dev start <service> # Start one local-dev service: desktop|openclaw|controller|web
pnpm dev restart # Restart the lightweight local stack
pnpm dev stop # Stop the lightweight local stack in reverse order
pnpm dev stop <service> # Stop one local-dev service
pnpm dev restart <service> # Restart one local-dev service
pnpm dev status <service> # Show status for one local-dev service
pnpm dev logs <service> # Show active-session log tail (max 200 lines) for one local-dev service
pnpm dev inspect screenshot # Capture the current desktop window screenshot (dev desktop only)
pnpm dev inspect eval "<expr>" # Evaluate a JS expression in the desktop renderer (dev desktop only)
pnpm dev inspect dom # Dump the current desktop renderer DOM summary (dev desktop only)
pnpm dev inspect logs # Show buffered desktop renderer console/error logs (dev desktop only)
pnpm dev:controller # Legacy controller-only direct dev entrypoint
pnpm dist:mac # Build signed macOS desktop distributables
pnpm dist:mac:arm64 # Build signed Apple Silicon macOS desktop distributables
pnpm dist:mac:x64 # Build signed Intel macOS desktop distributables
pnpm dist:mac:unsigned # Build unsigned macOS desktop distributables
pnpm dist:mac:unsigned:arm64 # Build unsigned Apple Silicon macOS desktop distributables
pnpm dist:mac:unsigned:x64 # Build unsigned Intel macOS desktop distributables
pnpm dist:win:local # Fast local Windows packaging check: reuse existing builds/runtime/sidecars when available and validate dir-only output
pnpm probe:slack prepare # Launch Chrome Canary with the dedicated Slack probe profile
pnpm probe:slack run # Run the local Slack reply smoke probe against an authenticated DM
pnpm --filter @nexu/web dev # Web only
pnpm build # Build all
pnpm check:esm-imports # Scan built dist for extensionless relative ESM specifiers
pnpm typecheck # Typecheck all
pnpm lint # Biome lint
pnpm format # Biome format
pnpm test # Vitest
pnpm generate-types # OpenAPI spec β frontend SDKAfter API route/schema changes: pnpm generate-types then pnpm typecheck.
This repo is desktop-first. Prefer the controller-first path and remove or ignore legacy API/gateway/container-era assets when encountered.
mainis the integration branch and should stay releasable.- Do feature work on short-lived branches named with a clear prefix such as
feat/...,fix/..., orchore/.... - Prefer merging the latest
maininto long-running feature branches instead of rewriting shared history once a PR is under review. - After a PR merges, sync local
main, then delete the merged feature branch locally and remotely when it is no longer needed.
- No co-author trailer. Never append
Co-Authored-By:lines to commit messages. - Conventional commit prefix. Use
chore:for changes that are invisible to end users (CI/CD, issue bots, tooling, config). These are excluded from release notes. Usefeat:/fix:/docs:etc. for user-visible changes. - Docs commit/PR prefix. Documentation-only changes must use
docs:for both commit titles and PR titles. - Non-user-facing commit/PR prefix. Any change that is not user-facing and should not appear in release notes must use
chore:for both commit titles and PR titles. - PR format. When creating a pull request, always follow
.github/pull_request_template.mdβ fill in What / Why / How / Affected areas / Checklist sections.
- Minimal cold-start setup on a fresh machine is:
pnpm install->pnpm --filter @nexu/shared build-> copytools/dev/.env.exampletotools/dev/.envonly if you need dev-only overrides. - Default daily flow is:
pnpm dev start->pnpm dev status <service>/pnpm dev logs <service>as needed ->pnpm dev stop. - Use
pnpm dev restartfor a clean full-stack recycle; usepnpm dev restart <service>only when you are intentionally touching one service. - Explicit single-service control remains available through
pnpm dev start <desktop|openclaw|controller|web>,pnpm dev stop <service>,pnpm dev restart <service>,pnpm dev status <service>, andpnpm dev logs <service>. - Desktop dev inspect is available through
pnpm dev inspect screenshot,pnpm dev inspect eval "<expr>",pnpm dev inspect dom, andpnpm dev inspect logsfor agent-friendly renderer inspection without exposing a public production API. pnpm devintentionally does not supportall; the full local stack order remainsopenclaw->controller->web->desktop.pnpm dev logs <service>is session-scoped, prints a fixed header, and tails at most the last 200 lines from the active service session.tools/dev/.env.exampleis the source-of-truth template for dev-only overrides. Copy it totools/dev/.envonly when you need to override ports, URLs, state paths, or the shared OpenClaw gateway token for local development.- Keep the detailed startup optimization rules, cache invalidation behavior, and troubleshooting notes in
specs/guides/desktop-runtime-guide.md; keep only the core workflow expectations here. - The repo also includes a local Slack reply smoke probe at
scripts/probe/slack-reply-probe.mjs(pnpm probe:slack prepare/pnpm probe:slack run) for verifying the end-to-end Slack DM reply path after local runtime or OpenClaw changes. - The Slack smoke probe is not zero-setup: install Chrome Canary first, then manually log into Slack in the opened Canary window before running
pnpm probe:slack run. - The desktop dev launcher is
tools/dev/; it is the unified source of truth for local dev orchestration, including platform-specific desktop launch preparation and runtime cleanup. pnpm devdesktop launch is owned bytools/dev, which starts the desktop Vite worker and Electron main process explicitly while routing platform-specific setup throughtools/dev/src/shared/platform/desktop-dev-platform.*. On macOS, the darwin helper patches the dev Electron binary'sLSUIElementand refreshes Launch Services metadata before launch.pnpm stopbehavior: sends SIGTERM first (triggersgracefulShutdowninside Electron β teardown launchd services β dispose orchestrator β kill orphans), waits up to 10 seconds for graceful exit, then SIGKILL as fallback. Also kills tsc watcher and web watcher background processes.- Treat
pnpm startas the canonical cold-start entrypoint for the full local desktop runtime. - The active desktop runtime path is controller-first: desktop launches
controller + web + openclawand no longer starts localapi,gateway, orpglitesidecars. - Desktop local runtime should not depend on PostgreSQL. In dev mode, all state (config, OpenClaw state, logs) lives under
.tmp/desktop/nexu-home/, fully isolated from the packaged app. Launchd plists go to.tmp/launchd/, runtime-ports.json also lives there. - In packaged mode, data is split across two directories (see table below). Launchd plists go to
~/Library/LaunchAgents/. - Local desktop runtime state is repo-scoped under
.tmp/desktop/in development.
| Directory | Purpose | Survives uninstall |
|---|---|---|
~/.nexu/ (NEXU_HOME) |
User config (config.json, cloud-profiles.json), compiled snapshots (compiled-openclaw.json), skill ledger (skill-ledger.json), skillhub cache, analytics state, logs |
Yes |
~/.nexu/runtime/nexu-runner.app/ |
APFS-cloned Electron binary + Frameworks for launchd services (avoids locking .app bundle during reinstall). Version-stamped; re-clones on app update. | Yes |
~/.nexu/runtime/controller-sidecar/ |
APFS-cloned controller sidecar (dist + node_modules). Same reason as runner. | Yes |
~/.nexu/runtime/openclaw-sidecar/ |
Extracted OpenClaw sidecar from .app payload. | Yes |
~/Library/Application Support/@nexu/desktop/ (Electron userData) |
OpenClaw runtime state: runtime/openclaw/state/agents/ (conversations), runtime/openclaw/state/extensions/ (channel state), runtime/openclaw/state/skills/, runtime/openclaw/state/openclaw.json, plus Electron internal data (Cache, IndexedDB, etc.) |
No (cleaned by uninstall tools) |
The split is intentional: NEXU_HOME holds lightweight user preferences and extracted runtime sidecars that should persist across reinstalls; Electron userData holds heavy runtime state tied to the app lifecycle. OPENCLAW_STATE_DIR is explicitly set by the desktop launcher to point to the userData path β do not rely on the controller's default fallback.
Launchd services reference ONLY paths under ~/.nexu/runtime/ (never inside the .app bundle), so the packaged app can be replaced by Finder drag-and-drop while services run in the background.
- For startup troubleshooting, use
pnpm logsto tail dev logs. - For proxy troubleshooting, inspect
desktop-diagnostics.jsonand checkproxy.source, redacted proxy env values, normalized bypass entries, andresolveProxy(...)results for controller/OpenClaw/external URLs. - To fully reset repo-local desktop runtime state, stop the stack and remove
.tmp/desktop/; this does not delete packaged app state. tmuxis no longer required for thepnpm devlocal-dev workflow; process state there is tracked by the platform-aware launcher entrypoints.- To fully reset local desktop + controller state, stop the stack, remove
.tmp/desktop/, then remove~/.nexu/and~/Library/Application Support/@nexu/desktop/. - Desktop already exposes an agent-friendly runtime observability surface; prefer subscribing/querying before adding temporary UI or ad hoc debug logging.
- For deeper desktop runtime inspection, use the existing event/query path (
onRuntimeEvent(...),runtime:query-events,queryRuntimeEvents(...)) instead of rebuilding one-off diagnostics. - Use
actionId,reasonCode, andcursor/nextCursoras the primary correlation and incremental-fetch primitives for desktop runtime debugging. - Desktop runtime guide:
specs/guides/desktop-runtime-guide.md. - The controller sidecar is packaged by
apps/desktop/scripts/prepare-controller-sidecar.mjswhich deep-copies all controllerdependenciesand their transitive deps into.dist-runtime/controller/node_modules/. Keep controller deps minimal to avoid bloating the desktop distributable. - SkillHub (catalog, install, uninstall) runs in the controller via HTTP β not in the Electron main process via IPC. The web app always uses HTTP SDK for skill operations.
- Desktop auto-update is channel-specific. Packaged builds should embed
NEXU_DESKTOP_UPDATE_CHANNEL(stable/beta/nightly) so the updater checks the matching feed, and update diagnostics should always log the effective feed URL plus remoteversion/releaseDatewhen available.
All quit/exit paths converge to runTeardownAndExit() in quit-handler.ts, which wraps cleanup in try/finally to guarantee app.exit(0) even if teardown throws.
Non-launchd mode (orchestrator): gracefulShutdown(reason) in apps/desktop/main/index.ts is the single entry point:
- before-quit (Cmd+Q / Dock Quit) β
gracefulShutdown("before-quit") - SIGTERM (external kill,
pnpm stop, system shutdown) βgracefulShutdown("signal:SIGTERM") - SIGINT (Ctrl+C) β
gracefulShutdown("signal:SIGINT")
Launchd mode (packaged / pnpm start): all exit triggers flow through runTeardownAndExit():
- Dev window close β
runTeardownAndExit("dev-close") - Dev Cmd+Q / app.quit() β
runTeardownAndExit("dev-before-quit") - Packaged "Quit Completely" dialog β
runTeardownAndExit("packaged-quit") - Packaged no-window exit (renderer crash) β
runTeardownAndExit("packaged-no-window") - Update install β
teardownLaunchdServices()+ensureNexuProcessesDead()+checkCriticalPathsLocked()viaupdate-manager.ts - SIGTERM / SIGINT β
gracefulShutdown()which also callsteardownLaunchdServices()internally
Both paths share teardownLaunchdServices() as the authoritative launchd service cleanup function. gracefulShutdown is idempotent (second call is a no-op) and has an 8-second hard timeout (process.exit(1) if teardown hangs).
On startup, bootstrapWithLaunchd() reads runtime-ports.json to decide whether to attach to already-running services or do a fresh cold start. The attach decision uses a multi-field identity check:
appVersionβ refuse attach if the app was updated (missing field = mismatch, conservative)userDataPathβ refuse attach across different Electron userData rootsbuildSourceβ refuse attach across packaged/dev/beta buildsopenclawStateDirβ refuse attach across different state directoriesNEXU_HOMEβ refuse attach across different home directories
If any identity field mismatches, stale services are auto-booted-out and a fresh cold start is performed (transparent to the user, ~2-3s slower).
update-manager.ts uses an evidence-based install decision:
teardownLaunchdServices()β bootout launchd services, kill orphansorchestrator.dispose()β stop managed child processesensureNexuProcessesDead()β two sweeps of SIGKILL (15s + 5s), using both launchd labels and pgrepcheckCriticalPathsLocked()βlsof +Dcheck on .app bundle, runner, and sidecar dirs- Decision: no critical locks β install; critical paths locked β skip this attempt (electron-updater retries next launch)
The desktop test suite includes real launchd integration tests that run on macOS CI runners:
tests/desktop/launchd-integration.test.tsβ reallaunchctlcommands, real processes (skipped on non-macOS)tests/desktop/entitlements-plist.test.tsβ V8 JIT entitlement regression guard (value-level assertions)tests/desktop/daemon-supervisor-restart.test.tsβ circuit breaker logic (MAX_CONSECUTIVE_RESTARTS=10)tests/desktop/launchd-bootstrap-lifecycle.test.tsβ stale session detection, web port retrytests/desktop/launchd-manager-bootout.test.tsβ bootoutService error tolerancescripts/launchd-lifecycle-e2e.shβ shell-based e2e: bootstrap β verify β teardown β orphan cleanup β re-bootstrapscripts/desktop-stop-smoke.shβ post-stop verification: no residual processes, free ports, no stale statetests/desktop/data-directory-runtime.test.tsβ verifies every plist env var value by calling realgeneratePlist()tests/desktop/dev-toolchain-invariants.test.tsβ guards against desktop dev-launch regressions (tools/dev platform helpers remain the single desktop launch decision point, launchd manifests keepELECTRON_RUN_AS_NODE, etc.)
- Debugging first principle: binary isolate, don't guess. For UI/runtime regressions, start with overall bisection and add tiny reversible
quick return/quick failprobes at key boundaries. Prefer changes that create obvious UI/log differences, narrow the fault domain quickly, and can be reverted immediately after verification. Do not start by rewriting route guards, state flows, or core logic based on intuition. - Never use
any. Useunknownwith narrowing orz.infer<typeof schema>. - No foreign keys in Drizzle schema β application-level joins only.
- Credentials (bot tokens, signing secrets) must never appear in logs or errors.
- Frontend must use generated SDK (
apps/web/lib/api/), never rawfetch. - All API routes must use
createRoute()+app.openapi()from@hono/zod-openapi. Never use plainapp.get()/app.post()etc β those bypass OpenAPI spec generation and the SDK won't have corresponding functions. - All request bodies, path params, query params, and responses must have Zod schemas. Shared schemas go in
packages/shared/src/schemas/, route-local param schemas (e.g.z.object({ id: z.string() })) can stay in the route file. - After adding or modifying API routes: run
pnpm generate-typesto regenerateopenapi.json->sdk.gen.ts->types.gen.ts, then update frontend call sites to use the new SDK functions. - Config generator output must match
specs/references/openclaw-config-schema.md. - Do not add dependencies without explicit approval.
- Do not modify OpenClaw source code.
- Never commit code changes until explicitly told to do so.
- Desktop packaged app: never use
npx,npm,pnpm, or any shell command that relies on the user's PATH. The packaged Electron app has no shell profile β resolve bin paths programmatically viarequire.resolve()and execute withprocess.execPath. The app must be fully self-contained. - Windows packaging split: use
pnpm dist:winfor the full installer/release path and keep it close to CI semantics. Usepnpm dist:win:localfor local Windows validation when you need fast iteration; it is intentionally dir-only and reuse-first, so it is not a substitute for the full release build. - Controller sidecar packaging: every dependency in
apps/controller/package.jsonis recursively deep-copied into the desktop distributable viaprepare-controller-sidecarβcopyRuntimeDependencyClosure. Never add heavy transitive-dependency packages (e.g.npm,yarn) to the controller. If the controller needs to shell out to a CLI tool, use PATH-basedexecFile("npm", ...)instead of bundling it as a dependency. Each MB added to controller deps adds ~1 MB to the final DMG/ZIP. - Native Node.js addons (e.g.
better-sqlite3) must live in the controller, NOT in the desktop Electron main process. Electron's built-in Node.js has a different ABI version (NODE_MODULE_VERSION) from system Node.js, requiringelectron-rebuildto recompile native modules. The controller runs as a regular Node.js process (ELECTRON_RUN_AS_NODE=1), so native addons work without recompilation. - OpenClaw provider/model registry is not hot-reload-safe. Any code path that mutates
models.providersinopenclaw.json(cloud login/logout, BYOK add/delete/bulk-update, OAuth connect/disconnect) MUST callopenclawProcess.restart(reason)aftersyncAll(). OpenClaw builds its registry once at boot; writing the file is not enough. In packaged desktop OpenClaw is supervised by launchd, soopenclawProcess.stop()/start()is a silent no-op βrestart()routes throughlaunchctl kickstart -kautomatically. Always smoke-test provider-lifecycle changes in the packaged build, not justpnpm dev. Seespecs/design-docs/2026-04-14-openclaw-registry-cache-invalidation.md.
- Request-level tracing must be created uniformly by middleware as the root trace.
- Logic with monitoring value must be split into named functions and annotated with
@Trace/@Span. - Do not introduce function-wrapper transitional APIs such as
runTrace/runSpan. - Iterate incrementally: add Trace/Span within established code patterns first, then refine based on metrics.
- Logger usage source of truth should follow the active package you are editing; prefer established nearby logger patterns in controller and desktop code.
pnpm typecheckβ after any TypeScript changespnpm lintβ after any code changespnpm generate-typesβ after API route/schema changespnpm testβ after logic changes
See ARCHITECTURE.md for the full bird's-eye view. Key points:
- Monorepo:
apps/controller(Hono),apps/web(React),apps/desktop(Electron),packages/shared(Zod schemas),nexu-skills/(skill repo) - Type safety: Zod -> OpenAPI -> generated frontend SDK. Never duplicate types.
- Config generator:
apps/controller/src/lib/openclaw-config-compiler.tsbuilds OpenClaw config from local controller state - Local runtime flow:
apps/controllerowns Nexu config/state, writes OpenClaw config/skills/templates, and manages the slimclaw-backed OpenClaw runtime contract directly; desktop wraps that controller-first stack with Electron + web sidecars - Key data flows: local config compilation, desktop runtime boot, channel sync, file-based skill catalog
- Biome: 2-space indent, double quotes, semicolons always
- Files:
kebab-case/ Types:PascalCase/ Variables:camelCase - Zod schemas:
camelCase+Schemasuffix - DB tables:
snake_casein Drizzle - Public IDs: cuid2 (
@paralleldrive/cuid2), never exposepk - Errors: throw
HTTPExceptionwith status + contextual message - Logging: structured (pino or console JSON), never log credentials
| Topic | Location |
|---|---|
| Architecture & data flows | ARCHITECTURE.md |
| System design | specs/designs/openclaw-multi-tenant.md |
| OpenClaw internals | specs/designs/openclaw-architecture-internals.md |
| OpenClaw error handling & compaction | specs/references/openclaw-error-handling-internals.md |
| OpenClaw registry cache invalidation (restart-on-provider-change) | specs/design-docs/2026-04-14-openclaw-registry-cache-invalidation.md |
| Engineering principles | specs/design-docs/core-beliefs.md |
| Config schema & pitfalls | specs/references/openclaw-config-schema.md |
| API coding patterns | specs/references/api-patterns.md |
| Workspace templates | specs/guides/workspace-templates.md |
| Local Slack smoke probe | scripts/probe/README.md, scripts/probe/slack-reply-probe.mjs |
| Local dev CLI guidance | tools/dev/AGENTS.md |
| Frontend conventions | specs/FRONTEND.md |
| Desktop runtime guide | specs/guides/desktop-runtime-guide.md |
| Desktop update testing guide | specs/guides/desktop-update-testing.md |
| Security posture | specs/SECURITY.md |
| Reliability | specs/RELIABILITY.md |
| Product model | specs/PRODUCT_SENSE.md |
| Quality signals | specs/QUALITY_SCORE.md |
| Product specs | specs/product-specs/ |
| Execution plans | specs/exec-plans/ |
| Documentation sync | skills/localdev/sync-specs/SKILL.md |
| Nano Banana (image gen) | skills/nexubot/nano-banana/SKILL.md |
| Skill repo & catalog | nexu-skills/, apps/controller/src/services/skillhub/ |
| File-based skills design | specs/plans/2026-03-15-skill-repo-design.md |
| Feishu channel setup | apps/web/src/components/channel-setup/feishu-setup-view.tsx |
| Desktop shutdown & lifecycle | apps/desktop/main/index.ts (gracefulShutdown), apps/desktop/main/services/quit-handler.ts (runTeardownAndExit) |
| Launchd service management | apps/desktop/main/services/launchd-manager.ts, apps/desktop/main/services/launchd-bootstrap.ts |
| External runner extraction | apps/desktop/main/services/launchd-bootstrap.ts (ensureExternalNodeRunner, resolveLaunchdPaths) |
| Desktop auto-updater | apps/desktop/main/updater/update-manager.ts (checkCriticalPathsLocked, ensureNexuProcessesDead) |
| Entitlements (V8 JIT) | apps/desktop/build/entitlements.mac.plist, apps/desktop/build/entitlements.mac.inherit.plist |
| Dev launch scripts | scripts/dev-launchd.sh, tools/dev/src/services/desktop.ts, tools/dev/src/shared/platform/desktop-dev-platform.* |
| Launchd stability tests | tests/desktop/launchd-integration.test.ts, scripts/launchd-lifecycle-e2e.sh |
| Entitlements regression tests | tests/desktop/entitlements-plist.test.ts |
| Stop smoke test | scripts/desktop-stop-smoke.sh |
After significant code changes, verify documentation is current.
git diff --name-only $(git merge-base HEAD origin/main)...HEAD| Changed area | Affected docs |
|---|---|
apps/web/src/pages/ or routing |
specs/FRONTEND.md |
apps/controller/src/routes/ |
specs/references/api-patterns.md, specs/product-specs/*.md |
apps/controller/src/runtime/ |
ARCHITECTURE.md, specs/RELIABILITY.md |
apps/web/src/components/channel-setup/ |
specs/FRONTEND.md |
nexu-skills/ |
ARCHITECTURE.md (monorepo layout) |
packages/shared/src/schemas/ |
ARCHITECTURE.md (type safety) |
package.json scripts |
AGENTS.md Commands section |
| New/moved doc files | AGENTS.md Where to look |
AGENTS.mdWhere to look table β all paths validspecs/DESIGN.md<->specs/design-specs/+specs/designs/(indexed)specs/product-specs/index.md<-> actual spec filesspecs/FRONTEND.mdPages <->apps/web/src/app.tsxroutes
- Regenerate
specs/generated/db-schema.mdfully from schema source - Preserve original language (English/Chinese)
- Do not auto-commit; present changes for review
Full reference: skills/localdev/sync-specs/SKILL.md
Nexu work must be synced into the team knowledge repo at:
agent-digital-cowork/clone/
When producing artifacts in this repo, sync them to the cross-project repo using this mapping:
| Artifact type | Target in agent-digital-cowork/clone/ |
|---|---|
| Design plans / architecture proposals | design/ |
| Debug summaries / incident analysis | debug/ |
| Ideas / product notes | ideas/ |
| Stable facts / decisions / runbooks | knowledge/ |
| Open blockers / follow-ups | blockers/ |
Project memory directory:
/Users/alche/.claude/projects/-Users-alche-Documents-digit-sutando-nexu/memory/
Keep these memory notes up to date:
- Cross-project sync rules memory (source of truth for sync expectations)
- Skills hot-reload findings memory (
skills-hotreload.md) - DB/dev environment quick-reference memory
For OpenClaw skills behavior and troubleshooting, maintain and consult:
skills-hotreload.mdin the Nexu memory directory above.
This note should track:
- End-to-end pipeline status (
Controller store -> compiler -> runtime writers -> OpenClaw) - Why
openclaw-managedskills may be missing from session snapshots - Watcher/snapshot refresh caveats and validation steps
- Controller env path:
apps/controller/.env - Fresh local-dev cold start:
pnpm install->pnpm --filter @nexu/shared build-> optionalcopy tools/dev/.env.example tools/dev/.env(Windows) orcp tools/dev/.env.example tools/dev/.env(POSIX) ->pnpm dev start - Daily local-dev flow:
pnpm dev start->pnpm dev logs <service>/pnpm dev status <service>when needed ->pnpm dev restartfor a clean recycle ->pnpm dev stop - Desktop inspect quick checks:
pnpm dev inspect screenshot,pnpm dev inspect eval "document.title",pnpm dev inspect dom --max-html-length 1200,pnpm dev inspect logs --limit 20 - Desktop proxy env vars:
HTTP_PROXY,HTTPS_PROXY,ALL_PROXY,NO_PROXY(desktop normalizes mixed-case inputs, always mergeslocalhost,127.0.0.1,::1intoNO_PROXY, and propagates uppercase values to child processes) - OpenClaw managed skills dir (expected default):
~/.openclaw/skills/ - Slack smoke probe setup: install Chrome Canary, set
PROBE_SLACK_URL, runpnpm probe:slack prepare, then manually log into Slack in Canary beforepnpm probe:slack run - the slimclaw-managed prepared OpenClaw runtime is installed implicitly by
pnpm install; local development should normally not use a globalopenclawCLI - Full-stack startup order is
openclaw->controller->web->desktop; shutdown order is the reverse - Prefer
./openclaw-wrapperover globalopenclawin local development; it resolves the prepared runtime entry through slimclaw and executes that local OpenClaw CLI entry - When OpenClaw is started manually, set
RUNTIME_MANAGE_OPENCLAW_PROCESS=falsefor@nexu/controllerto avoid launching a second OpenClaw process - If behavior differs, verify effective
OPENCLAW_STATE_DIR/OPENCLAW_CONFIG_PATHused by the running controller process.