diff --git a/.env.example b/.env.example index 709663e2ff..1662417c41 100644 --- a/.env.example +++ b/.env.example @@ -11,17 +11,21 @@ DATABASE_URL=postgres://multica:multica@localhost:5432/multica?sslmode=disable # DATABASE_MIN_CONNS=5 # Server -# APP_ENV gates dev-only auth shortcuts (primarily the 888888 master code). -# - Docker self-host: docker-compose.selfhost.yml already pins APP_ENV to -# "production" by default, so 888888 is DISABLED — a public instance can't -# be logged into with any email + 888888. -# - Local dev (make dev): leave APP_ENV unset so 888888 works out of the box. -# - Docker self-host on a private network you fully control, or evaluation -# without Resend: set APP_ENV=development to re-enable 888888. Do NOT -# enable on a publicly reachable instance. +# APP_ENV gates production safety checks. Docker self-host pins APP_ENV to +# "production" by default. Local dev can leave it unset. # See SELF_HOSTING.md for the full login setup. APP_ENV= +# Optional local/testing shortcut. Empty by default, so there is no fixed +# verification code. Without RESEND_API_KEY, generated codes print to stdout. +# If you need deterministic local automation, set a 6-digit value such as +# 888888 and keep APP_ENV non-production. This is ignored when APP_ENV=production. +MULTICA_DEV_VERIFICATION_CODE= PORT=8080 +# Prometheus metrics are disabled by default. When enabled, bind to loopback +# unless you protect the listener with private networking, allowlists, or +# proxy auth. Do not expose this endpoint through the public app/API ingress. +# HTTP request metrics start accumulating only when this listener is enabled. +# METRICS_ADDR=127.0.0.1:9090 JWT_SECRET=change-me-in-production MULTICA_SERVER_URL=ws://localhost:8080/ws MULTICA_APP_URL=http://localhost:3000 @@ -45,8 +49,7 @@ MULTICA_BACKEND_IMAGE=ghcr.io/multica-ai/multica-backend MULTICA_WEB_IMAGE=ghcr.io/multica-ai/multica-web # Email (Resend) -# For local/dev use, leave RESEND_API_KEY empty — codes print to stdout, and -# master code 888888 works (only when APP_ENV != "production"; see above). +# For local/dev use, leave RESEND_API_KEY empty — generated codes print to stdout. # For production, set your Resend API key and change RESEND_FROM_EMAIL to a domain verified in your Resend account. RESEND_API_KEY= RESEND_FROM_EMAIL=noreply@multica.ai diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8eaa5959b5..b75c47bfbc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,6 +56,12 @@ jobs: release: needs: verify + # Only run on the canonical upstream repo. Forks don't have the + # HOMEBREW_TAP_GITHUB_TOKEN secret and should not be publishing to + # `multica-ai/homebrew-tap` anyway. Without this guard, every fork's + # tag push fails this job (401 against the upstream tap), which makes + # downstream CI go red without affecting the actual artifact pipeline. + if: github.repository_owner == 'multica-ai' runs-on: ubuntu-latest steps: - name: Checkout diff --git a/CLAUDE.md b/CLAUDE.md index 1e5fc6218d..1718a86701 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -136,6 +136,17 @@ make start-worktree # Start using .env.worktree - Avoid broad refactors unless required by the task. - New global (pre-workspace) routes MUST use a single word (`/login`, `/inbox`) or a `/{noun}/{verb}` pair (`/workspaces/new`). NEVER add hyphenated word-group root routes (`/new-workspace`, `/create-team`) — they collide with common user workspace names and force endless reserved-slug audits. Reserving the noun (`workspaces`) automatically protects the entire `/workspaces/*` subtree. +### Backend Handler UUID Parsing Convention + +Every Go handler in `server/internal/handler/` follows these rules. The convention exists because `util.ParseUUID` used to silently return a zero UUID on invalid input, which caused #1661 — a `DELETE` returning 204 success while the SQL `DELETE` matched zero rows. + +- **Resource path params that accept either a UUID or a human-readable identifier** (e.g. `chi.URLParam(r, "id")` for an issue, which accepts both `MUL-123` and a UUID) MUST be resolved through the dedicated loader (`loadIssueForUser` / `loadSkillForUser` / `loadAgentForUser` / `requireDaemonRuntimeAccess`). After resolution, all subsequent DB calls — especially `Queries.Delete*` / `Queries.Update*` — MUST use `entity.ID` from the resolved object. Never round-trip the raw URL string through `parseUUID` for a write query. +- **Pure-UUID inputs from request boundaries** (URL params that are always UUIDs, request body fields, query params, headers) MUST be validated with `parseUUIDOrBadRequest(w, s, fieldName)`. On invalid input it writes a 400 and returns `ok=false` — return immediately. +- **Trusted UUID round-trips** (sqlc-returned UUIDs being passed back into queries, test fixtures) use `parseUUID(s)` which calls `util.MustParseUUID` and panics on invalid input. A panic here means an unguarded user-input string slipped in — that is a real bug. `chi`'s `middleware.Recoverer` translates the panic into a 500 so the process keeps running. +- **`util.ParseUUID(s) (pgtype.UUID, error)`** is the only safe variant outside the handler package. Always check the error. + +When adding a `Queries.Delete*` or `Queries.Update*` call, ask: "Where did this UUID come from?" If the answer is "raw user input that hasn't been validated," route it through `parseUUIDOrBadRequest` or a loader first. + ### Package Boundary Rules These are hard constraints. Violating them breaks the cross-platform architecture: diff --git a/CLI_AND_DAEMON.md b/CLI_AND_DAEMON.md index 2144d3cbfc..1fdf2a4c30 100644 --- a/CLI_AND_DAEMON.md +++ b/CLI_AND_DAEMON.md @@ -146,6 +146,8 @@ The daemon auto-detects these AI CLIs on your PATH: | Gemini | `gemini` | Google's coding agent | | [Pi](https://pi.dev/) | `pi` | Pi coding agent | | [Cursor Agent](https://cursor.com/) | `cursor-agent` | Cursor's headless coding agent | +| Kimi | `kimi` | Moonshot coding agent | +| Kiro CLI | `kiro-cli` | Kiro ACP coding agent | You need at least one installed. The daemon registers each detected CLI as an available runtime. @@ -166,6 +168,7 @@ Daemon behavior is configured via flags or environment variables: | Poll interval | `--poll-interval` | `MULTICA_DAEMON_POLL_INTERVAL` | `3s` | | Heartbeat interval | `--heartbeat-interval` | `MULTICA_DAEMON_HEARTBEAT_INTERVAL` | `15s` | | Agent timeout | `--agent-timeout` | `MULTICA_AGENT_TIMEOUT` | `2h` | +| Codex semantic inactivity timeout | `--codex-semantic-inactivity-timeout` | `MULTICA_CODEX_SEMANTIC_INACTIVITY_TIMEOUT` | `10m` | | Max concurrent tasks | `--max-concurrent-tasks` | `MULTICA_DAEMON_MAX_CONCURRENT_TASKS` | `20` | | Daemon ID | `--daemon-id` | `MULTICA_DAEMON_ID` | hostname | | Device name | `--device-name` | `MULTICA_DAEMON_DEVICE_NAME` | hostname | @@ -192,6 +195,10 @@ Agent-specific overrides: | `MULTICA_PI_MODEL` | Override the Pi model used | | `MULTICA_CURSOR_PATH` | Custom path to the `cursor-agent` binary | | `MULTICA_CURSOR_MODEL` | Override the Cursor Agent model used | +| `MULTICA_KIMI_PATH` | Custom path to the `kimi` binary | +| `MULTICA_KIMI_MODEL` | Override the Kimi model used | +| `MULTICA_KIRO_PATH` | Custom path to the `kiro-cli` binary | +| `MULTICA_KIRO_MODEL` | Override the Kiro model used | ### Self-Hosted Server diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b15178db9d..753dd47f7c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -373,7 +373,8 @@ done #### 2. Create a test user and token (automated auth) -In non-production environments the verification code is fixed at `888888`: +For deterministic local automation, set `MULTICA_DEV_VERIFICATION_CODE=888888` +in your env file before starting the backend: ```bash curl -s -X POST "$SERVER/auth/send-code" \ @@ -476,7 +477,9 @@ This automatically: 3. Starts and manages its own daemon instance 4. Connects to the local backend -Login in the Desktop UI with `dev@localhost` and code `888888`. +Login in the Desktop UI with `dev@localhost` and the generated code from the +backend logs. If you set `MULTICA_DEV_VERIFICATION_CODE=888888` before starting +the backend, you can use `888888` instead. If the backend runs on a non-default port (worktree), create `apps/desktop/.env.development.local`: diff --git a/Dockerfile b/Dockerfile index 2978968e53..cbb9f23060 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ COPY server/ ./server/ # Build binaries ARG VERSION=dev ARG COMMIT=unknown -RUN cd server && CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/server ./cmd/server +RUN cd server && CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION} -X main.commit=${COMMIT}" -o bin/server ./cmd/server RUN cd server && CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION} -X main.commit=${COMMIT}" -o bin/multica ./cmd/multica RUN cd server && CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/migrate ./cmd/migrate diff --git a/Makefile b/Makefile index 40fb1ab5f7..ea2c58f038 100644 --- a/Makefile +++ b/Makefile @@ -91,7 +91,7 @@ selfhost: ## Create .env if needed, then pull and start the official self-hosted echo " $${MULTICA_WEB_IMAGE:-ghcr.io/multica-ai/multica-web}:$${MULTICA_IMAGE_TAG:-latest}"; \ echo ""; \ echo "Log in: configure RESEND_API_KEY in .env for email codes,"; \ - echo " or set APP_ENV=development in .env (private networks only) to enable code 888888."; \ + echo " or read the generated code from backend logs when Resend is unset."; \ echo ""; \ echo "Next — install the CLI and connect your machine:"; \ echo " brew install multica-ai/tap/multica"; \ @@ -130,7 +130,7 @@ selfhost-build: ## Build backend/web from the current checkout and start the sel echo " Backend: http://localhost:$${PORT:-8080}"; \ echo ""; \ echo "Log in: configure RESEND_API_KEY in .env for email codes,"; \ - echo " or set APP_ENV=development in .env (private networks only) to enable code 888888."; \ + echo " or read the generated code from backend logs when Resend is unset."; \ echo ""; \ echo "Built images locally via docker-compose.selfhost.build.yml."; \ echo "Local tags: multica-backend:dev and multica-web:dev."; \ @@ -277,7 +277,7 @@ COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) DATE ?= $(shell date -u '+%Y-%m-%dT%H:%M:%SZ') build: ## Build the server, CLI, and migrate binaries into server/bin - cd server && go build -o bin/server ./cmd/server + cd server && go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT)" -o bin/server ./cmd/server cd server && go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.date=$(DATE)" -o bin/multica ./cmd/multica cd server && go build -o bin/migrate ./cmd/migrate diff --git a/README.md b/README.md index edbe069a60..817be3ded9 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Turn coding agents into real teammates — assign tasks, track progress, compoun Multica turns coding agents into real teammates. Assign issues to an agent like you'd assign to a colleague — they'll pick up the work, write code, report blockers, and update statuses autonomously. -No more copy-pasting prompts. No more babysitting runs. Your agents show up on the board, participate in conversations, and compound reusable skills over time. Think of it as open-source infrastructure for managed agents — vendor-neutral, self-hosted, and designed for human + AI teams. Works with **Claude Code**, **Codex**, **OpenClaw**, **OpenCode**, **Hermes**, **Gemini**, **Pi**, and **Cursor Agent**. +No more copy-pasting prompts. No more babysitting runs. Your agents show up on the board, participate in conversations, and compound reusable skills over time. Think of it as open-source infrastructure for managed agents — vendor-neutral, self-hosted, and designed for human + AI teams. Works with **Claude Code**, **Codex**, **OpenClaw**, **OpenCode**, **Hermes**, **Gemini**, **Pi**, **Cursor Agent**, **Kimi**, and **Kiro CLI**.

