# Soloco 客户端 daemon

本地执行权威(architecture §3.1)。维护任务树/状态机/事件/产物,调度,通过 Adapter 启停本地 agent runtime。

本目录以 `@soloco/client` 之名发布到 npm，是面向终端用户的客户端包；装好后命令行入口是 `soloco`。

## 安装与使用（npm）

需要 **Node ≥ 22.5**（用到内置 `node:sqlite`）。

```bash
# 全局安装（命令行入口是 soloco）—— 装完即自动起服务并打开仪表盘
npm i -g @soloco/client

soloco              # 后台启动 daemon + 本地 UI、开浏览器，随后还你终端
soloco status       # 是否在跑、端口 / 版本
soloco logs -f      # 跟随日志
soloco stop         # 停止后台 daemon
soloco update       # 更新到当前频道最新版（--check 只检查不装）
soloco --help       # 全部命令与环境变量
```

**装完即用**：交互式 `npm i -g` 后，`postinstall` 会自动在本地起 daemon 并打开浏览器，**无需手动敲命令**。CI / Docker / 非全局安装 / `npx` / SSH / 无显示 等场景**不会**自动启动（只打印提示），也**绝不**让安装失败。关掉自动启动：装前设 `SOLOCO_NO_AUTO_START=1`。

命令一览：

| 命令 | 作用 |
|---|---|
| `soloco` / `soloco start` | **后台**启动 daemon（同端口服务本地 UI + API @ `127.0.0.1:8751`），就绪后**自动开浏览器**到仪表盘，还你终端。`--no-open` 只打印地址不开浏览器 |
| `soloco run` | **前台**运行（dev / 调试 / 进程管理器；不后台化、不开浏览器） |
| `soloco stop` | 停止后台 daemon |
| `soloco status` | 查看运行状态（端口 / db / 版本） |
| `soloco logs [-f]` | 查看 / 跟随日志（`~/.soloco/daemon.log`） |
| `soloco update` | 查 npm 当前频道最新版，比当前新就全局重装并重启在跑的 daemon。`--check` 只检查不装；`--canary`/`--latest` 指定频道；`--pm <npm\|pnpm\|yarn\|bun>`（或 `SOLOCO_PM`）指定包管理器；`--no-restart` 不重启；`SOLOCO_NPM_REGISTRY` 覆盖源 |
| `soloco --version` / `--help` | 版本 / 帮助 |

> 不安装直接试：`npx @soloco/client run`（前台运行，`Ctrl-C` 退出）。
> 不想开浏览器：`soloco --no-open` 或设环境变量 `SOLOCO_NO_OPEN=1`。
>
> 已经在用预发布版（`*-canary.*`）想切到稳定版：`soloco update --latest`（或手动 `npm i -g @soloco/client@latest`）。

零配置即可运行：无 `.env` / 无 OS env 时，内置默认值生效，状态写入 `~/.soloco/`（见下）。
后台运行的 pid 记在 `~/.soloco/daemon.pid`、日志写 `~/.soloco/daemon.log`。
发布流程见仓库根 `docs/reference/2026-06-20-client-npm-packaging.md`。

## 配置：环境变量

daemon 面向 npm 用户必须零配置可启动：没有 `.env`、没有 OS env 时，代码内置默认值会自动生效，并把本地状态写入用户目录 `~/.soloco/`。环境变量只用于开发者、CI 或高级用户覆盖默认值。

所有用户默认值的代码入口是 `src/config/defaults.ts` 的 `DEFAULT_DAEMON_CONFIG`。新增默认值或修改默认值时先改这个对象，再让 env resolver 读取它；不要在调用点散落新的 fallback 数字或路径。

