Skip to content

Commit 158a038

Browse files
viktormarinhoclaudevibegui
authored
feat(local-mode): local-first developer experience with zero-ceremony setup (#2544)
* feat(local-mode): local-first developer experience with zero-ceremony setup Running `bunx @decocms/mesh` (or `bun run dev`) now works with no config: - Prompts for a data directory on first run (defaults to ~/deco/) - Auto-generates and persists BETTER_AUTH_SECRET + ENCRYPTION_KEY - Seeds an admin user from the OS username (e.g. viktor@localhost.mesh) and a default "Viktor Local" org on fresh databases - Auto-logs in the browser without a login form (MESH_LOCAL_MODE=true) - Skips org selection and redirects straight to the first org - Enables local filesystem object storage (/mcp/dev-assets) for assets - Adds --home and --no-local-mode CLI flags; renames old dev to dev:saas New files: - apps/mesh/scripts/dev.ts — mirrors CLI setup for `bun run dev` - apps/mesh/src/auth/local-mode.ts — seedLocalMode, getLocalAdminUser - POST /api/auth/custom/local-session — auto-sign-in endpoint (local only) Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * fix(local-mode): security hardening, UX improvements, and reliability fixes - Generate random ENCRYPTION_KEY instead of empty string (cli.ts, dev.ts) - Restrict secrets.json file permissions to 0o600 (cli.ts, dev.ts) - Add non-TTY guard to prevent prompt() hang in Docker/CI (cli.ts) - Normalize org slug from OS username to valid [a-z0-9-] (local-mode.ts) - Retry auto-login on 5xx with exponential backoff (login.tsx) - Always start with fresh chat on page load (use-task-manager.ts) - Autofocus chat input on load (tiptap/input.tsx) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(chat): stabilize fresh-chat initializer across remounts Use module-level UUID constant so the fresh-chat behavior is stable within a session while still generating a new chat on each page load. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve CI test and e2e failures - Fix TS2345 errors: add non-null assertions for Hono route params (threadId, toolName) that are always present in route patterns - Respect MESH_LOCAL_MODE env var in dev.ts instead of hardcoding true - Set MESH_LOCAL_MODE=false in e2e workflow so signup form is shown Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove unused getMeshHome export flagged by knip Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(local-mode): trusted origins, internal URLs, empty state, and title - Add localhost:PORT to Better Auth trusted origins for proxy hostnames - Add getInternalUrl() for server-to-server connections (mcp/self, dev-assets) - Use BASE_URL in startup logs and CLI banner - Simplify no-LLM empty state (remove tasks panel when no model connected) - Change HTML title to "Deco Studio" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(studio): add @decocms/studio wrapper package for npm alias Transitional package so `bunx @decocms/studio` works while the canonical package remains @decocms/mesh. Includes local publish script and CI workflow that auto-publishes with the same version as mesh. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(oauth): use localhost origin for redirect URIs behind proxy External OAuth servers (e.g. OpenRouter MCP) reject .localhost subdomains as redirect URIs. When running behind a proxy (tokyo.localhost → localhost:3000), the OAuth flow now uses localhost:PORT for the redirect URI. - Add setOAuthRedirectOrigin() to mesh-sdk for configuring redirect origin - Expose internalUrl in /api/config public endpoint - Set redirect origin from config on app init in ThemeProvider - Accept cross-origin postMessage between .localhost variants in local dev Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: gate localhost trusted origin to local mode, restore package.json on failure - Only add localhost:PORT to Better Auth trusted origins in local mode - Add EXIT trap to studio publish script to restore package.json on failure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(local-mode): security hardening and reliability improvements - Restrict /local-session endpoint to loopback requests only (127.0.0.1/::1) to prevent LAN access when server is bound to 0.0.0.0 - Fix tilde expansion in dev.ts and cli.ts: ~/dir was resolving to /dir instead of $HOME/dir (slice(1) → slice(2) to skip the ~/) - Gate local-session behind seed completion to prevent 500s on race - Respect user-provided DATABASE_URL in CLI instead of always overwriting - Add security comment documenting that loopback check (not password) is the security boundary for local-mode auto-login Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(tests): update OAuth callback tests for cross-origin postMessage Update test mocks to include hostname/port/protocol on window.location and adjust assertion for postMessage target origin ("*" in local dev). Also add DEBT.md tracking technical debt for local-first DX work. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(local-mode): use socket-level IP check and fix tilde expansion - Replace spoofable X-Forwarded-For header check with Bun's getConnInfo() (socket-level requestIP) for the local-session loopback guard - Pass Bun server as env to Hono so getConnInfo can access requestIP - Fix tilde expansion: only expand bare ~ and ~/path, not ~user inputs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(studio): rename package to decocms with deco CLI command Publish as `decocms` on npm so users can run `npx decocms` or `npm i -g decocms && deco`. Renamed bin from studio.js to deco.js and updated CI workflow, publish script, README, and DEBT.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(studio): update decocms README and package metadata for npm Replace internal wrapper docs with a proper product README showing features, quick start, and MCP client config. Add descriptive keywords and update main README to reference `npx decocms`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(config): only expose internalUrl in local mode Production deployments don't need the localhost URL in /api/config. Gate it behind isLocalMode() so it's only sent when running locally behind a proxy (e.g. tokyo.localhost). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove unused getBaseUrl import in org.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs(studio): align README with decocms.com/studio positioning Rewrite to lead with agents and tools, not just MCP plumbing. Matches the landing page: hire agents, connect 50+ integrations, track costs, run locally or scale to teams. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: ignore decocms runtime dependency in knip config @decocms/mesh is resolved via require() in bin/deco.js at runtime, which knip can't trace. Add ignoreDependencies for packages/studio. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: rebrand CLI and docs from @decocms/mesh to npx decocms / deco Update all user-facing references: CLI help text, release workflow, docs quickstart pages (en + pt-br), dev script comment, and OAuth client name to use "Deco Studio" / "npx decocms" / "deco". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(studio): use bun runtime in deco CLI wrapper The built CLI uses Bun APIs (Bun.file, Bun.serve), so the wrapper must run it with bun, not node. Auto-detects bun availability. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: rebrand CLI banners from MCP Mesh to Deco Studio Update startup banners, prompts, and subtitle in cli.ts and dev.ts to show "Deco Studio" instead of "MCP Mesh". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(studio): require bun runtime and update instructions to bunx The built CLI uses Bun APIs so bun is a hard requirement. Show a clear error with install instructions if bun is missing. Update all docs, CLI help, README, and release workflow from npx to bunx. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(docs): align section headings with bunx commands Update "(npx)" headings to "(bunx)" in quickstart docs and change "Install via npm" to "Install via Bun" in release workflow to match the actual bunx decocms commands. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(dev): respect existing DATABASE_URL and fix path resolution - Use nullish coalescing for DATABASE_URL to not override user-provided values (e.g., PostgreSQL connection strings), matching cli.ts behavior - Replace brittle string replace with join(import.meta.dir, "..") for cross-platform path handling Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: harden local-first DX — random password, seed gate, postMessage, prod guard - Extract shared bootstrap module (scripts/bootstrap.ts) from cli.ts and dev.ts - Generate per-install LOCAL_ADMIN_PASSWORD in secrets.json instead of hardcoded value - Fix seed gate: resolve immediately when not in local mode (prevents hang) - Add .catch() to dynamic import chain in index.ts - Tighten postMessage target from "*" to getOAuthRedirectOrigin() - Refuse local mode in production (NODE_ENV=production) unless explicitly overridden Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: security hardening, OAuth flow extraction, and deferred debt tracking - Remove hardcoded "admin@mesh" fallback password — throw on missing secrets.json - Fix seed gate hang when local-mode module import fails (markSeedComplete in catch) - Fix directory traversal in sanitizeKey() — use path.resolve() + containment check - Unify DEV_ASSETS_BASE_DIR to use MESH_HOME in both dev-assets files - Use crypto.timingSafeEqual() for HMAC signature verification - Remove production console.log leaking taskId on every stream resume - Cherry-pick OAuth flow extraction from feat/oauth-flow-extraction - Update DEBT.md with deferred review issues from merged PRs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: explicit MESH_ALLOW_LOCAL_PROD check and postMessage target for proxy setups - Use `!== "true"` instead of truthiness check for MESH_ALLOW_LOCAL_PROD to prevent unintended bypass with values like "false" - Use "*" as postMessage target in local dev so OAuth callbacks work behind proxies where opener origin differs from redirect origin Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(local-mode): probe ~/deco for secrets.json when MESH_HOME is unset getLocalAdminPassword() now checks the default ~/deco directory as a fallback when MESH_HOME is not set. This fixes auto-login when running via `bun run dev:server` directly (which doesn't set MESH_HOME), while still refusing to use a hardcoded credential. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(dev): check non-interactive before defaultPath existence in resolveMeshHome Swap the order so CI/non-TTY environments always use the CI-safe .mesh-dev path, even when ~/deco exists from a prior local run. Previously, existsSync(~/deco) was checked first, causing CI on shared runners to use the developer's real data directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: rename system prompt identity from "decopilot" to "Deco Pilot" The LLM was self-identifying as "decoCopilot" which reads as "decoco" in Portuguese. Use properly spaced "Deco Pilot" and "Deco Studio". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Guilherme Rodrigues <gui@deco.cx>
1 parent 7ed212a commit 158a038

49 files changed

Lines changed: 1687 additions & 308 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/e2e.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ jobs:
3636

3737
- name: Run e2e tests
3838
run: bun run test:e2e
39+
env:
40+
MESH_LOCAL_MODE: "false"
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: Publish decocms
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- "apps/mesh/package.json"
8+
- "packages/studio/**"
9+
workflow_dispatch:
10+
11+
jobs:
12+
publish:
13+
name: Publish to npm
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: read
17+
id-token: write
18+
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@v4
22+
23+
- name: Setup Node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: "24"
27+
registry-url: "https://registry.npmjs.org"
28+
29+
- name: Read mesh version
30+
id: mesh-version
31+
run: |
32+
VERSION=$(node -e "console.log(require('./apps/mesh/package.json').version)")
33+
echo "version=$VERSION" >> $GITHUB_OUTPUT
34+
35+
if [[ "$VERSION" == *-* ]]; then
36+
echo "npm-tag=next" >> $GITHUB_OUTPUT
37+
else
38+
echo "npm-tag=latest" >> $GITHUB_OUTPUT
39+
fi
40+
41+
echo "📦 @decocms/mesh version: $VERSION"
42+
43+
- name: Check if already published
44+
id: check
45+
run: |
46+
VERSION=${{ steps.mesh-version.outputs.version }}
47+
if npm view "decocms@$VERSION" version >/dev/null 2>&1; then
48+
echo "skip=true" >> $GITHUB_OUTPUT
49+
echo "⏭️ decocms@$VERSION already published"
50+
else
51+
echo "skip=false" >> $GITHUB_OUTPUT
52+
echo "✅ Will publish decocms@$VERSION"
53+
fi
54+
55+
- name: Patch studio package.json
56+
if: steps.check.outputs.skip == 'false'
57+
run: |
58+
VERSION=${{ steps.mesh-version.outputs.version }}
59+
cd packages/studio
60+
node -e "
61+
const pkg = require('./package.json');
62+
pkg.version = '$VERSION';
63+
pkg.dependencies['@decocms/mesh'] = '$VERSION';
64+
require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
65+
"
66+
echo "✅ Patched to v$VERSION"
67+
cat package.json
68+
69+
- name: Publish to npm
70+
if: steps.check.outputs.skip == 'false'
71+
run: npm publish --access public --tag ${{ steps.mesh-version.outputs.npm-tag }} --provenance
72+
working-directory: packages/studio
73+
env:
74+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

.github/workflows/release.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,15 @@ jobs:
143143
uses: softprops/action-gh-release@v2
144144
with:
145145
tag_name: v${{ steps.version-check.outputs.current-version }}
146-
name: "@decocms/mesh v${{ steps.version-check.outputs.current-version }}"
146+
name: "Deco Studio v${{ steps.version-check.outputs.current-version }}"
147147
body: |
148-
## MCP Mesh v${{ steps.version-check.outputs.current-version }}
148+
## Deco Studio v${{ steps.version-check.outputs.current-version }}
149149
150-
Self-hostable Virtual MCP for managing AI connections and tools.
150+
Open-source control plane for your AI agents.
151151
152-
### Install via npm
152+
### Install via Bun
153153
```bash
154-
bunx @decocms/mesh@${{ steps.version-check.outputs.current-version }}
154+
bunx decocms@${{ steps.version-check.outputs.current-version }}
155155
```
156156
157157
### Install via Docker
@@ -175,11 +175,11 @@ jobs:
175175
run: |
176176
echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
177177
echo "" >> $GITHUB_STEP_SUMMARY
178-
echo "🎉 **@decocms/mesh v${{ steps.version-check.outputs.current-version }} Released**" >> $GITHUB_STEP_SUMMARY
178+
echo "🎉 **Deco Studio v${{ steps.version-check.outputs.current-version }} Released**" >> $GITHUB_STEP_SUMMARY
179179
echo "" >> $GITHUB_STEP_SUMMARY
180180
echo "### npm" >> $GITHUB_STEP_SUMMARY
181181
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
182-
echo "bunx @decocms/mesh@${{ steps.version-check.outputs.current-version }}" >> $GITHUB_STEP_SUMMARY
182+
echo "bunx decocms@${{ steps.version-check.outputs.current-version }}" >> $GITHUB_STEP_SUMMARY
183183
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
184184
echo "" >> $GITHUB_STEP_SUMMARY
185185
echo "### Docker" >> $GITHUB_STEP_SUMMARY

DEBT.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Technical Debt — Local-First DX
2+
3+
Items to address after the core local-first DX lands.
4+
5+
## NATS onboarding in local mode
6+
7+
The agentic onboarding flow should detect whether NATS is available and, if not,
8+
offer to install and start it (via `brew install nats-server` or Docker). NATS
9+
enables stream recovery (switch tabs / refresh → restore in-progress AI
10+
responses). Without it, `NoOpStreamBuffer` is used and late-join replay is
11+
disabled.
12+
13+
- Relevant code: `apps/mesh/src/api/app.ts` (NATS_URL detection),
14+
`apps/mesh/src/api/routes/decopilot/stream-buffer.ts` (NoOpStreamBuffer fallback)
15+
16+
## decocms package rename
17+
18+
`packages/studio/` publishes as `decocms` on npm (`bunx decocms` / `deco` CLI).
19+
It's a thin wrapper that depends on `@decocms/mesh`. Eventually the source
20+
should move to `decocms` as the canonical package and `@decocms/mesh` becomes
21+
the wrapper (or is deprecated).
22+
23+
## Playwright e2e excluded from `bun test`
24+
25+
The Playwright e2e test (`apps/mesh/src/**/*.e2e.test.ts`) is picked up by
26+
`bun test` and fails because `@playwright/test` conflicts with bun's test
27+
runner. Should be excluded via bun test config or file naming convention.
28+
29+
---
30+
31+
## Items from PR review (deferred — belong to already-merged PRs)
32+
33+
### PROJECT_PINNED_VIEWS_UPDATE missing org ownership check (PR #2567)
34+
35+
The tool calls `requireAuth(ctx)` + `ctx.access.check()` but does not call
36+
`requireOrganization(ctx)` or validate `project.organizationId !== organization.id`.
37+
An authenticated user from org A could update pinned views on org B's project.
38+
39+
- File: `apps/mesh/src/tools/projects/pinned-views-update.ts`
40+
41+
### N+1 query in PROJECT_CONNECTION_LIST (PR #2567)
42+
43+
Each connection is fetched individually via `findById` inside `Promise.all(map(...))`.
44+
No `findByIds` batch method exists on `ConnectionStorage`.
45+
46+
- Fix: Add `findByIds(ids: string[])` using `WHERE id IN (...)`
47+
- File: `apps/mesh/src/tools/projects/connection-list.ts:52-56`
48+
49+
### COLLECTION_CONNECTIONS_GET write side-effect in readOnly tool (PR #2567)
50+
51+
The GET handler (annotated `readOnlyHint: true`) backfills missing tools via
52+
`fetchToolsFromMCP` with a 2s timeout and calls `ctx.storage.connections.update()`.
53+
A read operation should not have write side-effects.
54+
55+
- Fix: Move backfill to an explicit tool or post-OAuth event trigger
56+
- File: `apps/mesh/src/tools/connection/get.ts`
57+
58+
### TaskStreamManager useSyncExternalStore misuse (PR #2563)
59+
60+
`subscribe` is a new function reference every render, causing interval leaks.
61+
`useSyncExternalStore` is used as a lifecycle hook rather than for external store
62+
subscription, which is semantically incorrect under React 19.
63+
64+
- Fix: Stabilize `subscribe` via `useRef` or restructure
65+
- File: `apps/mesh/src/web/components/chat/context.tsx`
66+
67+
### ResizeObserver memory leak in monitoring dashboard (PR #2554)
68+
69+
Inline ref callback creates a new `ResizeObserver` on every render without
70+
disconnecting the old one. Use React 19 ref callback cleanup:
71+
`return () => observer.disconnect();`
72+
73+
- File: monitoring dashboard component (TBD exact location)
74+
75+
### Monitoring fetches 2000 raw rows to browser (PR #2554)
76+
77+
Dashboard fetches 2000 full `MonitoringLog` rows (including `input`/`output` JSON)
78+
to the browser for client-side bucketing. Only 5 scalar fields are needed.
79+
80+
- Fix: Push aggregation server-side or add field projection to `MONITORING_LOGS_LIST`
81+
- File: `apps/mesh/src/web/components/monitoring/hooks.ts:37`
82+
83+
### ondownloadfile handler should validate URI scheme (PR #2571)
84+
85+
`window.open(item.uri, "_blank")` accepts arbitrary URIs including `javascript:`.
86+
Validate scheme is `http:` or `https:` before calling `window.open`.
87+
88+
- File: `apps/mesh/src/mcp-apps/use-app-bridge.ts`
89+
90+
### Thread/Task naming asymmetry
91+
92+
UI uses "task" but backend protocol uses "thread" everywhere (`COLLECTION_THREADS_LIST`,
93+
`thread_id`, etc.). Either rename fully or document the mapping explicitly.
94+
95+
### Large component files should be split
96+
97+
- `apps/mesh/src/web/components/settings-modal/pages/org-billing.tsx` (1,732 lines)
98+
- `apps/mesh/src/web/routes/orgs/monitoring.tsx` (1,510 lines)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ bun run dev
6161

6262
→ runs at [http://localhost:3000](http://localhost:3000) (client) + API server
6363

64-
Or use `npx @decocms/mesh` to instantly get a mesh running.
64+
Or use `bunx decocms` to instantly get a mesh running.
6565

6666
---
6767

apps/docs/client/src/content/draft/en/mcp-mesh/self-hosting/quickstart.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import Callout from "../../../../../components/ui/Callout.astro";
1010

1111
Pick one:
1212

13-
### Option A: one-command local setup (npx)
13+
### Option A: one-command local setup (bunx)
1414

1515
```bash
16-
npx @decocms/mesh
16+
bunx decocms
1717
```
1818

1919
### Option B: local setup with Docker Compose

apps/docs/client/src/content/draft/pt-br/introduction.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ Se você conheceu a gente antes, como **deco.cx**, e está buscando **headless C
2525

2626
Escolha uma opção:
2727

28-
### Opção A: setup local com um comando (npx)
28+
### Opção A: setup local com um comando (bunx)
2929

3030
```bash
31-
npx @decocms/mesh
31+
bunx decocms
3232
```
3333

3434
### Opção B: setup local com Docker Compose

apps/docs/client/src/content/draft/pt-br/mcp-mesh/quickstart.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import Callout from "../../../../components/ui/Callout.astro";
1010

1111
Escolha uma opção:
1212

13-
### Opção A: setup local com um comando (npx)
13+
### Opção A: setup local com um comando (bunx)
1414

1515
```bash
16-
npx @decocms/mesh
16+
bunx decocms
1717
```
1818

1919
### Opção B: setup local com Docker Compose

apps/docs/client/src/content/latest/en/introduction.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ If you know us from before (as **deco.cx**) and you’re looking for **headless
2626

2727
Choose one:
2828

29-
### Option A: one-command local setup (npx)
29+
### Option A: one-command local setup (bunx)
3030

3131
```bash
32-
npx @decocms/mesh
32+
bunx decocms
3333
```
3434

3535
### Option B: local setup with Docker Compose

apps/docs/client/src/content/latest/en/mcp-mesh/quickstart.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import Callout from "../../../../components/ui/Callout.astro";
1010

1111
Pick one:
1212

13-
### Option A: one-command local setup (npx)
13+
### Option A: one-command local setup (bunx)
1414

1515
```bash
16-
npx @decocms/mesh
16+
bunx decocms
1717
```
1818

1919
### Option B: local setup with Docker Compose

0 commit comments

Comments
 (0)