Multica board view @@ -98,7 +98,7 @@ multica setup # Connect to Multica Cloud, log in, start daemon multica setup # Configure, authenticate, and start the daemon ``` -The daemon runs in the background and auto-detects agent CLIs (`claude`, `codex`, `openclaw`, `opencode`, `hermes`, `gemini`, `pi`, `cursor-agent`) on your PATH. +The daemon runs in the background and auto-detects agent CLIs (`claude`, `codex`, `openclaw`, `opencode`, `hermes`, `gemini`, `pi`, `cursor-agent`, `kimi`, `kiro-cli`) on your PATH. ### 2. Verify your runtime @@ -108,7 +108,7 @@ Open your workspace in the Multica web app. Navigate to **Settings → Runtimes* ### 3. Create an agent -Go to **Settings → Agents** and click **New Agent**. Pick the runtime you just connected and choose a provider (Claude Code, Codex, OpenClaw, OpenCode, Hermes, Gemini, Pi, or Cursor Agent). Give your agent a name — this is how it will appear on the board, in comments, and in assignments. +Go to **Settings → Agents** and click **New Agent**. Pick the runtime you just connected and choose a provider (Claude Code, Codex, OpenClaw, OpenCode, Hermes, Gemini, Pi, Cursor Agent, Kimi, or Kiro CLI). Give your agent a name — this is how it will appear on the board, in comments, and in assignments. ### 4. Assign your first task @@ -162,7 +162,8 @@ See the [CLI and Daemon Guide](CLI_AND_DAEMON.md) for the full command reference │ Agent Daemon │ runs on your machine └──────────────┘ (Claude Code, Codex, OpenCode, OpenClaw, Hermes, Gemini, - Pi, Cursor Agent) + Pi, Cursor Agent, Kimi, + Kiro CLI) ``` | Layer | Stack | @@ -170,7 +171,7 @@ See the [CLI and Daemon Guide](CLI_AND_DAEMON.md) for the full command reference | Frontend | Next.js 16 (App Router) | | Backend | Go (Chi router, sqlc, gorilla/websocket) | | Database | PostgreSQL 17 with pgvector | -| Agent Runtime | Local daemon executing Claude Code, Codex, OpenClaw, OpenCode, Hermes, Gemini, Pi, or Cursor Agent | +| Agent Runtime | Local daemon executing Claude Code, Codex, OpenClaw, OpenCode, Hermes, Gemini, Pi, Cursor Agent, Kimi, or Kiro CLI | ## Development diff --git a/SELF_HOSTING.md b/SELF_HOSTING.md index 653bcd0a22..7e86fb9e42 100644 --- a/SELF_HOSTING.md +++ b/SELF_HOSTING.md @@ -26,7 +26,7 @@ multica setup self-host This installs the `multica` CLI, checks out the latest self-host assets, pulls the official Multica images from GHCR, and configures everything for localhost. -Open http://localhost:3000. To log in, configure `RESEND_API_KEY` in `.env` for email-based codes (recommended), or set `APP_ENV=development` in `.env` to enable the dev master code **`888888`**. See [Step 2 — Log In](#step-2--log-in) for details. +Open http://localhost:3000. To log in, configure `RESEND_API_KEY` in `.env` for email-based codes (recommended), or leave Resend unset and copy the generated code from the backend logs. See [Step 2 — Log In](#step-2--log-in) for details. > **Prerequisites:** Docker and Docker Compose must be installed. The script checks for this and provides install links if missing. > @@ -67,15 +67,15 @@ Once ready: ### Step 2 — Log In -Open http://localhost:3000 in your browser. The Docker self-host stack defaults to `APP_ENV=production` (set in `docker-compose.selfhost.yml`), so the dev master code is **disabled by default** for safety on public deployments. Pick one of the following to log in: +Open http://localhost:3000 in your browser. The Docker self-host stack defaults to `APP_ENV=production` (set in `docker-compose.selfhost.yml`), and there is no fixed verification code by default. Pick one of the following to log in: - **Recommended (production):** configure `RESEND_API_KEY` in `.env`, then restart the backend. Real verification codes will be sent to the email address you enter. See [Advanced Configuration → Email](SELF_HOSTING_ADVANCED.md#email-required-for-authentication). -- **Evaluation / private network:** set `APP_ENV=development` in `.env` and restart the backend. Verification code **`888888`** will then work for any email address. -- **Without configuring either:** the verification code is generated server-side and printed to the backend container logs (look for `[DEV] Verification code for ...:`). Useful for one-off testing on a single machine. +- **Without email configured:** the verification code is generated server-side and printed to the backend container logs (look for `[DEV] Verification code for ...:`). Useful for one-off testing on a single machine. +- **Deterministic local/private testing:** set `APP_ENV=development` and `MULTICA_DEV_VERIFICATION_CODE=888888` in `.env`, then restart the backend. This fixed code is ignored when `APP_ENV=production`. Changes to `ALLOW_SIGNUP` and `GOOGLE_CLIENT_ID` also take effect after restarting the backend / compose stack. The web UI reads both from `/api/config` at runtime, so no web rebuild is needed. -> **Warning:** do **not** set `APP_ENV=development` on a publicly reachable instance — anyone who knows an email address can then log in with `888888`. +> **Warning:** do **not** set `MULTICA_DEV_VERIFICATION_CODE` on a publicly reachable instance — anyone who knows an email address can then log in with that fixed code. ### Step 3 — Install CLI & Start Daemon @@ -98,6 +98,8 @@ You also need at least one AI agent CLI installed: - Gemini (`gemini` on PATH) - [Pi](https://pi.dev/) (`pi` on PATH) - [Cursor Agent](https://cursor.com/) (`cursor-agent` on PATH) +- Kimi (`kimi` on PATH) +- Kiro CLI (`kiro-cli` on PATH) ### b) One-command setup diff --git a/SELF_HOSTING_ADVANCED.md b/SELF_HOSTING_ADVANCED.md index 7d86a56245..eb3ceae0af 100644 --- a/SELF_HOSTING_ADVANCED.md +++ b/SELF_HOSTING_ADVANCED.md @@ -32,7 +32,7 @@ Multica uses email-based magic link authentication via [Resend](https://resend.c | `RESEND_API_KEY` | Your Resend API key | | `RESEND_FROM_EMAIL` | Sender email address (default: `noreply@multica.ai`) | -> **Note:** The dev master verification code `888888` is gated by `APP_ENV != "production"`. The Docker self-host stack defaults to `APP_ENV=production` (so `888888` is disabled), which protects publicly reachable instances. For local development without email configured, set `APP_ENV=development` in your `.env` to enable `888888` — never do this on a public instance. +> **Note:** If Resend is not configured, generated verification codes are printed to backend logs. A fixed local testing code is disabled by default; to opt in on a private test instance, set `APP_ENV=development` and `MULTICA_DEV_VERIFICATION_CODE` to a 6-digit value. It is ignored when `APP_ENV=production`. ### Google OAuth (Optional) @@ -79,6 +79,7 @@ The `Secure` flag on session cookies is derived automatically from the scheme of | Variable | Default | Description | |----------|---------|-------------| | `PORT` | `8080` | Backend server port | +| `METRICS_ADDR` | empty | Optional Prometheus metrics listener, for example `127.0.0.1:9090` | | `FRONTEND_PORT` | `3000` | Frontend port | | `CORS_ALLOWED_ORIGINS` | Value of `FRONTEND_ORIGIN` | Comma-separated list of allowed origins | | `LOG_LEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error` | @@ -308,6 +309,28 @@ dependency-aware readiness probes and external monitoring that should fail when the database is unavailable or migrations are not fully applied. `/healthz` is kept as an alias for operator familiarity. +## Prometheus Metrics + +The backend can expose Prometheus metrics on a separate management listener: + +```bash +METRICS_ADDR=127.0.0.1:9090 ./server/bin/server +curl http://127.0.0.1:9090/metrics +``` + +`METRICS_ADDR` is empty by default, so no metrics listener is started. The +public API port does not serve `/metrics`; keep it that way for internet-facing +deployments. HTTP request metrics start accumulating only after the metrics +listener is enabled. Metrics can reveal internal routes, traffic volume, +dependency state, and runtime health. + +For Docker or Kubernetes deployments, prefer a private scrape path: bind the +metrics listener to an internal interface and protect it with private +networking, allowlists, NetworkPolicy, or proxy authentication. If you bind +`METRICS_ADDR=0.0.0.0:9090` inside a container, only publish that port to a +trusted network, for example a host-local mapping such as +`127.0.0.1:9090:9090`. + ## Upgrading ```bash diff --git a/SELF_HOSTING_AI.md b/SELF_HOSTING_AI.md index 2c534c4151..ea0cbcbdbb 100644 --- a/SELF_HOSTING_AI.md +++ b/SELF_HOSTING_AI.md @@ -37,7 +37,7 @@ multica setup self-host The `multica setup self-host` command will: 1. Configure CLI to connect to localhost:8080 / localhost:3000 -2. Open a browser for login — use verification code `888888` with any email +2. Open a browser for login — use the emailed code, or the generated code printed in backend logs when Resend is unset 3. Discover workspaces automatically 4. Start the daemon in the background diff --git a/apps/desktop/electron-builder.yml b/apps/desktop/electron-builder.yml index 01354a3daa..2680789825 100644 --- a/apps/desktop/electron-builder.yml +++ b/apps/desktop/electron-builder.yml @@ -37,6 +37,14 @@ linux: - deb - rpm artifactName: multica-desktop-${version}-linux-${arch}.${ext} +rpm: + # Disable RPM build-id symlinks. Electron apps embed the upstream Electron + # binary, whose GNU build-id is identical across every app shipping the same + # Electron version (Slack, VS Code, Discord, ...). Without this, our RPM + # would own /usr/lib/.build-id/ paths and collide with any other + # Electron RPM already installed, breaking `dnf install` on Fedora/RHEL. + fpm: + - "--rpm-rpmbuild-define=_build_id_links none" win: target: - nsis diff --git a/apps/desktop/src/main/index.ts b/apps/desktop/src/main/index.ts index 970cafcde0..d29470e5d1 100644 --- a/apps/desktop/src/main/index.ts +++ b/apps/desktop/src/main/index.ts @@ -1,4 +1,4 @@ -import { app, BrowserWindow, ipcMain, nativeImage } from "electron"; +import { app, BrowserWindow, ipcMain, nativeImage, Notification } from "electron"; import { homedir } from "os"; import { join } from "path"; import { electronApp, optimizer, is } from "@electron-toolkit/utils"; @@ -214,6 +214,64 @@ if (!gotTheLock) { mainWindow?.setWindowButtonVisibility(!immersive); }); + // IPC: show a native OS notification for a new inbox item. The renderer + // only fires this when the app is unfocused (it gates on + // `document.hasFocus()`), so we don't fight macOS foreground suppression + // here. Clicking the banner focuses the main window and routes to the + // inbox item via a renderer-side listener. + ipcMain.on( + "notification:show", + ( + _event, + { + slug, + itemId, + issueKey, + title, + body, + }: { + slug: string; + itemId: string; + issueKey: string; + title: string; + body: string; + }, + ) => { + if (!Notification.isSupported()) return; + const notification = new Notification({ title, body }); + notification.on("click", () => { + if (!mainWindow) return; + if (mainWindow.isMinimized()) mainWindow.restore(); + mainWindow.show(); + mainWindow.focus(); + // Ship the full context back — the renderer pins the route to the + // source workspace (slug), marks the row read (itemId), and uses + // issueKey as the ?issue=<…> selector. + mainWindow.webContents.send("inbox:open", { + slug, + itemId, + issueKey, + }); + }); + notification.show(); + }, + ); + + // IPC: update the dock / taskbar unread badge. Values above 99 render as + // "99+". macOS is the primary target (user-visible dock badge); Linux + // Unity launchers also respect `setBadgeCount`. Windows' taskbar overlay + // needs a pre-rendered PNG and is deferred — the OS notification + the + // in-app inbox sidebar cover the core UX there for now. + ipcMain.on("badge:set", (_event, rawCount: number) => { + const count = Math.max(0, Math.floor(rawCount)); + if (process.platform === "darwin") { + const label = count === 0 ? "" : count > 99 ? "99+" : String(count); + app.dock?.setBadge(label); + } else { + app.setBadgeCount(count); + } + }); + createWindow(); setupAutoUpdater(() => mainWindow); diff --git a/apps/desktop/src/preload/index.d.ts b/apps/desktop/src/preload/index.d.ts index 97b2c179fa..e9136e4c27 100644 --- a/apps/desktop/src/preload/index.d.ts +++ b/apps/desktop/src/preload/index.d.ts @@ -14,6 +14,24 @@ interface DesktopAPI { openExternal: (url: string) => Promise; /** Hide macOS traffic lights for full-screen modals; restore when false. */ setImmersiveMode: (immersive: boolean) => Promise; + /** Show a native OS notification for a new inbox item. */ + showNotification: (payload: { + slug: string; + itemId: string; + issueKey: string; + title: string; + body: string; + }) => void; + /** Update the OS dock / taskbar unread badge. Pass 0 to clear. */ + setUnreadBadge: (count: number) => void; + /** Listen for "open inbox row" requests from notification clicks. Returns an unsubscribe function. */ + onInboxOpen: ( + callback: (payload: { + slug: string; + itemId: string; + issueKey: string; + }) => void, + ) => () => void; } interface DaemonStatus { diff --git a/apps/desktop/src/preload/index.ts b/apps/desktop/src/preload/index.ts index 8843f3f821..78d45de63e 100644 --- a/apps/desktop/src/preload/index.ts +++ b/apps/desktop/src/preload/index.ts @@ -50,6 +50,50 @@ const desktopAPI = { /** Toggle immersive mode — hide macOS traffic lights for full-screen modals */ setImmersiveMode: (immersive: boolean) => ipcRenderer.invoke("window:setImmersive", immersive), + /** + * Show a native OS notification for a new inbox item. Fired from the + * renderer only when the app is unfocused — in-focus feedback is the + * inbox sidebar's unread styling. `slug`, `itemId`, and `issueKey` are + * all round-tripped on click: slug pins routing to the source workspace + * (the user may switch workspaces before clicking the banner), itemId + * lets the renderer mark the row read, issueKey maps to the inbox URL + * param. + */ + showNotification: (payload: { + slug: string; + itemId: string; + issueKey: string; + title: string; + body: string; + }) => ipcRenderer.send("notification:show", payload), + /** + * Update the OS dock / taskbar unread badge. Pass 0 to clear. Values + * above 99 render as "99+" (capping is handled in the main process). + */ + setUnreadBadge: (count: number) => + ipcRenderer.send("badge:set", Math.max(0, Math.floor(count))), + /** + * Subscribe to "open this inbox row" requests sent by the main process + * when the user clicks an OS notification banner. Returns an unsubscribe + * function. The payload echoes the `slug`, `itemId`, and `issueKey` that + * were passed to `showNotification`. + */ + onInboxOpen: ( + callback: (payload: { + slug: string; + itemId: string; + issueKey: string; + }) => void, + ) => { + const handler = ( + _event: Electron.IpcRendererEvent, + payload: { slug: string; itemId: string; issueKey: string }, + ) => callback(payload); + ipcRenderer.on("inbox:open", handler); + return () => { + ipcRenderer.removeListener("inbox:open", handler); + }; + }, }; interface DaemonStatus { diff --git a/apps/desktop/src/renderer/src/components/desktop-layout.tsx b/apps/desktop/src/renderer/src/components/desktop-layout.tsx index f8ecfddf9b..54896fe725 100644 --- a/apps/desktop/src/renderer/src/components/desktop-layout.tsx +++ b/apps/desktop/src/renderer/src/components/desktop-layout.tsx @@ -12,9 +12,11 @@ import { import { ModalRegistry } from "@multica/views/modals/registry"; import { AppSidebar } from "@multica/views/layout"; import { SearchCommand, SearchTrigger } from "@multica/views/search"; +import { ChatFab, ChatWindow } from "@multica/views/chat"; import { StarterContentPrompt } from "@multica/views/onboarding"; -import { WorkspaceSlugProvider } from "@multica/core/paths"; +import { WorkspaceSlugProvider, paths, useCurrentWorkspace } from "@multica/core/paths"; import { getCurrentSlug, subscribeToCurrentSlug } from "@multica/core/platform"; +import { useDesktopUnreadBadge } from "@multica/views/platform"; import { DesktopNavigationProvider } from "@/platform/navigation"; import { TabBar } from "./tab-bar"; import { TabContent } from "./tab-content"; @@ -96,6 +98,38 @@ function useInternalLinkHandler() { }, []); } +/** + * Bridge between the renderer and the Electron main process for inbox-level + * OS integration. Mounted inside WorkspaceSlugProvider so it can resolve the + * current workspace's id for the badge hook. + * + * Two responsibilities: + * 1. Mirror the unread inbox count onto the dock/taskbar badge. + * 2. When the user clicks an OS notification, open the notified + * workspace's inbox focused on that item. The route uses the `slug` + * that the notification was *emitted* with — not the currently active + * workspace — so a notification from workspace A always opens A's + * inbox even if the user has since switched to workspace B. Marking + * the row read is handled by InboxPage's selected-item effect, which + * covers both click-to-select and URL-param-select paths. + */ +function DesktopInboxBridge() { + const workspace = useCurrentWorkspace(); + useDesktopUnreadBadge(workspace?.id ?? null); + + useEffect(() => { + return window.desktopAPI.onInboxOpen(({ slug, issueKey }) => { + if (!slug) return; + const inboxPath = `${paths.workspace(slug).inbox()}?issue=${encodeURIComponent(issueKey)}`; + window.dispatchEvent( + new CustomEvent("multica:navigate", { detail: { path: inboxPath } }), + ); + }); + }, []); + + return null; +} + export function DesktopShell() { useInternalLinkHandler(); useActiveTitleSync(); @@ -117,15 +151,18 @@ export function DesktopShell() { users see the window-level overlay (new-workspace flow) triggered by IndexRedirect, not a route. */} +