| 变量 | 默认 | 作用 |
|---|---|---|
| `SOLOCO_DAEMON_PORT` | `8751` | 本地 HTTP 端口 |
| `SOLOCO_SERVER_BASE_URL` | `https://app.soloco.cloud` | Soloco 产品 server 地址。npm 零配置默认使用 Vercel 上的产品后端；repo 本地 `.env` 推荐覆盖为 `http://localhost:3000`，配合根目录 `pnpm dev` 的本地 server / dev DB 闭环 |
| `SOLOCO_DAEMON_DB` | `~/.soloco/daemon.db` | SQLite 持久化路径（typed state） |
| `SOLOCO_FILES_DIR` | `~/.soloco/files` | 产物文件库（artifact 内容，内容寻址） |
| `SOLOCO_WORKSPACE_ROOT` | `~/.soloco/workspaces` | daemon 为未显式指定 cwd 的 Goal 创建隔离工作区的根目录。只改变默认工作区位置；显式 cwd 仍需通过可信根目录与 deny-list 校验 |
| `SOLOCO_PLANNING_ROUNDS` | `5` | 首轮 Conductor 规划挑刺轮数（设 `1` = 只出一版、不挑刺，最快；上限 5） |
| `SOLOCO_CYCLE_PLAN_ROUNDS` | `2` | 周期边界 replan 挑刺轮数（上限 2，保持 cycle 轻量） |
| `SOLOCO_MAX_CYCLES` | `100` | 周期循环上限（跑飞兜底，上限 100） |
| `SOLOCO_MAX_REPLANS` | `100` | 自治模式重规划次数上限（上限 100） |
| `SOLOCO_MAX_TASKS_PER_PLAN` | `25` | 单次 Conductor 计划的任务数软上限（launch/replan 共用）。超限先走软打回再规划一轮（计入精炼预算），预算耗尽仍超限则 fail-open 放行并留 `accept_oversized` 事件。下限抬到 `1`，刻意不设上限 clamp——本地单 owner 可自由调大 |
| `SOLOCO_RUN_TIMEOUT_MS` | （无） | 单次 agent 进程墙钟上限（毫秒）。**不设 = 永不超时**——长任务可能推理数小时，默认不杀进程；只有显式设置才启用 |
| `SOLOCO_EXPERIMENTAL_LIBRETTO_BROWSER` | `false` | 开启 Libretto anonymous-only Mission P0。仅用于开启后新建的无登录态、无密码/secret 的交互 Mission；关闭时不改变现有 Chrome 路线，开启后也绝不回退用户 Chrome |
| `SOLOCO_EXPERIMENTAL_LIBRETTO_BROWSER_ALLOWED_ORIGINS` | （空，严格模式不可用） | 逗号分隔的精确 HTTPS origin 白名单。仅本机操作者可配置；daemon 只允许这些 origin 的 `GET`/`HEAD` 请求，拒绝重定向或子资源越域、本机/IP、userinfo 与其它方法。 |
| `SOLOCO_EXPERIMENTAL_LIBRETTO_BROWSER_UNSAFE_ALLOW_ALL_PUBLIC_NETWORK` | `false` | **仅本机内部兼容性测试。** 必须同时开启实验总开关；允许匿名临时页面访问任意公网 HTTPS origin 和页面自行产生的 HTTP 方法，因此不再提供严格 origin/method containment。仍拒绝本机/IP、userinfo、WebSocket，不增加点击/填表工具；不得用于 secret、登录、生产或安全验收。 |
| `SOLOCO_EXPERIMENTAL_LIBRETTO_BROWSER_HEADED` | `false` | 为上述 P0 显示独立临时 Chromium 窗口。只供观察，不授予人工接管；每个 Run 结束即关闭 |
| `SOLOCO_WORKSPACE_ALLOWED_ROOTS` | （仅 `~/.soloco/workspaces`） | PATH 风格 `:` 分隔的绝对目录列表，登记**额外**允许的显式启动工作目录（#142）。显式 cwd 必须 realpath 落在某个允许根之内才被接受，否则拒绝——杜绝把 executor 的工作区写权限交给 `~/.ssh`/`$HOME` 等敏感目录。敏感目标（`$HOME`/`~/.ssh`/`~/.aws`/`~/.config`/`~/.soloco`/`/`）即便其父目录被登记也按精确路径拒绝（§6.4 workspace 隔离硬策略） |
| `SOLOCO_DEV_PANEL` | （未设置 = OFF） | **仅 dev**：开启「agent 调用录像 / 回放」调试面板（`/dev/mock/*` 路由 + 拦截）。**仅字面量 `true` 生效**（`SOLOCO_DEV_PANEL=true`）——门禁用严格 `=== 'true'` 而非通用 bool 解析，是为让 tsup 构建期 define 把门禁折成死分支、esbuild 再把 mock 代码从 `dist/cli.js` 物理剔除（详见 design-dev-mock-replay-panel §7.1）。所以 `1`/`on`/`yes`/`TRUE` 不会启用（与其它 daemon bool 变量不同，这是有意为之）。OFF 时 dev 路由不挂载；发布的 `soloco` 二进制根本不含回放能力。dev daemon（`pnpm dev:isolated`）不经 tsup，运行时读此 env |
| `SOLOCO_SCENARIOS_DIR` | `~/.soloco/scenarios`（dev 隔离态 `./.soloco/scenarios/`） | 录像剧本目录。默认跟 `daemonRuntimeDir()`，所以 dev 隔离（`SOLOCO_DAEMON_DB=./.soloco/daemon.db`）自动把剧本落进 `./.soloco/scenarios/`。**剧本含 agent 原始 stdout，可能嵌 workspace 文件内容——勿提交主仓**（结构化字段已过 `redactDeep` 脱敏，但 rawOutput 未必干净） |
| `SOLOCO_MODEL_CATALOG_URL` | （未设置 = 用内置列表） | 模型可选列表的远程覆盖：指向一个返回 `{ "claude": string[], "codex": string[] }` 的 HTTP 地址，`GET /model-catalog` 即改为下发该内容（5 分钟缓存），组合框下拉不发版即可更新。全程 fail-open：不可达 / 非 2xx / 格式错误 → 上次好值 → 内置默认，且首次失败会 `console.warn` 提示覆盖未生效 |