{slug && } searchSlot={} />} {/* Right side: header + content container */}
- {/* Content area with inset styling */} + {/* Content area with inset styling — relative so ChatWindow/ChatFab are constrained here */}
+ {slug && } + {slug && }
diff --git a/apps/desktop/src/renderer/src/components/tab-bar.tsx b/apps/desktop/src/renderer/src/components/tab-bar.tsx index 12ac363933..93d139ac65 100644 --- a/apps/desktop/src/renderer/src/components/tab-bar.tsx +++ b/apps/desktop/src/renderer/src/components/tab-bar.tsx @@ -5,7 +5,6 @@ import { Bot, Monitor, BookOpenText, - MessageSquare, Settings, X, Plus, @@ -40,7 +39,6 @@ const TAB_ICONS: Record = { Bot, Monitor, BookOpenText, - MessageSquare, Settings, }; diff --git a/apps/desktop/src/renderer/src/platform/navigation.tsx b/apps/desktop/src/renderer/src/platform/navigation.tsx index 01043331a9..494916ed59 100644 --- a/apps/desktop/src/renderer/src/platform/navigation.tsx +++ b/apps/desktop/src/renderer/src/platform/navigation.tsx @@ -115,10 +115,10 @@ export function DesktopNavigationProvider({ const { tabId: activeTabId } = useActiveTabIdentity(); const router = useActiveTabRouter(); // Mirror the active tab router's full location (pathname + search) so - // shell-level consumers of useNavigation() can read URL search params. - // Must stay in sync with TabNavigationProvider below; a partial shape - // here (just pathname) silently broke focus-mode anchor resolution on - // `/inbox?issue=…`. + // shell-level consumers of useNavigation() — ChatWindow in particular — + // can read URL search params. Must stay in sync with TabNavigationProvider + // below; a partial shape here (just pathname) silently broke focus-mode + // anchor resolution on `/inbox?issue=…`. const [location, setLocation] = useState<{ pathname: string; search: string }>( () => ({ pathname: router?.state.location.pathname ?? "/", diff --git a/apps/desktop/src/renderer/src/routes.tsx b/apps/desktop/src/renderer/src/routes.tsx index 406585ae4c..09f65415b5 100644 --- a/apps/desktop/src/renderer/src/routes.tsx +++ b/apps/desktop/src/renderer/src/routes.tsx @@ -20,7 +20,6 @@ import { SkillsPage } from "@multica/views/skills"; import { DesktopRuntimesPage } from "./components/desktop-runtimes-page"; import { AgentsPage } from "@multica/views/agents"; import { InboxPage } from "@multica/views/inbox"; -import { ChatPage } from "@multica/views/chat"; import { SettingsPage } from "@multica/views/settings"; import { Download, Server } from "lucide-react"; import { DaemonSettingsTab } from "./components/daemon-settings-tab"; @@ -138,7 +137,6 @@ export const appRoutes: RouteObject[] = [ handle: { title: "Agent" }, }, { path: "inbox", element: , handle: { title: "Inbox" } }, - { path: "chat", element: , handle: { title: "Chat" } }, { path: "settings", element: ( diff --git a/apps/desktop/src/renderer/src/stores/tab-store.ts b/apps/desktop/src/renderer/src/stores/tab-store.ts index d31454328b..6ca66893d8 100644 --- a/apps/desktop/src/renderer/src/stores/tab-store.ts +++ b/apps/desktop/src/renderer/src/stores/tab-store.ts @@ -101,7 +101,6 @@ interface TabStore { const ROUTE_ICONS: Record = { inbox: "Inbox", - chat: "MessageSquare", "my-issues": "CircleUser", issues: "ListTodo", projects: "FolderKanban", diff --git a/apps/docs/content/docs/agents-create.mdx b/apps/docs/content/docs/agents-create.mdx index 3a9e48a1a5..2d4a7af5c5 100644 --- a/apps/docs/content/docs/agents-create.mdx +++ b/apps/docs/content/docs/agents-create.mdx @@ -21,7 +21,7 @@ The form has only two required fields: **name** (unique within the workspace) an ## Pick an AI coding tool -Each runtime is backed by a specific AI coding tool. Multica supports 10 of them. The most common choices: +Each runtime is backed by a specific AI coding tool. Multica supports 11 of them. The most common choices: | Tool | Good for | |---|---| @@ -31,7 +31,7 @@ Each runtime is backed by a specific AI coding tool. Multica supports 10 of them | **Copilot** | Teams leveraging their GitHub account entitlements | | **Gemini** | Users in the Google ecosystem | -The other five (Hermes, Kimi, OpenCode, Pi, OpenClaw), along with each tool's full capability matrix (session resume, MCP, skill injection path, model selection), are covered in [AI coding tools comparison](/providers). +The other six (Hermes, Kimi, Kiro CLI, OpenCode, Pi, OpenClaw), along with each tool's full capability matrix (session resume, MCP, skill injection path, model selection), are covered in [AI coding tools comparison](/providers). ## Writing system instructions @@ -123,5 +123,5 @@ Archived agents can't be assigned new tasks. ## Next steps - [Skills](/skills) — attach knowledge packs to an agent -- [AI coding tools comparison](/providers) — full capability matrix across all 10 tools +- [AI coding tools comparison](/providers) — full capability matrix across all 11 tools - [Assigning issues to agents](/assigning-issues) — put your new agent to work diff --git a/apps/docs/content/docs/agents-create.zh.mdx b/apps/docs/content/docs/agents-create.zh.mdx index fb0eac6f3a..57eb645c7e 100644 --- a/apps/docs/content/docs/agents-create.zh.mdx +++ b/apps/docs/content/docs/agents-create.zh.mdx @@ -21,7 +21,7 @@ multica agent create ## 选一款 AI 编程工具 -运行时背后是一款具体的 AI 编程工具。Multica 支持 10 款,最常用的几款: +运行时背后是一款具体的 AI 编程工具。Multica 支持 11 款,最常用的几款: | 工具 | 适合 | |---|---| @@ -31,7 +31,7 @@ multica agent create | **Copilot** | 用 GitHub 账号权益的团队 | | **Gemini** | Google 生态用户 | -另外 5 款(Hermes、Kimi、OpenCode、Pi、OpenClaw)以及每款工具的完整能力差别(会话恢复、MCP、skill 注入路径、模型选择)见 [AI 编程工具对照](/providers)。 +另外 6 款(Hermes、Kimi、Kiro CLI、OpenCode、Pi、OpenClaw)以及每款工具的完整能力差别(会话恢复、MCP、skill 注入路径、模型选择)见 [AI 编程工具对照](/providers)。 ## 写系统指令 @@ -123,5 +123,5 @@ claude --model --max-turns 100 --append-system-prompt "always respond in ## 下一步 - [Skills](/skills) —— 给智能体挂专业知识包 -- [AI 编程工具对照](/providers) —— 10 款工具的完整能力差别 +- [AI 编程工具对照](/providers) —— 11 款工具的完整能力差别 - [把 issue 分配给智能体](/assigning-issues) —— 创建完之后怎么用起来 diff --git a/apps/docs/content/docs/auth-setup.mdx b/apps/docs/content/docs/auth-setup.mdx index 78aaaf6acf..7d8fb29085 100644 --- a/apps/docs/content/docs/auth-setup.mdx +++ b/apps/docs/content/docs/auth-setup.mdx @@ -1,6 +1,6 @@ --- title: Sign-in and signup configuration -description: Configure email + verification code sign-in, Google OAuth, and signup allowlists. Avoid the 888888 trap. +description: Configure email + verification code sign-in, Google OAuth, signup allowlists, and local test codes. --- import { Callout } from "fumadocs-ui/components/callout"; @@ -27,17 +27,24 @@ The user enters an email on the sign-in page → the server sends a 6-digit code **What happens if you don't set `RESEND_API_KEY`**: the server doesn't error, but **every email that should have been sent is written to the server's stdout only**. Handy for local development (copy the code from the logs); in production it's a black hole. -## The 888888 trap +## Fixed local testing codes -**If `APP_ENV` is not set to `production`, anyone can sign in to any account with the code `888888`.** +**Do not enable a fixed verification code on a publicly reachable instance.** -Multica has a development-only master code, `888888` — a backdoor so local development doesn't depend on Resend. The rule is straightforward: when `APP_ENV != "production"`, **any email** plus `888888` passes verification. +The old behavior where non-production instances accepted `888888` by default has been removed. Unless you explicitly configure it, typing `888888` is treated like any other wrong code. -**Production deployments must set `APP_ENV=production`**. If you deploy via `make selfhost` / `docker-compose.selfhost.yml`, this value is already set to `production` by default; but if you deploy from source yourself, write your own Docker config, or redefine environment variables in Kubernetes — you must add `APP_ENV=production` yourself. +Local development without Resend should use the generated code printed in server logs. If you need deterministic local/private automation, set `MULTICA_DEV_VERIFICATION_CODE` to a 6-digit value such as `888888`, and keep `APP_ENV` non-production: + +```bash +APP_ENV=development +MULTICA_DEV_VERIFICATION_CODE=888888 +``` + +This shortcut is ignored when `APP_ENV=production`. -To check whether your deployment has this trap: open the sign-in page, enter **any email** to request a code, then enter `888888`. If you get in, your `APP_ENV` is not set to `production`, and **the entire instance is wide open**. +Production deployments should leave `MULTICA_DEV_VERIFICATION_CODE` empty and set `APP_ENV=production`. If you deploy via `make selfhost` / `docker-compose.selfhost.yml`, `APP_ENV` defaults to `production`. ## Google OAuth configuration diff --git a/apps/docs/content/docs/auth-setup.zh.mdx b/apps/docs/content/docs/auth-setup.zh.mdx index aabe40d829..7ce8115361 100644 --- a/apps/docs/content/docs/auth-setup.zh.mdx +++ b/apps/docs/content/docs/auth-setup.zh.mdx @@ -1,12 +1,12 @@ --- title: 登录与注册配置 -description: 配 Email 验证码登录 + Google OAuth + 注册白名单。避开最坑的 888888 陷阱。 +description: 配 Email 验证码登录、Google OAuth、注册白名单和本地测试验证码。 --- import { Callout } from "fumadocs-ui/components/callout"; import { Mermaid } from "@/components/mermaid"; -Multica 支持两种登录方式:**Email + 验证码**(默认)和 **Google OAuth**(可选)。登录成功后 server 签发一个 30 天有效期的 JWT cookie。这一页讲怎么配、怎么限制谁能注册、以及自部署最容易踩的一个陷阱。 +Multica 支持两种登录方式:**Email + 验证码**(默认)和 **Google OAuth**(可选)。登录成功后 server 签发一个 30 天有效期的 JWT cookie。这一页讲怎么配、怎么限制谁能注册、以及本地测试验证码怎么安全使用。 上面用到的环境变量的清单见 [环境变量](/environment-variables);token 怎么用、生命周期细节见 [认证与令牌](/auth-tokens)。 @@ -27,17 +27,24 @@ Multica 支持两种登录方式:**Email + 验证码**(默认)和 **Google **不配 `RESEND_API_KEY` 的后果**:server 不报错,但**所有本该发出去的邮件只打到 server 的 stdout**。本地开发方便(你从日志抄验证码),生产环境等于黑洞。 -## 888888 陷阱 +## 固定本地测试验证码 -**`APP_ENV` 不设为 `production`,任何人都能用验证码 `888888` 登录任何账号。** +**不要在公网可访问实例上启用固定验证码。** -Multica 有一个开发用的主验证码(master code)`888888`——为了本地开发不依赖 Resend 而设的后门。判定逻辑很简单:`APP_ENV != "production"` 时,**任何邮箱**输 `888888` 都能通过。 +旧版「非 production 默认接受 `888888`」的行为已经移除。除非你显式配置,否则输入 `888888` 会和普通错误验证码一样被拒绝。 -**生产部署必须设 `APP_ENV=production`**。如果你用 `make selfhost` / `docker-compose.selfhost.yml` 自部署,这个值已经默认设为 `production`;但如果你自己从源码部署、自己写 Docker 配置、或者在 Kubernetes 里重新定义环境变量——一定要自己把 `APP_ENV=production` 加上。 +不配 Resend 的本地开发,应使用 server 日志里打印的随机验证码。如果你需要确定性的本地/私有自动化测试,可以把 `MULTICA_DEV_VERIFICATION_CODE` 设成一个 6 位数字,比如 `888888`,并保持 `APP_ENV` 为非 production: + +```bash +APP_ENV=development +MULTICA_DEV_VERIFICATION_CODE=888888 +``` + +`APP_ENV=production` 时这个快捷码会被忽略。 -检查你的部署是否有这个陷阱:打开登录页,输入**任意邮箱**请求验证码,再在验证码栏输 `888888`。如果能登进去 = 你的 `APP_ENV` 没设成 `production`,**整个实例处于完全开放状态**。 +生产部署应保持 `MULTICA_DEV_VERIFICATION_CODE` 为空,并设置 `APP_ENV=production`。如果你用 `make selfhost` / `docker-compose.selfhost.yml` 自部署,`APP_ENV` 默认就是 `production`。 ## 怎么配 Google OAuth diff --git a/apps/docs/content/docs/auth-tokens.mdx b/apps/docs/content/docs/auth-tokens.mdx index 3472f9f3a2..27aea8704b 100644 --- a/apps/docs/content/docs/auth-tokens.mdx +++ b/apps/docs/content/docs/auth-tokens.mdx @@ -38,7 +38,7 @@ In day-to-day use you'll only touch the first two directly. The **[daemon](/daem 2. Enter the code; the server issues a JWT cookie (browser) or exchanges it for a PAT (CLI). -**Self-hosting operators, take note**: if `APP_ENV` is not set to `production`, the verification code is always `888888` — anyone can sign in as anyone. See [Self-host auth configuration](/auth-setup). +**Self-hosting operators, take note**: keep `MULTICA_DEV_VERIFICATION_CODE` empty on public deployments. If you enable a fixed local test code, anyone who can request a code can sign in with that value while `APP_ENV` is non-production. See [Self-host auth configuration](/auth-setup). ### Google OAuth diff --git a/apps/docs/content/docs/auth-tokens.zh.mdx b/apps/docs/content/docs/auth-tokens.zh.mdx index 0c04e01ba5..e907bfb23f 100644 --- a/apps/docs/content/docs/auth-tokens.zh.mdx +++ b/apps/docs/content/docs/auth-tokens.zh.mdx @@ -38,7 +38,7 @@ Multica 有三种令牌,对应三种使用场景:浏览器 Web UI、命令 2. 输入验证码,server 签发 JWT cookie(浏览器)或交换出 PAT(CLI) -**自部署运维注意**:如果环境变量 `APP_ENV` 不是 `production`,验证码恒为 `888888`——任何人能登录任何账号。详见 [自部署的认证配置](/auth-setup)。 +**自部署运维注意**:公网部署时保持 `MULTICA_DEV_VERIFICATION_CODE` 为空。如果启用固定本地测试验证码,在 `APP_ENV` 非 production 时,任何能请求验证码的人都能用该固定值登录。详见 [自部署的认证配置](/auth-setup)。 ### Google OAuth diff --git a/apps/docs/content/docs/cli/installation.zh.mdx b/apps/docs/content/docs/cli/installation.zh.mdx index b5c8f02ff4..81ae5bbada 100644 --- a/apps/docs/content/docs/cli/installation.zh.mdx +++ b/apps/docs/content/docs/cli/installation.zh.mdx @@ -78,7 +78,7 @@ multica daemon status Confirm: 1. Status is `running` -2. At least one agent is listed (e.g. `claude`, `codex`, `gemini`, `opencode`, `openclaw`, `hermes`, or `pi`) +2. At least one agent is listed (e.g. `claude`, `codex`, `gemini`, `opencode`, `openclaw`, `hermes`, `kiro`, or `pi`) 3. At least one workspace is being watched If the agents list is empty, install at least one supported AI agent CLI: @@ -88,6 +88,8 @@ If the agents list is empty, install at least one supported AI agent CLI: - OpenCode (`opencode`) - OpenClaw (`openclaw`) - Hermes (`hermes`) +- Kimi (`kimi`) +- Kiro CLI (`kiro-cli`) Then restart the daemon: diff --git a/apps/docs/content/docs/cli/reference.zh.mdx b/apps/docs/content/docs/cli/reference.zh.mdx index f43c534cd3..f87579c815 100644 --- a/apps/docs/content/docs/cli/reference.zh.mdx +++ b/apps/docs/content/docs/cli/reference.zh.mdx @@ -92,6 +92,10 @@ The daemon auto-detects these AI CLIs on your PATH: | OpenCode | `opencode` | Open-source coding agent | | OpenClaw | `openclaw` | Open-source coding agent | | Hermes | `hermes` | Nous Research coding agent | +| Kimi | `kimi` | Moonshot coding agent | +| Kiro CLI | `kiro-cli` | Kiro ACP coding agent | +| Pi | `pi` | Inflection coding agent | +| Cursor Agent | `cursor-agent` | Cursor coding agent | You need at least one installed. The daemon registers each detected CLI as an available runtime. @@ -134,6 +138,14 @@ Agent-specific overrides: | `MULTICA_HERMES_MODEL` | Override the Hermes model used | | `MULTICA_GEMINI_PATH` | Custom path to the `gemini` binary | | `MULTICA_GEMINI_MODEL` | Override the Gemini model used | +| `MULTICA_PI_PATH` | Custom path to the `pi` binary | +| `MULTICA_PI_MODEL` | Override the Pi model used | +| `MULTICA_CURSOR_PATH` | Custom path to the `cursor-agent` binary | +| `MULTICA_CURSOR_MODEL` | Override the Cursor model used | +| `MULTICA_KIMI_PATH` | Custom path to the `kimi` binary | +| `MULTICA_KIMI_MODEL` | Override the Kimi model used | +| `MULTICA_KIRO_PATH` | Custom path to the `kiro-cli` binary | +| `MULTICA_KIRO_MODEL` | Override the Kiro model used | ### Self-Hosted Server diff --git a/apps/docs/content/docs/cloud-quickstart.mdx b/apps/docs/content/docs/cloud-quickstart.mdx index bdd1725261..01d86193d1 100644 --- a/apps/docs/content/docs/cloud-quickstart.mdx +++ b/apps/docs/content/docs/cloud-quickstart.mdx @@ -7,7 +7,7 @@ import { Callout } from "fumadocs-ui/components/callout"; This page walks you end-to-end through Multica Cloud — **sign up → install the [CLI](/cli) → start the [daemon](/daemon-runtimes) → create an [agent](/agents) → assign your first [task](/tasks)**. Takes about 5 minutes. -One prerequisite: you already have at least one [AI coding tool](/providers) installed locally ([Claude Code](/providers#claude-code), [Codex](/providers#codex), [Cursor](/providers#cursor), [Copilot](/providers#copilot), [Gemini](/providers#gemini), [Hermes](/providers#hermes), [Kimi](/providers#kimi), [OpenCode](/providers#opencode), [OpenClaw](/providers#openclaw), or [Pi](/providers#pi)). The daemon auto-detects them on startup and refuses to start if none are present. +One prerequisite: you already have at least one [AI coding tool](/providers) installed locally ([Claude Code](/providers#claude-code), [Codex](/providers#codex), [Cursor](/providers#cursor), [Copilot](/providers#copilot), [Gemini](/providers#gemini), [Hermes](/providers#hermes), [Kimi](/providers#kimi), [Kiro CLI](/providers#kiro-cli), [OpenCode](/providers#opencode), [OpenClaw](/providers#openclaw), or [Pi](/providers#pi)). The daemon auto-detects them on startup and refuses to start if none are present. ## 1. Create an account @@ -114,6 +114,6 @@ The web UI updates in **real time** (via WebSocket) — no refresh needed. - [Daemon and runtimes](/daemon-runtimes) — how the daemon operates and what runtimes mean - [Tasks](/tasks) — task lifecycle and retry rules -- [AI coding tools compared](/providers) — capability differences across the 10 tools +- [AI coding tools compared](/providers) — capability differences across the 11 tools - [Desktop app](/desktop-app) — if you'd rather not run the daemon yourself - [Self-host quickstart](/self-host-quickstart) — run your own backend diff --git a/apps/docs/content/docs/cloud-quickstart.zh.mdx b/apps/docs/content/docs/cloud-quickstart.zh.mdx index 3a2c4d77c1..25baabda9a 100644 --- a/apps/docs/content/docs/cloud-quickstart.zh.mdx +++ b/apps/docs/content/docs/cloud-quickstart.zh.mdx @@ -7,7 +7,7 @@ import { Callout } from "fumadocs-ui/components/callout"; 这一页带你走一遍 Multica Cloud 的端到端流程——**注册 → 装 [命令行工具](/cli) → 启动 [守护进程](/daemon-runtimes) → 创建 [智能体](/agents) → 分配第一个 [任务](/tasks)**,约 5 分钟完成。 -前置只有一个:你本地已经装了至少一款 [AI 编程工具](/providers)([Claude Code](/providers#claude-code)、[Codex](/providers#codex)、[Cursor](/providers#cursor)、[Copilot](/providers#copilot)、[Gemini](/providers#gemini)、[Hermes](/providers#hermes)、[Kimi](/providers#kimi)、[OpenCode](/providers#opencode)、[OpenClaw](/providers#openclaw)、[Pi](/providers#pi))中的一款。守护进程启动时会自动探测它们,没装任何一个的话守护进程会直接拒绝启动。 +前置只有一个:你本地已经装了至少一款 [AI 编程工具](/providers)([Claude Code](/providers#claude-code)、[Codex](/providers#codex)、[Cursor](/providers#cursor)、[Copilot](/providers#copilot)、[Gemini](/providers#gemini)、[Hermes](/providers#hermes)、[Kimi](/providers#kimi)、[Kiro CLI](/providers#kiro-cli)、[OpenCode](/providers#opencode)、[OpenClaw](/providers#openclaw)、[Pi](/providers#pi))中的一款。守护进程启动时会自动探测它们,没装任何一个的话守护进程会直接拒绝启动。 ## 1. 注册账号 @@ -114,6 +114,6 @@ Web 界面会**实时**(通过 WebSocket)显示进度——不需要刷新 - [守护进程与运行时](/daemon-runtimes) —— 守护进程怎么运作、运行时概念 - [执行任务](/tasks) —— 任务生命周期、重试规则 -- [AI 编程工具对照](/providers) —— 10 款工具的能力差异 +- [AI 编程工具对照](/providers) —— 11 款工具的能力差异 - [桌面应用](/desktop-app) —— 不想自己跑守护进程的话 - [Self-Host 快速上手](/self-host-quickstart) —— 在自己服务器上跑一套 diff --git a/apps/docs/content/docs/daemon-runtimes.mdx b/apps/docs/content/docs/daemon-runtimes.mdx index 9aac2c25a3..b60c07f5bc 100644 --- a/apps/docs/content/docs/daemon-runtimes.mdx +++ b/apps/docs/content/docs/daemon-runtimes.mdx @@ -21,7 +21,7 @@ multica daemon start On startup it does four things: 1. Reads the credentials saved when you logged in -2. Detects AI coding tools installed on your `PATH` (10 built-in: [Claude Code](/providers#claude-code), [Codex](/providers#codex), [Cursor](/providers#cursor), [Copilot](/providers#copilot), [Gemini](/providers#gemini), [Hermes](/providers#hermes), [Kimi](/providers#kimi), [OpenCode](/providers#opencode), [OpenClaw](/providers#openclaw), [Pi](/providers#pi)) +2. Detects AI coding tools installed on your `PATH` (11 built-in: [Claude Code](/providers#claude-code), [Codex](/providers#codex), [Cursor](/providers#cursor), [Copilot](/providers#copilot), [Gemini](/providers#gemini), [Hermes](/providers#hermes), [Kimi](/providers#kimi), [Kiro CLI](/providers#kiro-cli), [OpenCode](/providers#opencode), [OpenClaw](/providers#openclaw), [Pi](/providers#pi)) 3. Registers itself with the server, along with a runtime for each detected tool 4. Keeps **polling every 3 seconds** for tasks to pick up, and **sends a heartbeat every 15 seconds** @@ -108,4 +108,4 @@ More scenarios in [Troubleshooting](/troubleshooting). ## Next - [Tasks](/tasks) — the full lifecycle of a task once the daemon picks it up -- [Providers Matrix](/providers) — capability differences across the 10 AI coding tools +- [Providers Matrix](/providers) — capability differences across the 11 AI coding tools diff --git a/apps/docs/content/docs/daemon-runtimes.zh.mdx b/apps/docs/content/docs/daemon-runtimes.zh.mdx index 7553b75ed3..3de87d2a55 100644 --- a/apps/docs/content/docs/daemon-runtimes.zh.mdx +++ b/apps/docs/content/docs/daemon-runtimes.zh.mdx @@ -21,7 +21,7 @@ multica daemon start 启动后它会做四件事: 1. 读取你登录时保存的凭证 -2. 探测本机 `PATH` 上已安装的 AI 编程工具(内置支持 10 款:[Claude Code](/providers#claude-code)、[Codex](/providers#codex)、[Cursor](/providers#cursor)、[Copilot](/providers#copilot)、[Gemini](/providers#gemini)、[Hermes](/providers#hermes)、[Kimi](/providers#kimi)、[OpenCode](/providers#opencode)、[OpenClaw](/providers#openclaw)、[Pi](/providers#pi)) +2. 探测本机 `PATH` 上已安装的 AI 编程工具(内置支持 11 款:[Claude Code](/providers#claude-code)、[Codex](/providers#codex)、[Cursor](/providers#cursor)、[Copilot](/providers#copilot)、[Gemini](/providers#gemini)、[Hermes](/providers#hermes)、[Kimi](/providers#kimi)、[Kiro CLI](/providers#kiro-cli)、[OpenCode](/providers#opencode)、[OpenClaw](/providers#openclaw)、[Pi](/providers#pi)) 3. 向服务器注册自己,以及每款检测到的工具对应的运行时 4. 持续**每 3 秒轮询一次**是否有任务要领,**每 15 秒发一次心跳** @@ -108,4 +108,4 @@ Multica 对并发有两层限额: ## 下一步 - [执行任务](/tasks) —— 守护进程领到任务后,它的完整生命周期 -- [Providers Matrix](/providers) —— 10 款 AI 编程工具的能力差异对照 +- [Providers Matrix](/providers) —— 11 款 AI 编程工具的能力差异对照 diff --git a/apps/docs/content/docs/desktop-app.mdx b/apps/docs/content/docs/desktop-app.mdx index 6b91655afd..a988c092f8 100644 --- a/apps/docs/content/docs/desktop-app.mdx +++ b/apps/docs/content/docs/desktop-app.mdx @@ -66,12 +66,25 @@ Grab the installer for your platform from the [Multica downloads page](https://m On first launch you'll need to sign in — the same email + verification code flow as the web app. Once you're in, Desktop syncs your workspace list automatically. - -**Which backend Desktop connects to** is determined by the address you select at sign-in. It defaults to Multica Cloud; if you're running self-hosted, click "Connect to a self-hosted instance" on the first login screen and fill in your server address. + +**Released Desktop builds are pinned to Multica Cloud.** The backend, websocket, and web URLs are baked in at build time (`VITE_API_URL` / `VITE_WS_URL` / `VITE_APP_URL`) — there is no in-app option to point Desktop at a self-hosted instance. To use Desktop against a self-hosted backend you need to build it yourself: + +```bash +git clone https://github.com/multica-ai/multica.git +cd multica +# Edit apps/desktop/.env.production: +# VITE_API_URL=https://api.your-domain +# VITE_WS_URL=wss://api.your-domain/ws +# VITE_APP_URL=https://your-domain +pnpm install +pnpm --filter @multica/desktop package +``` + +If you'd rather not build from source, the supported self-hosted path is **web frontend + CLI** — see [Self-host quickstart](/self-host-quickstart). Runtime backend configuration in Desktop is tracked in [issue #1371](https://github.com/multica-ai/multica/issues/1371). ## Next steps - [Cloud Quickstart](/cloud-quickstart) — the Cloud onboarding flow for Desktop -- [Self-Host Quickstart](/self-host-quickstart) — connecting Desktop to a self-hosted backend +- [Self-Host Quickstart](/self-host-quickstart) — running your own backend (Desktop against self-host requires a custom build, see the callout above) - [Daemon and runtimes](/daemon-runtimes) — how the daemon works (Desktop starts it for you, but the behavior is the same) diff --git a/apps/docs/content/docs/desktop-app.zh.mdx b/apps/docs/content/docs/desktop-app.zh.mdx index 45656f43f0..30e6bd8034 100644 --- a/apps/docs/content/docs/desktop-app.zh.mdx +++ b/apps/docs/content/docs/desktop-app.zh.mdx @@ -66,12 +66,25 @@ macOS 版本已经签名 + 公证,第一次打开不会有"未知开发者"的 安装后第一次打开需要登录——和 Web 版一样的 email + 验证码流程。登录成功后 Desktop 自动把工作区列表同步下来。 - -**桌面版连哪个后端** 由登录时选的地址决定。默认连 Multica Cloud;如果你用自部署版本,在首次登录页点"连接到自部署实例"填你的 server 地址即可。 + +**发布版的 Desktop 是锁死连 Multica Cloud 的**。后端 / WebSocket / Web 前端 URL(`VITE_API_URL` / `VITE_WS_URL` / `VITE_APP_URL`)在构建时就写死了,应用内**没有切换后端的入口**。要让 Desktop 连自部署后端,需要你自己从源码 build: + +```bash +git clone https://github.com/multica-ai/multica.git +cd multica +# 编辑 apps/desktop/.env.production: +# VITE_API_URL=https://api.your-domain +# VITE_WS_URL=wss://api.your-domain/ws +# VITE_APP_URL=https://your-domain +pnpm install +pnpm --filter @multica/desktop package +``` + +不想自己 build 的话,自部署的官方路径是 **Web 前端 + CLI**——见 [自部署快速上手](/self-host-quickstart)。Desktop 运行时切换后端的能力跟踪在 [issue #1371](https://github.com/multica-ai/multica/issues/1371)。 ## 下一步 - [Cloud Quickstart](/cloud-quickstart) —— Desktop 版的 Cloud 接入流程 -- [Self-Host Quickstart](/self-host-quickstart) —— Desktop 连自部署后端 +- [Self-Host Quickstart](/self-host-quickstart) —— 自部署后端(Desktop 连自部署需要自行构建,见上方提示) - [守护进程与运行时](/daemon-runtimes) —— 守护进程机制(Desktop 自动起它,但行为一样) diff --git a/apps/docs/content/docs/environment-variables.mdx b/apps/docs/content/docs/environment-variables.mdx index 81e03c6a83..88b4cb60d4 100644 --- a/apps/docs/content/docs/environment-variables.mdx +++ b/apps/docs/content/docs/environment-variables.mdx @@ -7,20 +7,21 @@ import { Callout } from "fumadocs-ui/components/callout"; A self-hosted Multica [server](/self-host-quickstart) reads its configuration from environment variables at startup — database, sign-in, email, storage, signup allowlists all live here. This page groups every variable by purpose: each section spells out **what happens if you leave it unset** and **which ones you must set in production**. For how to actually configure the auth-related ones, see [Sign-in and signup configuration](/auth-setup). -## The five required at startup +## Core server variables -These are the five you must think about before deploying — some have defaults that let the server start, but in production you should set all of them explicitly. +These are the core variables you must think about before deploying — some have defaults that let the server start, but in production you should set the required ones explicitly. | Variable | Default | Required in production? | |---|---|---| | `DATABASE_URL` | `postgres://multica:multica@localhost:5432/multica?sslmode=disable` | **Yes** | | `PORT` | `8080` | No (unless you change the port) | | `JWT_SECRET` | `multica-dev-secret-change-in-production` | **Yes** (the default is unsafe) | -| `APP_ENV` | empty | **Yes** (must be `production` — see the next section for the trap) | +| `APP_ENV` | empty | **Yes** (must be `production`) | | `FRONTEND_ORIGIN` | empty | **Yes** (self-host must set its own domain) | +| `MULTICA_DEV_VERIFICATION_CODE` | empty | No (must stay empty in production) | -**If `APP_ENV` is not set to `production`, anyone can sign in to any account using the code `888888`.** Multica has a development-only master code, `888888` — when `APP_ENV != "production"`, **any email** plus `888888` passes verification. The behavior is intentional for local development (no Resend dependency); **in production, failing to set `production` is equivalent to disabling auth entirely**. See [Sign-in and signup configuration → The 888888 trap](/auth-setup#the-888888-trap). +**Keep `MULTICA_DEV_VERIFICATION_CODE` empty in production.** A fixed local test code is disabled by default, but if you opt in with `MULTICA_DEV_VERIFICATION_CODE=888888`, anyone who can request a code can sign in with that fixed value while `APP_ENV` is non-production. The shortcut is ignored when `APP_ENV=production`. ### Database connection pool diff --git a/apps/docs/content/docs/environment-variables.zh.mdx b/apps/docs/content/docs/environment-variables.zh.mdx index a2388bc740..abde447ed7 100644 --- a/apps/docs/content/docs/environment-variables.zh.mdx +++ b/apps/docs/content/docs/environment-variables.zh.mdx @@ -7,20 +7,21 @@ import { Callout } from "fumadocs-ui/components/callout"; Multica 的 [自部署](/self-host-quickstart) 服务器启动时从环境变量读取配置——数据库、登录、邮件、存储、注册白名单都在这里配。这一页按用途分组给完整清单:每组说清楚**不设会怎样**、**生产必须设哪几个**。Auth 相关那几个怎么真正配见 [登录与注册配置](/auth-setup)。 -## 启动必填的五个 +## 核心 server 环境变量 -这五个是你部署前必须考虑的——有些有默认值能让 server 启动,但生产环境里你应该全部显式配。 +这些是你部署前必须考虑的核心变量——有些有默认值能让 server 启动,但生产环境里你应该显式配置必填项。 | 环境变量 | 默认值 | 生产必须设? | |---|---|---| | `DATABASE_URL` | `postgres://multica:multica@localhost:5432/multica?sslmode=disable` | **是** | | `PORT` | `8080` | 否(除非换端口)| | `JWT_SECRET` | `multica-dev-secret-change-in-production` | **是**(默认值不安全)| -| `APP_ENV` | 空 | **是**(必须 `production`——见下一节陷阱)| +| `APP_ENV` | 空 | **是**(必须 `production`)| | `FRONTEND_ORIGIN` | 空 | **是**(self-host 要填你自己的域名)| +| `MULTICA_DEV_VERIFICATION_CODE` | 空 | 否(生产必须保持为空)| -**`APP_ENV` 不设为 `production`,任何人都能用 `888888` 登录任何账号。** Multica 有一个开发用的主验证码(master code)`888888`——`APP_ENV != "production"` 时**任何邮箱**输 `888888` 都能通过。本地开发时故意留空方便调试;**生产环境一旦不设 `production`,等于 auth 完全失效**。详见 [登录与注册配置 → 888888 陷阱](/auth-setup#888888-陷阱)。 +**生产环境保持 `MULTICA_DEV_VERIFICATION_CODE` 为空。** 固定本地测试验证码默认关闭;如果你设置 `MULTICA_DEV_VERIFICATION_CODE=888888`,在 `APP_ENV` 非 production 时,任何能请求验证码的人都能用这个固定值登录。`APP_ENV=production` 时该快捷码会被忽略。 ### 数据库连接池 diff --git a/apps/docs/content/docs/getting-started/self-hosting.zh.mdx b/apps/docs/content/docs/getting-started/self-hosting.zh.mdx index 96700ba00d..acf33b9962 100644 --- a/apps/docs/content/docs/getting-started/self-hosting.zh.mdx +++ b/apps/docs/content/docs/getting-started/self-hosting.zh.mdx @@ -31,7 +31,7 @@ curl -fsSL https://raw.githubusercontent.com/multica-ai/multica/main/scripts/ins multica setup self-host ``` -This installs the CLI, checks out the latest self-host assets, pulls the official Multica images from GHCR, and configures everything for localhost. Then open http://localhost:3000 and pick a login method: configure `RESEND_API_KEY` in `.env` for email-based codes (recommended), or set `APP_ENV=development` in `.env` to enable the dev master code **`888888`**. See [Step 2 — Log In](#step-2--log-in) for details. +This installs the CLI, checks out the latest self-host assets, pulls the official Multica images from GHCR, and configures everything for localhost. Then open http://localhost:3000 and pick a login method: configure `RESEND_API_KEY` in `.env` for email-based codes (recommended), or leave Resend unset and copy the generated code from backend logs. See [Step 2 — Log In](#step-2--log-in) for details. If the self-host server is already running and you only need the CLI on a macOS/Linux machine, install it with Homebrew: `brew install multica-ai/tap/multica`. @@ -68,16 +68,16 @@ If you prefer running the Docker Compose steps manually: `cp .env.example .env`, ### Step 2 — Log In -Open http://localhost:3000. The Docker self-host stack defaults to `APP_ENV=production` (set in `docker-compose.selfhost.yml`), so the dev master code is **disabled by default** for safety on public deployments. Pick one of the following to log in: +Open http://localhost:3000. The Docker self-host stack defaults to `APP_ENV=production` (set in `docker-compose.selfhost.yml`), and there is no fixed verification code by default. Pick one of the following to log in: - **Recommended (production):** configure `RESEND_API_KEY` in `.env`, then restart the backend. Real verification codes will be sent to the email address you enter. See [Configuration](#configuration) below. -- **Evaluation / private network:** set `APP_ENV=development` in `.env` and restart the backend. Verification code **`888888`** will then work for any email address. -- **Without configuring either:** the verification code is generated server-side and printed to the backend container logs (look for `[DEV] Verification code for ...:`). Useful for one-off testing on a single machine. +- **Without email configured:** the verification code is generated server-side and printed to the backend container logs (look for `[DEV] Verification code for ...:`). Useful for one-off testing on a single machine. +- **Deterministic local/private testing:** set `APP_ENV=development` and `MULTICA_DEV_VERIFICATION_CODE=888888` in `.env`, then restart the backend. This fixed code is ignored when `APP_ENV=production`. Changes to `ALLOW_SIGNUP` and `GOOGLE_CLIENT_ID` also take effect after restarting the backend / compose stack. The web UI reads both from `/api/config` at runtime, so no web rebuild is needed. -**Warning:** do **not** set `APP_ENV=development` on a publicly reachable instance — anyone who knows an email address can then log in with `888888`. +**Warning:** do **not** set `MULTICA_DEV_VERIFICATION_CODE` on a publicly reachable instance — anyone who knows an email address can then log in with that fixed code. ### Step 3 — Install CLI & Start Daemon diff --git a/apps/docs/content/docs/how-multica-works.mdx b/apps/docs/content/docs/how-multica-works.mdx index 413c84392c..659c34ca29 100644 --- a/apps/docs/content/docs/how-multica-works.mdx +++ b/apps/docs/content/docs/how-multica-works.mdx @@ -13,7 +13,7 @@ Multica is a **distributed** platform. The web interface you see is just the fro - **Multica server** — the workspaces, issue lists, and comment threads you see all live in its database. It's also a WebSocket hub that pushes real-time updates between you and your teammates. It does **not** execute any agent tasks. - **Daemon** — part of the Multica CLI, running on your own machine. On start it detects which AI coding tools are installed locally, registers with the server, and begins polling for tasks every 3 seconds and sending heartbeats every 15 seconds. -- **AI coding tools** — one of the ten (or several in parallel): [Claude Code](/providers#claude-code), [Codex](/providers#codex), [Cursor](/providers#cursor), [Copilot](/providers#copilot), [Gemini](/providers#gemini), [Hermes](/providers#hermes), [Kimi](/providers#kimi), [OpenCode](/providers#opencode), [OpenClaw](/providers#openclaw), [Pi](/providers#pi). Once the daemon has picked up a task, it uses these tools to actually do the work. +- **AI coding tools** — one of the eleven (or several in parallel): [Claude Code](/providers#claude-code), [Codex](/providers#codex), [Cursor](/providers#cursor), [Copilot](/providers#copilot), [Gemini](/providers#gemini), [Hermes](/providers#hermes), [Kimi](/providers#kimi), [Kiro CLI](/providers#kiro-cli), [OpenCode](/providers#opencode), [OpenClaw](/providers#openclaw), [Pi](/providers#pi). Once the daemon has picked up a task, it uses these tools to actually do the work. Because the toolchain stays local, **your API keys, code directories, and authorized tools** are only ever used on your machine — the Multica server never sees any of them. This holds whether you self-host or use Cloud. diff --git a/apps/docs/content/docs/how-multica-works.zh.mdx b/apps/docs/content/docs/how-multica-works.zh.mdx index 705889bfac..280eba860e 100644 --- a/apps/docs/content/docs/how-multica-works.zh.mdx +++ b/apps/docs/content/docs/how-multica-works.zh.mdx @@ -13,7 +13,7 @@ Multica 是一个**分布式**平台。你看到的 Web 界面只是前台—— - **Multica 服务器**——你看到的工作区、issue 列表、评论线都存在它的数据库里。它同时是 WebSocket hub,把你和同事之间的实时更新推送过去。它**不**执行任何智能体任务。 - **守护进程**(daemon)——Multica CLI 的一部分,跑在你自己的机器上。启动后它探测本地装了哪些 AI 编程工具,注册到 server,开始每 3 秒领一次任务、每 15 秒发一次心跳。 -- **AI 编程工具**——[Claude Code](/providers#claude-code)、[Codex](/providers#codex)、[Cursor](/providers#cursor)、[Copilot](/providers#copilot)、[Gemini](/providers#gemini)、[Hermes](/providers#hermes)、[Kimi](/providers#kimi)、[OpenCode](/providers#opencode)、[OpenClaw](/providers#openclaw)、[Pi](/providers#pi) 十款之一(或多款并存)。守护进程领到任务后,用这些工具真正去写代码。 +- **AI 编程工具**——[Claude Code](/providers#claude-code)、[Codex](/providers#codex)、[Cursor](/providers#cursor)、[Copilot](/providers#copilot)、[Gemini](/providers#gemini)、[Hermes](/providers#hermes)、[Kimi](/providers#kimi)、[Kiro CLI](/providers#kiro-cli)、[OpenCode](/providers#opencode)、[OpenClaw](/providers#openclaw)、[Pi](/providers#pi) 11 款之一(或多款并存)。守护进程领到任务后,用这些工具真正去写代码。 工具链在本地的结果:**你的 API 密钥、代码目录、已授权的工具**都只在本地使用;Multica 服务器一个都看不到。自部署还是用 Cloud 都不改变这一点。 diff --git a/apps/docs/content/docs/index.mdx b/apps/docs/content/docs/index.mdx index ad94c18002..ff2e9a731e 100644 --- a/apps/docs/content/docs/index.mdx +++ b/apps/docs/content/docs/index.mdx @@ -13,7 +13,7 @@ This page explains where agents run and the ways you can start using Multica. Agents do **not** execute tasks on Multica's servers. Multica currently supports one runtime model: -- **Local [daemon](/daemon-runtimes)** — you run `multica daemon` on your own machine, and it drives the [AI coding tools](/providers) installed locally. Ten are built in today: [Claude Code](/providers#claude-code), [Codex](/providers#codex), [Cursor](/providers#cursor), [Copilot](/providers#copilot), [Gemini](/providers#gemini), [Hermes](/providers#hermes), [Kimi](/providers#kimi), [OpenCode](/providers#opencode), [OpenClaw](/providers#openclaw), [Pi](/providers#pi). Your API keys, toolchain, and code directories stay on your machine. +- **Local [daemon](/daemon-runtimes)** — you run `multica daemon` on your own machine, and it drives the [AI coding tools](/providers) installed locally. Eleven are built in today: [Claude Code](/providers#claude-code), [Codex](/providers#codex), [Cursor](/providers#cursor), [Copilot](/providers#copilot), [Gemini](/providers#gemini), [Hermes](/providers#hermes), [Kimi](/providers#kimi), [Kiro CLI](/providers#kiro-cli), [OpenCode](/providers#opencode), [OpenClaw](/providers#openclaw), [Pi](/providers#pi). Your API keys, toolchain, and code directories stay on your machine. **Cloud runtimes are coming**, currently waitlist-only. Once live, you won't need a local daemon — agent tasks will execute on Multica Cloud directly. Sign up on the [Downloads](https://multica.ai/download) page to get notified. diff --git a/apps/docs/content/docs/index.zh.mdx b/apps/docs/content/docs/index.zh.mdx index a298044dfc..a74ddc311a 100644 --- a/apps/docs/content/docs/index.zh.mdx +++ b/apps/docs/content/docs/index.zh.mdx @@ -13,7 +13,7 @@ Multica 是一个任务协作平台,让人类和 AI [智能体](/agents) 在 智能体执行任务**不**发生在 Multica 服务器上。目前 Multica 支持一种运行方式: -- **本地 [守护进程](/daemon-runtimes)** — 你在自己的机器上运行 `multica daemon`,由它调用本地安装的 [AI 编程工具](/providers)。目前内置十种:[Claude Code](/providers#claude-code)、[Codex](/providers#codex)、[Cursor](/providers#cursor)、[Copilot](/providers#copilot)、[Gemini](/providers#gemini)、[Hermes](/providers#hermes)、[Kimi](/providers#kimi)、[OpenCode](/providers#opencode)、[OpenClaw](/providers#openclaw)、[Pi](/providers#pi)。你的 API 密钥、工具链、代码目录都保留在本地。 +- **本地 [守护进程](/daemon-runtimes)** — 你在自己的机器上运行 `multica daemon`,由它调用本地安装的 [AI 编程工具](/providers)。目前内置 11 款:[Claude Code](/providers#claude-code)、[Codex](/providers#codex)、[Cursor](/providers#cursor)、[Copilot](/providers#copilot)、[Gemini](/providers#gemini)、[Hermes](/providers#hermes)、[Kimi](/providers#kimi)、[Kiro CLI](/providers#kiro-cli)、[OpenCode](/providers#opencode)、[OpenClaw](/providers#openclaw)、[Pi](/providers#pi)。你的 API 密钥、工具链、代码目录都保留在本地。 **云端运行时即将开放**,目前处于等待名单阶段。上线后,你无需在本地运行守护进程,即可在 Multica Cloud 上直接执行智能体任务。在 [下载页面](https://multica.ai/download) 登记邮箱以获取通知。 diff --git a/apps/docs/content/docs/providers.mdx b/apps/docs/content/docs/providers.mdx index 811d159fbf..a2ece008b1 100644 --- a/apps/docs/content/docs/providers.mdx +++ b/apps/docs/content/docs/providers.mdx @@ -1,11 +1,11 @@ --- title: AI coding tools matrix -description: Multica supports 10 AI coding tools; they implement the same interface, but the capability details diverge significantly. +description: Multica supports 11 AI coding tools; they implement the same interface, but the capability details diverge significantly. --- import { Callout } from "fumadocs-ui/components/callout"; -Multica ships with built-in support for **10 AI coding tools**. They all implement the same interface — queue, dispatch, execute, return results — so you can drive any of them from the same Multica board. **But the capability details diverge significantly**: whether session resumption actually works, whether MCP is supported, where skill files live, how models are selected. This page is the full matrix. +Multica ships with built-in support for **11 AI coding tools**. They all implement the same interface — queue, dispatch, execute, return results — so you can drive any of them from the same Multica board. **But the capability details diverge significantly**: whether session resumption actually works, whether MCP is supported, where skill files live, how models are selected. This page is the full matrix. For guidance on picking a tool when creating an agent, see [Creating and configuring agents](/agents-create). @@ -20,6 +20,7 @@ For guidance on picking a tool when creating an agent, see [Creating and configu | **Gemini** | Google | ❌ | ❌ | `.agent_context/skills/` | Static | | **Hermes** | Nous Research | ✅ | ❌ | `.agent_context/skills/` (fallback) | Dynamic discovery | | **Kimi** | Moonshot | ✅ | ❌ | `.kimi/skills/` | Dynamic discovery | +| **Kiro CLI** | Amazon | ✅ | ❌ | `.kiro/skills/` | Dynamic discovery | | **OpenCode** | SST | ✅ | ❌ | `.config/opencode/skills/` | Dynamic discovery | | **OpenClaw** | Open source | ✅ | ❌ | `.agent_context/skills/` (fallback) | Bound to the agent, can't be switched per task | | **Pi** | Inflection AI | ✅ (session is a file path) | ❌ | `.pi/skills/` | Dynamic discovery | @@ -28,7 +29,7 @@ For guidance on picking a tool when creating an agent, see [Creating and configu ### Claude Code -From Anthropic. **First choice for new users** — the most complete feature set: session resumption actually works, it's the **only one of the 10 that truly reads MCP configuration**, and it supports fine-tuning flags like `--max-turns` and `--append-system-prompt`. Requires an Anthropic API key. +From Anthropic. **First choice for new users** — the most complete feature set: session resumption actually works, it's the **only one of the 11 that truly reads MCP configuration**, and it supports fine-tuning flags like `--max-turns` and `--append-system-prompt`. Requires an Anthropic API key. ### Codex @@ -54,6 +55,10 @@ From Nous Research. Uses the ACP protocol (shares a transport with Kimi). Sessio From Moonshot, aimed at the Chinese market. Shares the ACP protocol with Hermes, but the skill path `.kimi/skills/` is Kimi CLI's native discovery mechanism — different from Hermes's fallback. +### Kiro CLI + +From Amazon. Uses ACP over stdio via `kiro-cli acp`. Session resumption works through ACP `session/load`, model selection works through `session/set_model`, and skills are copied into `.kiro/skills/` for native project-level discovery. + ### OpenCode From SST, open source. Dynamically discovers available models (scans the CLI's configuration file). Session resumption works. **Suitable for tinkerers who want to customize their model catalog.** @@ -72,7 +77,7 @@ The session resumption mechanism is covered in [Tasks](/tasks#can-a-task-continu | Status | Tools | Meaning | |---|---|---| -| ✅ Really works | Claude Code, Copilot, Hermes, Kimi, OpenCode, OpenClaw, Pi | Pass the resume id and it continues from the previous context | +| ✅ Really works | Claude Code, Copilot, Hermes, Kimi, Kiro CLI, OpenCode, OpenClaw, Pi | Pass the resume id and it continues from the previous context | | ⚠️ Code exists but unreachable | Codex, Cursor | Resume paths exist in the code but aren't actually reached (Codex silently falls back; Cursor doesn't return session id) — **treat as unsupported** | | ❌ None | Gemini | The CLI has no resume mechanism | @@ -80,7 +85,7 @@ The session resumption mechanism is covered in [Tasks](/tasks#can-a-task-continu ## MCP configuration: only Claude Code actually reads it -**Of the 10 tools, only Claude Code actually consumes `mcp_config`**. The other 9 accept the field but **completely ignore it** — no error, no warning, the config just has no effect. +**Of the 11 tools, only Claude Code actually consumes `mcp_config`**. The other 10 accept the field but **completely ignore it** — no error, no warning, the config just has no effect. If you set `mcp_config` in an agent configuration but pick a tool other than Claude Code, your MCP servers have **no effect** on that agent. MCP integration currently covers Claude Code only. @@ -97,6 +102,7 @@ Each tool uses **its own** skill discovery path. Before a task runs, the Multica | Copilot | `.github/skills/` | ✅ Native | | Cursor | `.cursor/skills/` | ✅ Native | | Kimi | `.kimi/skills/` | ✅ Native | +| Kiro CLI | `.kiro/skills/` | ✅ Native | | OpenCode | `.config/opencode/skills/` | ✅ Native | | Pi | `.pi/skills/` | ✅ Native | | Gemini | `.agent_context/skills/` | ⚠️ Generic fallback | diff --git a/apps/docs/content/docs/providers.zh.mdx b/apps/docs/content/docs/providers.zh.mdx index d230f2c7ea..98b5105895 100644 --- a/apps/docs/content/docs/providers.zh.mdx +++ b/apps/docs/content/docs/providers.zh.mdx @@ -1,11 +1,11 @@ --- title: AI 编程工具对照 -description: Multica 支持 10 款 AI 编程工具;它们实现同一套接口,但能力细节差异很大。 +description: Multica 支持 11 款 AI 编程工具;它们实现同一套接口,但能力细节差异很大。 --- import { Callout } from "fumadocs-ui/components/callout"; -Multica 内置支持 **10 款 AI 编程工具**。它们都实现了同一套接口——排队、派发、执行、结果回传,所以你可以从 Multica 的同一个看板上指挥任意一款。**但它们在能力细节上差异很大**:会话恢复是否真用、是否支持 MCP、skill 文件该放在哪里、模型怎么选。这一页是完整对照。 +Multica 内置支持 **11 款 AI 编程工具**。它们都实现了同一套接口——排队、派发、执行、结果回传,所以你可以从 Multica 的同一个看板上指挥任意一款。**但它们在能力细节上差异很大**:会话恢复是否真用、是否支持 MCP、skill 文件该放在哪里、模型怎么选。这一页是完整对照。 创建智能体时挑选工具的指引见 [创建和配置智能体](/agents-create)。 @@ -20,6 +20,7 @@ Multica 内置支持 **10 款 AI 编程工具**。它们都实现了同一套接 | **Gemini** | Google | ❌ | ❌ | `.agent_context/skills/` | 静态 | | **Hermes** | Nous Research | ✅ | ❌ | `.agent_context/skills/` (fallback)| 动态发现 | | **Kimi** | Moonshot | ✅ | ❌ | `.kimi/skills/` | 动态发现 | +| **Kiro CLI** | Amazon | ✅ | ❌ | `.kiro/skills/` | 动态发现 | | **OpenCode** | SST | ✅ | ❌ | `.config/opencode/skills/` | 动态发现 | | **OpenClaw** | 开源项目 | ✅ | ❌ | `.agent_context/skills/` (fallback)| 绑定在智能体上,不能在任务里切换 | | **Pi** | Inflection AI | ✅(session 为文件路径)| ❌ | `.pi/skills/` | 动态发现 | @@ -28,7 +29,7 @@ Multica 内置支持 **10 款 AI 编程工具**。它们都实现了同一套接 ### Claude Code -Anthropic 出品。**新用户首选**——功能最完整:会话恢复真用,是 **10 款里唯一真读 MCP 配置**的工具,支持 `--max-turns`、`--append-system-prompt` 等细调参数。需要一个 Anthropic API 密钥。 +Anthropic 出品。**新用户首选**——功能最完整:会话恢复真用,是 **11 款里唯一真读 MCP 配置**的工具,支持 `--max-turns`、`--append-system-prompt` 等细调参数。需要一个 Anthropic API 密钥。 ### Codex @@ -54,6 +55,10 @@ Nous Research 出品。使用 ACP 协议(和 Kimi 共享传输层)。会话 Moonshot 出品,中国市场向。和 Hermes 共享 ACP 协议,但 skill 路径 `.kimi/skills/` 是 Kimi CLI 的原生发现机制——和 Hermes 的 fallback 不一样。 +### Kiro CLI + +Amazon 出品。通过 `kiro-cli acp` 使用 ACP stdio 协议。会话恢复走 ACP `session/load`,模型选择走 `session/set_model`,skill 会复制到 `.kiro/skills/` 让 Kiro 做项目级原生发现。 + ### OpenCode SST 出品,开源。动态发现可用模型(扫 CLI 的配置文件)。会话恢复真用。**适合爱折腾、想自定义模型目录**的开发者。 @@ -72,7 +77,7 @@ Inflection AI 出品,极简主义。**会话恢复机制特殊**——session | 状态 | 工具 | 含义 | |---|---|---| -| ✅ 真用 | Claude Code、Copilot、Hermes、Kimi、OpenCode、OpenClaw、Pi | 传 resume id,会从上次上下文接着继续 | +| ✅ 真用 | Claude Code、Copilot、Hermes、Kimi、Kiro CLI、OpenCode、OpenClaw、Pi | 传 resume id,会从上次上下文接着继续 | | ⚠️ 代码存在但不可达 | Codex、Cursor | 代码里有 resume 路径但实际走不到(Codex 静默回落、Cursor session id 不回传)—— **当作不支持** | | ❌ 无 | Gemini | CLI 无 resume 机制 | @@ -80,7 +85,7 @@ Inflection AI 出品,极简主义。**会话恢复机制特殊**——session ## MCP 配置:只有 Claude Code 真的读 -**10 款工具里只有 Claude Code 实际消费 `mcp_config`**。其他 9 款会接收这个字段但**完全忽略**——不报错、不警告,只是配置不生效。 +**11 款工具里只有 Claude Code 实际消费 `mcp_config`**。其他 10 款会接收这个字段但**完全忽略**——不报错、不警告,只是配置不生效。 如果你在智能体配置里设置了 `mcp_config`,但选了 Claude Code 之外的工具,你的 MCP server 对这个智能体**没有效果**。目前的 MCP 集成只覆盖 Claude Code。 @@ -97,6 +102,7 @@ Inflection AI 出品,极简主义。**会话恢复机制特殊**——session | Copilot | `.github/skills/` | ✅ 原生 | | Cursor | `.cursor/skills/` | ✅ 原生 | | Kimi | `.kimi/skills/` | ✅ 原生 | +| Kiro CLI | `.kiro/skills/` | ✅ 原生 | | OpenCode | `.config/opencode/skills/` | ✅ 原生 | | Pi | `.pi/skills/` | ✅ 原生 | | Gemini | `.agent_context/skills/` | ⚠️ 通用 fallback | diff --git a/apps/docs/content/docs/self-host-quickstart.mdx b/apps/docs/content/docs/self-host-quickstart.mdx index e5be0cf432..6acb73e188 100644 --- a/apps/docs/content/docs/self-host-quickstart.mdx +++ b/apps/docs/content/docs/self-host-quickstart.mdx @@ -45,19 +45,19 @@ Once it's up: - **Frontend**: [http://localhost:3000](http://localhost:3000) - **Backend**: [http://localhost:8080](http://localhost:8080) -## 2. Important: set `APP_ENV` to `production` +## 2. Important: keep production safety on -**`docker-compose.selfhost.yml` sets `APP_ENV` to `production` by default** — this prevents the development "master code `888888`" from being enabled on an instance you've exposed to the public internet. +**`docker-compose.selfhost.yml` sets `APP_ENV` to `production` by default** and leaves `MULTICA_DEV_VERIFICATION_CODE` empty, so there is no fixed code on public instances. -**But if your `.env` leaves `APP_ENV` empty or sets it to another value**, `888888` is enabled — **anyone can log in as any email by typing `888888` as the verification code**. See [Auth setup → The 888888 trap](/auth-setup#the-888888-trap). +Only set `MULTICA_DEV_VERIFICATION_CODE` for local or private test automation. If a fixed code is enabled while `APP_ENV` is non-production, anyone who can request a code can sign in with that fixed value. See [Auth setup → Fixed local testing codes](/auth-setup#fixed-local-testing-codes). -Before any public deployment, make sure `.env` has `APP_ENV=production`. +Before any public deployment, make sure `.env` has `APP_ENV=production` and `MULTICA_DEV_VERIFICATION_CODE` is empty. ## 3. Configure the email service (optional but recommended) -Without email configured, your users can't receive verification codes — **unless `APP_ENV != production`, in which case `888888` works** (see the warning above). +Without email configured, your users can't receive verification codes by email; the server prints generated codes to stdout instead. To actually send verification emails: @@ -80,6 +80,7 @@ Open [http://localhost:3000](http://localhost:3000): - Enter your email - Grab the verification code from the Resend email (or, if you haven't configured Resend, from the server container stdout — look for the `[DEV] Verification code` line) +- Do not use `888888` unless you explicitly set `MULTICA_DEV_VERIFICATION_CODE=888888` on a non-production private instance - Log in and create your first workspace ## 5. Point the CLI at your own server @@ -115,4 +116,4 @@ Same flow as Cloud — see [Cloud quickstart → Steps 5-6](/cloud-quickstart#5- - [Environment variables](/environment-variables) — full env reference - [Auth setup](/auth-setup) — Resend / OAuth / signup allowlist in detail - [Troubleshooting](/troubleshooting) — start here when things go wrong -- [Desktop app](/desktop-app) — the desktop app can also connect to your self-hosted backend +- [Desktop app](/desktop-app) — released Desktop builds connect to Multica Cloud only; using Desktop with self-host requires a custom build (see the callout in the desktop-app page) diff --git a/apps/docs/content/docs/self-host-quickstart.zh.mdx b/apps/docs/content/docs/self-host-quickstart.zh.mdx index b333fd8ebd..8321de6484 100644 --- a/apps/docs/content/docs/self-host-quickstart.zh.mdx +++ b/apps/docs/content/docs/self-host-quickstart.zh.mdx @@ -44,19 +44,19 @@ make selfhost - **前端**:[http://localhost:3000](http://localhost:3000) - **后端**:[http://localhost:8080](http://localhost:8080) -## 2. 重要:改 `APP_ENV` 成 `production` +## 2. 重要:保持生产安全配置 -**`docker-compose.selfhost.yml` 默认把 `APP_ENV` 设成 `production`**——这防止开发用的"万能验证码 `888888`"在你公网暴露的实例上启用。 +**`docker-compose.selfhost.yml` 默认把 `APP_ENV` 设成 `production`**,并让 `MULTICA_DEV_VERIFICATION_CODE` 为空,所以公网实例默认没有固定验证码。 -**但如果你的 `.env` 里把 `APP_ENV` 留空或改成其他值**,`888888` 会被启用——**任何人输入任何邮箱 + `888888` 都能登录**。详见 [登录与注册配置 → 888888 陷阱](/auth-setup#888888-陷阱)。 +只在本地或私有测试自动化里设置 `MULTICA_DEV_VERIFICATION_CODE`。如果在 `APP_ENV` 非 production 时启用了固定验证码,任何能请求验证码的人都能用这个固定值登录。详见 [登录与注册配置 → 固定本地测试验证码](/auth-setup#固定本地测试验证码)。 -公网部署前一定检查 `.env` 里 `APP_ENV=production`。 +公网部署前一定检查 `.env` 里 `APP_ENV=production`,且 `MULTICA_DEV_VERIFICATION_CODE` 为空。 ## 3. 配置邮件服务(可选但推荐) -如果不配邮件,你的用户无法收到验证码——**但如果 `APP_ENV != production` 你可以用 `888888` 登录**(见上方警告)。 +如果不配邮件,用户无法通过邮件收到验证码;server 会把生成的验证码打印到 stdout。 要真的发验证码邮件: @@ -79,6 +79,7 @@ make selfhost - 输入你的邮箱 - 从 Resend 邮件里拿验证码(或者前面没配 Resend 的话从 server 容器的 stdout 里抄 `[DEV] Verification code` 这行) +- 不要直接使用 `888888`;只有在非 production 私有实例上显式设置 `MULTICA_DEV_VERIFICATION_CODE=888888` 后它才会生效 - 登录后创建第一个工作区 ## 5. 连接命令行工具到你自己的 server @@ -114,4 +115,4 @@ multica setup self-host - [环境变量](/environment-variables) —— 完整 env 清单 - [登录与注册配置](/auth-setup) —— Resend / OAuth / 注册白名单详细配置 - [故障排查](/troubleshooting) —— 遇到问题先来这里 -- [桌面应用](/desktop-app) —— 桌面应用也能连你的自部署后端 +- [桌面应用](/desktop-app) —— 发布版 Desktop 只连 Multica Cloud;要让 Desktop 连自部署后端需要自行构建(详见 desktop-app 页的提示) diff --git a/apps/docs/content/docs/skills.mdx b/apps/docs/content/docs/skills.mdx index 5d799d6cf3..1971a56e9e 100644 --- a/apps/docs/content/docs/skills.mdx +++ b/apps/docs/content/docs/skills.mdx @@ -64,4 +64,4 @@ By now you know what an agent is, how to create one, and how to attach skills. T - [Daemon and runtimes](/daemon-runtimes) — where agents actually run, and how to tell online from offline - [Executing tasks](/tasks) — the full lifecycle of one "agent work session" -- [AI coding tools comparison](/providers) — full comparison of all 10 tools (including each one's skill injection path) +- [AI coding tools comparison](/providers) — full comparison of all 11 tools (including each one's skill injection path) diff --git a/apps/docs/content/docs/skills.zh.mdx b/apps/docs/content/docs/skills.zh.mdx index ef516b85f1..47578371ab 100644 --- a/apps/docs/content/docs/skills.zh.mdx +++ b/apps/docs/content/docs/skills.zh.mdx @@ -64,4 +64,4 @@ Skill 导入后需要**挂载到具体的智能体**才会生效。一个智能 - [守护进程与运行时](/daemon-runtimes) —— 智能体到底跑在哪、怎么判断在线 / 离线 - [执行任务](/tasks) —— 一次"智能体工作"的完整生命周期 -- [AI 编程工具对照](/providers) —— 10 款工具的完整对比(含每款的 Skill 注入路径) +- [AI 编程工具对照](/providers) —— 11 款工具的完整对比(含每款的 Skill 注入路径) diff --git a/apps/docs/content/docs/tasks.mdx b/apps/docs/content/docs/tasks.mdx index b10596ffc2..5952e80f8a 100644 --- a/apps/docs/content/docs/tasks.mdx +++ b/apps/docs/content/docs/tasks.mdx @@ -100,7 +100,7 @@ Multica pins the session ID **twice** during a task: once at the start (when the But **which AI coding tools actually support this** varies a lot: -- ✅ **Real support** — Claude Code, Copilot, Hermes, Kimi, OpenCode, OpenClaw, Pi +- ✅ **Real support** — Claude Code, Copilot, Hermes, Kimi, Kiro CLI, OpenCode, OpenClaw, Pi - ⚠️ **Code exists but unusable** — Codex, Cursor - ❌ **No support** — Gemini @@ -108,5 +108,5 @@ See [Providers Matrix → Session resumption](/providers#session-resumption-who- ## Next -- [Providers Matrix](/providers) — capability differences across the 10 AI coding tools (including the exact session-resumption status) +- [Providers Matrix](/providers) — capability differences across the 11 AI coding tools (including the exact session-resumption status) - [Assigning issues to agents](/assigning-issues) / [@-mentioning agents in comments](/mentioning-agents) / [Chat](/chat) / [Autopilots](/autopilots) — the four ways to trigger a task diff --git a/apps/docs/content/docs/tasks.zh.mdx b/apps/docs/content/docs/tasks.zh.mdx index 916716d8ce..f682ef5e0f 100644 --- a/apps/docs/content/docs/tasks.zh.mdx +++ b/apps/docs/content/docs/tasks.zh.mdx @@ -100,7 +100,7 @@ Multica 在任务过程中**两次**保存会话 ID——任务一开始(AI 但**哪些 AI 编程工具真的支持**差别很大: -- ✅ **真支持**——Claude Code、Copilot、Hermes、Kimi、OpenCode、OpenClaw、Pi +- ✅ **真支持**——Claude Code、Copilot、Hermes、Kimi、Kiro CLI、OpenCode、OpenClaw、Pi - ⚠️ **代码看起来支持但实际不可用**——Codex、Cursor - ❌ **不支持**——Gemini @@ -108,5 +108,5 @@ Multica 在任务过程中**两次**保存会话 ID——任务一开始(AI ## 下一步 -- [Providers Matrix](/providers) —— 10 款 AI 编程工具的能力差异对照(包括会话恢复的精确状态) +- [Providers Matrix](/providers) —— 11 款 AI 编程工具的能力差异对照(包括会话恢复的精确状态) - [分配 issue 给智能体](/assigning-issues) / [在评论里 @智能体](/mentioning-agents) / [聊天](/chat) / [Autopilots](/autopilots) —— 触发执行任务的四种方式 diff --git a/apps/docs/content/docs/troubleshooting.mdx b/apps/docs/content/docs/troubleshooting.mdx index 2b0a0a9be4..1c8864e3c2 100644 --- a/apps/docs/content/docs/troubleshooting.mdx +++ b/apps/docs/content/docs/troubleshooting.mdx @@ -108,28 +108,29 @@ On the server side (self-host), grep for `"no_tasks"` / `"no_capacity"` to see t - Domain not verified → run the DNS verification flow in the Resend console (add SPF / DKIM records) - In an emergency (internal testing) → copy the code printed under `[DEV]` from the server logs -## Verification code `888888` doesn't work +## Fixed local test code doesn't work -**Symptom**: on a self-hosted instance, you try to sign in with the development-only master code `888888` and it's rejected with `invalid or expired code`. +**Symptom**: on a self-hosted instance, you try to sign in with a fixed local test code such as `888888` and it's rejected with `invalid or expired code`. **Likely causes** (mutually exclusive): -1. **`APP_ENV=production`** — this is the **correct** production configuration; `888888` is **disabled** when `APP_ENV=production`. Intentional design, not a bug -2. **You received a real code via Resend** — if Resend is configured, the server sent an actual email; `888888` is only a dev fallback +1. **`MULTICA_DEV_VERIFICATION_CODE` is empty** — fixed codes are disabled by default +2. **`APP_ENV=production`** — this is the **correct** production configuration; fixed local test codes are ignored in production +3. **The configured code is not 6 digits** — the shortcut only accepts a 6-digit value **How to diagnose**: ```bash -cat .env | grep APP_ENV # inspect current config -docker exec env | grep APP_ENV # docker deployment +cat .env | grep -E 'APP_ENV|MULTICA_DEV_VERIFICATION_CODE' +docker exec env | grep -E 'APP_ENV|MULTICA_DEV_VERIFICATION_CODE' ``` Check your inbox (including spam) for the real verification code. **How to fix**: -- In production, you shouldn't be using `888888` at all — configure Resend and use real codes -- **For local development or internal testing**, if you need `888888`, ensure `APP_ENV` is unset or not `production` — but **never** run a public instance this way (see [Sign-in and signup configuration → The 888888 trap](/auth-setup#the-888888-trap)) +- In production, leave `MULTICA_DEV_VERIFICATION_CODE` empty — configure Resend and use real codes +- For local development or internal testing, either copy the generated code from server logs or set `APP_ENV=development` plus `MULTICA_DEV_VERIFICATION_CODE=888888` — never enable a fixed code on a public instance (see [Sign-in and signup configuration → Fixed local testing codes](/auth-setup#fixed-local-testing-codes)) ## Port conflicts diff --git a/apps/docs/content/docs/troubleshooting.zh.mdx b/apps/docs/content/docs/troubleshooting.zh.mdx index 5b3390e34a..ef73228dd3 100644 --- a/apps/docs/content/docs/troubleshooting.zh.mdx +++ b/apps/docs/content/docs/troubleshooting.zh.mdx @@ -108,28 +108,29 @@ multica issue show # 看 task 历史 - 域名没验证 → Resend console 里走 DNS 验证流程(加 SPF / DKIM 记录) - 紧急情况下(如内部测试)→ 从 server 日志里抄 `[DEV]` 打印出的验证码 -## 验证码是 `888888` 但登不进去 +## 固定本地测试验证码登不进去 -**症状**:自部署实例,想用开发用的主验证码 `888888` 登录,但被拒 `invalid or expired code`。 +**症状**:自部署实例,想用 `888888` 这类固定本地测试验证码登录,但被拒 `invalid or expired code`。 -**可能原因**(这俩互斥): +**可能原因**(互斥): -1. **`APP_ENV=production`** —— 这正是你**应该**的生产配置;`888888` 在 `APP_ENV=production` 时**被禁用**。这是刻意设计,不是 bug -2. **你在 Resend 收到了真实验证码** —— 如果 Resend 已配,server 实际发了真邮件,`888888` 只作为 dev fallback +1. **`MULTICA_DEV_VERIFICATION_CODE` 为空** —— 固定验证码默认关闭 +2. **`APP_ENV=production`** —— 这是正确的生产配置;固定本地测试验证码在 production 中会被忽略 +3. **配置的验证码不是 6 位数字** —— 这个快捷码只接受 6 位数字 **怎么查**: ```bash -cat .env | grep APP_ENV # 看当前配置 -docker exec env | grep APP_ENV # docker 部署 +cat .env | grep -E 'APP_ENV|MULTICA_DEV_VERIFICATION_CODE' +docker exec env | grep -E 'APP_ENV|MULTICA_DEV_VERIFICATION_CODE' ``` 检查邮箱(含 spam)看有没有收到真实验证码。 **怎么修**: -- 生产环境你本来就不该用 `888888`—— 配好 Resend 用真实验证码 -- **本地开发或内网测试**若需要 `888888`,确保 `APP_ENV` 未设或不是 `production`——但**绝对不要**这样跑公网实例(详见 [登录与注册配置 → 888888 陷阱](/auth-setup#888888-陷阱)) +- 生产环境保持 `MULTICA_DEV_VERIFICATION_CODE` 为空,配好 Resend 后使用真实验证码 +- 本地开发或内网测试可以从 server 日志抄生成的验证码;如果需要 `888888`,设置 `APP_ENV=development` 和 `MULTICA_DEV_VERIFICATION_CODE=888888`。不要在公网实例启用固定验证码(详见 [登录与注册配置 → 固定本地测试验证码](/auth-setup#固定本地测试验证码)) ## 端口冲突 diff --git a/apps/web/app/[workspaceSlug]/(dashboard)/chat/page.tsx b/apps/web/app/[workspaceSlug]/(dashboard)/chat/page.tsx deleted file mode 100644 index dfeb9b95c0..0000000000 --- a/apps/web/app/[workspaceSlug]/(dashboard)/chat/page.tsx +++ /dev/null @@ -1 +0,0 @@ -export { ChatPage as default } from "@multica/views/chat"; diff --git a/apps/web/app/[workspaceSlug]/(dashboard)/layout.tsx b/apps/web/app/[workspaceSlug]/(dashboard)/layout.tsx index 2a4a7fe690..43769cc98e 100644 --- a/apps/web/app/[workspaceSlug]/(dashboard)/layout.tsx +++ b/apps/web/app/[workspaceSlug]/(dashboard)/layout.tsx @@ -3,6 +3,7 @@ import { DashboardLayout } from "@multica/views/layout"; import { MulticaIcon } from "@multica/ui/components/common/multica-icon"; import { SearchCommand, SearchTrigger } from "@multica/views/search"; +import { ChatFab, ChatWindow } from "@multica/views/chat"; import { StarterContentPrompt } from "@multica/views/onboarding"; export default function Layout({ children }: { children: React.ReactNode }) { @@ -13,6 +14,8 @@ export default function Layout({ children }: { children: React.ReactNode }) { extra={ <> + + } diff --git a/apps/web/features/landing/i18n/en.ts b/apps/web/features/landing/i18n/en.ts index a1a62535be..8529ba4798 100644 --- a/apps/web/features/landing/i18n/en.ts +++ b/apps/web/features/landing/i18n/en.ts @@ -283,6 +283,51 @@ export function createEnDict(allowSignup: boolean): LandingDict { fixes: "Bug Fixes", }, entries: [ + { + version: "0.2.19", + date: "2026-04-28", + title: "Kiro CLI Runtime, Desktop Notifications & Issue Label Filter", + changes: [], + features: [ + "Kiro CLI added as a local agent runtime option", + "macOS dock badge for unread issues, plus a native notification when the window is unfocused — click to jump straight to the issue", + "Issue list now supports filtering by label, combinable with status / priority / assignee", + "Daemon receives task wakeups over WebSocket — task startup latency drops noticeably", + ], + improvements: [ + "List and board status group headers are simpler, with clearer color cues", + "Author-written markdown links are preserved through linkify", + "Label attach now applies optimistically, no server round-trip wait", + "Mention picker's issue search refreshes as you type", + ], + fixes: [ + "Deleting a comment now cancels any agent task it triggered — no more ghost runs", + "Stalled Codex turns now time out instead of holding the slot", + "Windows daemon no longer dies when the parent shell closes", + "Agent-to-agent mention threads no longer cause feedback loops", + ], + }, + { + version: "0.2.18", + date: "2026-04-27", + title: "Issue Labels, Labs Tab & Sidebar Invite Dot", + changes: [], + features: [ + "Issue labels — color-code and filter issues across list, board and detail views", + "Labs settings tab for experimental toggles", + "Sidebar shows a dot when you have an unread workspace invite", + ], + improvements: [ + "Project picker now shows the selected project's icon", + "Sidebar parent items stay highlighted on detail pages", + "Self-hosted deployments correctly honor signup gating env vars", + ], + fixes: [ + "Agent comments preserve line breaks again", + "Desktop RPM no longer conflicts with Slack / VS Code on Fedora", + "Windows agents handle multi-line prompts correctly", + ], + }, { version: "0.2.17", date: "2026-04-26", diff --git a/apps/web/features/landing/i18n/zh.ts b/apps/web/features/landing/i18n/zh.ts index 2e403ec9b4..2ac6b8d019 100644 --- a/apps/web/features/landing/i18n/zh.ts +++ b/apps/web/features/landing/i18n/zh.ts @@ -283,6 +283,51 @@ export function createZhDict(allowSignup: boolean): LandingDict { fixes: "问题修复", }, entries: [ + { + version: "0.2.19", + date: "2026-04-28", + title: "Kiro CLI Runtime、桌面通知红点与 Issue 标签过滤", + changes: [], + features: [ + "新增 Kiro CLI 作为本地 Agent runtime 选项", + "macOS Dock 显示未读 Issue 红点;窗口失焦时弹出原生通知,点击直达对应 Issue", + "Issue 列表新增 Label 过滤,可与状态、优先级、Assignee 等组合使用", + "Daemon 通过 WebSocket 接收任务唤醒,任务起跑延迟显著降低", + ], + improvements: [ + "List/Board 视图的状态分组 header 更简洁,颜色提示更清晰", + "评论中作者手写的 Markdown 链接不再被自动 linkify 替换", + "添加 Label 现在乐观更新,无需等待服务端往返", + "Mention 输入时的 Issue 搜索结果会随着输入实时刷新", + ], + fixes: [ + "Comment 被删除时会取消已触发的 Agent 任务,不再有幽灵 run", + "Codex 卡住的对话回合会超时退出,避免占用配额", + "Windows Daemon 不再随父 shell 关闭被一同杀掉", + "Agent 之间的 mention 不再相互触发,避免死循环", + ], + }, + { + version: "0.2.18", + date: "2026-04-27", + title: "Issue 标签、Labs 设置页与邀请红点", + changes: [], + features: [ + "Issue 标签——给 Issue 上色、分类,列表、看板和详情页都能用", + "新增 Labs 设置页,集中放实验性开关", + "有未读工作区邀请时,侧边栏会出现红点提示", + ], + improvements: [ + "Project 选择器会显示当前所选 Project 的图标", + "进入详情页时,侧边栏父级菜单保持高亮", + "自托管部署正确读取注册放行相关的环境变量", + ], + fixes: [ + "Agent 评论的换行恢复正常显示", + "桌面端 RPM 不再与 Slack / VS Code 在 Fedora 上冲突", + "Windows 下 Agent 能正确处理多行 prompt", + ], + }, { version: "0.2.17", date: "2026-04-26", diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts index c4b7818fbb..9edff1c7ca 100644 --- a/apps/web/next-env.d.ts +++ b/apps/web/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/docker-compose.selfhost.yml b/docker-compose.selfhost.yml index bdbd01afac..acb0df47d3 100644 --- a/docker-compose.selfhost.yml +++ b/docker-compose.selfhost.yml @@ -40,6 +40,7 @@ services: environment: DATABASE_URL: postgres://${POSTGRES_USER:-multica}:${POSTGRES_PASSWORD:-multica}@postgres:5432/${POSTGRES_DB:-multica}?sslmode=disable PORT: "8080" + METRICS_ADDR: ${METRICS_ADDR:-} JWT_SECRET: ${JWT_SECRET:-change-me-in-production} FRONTEND_ORIGIN: ${FRONTEND_ORIGIN:-http://localhost:3000} CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-} @@ -55,7 +56,11 @@ services: CLOUDFRONT_PRIVATE_KEY: ${CLOUDFRONT_PRIVATE_KEY:-} COOKIE_DOMAIN: ${COOKIE_DOMAIN:-} APP_ENV: ${APP_ENV:-production} + MULTICA_DEV_VERIFICATION_CODE: ${MULTICA_DEV_VERIFICATION_CODE:-} MULTICA_APP_URL: ${MULTICA_APP_URL:-http://localhost:3000} + ALLOW_SIGNUP: ${ALLOW_SIGNUP:-true} + ALLOWED_EMAILS: ${ALLOWED_EMAILS:-} + ALLOWED_EMAIL_DOMAINS: ${ALLOWED_EMAIL_DOMAINS:-} restart: unless-stopped frontend: diff --git a/docs/docs-outline.md b/docs/docs-outline.md index 61b727fbdb..ded80ff7fc 100644 --- a/docs/docs-outline.md +++ b/docs/docs-outline.md @@ -147,7 +147,7 @@ multica issue assign --agent **关键约定**: -- **Callout**:`...`。warning 用于陷阱(如 888888),info 用于补充说明,tip 用于最佳实践 +- **Callout**:`...`。warning 用于陷阱(如固定测试验证码),info 用于补充说明,tip 用于最佳实践 - **代码块**:shell 命令用 \`\`\`bash;配置用 \`\`\`yaml / \`\`\`env;JSON 用 \`\`\`json - **Cross-link**:用 markdown 链接 `[显示文字](/docs/page-slug)`,不要写成 "详见 Tasks 章节" - **表格**:有 3 行以上对照才用表格,不要 1-2 行也用 @@ -723,11 +723,11 @@ multica issue assign --agent > **合并说明**:原 7.3 Auth Setup + 7.10 Signup Controls 合并。 -- **Source files**: `server/internal/handler/auth.go`(APP_ENV 判断 + checkSignupAllowed), `.env.example`(auth 相关注释) +- **Source files**: `server/internal/handler/auth.go`(固定测试验证码 + checkSignupAllowed), `.env.example`(auth 相关注释) - **目标读者**: self-host 运维 - **叙事位置**: self-host 的 auth 配置。 - **写什么**(1500-2000 字): - - **🚨 超醒目 warning block**:`APP_ENV=production` 必须设置,否则 verification code 恒为 `888888`(任何人登录任何账号) + - **🚨 超醒目 warning block**:生产环境必须保持 `MULTICA_DEV_VERIFICATION_CODE` 为空;固定测试验证码只用于非 production 私有测试 - Email + verification code 登录流程(依赖 Resend) - Google OAuth 配置步骤(创建 OAuth client → redirect URI → 填 env) - **Signup 白名单三层优先级决策树**: @@ -737,9 +737,9 @@ multica issue assign --agent - 典型场景:开放给公司域 / 限定几个邮箱 / 完全关闭 signup - 和邀请的关系(signup 关了也能通过邀请加人) - **不写**: JWT 实现、token 类型(§8.2 讲) -- **写前要验证**: APP_ENV 判断条件;OAuth 流程最新;Signup 优先级 +- **写前要验证**: 固定测试验证码的 env 条件;OAuth 流程最新;Signup 优先级 - **⚠️ 动笔前必读**: - - ⚠️⚠️ **888888 陷阱必须最醒目**(红色 warning block),这是 self-host 最大坑 + - ⚠️⚠️ **固定测试验证码风险必须最醒目**(红色 warning block),这是 self-host 最大坑 - OAuth 给外部步骤截图,别假设读者懂 GCP Console - 决策树建议用 Mermaid 图 - **Owner**: – @@ -754,7 +754,7 @@ multica issue assign --agent - 任务一直 queued(runtime offline / max_concurrent 满 / agent 配错) - WebSocket 连不上(cookie / CORS / proxy) - Email 没收到(Resend 未配置 → 看 stderr) - - 验证码收到是 888888 但不工作(APP_ENV 检查) + - 固定测试验证码不工作(APP_ENV / MULTICA_DEV_VERIFICATION_CODE 检查) - Port 冲突 - 日志位置:daemon / server / browser console - **不写**: 深度 bug report(去 GitHub issue) diff --git a/docs/docs-rewrite-plan.md b/docs/docs-rewrite-plan.md index d1dd6ed9a3..ecc07ab1a0 100644 --- a/docs/docs-rewrite-plan.md +++ b/docs/docs-rewrite-plan.md @@ -118,7 +118,7 @@ Multica = **人 + AI agent 在同一个看板上协作的任务管理平台**。 | Overview | 决策树(哪种部署模式适合你) | | Docker Compose deployment | `make selfhost` vs `make selfhost-build` | | Environment variables reference | 完整 env 表 | -| Authentication setup | **🚨 `APP_ENV != "production"` 会让 verification code 固定为 `888888`** —— 生产必须设置 `APP_ENV=production`;Google OAuth 配置;signup 白名单 | +| Authentication setup | **🚨 固定测试验证码必须显式设置 `MULTICA_DEV_VERIFICATION_CODE`,生产保持为空**;Google OAuth 配置;signup 白名单 | | Storage | S3 / CloudFront / 本地磁盘 | | Email | Resend 配置;**没配会落到 stderr** | | Upgrading | 版本升级 + migration 策略 | @@ -145,7 +145,7 @@ Installation / Authentication / Setup / Daemon / Workspace / Issue / Comment / A | 5 | Webhook autopilot trigger 字段建了但没接路由——第一版不文档化 | Autopilots | | 6 | custom_env merge 是覆盖而非合并——不能用 custom_env"取消设置"系统 env | Agents | | 7 | 旧 assignee 取消分配后不会被取消订阅 | Subscriptions | -| 8 | `APP_ENV != "production"` 时 verification code 恒为 `888888` | Self-Hosting → Auth | +| 8 | 固定本地测试验证码默认关闭;`MULTICA_DEV_VERIFICATION_CODE` 仅用于非 production 私有测试 | Self-Hosting → Auth | | 9 | Signup 白名单优先级:ALLOWED_EMAILS > ALLOWED_EMAIL_DOMAINS > ALLOW_SIGNUP | Self-Hosting → Auth | | 10 | One daemon ↔ many runtimes;one runtime ↔ ONE provider;同 daemon_id 重启复用旧 runtime 行 | Runtimes / Daemon | | 11 | Inbox 10 种类型,mention dedup 只在单 event 内生效 | Inbox | @@ -159,7 +159,7 @@ Installation / Authentication / Setup / Daemon / Workspace / Issue / Comment / A |---|---| | Mermaid diagram | 架构图 / task 生命周期 / trigger 流向 / autopilot 调度链 | | Tabs | Cloud / Self-Host / Desktop 并列;CLI / UI 并列 | -| Callouts(内置)| Tip / Warning / Note — **警告类密集用在 Agents 的 custom_env 和 Self-Host 的 888888** | +| Callouts(内置)| Tip / Warning / Note — **警告类密集用在 Agents 的 custom_env 和 Self-Host 的固定测试验证码** | | Code Tabs | API 调用多语言(Shell / Node / Go) | | Video / GIF | "Create your first agent"、"Follow an agent working" | | DeploymentPicker(定制)| 交互式决策树:回答 3 个问题 → 推荐部署路径 | diff --git a/docs/product-overview.md b/docs/product-overview.md index 2d9b9c2324..8c85f0daff 100644 --- a/docs/product-overview.md +++ b/docs/product-overview.md @@ -82,7 +82,7 @@ Multica 做的事: Multica **不自己训模型**,也不锁定某一家厂商。它是调度器,本地 daemon 会自动探测以下 CLI 工具并接入: -Claude Code · Codex · OpenClaw · OpenCode · Hermes · Gemini · Pi · Cursor Agent +Claude Code · Codex · OpenClaw · OpenCode · Hermes · Gemini · Pi · Cursor Agent · Kimi · Kiro CLI 每个 agent 可以配置自己的模型、API Key、环境变量、MCP 服务器。 @@ -244,7 +244,7 @@ Project 相比 Issue 是更高层的组织单元。一个 issue 可以不属于 #### 配置字段 - **基本信息**:名字、描述、头像(自动生成) -- **Provider**:选择底层是 Claude / Codex / OpenClaw / OpenCode / Hermes / Gemini / Pi / Cursor 中的哪一个 +- **Provider**:选择底层是 Claude / Codex / OpenClaw / OpenCode / Hermes / Gemini / Pi / Cursor / Kimi / Kiro 中的哪一个 - **Runtime**:绑定到哪个运行时(即在哪台机器上跑) - **Instructions 说明书**:agent 的系统提示词("你是一个资深工程师...") - **Custom Env**:要注入到 CLI 进程的环境变量(如 `ANTHROPIC_API_KEY`、`ANTHROPIC_BASE_URL`、`CLAUDE_CODE_USE_BEDROCK`) @@ -291,7 +291,7 @@ Agent 是 Multica 的灵魂。几乎所有功能都围绕"如何让一个 agent `multica` CLI 在用户的机器上启动一个后台进程(macOS launchd / Linux systemd / Windows 服务风格),它: -1. **自动探测** `$PATH` 上安装的 coding CLI(`claude`, `codex`, `opencode`, `openclaw`, `hermes`, `gemini`, `pi`, `cursor-agent`) +1. **自动探测** `$PATH` 上安装的 coding CLI(`claude`, `codex`, `opencode`, `openclaw`, `hermes`, `gemini`, `pi`, `cursor-agent`, `kimi`, `kiro-cli`) 2. 向 server **注册** 为一组 runtime(一个 CLI = 一个 runtime) 3. 每 3 秒 **轮询** 一次 server,有任务就认领 4. 每 15 秒 **心跳**(keepalive),报告自己还活着 diff --git a/packages/core/api/client.ts b/packages/core/api/client.ts index dea6971b45..a54af46ff6 100644 --- a/packages/core/api/client.ts +++ b/packages/core/api/client.ts @@ -55,6 +55,11 @@ import type { CreateProjectRequest, UpdateProjectRequest, ListProjectsResponse, + Label, + CreateLabelRequest, + UpdateLabelRequest, + ListLabelsResponse, + IssueLabelsResponse, PinnedItem, CreatePinRequest, PinnedItemType, @@ -1030,6 +1035,50 @@ export class ApiClient { await this.fetch(`/api/projects/${id}`, { method: "DELETE" }); } + // Labels + async listLabels(): Promise { + return this.fetch(`/api/labels`); + } + + async getLabel(id: string): Promise