显式请求参数（如 cycle body 的 `maxCycles`/`maxReplans`）优先级高于环境变量；环境变量高于内置默认。详见 architecture §6.2/§7.3（轮数/上限是 Run Policy 配置项，不写死内核）。

## 设置方式

**三种，按部署环境择一：**

1. **零配置（npm 用户默认）**：`soloco` 即可运行；不需要 `.env`。状态默认进 `~/.soloco/daemon.db`，产物默认进 `~/.soloco/files`，Goal 工作区默认进 `~/.soloco/workspaces`。
2. **`.env` 文件（repo 本地开发推荐）**：复制 `.env.example` 为 `.env`，`pnpm dev` 会经 `--env-file=.env` 自动加载（Node 22 原生，无依赖）。`.env` 已被 gitignore。开发时如果希望把状态留在 repo 内，可设置 `SOLOCO_DAEMON_DB=./.soloco/daemon.db`。repo 本地开发默认把 `SOLOCO_SERVER_BASE_URL` 指向 `http://localhost:3000`，配合根目录 `pnpm dev` 启动的本地 server，把账号和云同步写入 dev 环境；调线上后端时再显式切回 `https://app.soloco.cloud`。
3. **命令行内联**：`SOLOCO_PLANNING_ROUNDS=1 SOLOCO_CYCLE_PLAN_ROUNDS=1 SOLOCO_DAEMON_PORT=4399 pnpm --dir apps/client-daemon dev`
4. **OS 环境变量 / 客户端启动参数**：客户端打包后无 `.env` 时，由宿主进程注入这些变量即可，语义完全一致。

## 启动

```bash
pnpm dev:daemon          # 默认配置：端口 8751；repo .env 通常把 db/auth/server 指到 dev 环境
```

### 隔离 dev 库（开发持久化时用这个）

`pnpm dev:daemon` / `pnpm dev` 默认连的就是 `~/.soloco/daemon.db`——和全局 `soloco`
是同一份库。开发／改 schema 时不想动到它，用 **隔离档**（仓库根跑）：

```bash
pnpm dev:isolated        # daemon + client-web，状态全落到 apps/client-daemon/.soloco/（gitignore）
```

它加载已提交的 `apps/client-daemon/dev.env`，把 db / files / backups / auth 全部钉在
repo 内 `./.soloco/`，**绝不碰生产 `~/.soloco/`**。根目录编排器会自动探测空闲
daemon / Vite 端口（优先 8751 / 5174，被占用则向上找），同步 `SOLOCO_DAEMON_URL`
与 CSRF 信任源，因此可以与占用默认端口的全局 / 其他 worktree 并存。想自定义就改用
`cp .env.example .env` + `pnpm dev`（`.env.example` 已是同款隔离配置，且 `.env` 被 gitignore）。
从零重来：停掉 → 删 `apps/client-daemon/.soloco/` → 再起。
