mio-agent-runtime 0.5.15 → 0.5.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,253 +1,161 @@
1
- # mio-agent-runtime
2
-
3
- > Your AI agents forget everything between sessions. Mio fixes that.
4
-
5
- Mio is a drop-in intelligence layer for Codex, OpenCode, Hermes, and WorkBuddy. Install once, and every agent you use starts remembering decisions, learning from mistakes, and sharing experience across tools — without any workflow changes.
6
-
7
- ```
8
- Codex ─┐
9
- OpenCode ─┤── MCP ──> Mio Intelligence Core
10
- Hermes ─┘ ├─ Memory decisions, context, problems
11
- ├─ Observer passive session monitoring
12
- ├─ Policy risk checks before dangerous ops
13
- ├─ Experience cross-agent knowledge transfer
14
- ├─ Ideas memory-powered idea generation
15
- └─ Evolution ecosystem-level optimization
1
+ # mio-agent-runtime
2
+
3
+ Mio Agent Runtime 的可发布 CLI 包。
4
+
5
+ 安装一次,所有 Agent 自动接入 Mio。
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g mio-agent-runtime
11
+ ```
12
+
13
+ > 新机器 / 升级:请安装 `>=0.3.6`(Codex + OpenCode + Hermes 被动观察)。
14
+ > 旧版本只有主动 MCP 规则注入,没有「无感存/取」。安装后执行一次
15
+ > `mio init && mio install <host>`。
16
+
17
+ ## Commands
18
+
19
+ ```text
20
+ mio init Initialize MIO_HOME
21
+ mio mcp Start Mio MCP server (stdio)
22
+ mio install <host> Install Mio into a host (codex|opencode|workbuddy|hermes)
23
+ mio status Show runtime and adapter status
24
+ mio agents List installed agents
25
+ mio evolution status Show composed evolution module health
26
+ mio evolution shadow record Record a shadow comparison sample
27
+ mio evolution dual-write record Record a dual-write comparison sample
28
+ mio evolution cutover readiness Assess shadow/dual-write cutover readiness
29
+ mio evolution authority plan Preview a gated authority switch plan
30
+ mio evolution migration plan Preview state migration diffs
31
+ mio evolution cutover apply --dry-run Dry-run a cutover plan without switching authority
32
+ mio observe Watch WorkBuddy transcripts and auto-ingest task outcomes (foreground)
33
+ mio observe --start|--stop|--status|--once Manage the background observer daemon
34
+ mio --json status Machine-readable status
35
+ mio --json agents Machine-readable agents
36
+ mio --json evolution status Machine-readable evolution module health
16
37
  ```
17
38
 
18
- ---
39
+ ## Release verification
19
40
 
20
- ## Install
41
+ Before publishing the runtime packages, run:
21
42
 
22
43
  ```bash
23
- npm install -g mio-agent-runtime
24
- mio init
25
- mio install codex # or: opencode, hermes, workbuddy
26
- mio status
27
- ```
28
-
29
- Restart your agent after install. That's it.
30
-
31
- ---
32
-
33
- ## What it actually does
34
-
35
- **Without Mio**, every agent starts from zero each session. Your Codex forgets why you chose Postgres over Redis. Your OpenCode re-discovers the same config bug. Your WorkBuddy re-reads the same ADR you already explained.
36
-
37
- **With Mio**, the first agent to solve a problem records the solution. The next agent — any agent — finds it automatically.
38
-
39
- ```
40
- Session 1 (Codex) Session 2 (OpenCode)
41
- ───────────────── ──────────────────
42
- "Why not Redis?" "How should we cache?"
43
- → records decision → queries memory
44
- → Mio saves it → finds the Redis answer
45
- → skips the dead end
46
- ```
47
-
48
- ---
49
-
50
- ## CLI Reference
51
-
52
- | Command | Description |
53
- |---------|-------------|
54
- | `mio init` | Create `~/.mio-intelligence` and default config |
55
- | `mio install <host>` | Wire MCP + rules + observer into a host |
56
- | `mio uninstall <host>` | Remove Mio MCP, rules, approvals, and observer from a host |
57
- | `mio upgrade` | Upgrade Mio via npm and re-wire all installed hosts to the new server |
58
- | `mio doctor` | Diagnose install/runtime issues: hosts, observer, version drift, PATH |
59
- | `mio dashboard [--start\|--stop\|--status\|--open]` | Start the live dashboard HTTP server (browser view of MIO_HOME data) |
60
- | `mio mcp` | Start MCP server (stdio, for agent integration) |
61
- | `mio status` | Show runtime state, adapter status, observer PID |
62
- | `mio agents` | List installed hosts |
63
- | `mio observe` | Foreground session monitor (Ctrl+C to stop) |
64
- | `mio observe --start` | Start background observer daemon |
65
- | `mio observe --stop` | Stop background daemon |
66
- | `mio observe --status` | Check daemon state |
67
- | `mio observe --once` | Run one collection cycle |
68
- | `mio --json <cmd>` | Any command outputs JSON |
69
-
70
- **What `mio install` does under the hood:**
71
-
72
- ```
73
- mio install codex
74
- |
75
- +-- Writes MCP server config to ~/.codex/config.json
76
- +-- Injects usage rules into AGENTS.md
77
- +-- Backfills last 24h of session history
78
- +-- Starts background passive observer
44
+ npm run verify:pack-install --workspace mio-agent-runtime
79
45
  ```
80
46
 
81
- ---
82
-
83
- ## MCP Tools (22 tools)
84
-
85
- ### Memory
86
-
87
- | Tool | Required | Optional | Description |
88
- |------|----------|----------|-------------|
89
- | `mio.memory.record` | `content` | `kind`, `tags`, `project`, `scope`, `source` | Save a decision, context, problem, or note |
90
- | `mio.memory.query` | `query` | `project`, `limit`, `kind`, `tags`, `scope` | Search memory by keyword, tag, or type |
91
- | `mio.memory.analyze` | — | `project`, `limit` | Audit quality: duplicates, low-quality, type distribution |
92
- | `mio.memory.archive` | `ids` | `project`, `restore`, `reason` | Archive records (recoverable) |
93
- | `mio.memory.migrate` | `ids`, `scope` | `project`, `reason` | Move between project and global layers |
94
-
95
- ### Observer
96
-
97
- | Tool | Required | Optional | Description |
98
- |------|----------|----------|-------------|
99
- | `mio.observer.ingest` | `trace_id`, `event_type` | `payload`, `outcome`, `agent`, `host`, `project` | Report tool calls, errors, retries, task outcomes |
100
- | `mio.observer.subscribe` | — | `eventTypes`, `project`, `topic`, `ttlDays` | Subscribe to event stream by type/project |
101
- | `mio.observer.digest` | — | `project`, `eventTypes`, `limit` | Pull new events since last digest |
102
-
103
- ### Policy
104
-
105
- | Tool | Required | Optional | Description |
106
- |------|----------|----------|-------------|
107
- | `mio.policy.check` | `action` | `project`, `context` | Pre-flight risk check against historical failure data |
108
-
109
- ### Experience
110
-
111
- | Tool | Required | Optional | Description |
112
- |------|----------|----------|-------------|
113
- | `mio.experience.reuse` | `sourceAgent`, `targetAgent`, `experienceId`, `reuse`, `behaviorChanged`, `outcomeImproved` | `notes`, `project` | Record cross-agent experience reuse with evidence |
114
- | `mio.experience.confirm` | `id` | `confirmedBy`, `notes`, `outcomeImproved` | Human-confirm an auto-claimed reuse record |
115
- | `mio.experience.list` | — | `status`, `targetAgent`, `limit`, `project` | Query reuse evidence, filter by status |
116
-
117
- ### Task
118
-
119
- | Tool | Required | Optional | Description |
120
- |------|----------|----------|-------------|
121
- | `mio.task.route` | `task` | `project`, `scope`, `limit` | Recommend best agent for a task based on history |
122
- | `mio.task.record_outcome` | — | (event-based) | Record task success/failure/abort |
47
+ For CI or reproducible release evidence through `npm run`, set deterministic
48
+ directories through environment variables:
123
49
 
124
- ### Agent
125
-
126
- | Tool | Required | Optional | Description |
127
- |------|----------|----------|-------------|
128
- | `mio.agent.register` | — | (metadata) | Register agent capabilities and host |
129
- | `mio.agent.list` | — | — | List registered agents |
130
- | `mio.agent.report` | — | (agent ID) | Query agent behavior summary |
131
-
132
- ### Ideas
133
-
134
- | Tool | Required | Optional | Description |
135
- |------|----------|----------|-------------|
136
- | `mio.idea.generate` | `goal` | `context`, `constraints` | Generate ideas using 5 strategies: SCAMPER, analogy, first-principles, random-stimulus, constraint-inversion |
137
- | `mio.idea.list` | — | `goal`, `strategy`, `period` | Query saved ideas |
138
-
139
- ### Evolution
140
-
141
- | Tool | Required | Optional | Description |
142
- |------|----------|----------|-------------|
143
- | `mio.evolution.report` | — | `project` | Ecosystem analysis report |
144
- | `mio.evolution.suggest` | — | `project` | Actionable optimization suggestions with nextAction + impact |
145
-
146
- ### Validation
147
-
148
- | Tool | Required | Optional | Description |
149
- |------|----------|----------|-------------|
150
- | `mio.phase0.report` | — | `project`, `format` | Cross-agent value validation report (JSON / Markdown) |
151
-
152
- ---
153
-
154
- ## Supported Hosts
155
-
156
- | Host | Config Location | Observer Method | Write-back |
157
- |------|----------------|-----------------|------------|
158
- | **Codex** | `~/.codex/config.json` | Not needed (agent calls MCP directly) | AGENTS.md rules |
159
- | **OpenCode** | `~/.config/opencode/opencode.json` | `opencode db` incremental read | `MIO_CONTEXT` block in AGENTS.md |
160
- | **Hermes** | `hermes mcp add` | `state.db` cursor (no CLI needed) | AGENTS.md (git repos only) |
161
- | **WorkBuddy** | `~/.workbuddy/mcp.json` | Tails `projects/**/*.jsonl` | `CODEBUDDY.md` + workspace AGENTS.md |
162
-
163
- ### How passive observation works
164
-
165
- Most agents don't call memory tools on their own, even when rules are injected. The passive observer solves this by reading session logs directly:
166
-
167
- ```
168
- Agent session logs
169
- |
170
- mio observe (background daemon)
171
- |
172
- +-- Extracts task_outcome, errors, summaries
173
- +-- Writes to MIO_HOME/traces.jsonl + memory.jsonl
174
- +-- Writes summary back to agent's native memory file
175
- |
176
- Next session: agent sees Mio's notes without calling any tool
50
+ ```bash
51
+ MIO_PACK_ARTIFACT_DIR=./dist/mio-pack-artifacts \
52
+ MIO_PACK_INSTALL_DIR=./dist/mio-pack-install \
53
+ MIO_PACK_REPORT_PATH=./dist/mio-pack-report.json \
54
+ npm run verify:pack-install --workspace mio-agent-runtime
177
55
  ```
178
56
 
179
- - **Incremental**: file offsets tracked in `observe-state.json`, no duplicates
180
- - **Scoped**: only backfills sessions updated in last 24h
181
- - **Per-host**: each adapter reads its own format (JSONL / SQLite / DB CLI)
57
+ PowerShell:
182
58
 
183
- ---
184
-
185
- ## MIO_HOME
186
-
187
- Default: `~/.mio-intelligence` (override with `MIO_HOME` env var).
188
-
189
- ```
190
- ~/.mio-intelligence/
191
- config.json runtime config + installed hosts
192
- memory.jsonl decisions, context, problems
193
- traces.jsonl agent behavior events
194
- experience-reuse.jsonl cross-agent reuse evidence
195
- ideas.jsonl generated ideas
196
- suggestions.jsonl evolution suggestions
197
- observe-state.json observer increment cursors
198
- observe.pid background daemon PID
199
- logs/ runtime logs
59
+ ```powershell
60
+ $env:MIO_PACK_ARTIFACT_DIR = './dist/mio-pack-artifacts'
61
+ $env:MIO_PACK_INSTALL_DIR = './dist/mio-pack-install'
62
+ $env:MIO_PACK_REPORT_PATH = './dist/mio-pack-report.json'
63
+ npm run verify:pack-install --workspace mio-agent-runtime
200
64
  ```
201
65
 
202
- ---
203
-
204
- ## Architecture
66
+ When invoking the script directly, command-line flags are also supported:
205
67
 
68
+ ```bash
69
+ node packages/mio-cli/scripts/verify-packed-runtime.js \
70
+ --artifact-dir ./dist/mio-pack-artifacts \
71
+ --install-dir ./dist/mio-pack-install \
72
+ --report-path ./dist/mio-pack-report.json
206
73
  ```
207
- Before Mio:
208
-
209
- Codex ──── (forgets everything)
210
- OpenCode ── (starts from zero)
211
- Hermes ──── (isolated memory)
212
- WorkBuddy ─ (no continuity)
213
-
214
- After Mio:
215
74
 
216
- Codex ─┐
217
- OpenCode ─┤ Mio Intelligence Core
218
- Hermes ─┼────> + Memory ........... shared knowledge base
219
- WorkBuddy ─┘ + Observer ......... automatic data collection
220
- + Policy ........... risk management
221
- + Experience ........ cross-agent reuse with evidence
222
- + Ideas ............ memory-powered creativity
223
- + Evolution ........ ecosystem optimization
75
+ This packs all seven `@akemi-mio/*` runtime modules plus `mio-agent-runtime`, installs
76
+ the local tarballs into a clean temporary npm project, and runs installed CLI
77
+ smoke checks for `mio --json evolution status`, shadow sample recording,
78
+ dual-write sample recording, cutover readiness, and dry-run cutover apply. The
79
+ JSON output includes each packed tarball path, size, and SHA-512 digest for
80
+ release evidence. When `MIO_PACK_REPORT_PATH` or `--report-path` is set, the
81
+ same JSON is written to that file; existing report files are not overwritten. It
82
+ fails closed if any runtime module is missing, unloaded, or unhealthy, if
83
+ shadow/dual-write parity fails, or if cutover apply stops behaving as a dry run.
84
+ It does not publish packages or switch authority.
85
+
86
+ ## Host notes
87
+
88
+ - OpenCode uses `~/.config/opencode/opencode.json` for MCP registration and
89
+ `~/.config/opencode/AGENTS.md` for global instructions. `mio install opencode`
90
+ writes both files and injects Mio usage rules into `AGENTS.md` so OpenCode
91
+ calls Mio memory/observer/policy automatically. If OpenCode is already
92
+ running, restart it once so it reloads both files. `mio observe` also
93
+ passively tails the OpenCode session database (`opencode db`) without
94
+ requiring the model to call Mio tools.
95
+ - Hermes uses `hermes mcp add mio-intelligence --command node --args <server>`
96
+ to register the Mio MCP server. Hermes has native memory and reads
97
+ AGENTS.md Context Files, so retrieval works without Mio rule injection.
98
+ - WorkBuddy uses `~/.workbuddy/mcp.json` and requires each custom MCP server to
99
+ be approved in `~/.workbuddy/mcp-approvals.json`. `mio install workbuddy`
100
+ writes both files and injects Mio usage rules into
101
+ `~/.workbuddy/CODEBUDDY.md` (user-level memory rules, auto-loaded by
102
+ WorkBuddy into every session; the CLI hardcodes the CodeBuddy product name, so the file is CODEBUDDY.md not WORKBUDDY.md) plus `<workspace>/AGENTS.md` (project-level).
103
+ If WorkBuddy is already running, restart it once so it reloads the approval
104
+ and memory rule files.
105
+
106
+ ## Passive observer (`mio observe`)
107
+
108
+ WorkBuddy 的默认模型不一定会主动调用 Mio MCP 工具,即使规则已注入
109
+ (`CODEBUDDY.md` / `AGENTS.md`)。`mio observe` 是一个不依赖模型自觉的
110
+ 兜底:它持续监听 `~/.workbuddy/projects/**/*.jsonl` 会话记录,把每个任务的
111
+ 结果(`task_outcome`)、工具调用错误(`error`)以及完成任务的摘要自动写入
112
+ `MIO_HOME/traces.jsonl` 和 `MIO_HOME/memory.jsonl`(与 MCP 服务同一套
113
+ schema,`source=workbuddy-observer`)。
114
+
115
+ - `mio install <host>` 会自动执行一次回填并启动后台观察器。
116
+
117
+ - 除了写入 `MIO_HOME`,观察器还会把任务摘要**写回 WorkBuddy 自己的记忆文件**
118
+ (`<workspace>/.workbuddy/memory/YYYY-MM-DD.md`,标为 `## Mio 自动摘要`)。
119
+ WorkBuddy 的系统提示要求模型每次会话维护并阅读这些文件,所以下一次会话
120
+ WorkBuddy 会**被动取到** Mio 记录的内容——单 Agent 的「存 + 取」闭环不依赖
121
+ 模型调用 Mio 工具。
122
+ - 已处理的文件偏移记录在 `MIO_HOME/observe-state.json`,不会重复写入。
123
+ - 只回填最近 24h 内更新的会话,避免把历史记录灌入记忆库。
124
+
125
+ OpenCode 会话不依赖模型自觉:`mio observe` 通过 `opencode db` 增量读取
126
+ `~/.local/share/opencode/opencode.db`(Windows 下该文件被 OpenCode 独占锁定,
127
+ 必须经 opencode 自带 CLI 读取),按 `part` 表的 rowid 做增量游标,只传输
128
+ `text`/`tool` 两种 part,把每个用户回合的 `task_outcome` 与工具错误写入
129
+ `MIO_HOME/traces.jsonl` / `memory.jsonl`(`source=opencode-observer`),并同样
130
+ 把摘要写回 `<workspace>/AGENTS.md` 的 `MIO_CONTEXT` 块,供 OpenCode 下一会话
131
+ 被动取用。首次启动只回填最近 24h 的 part,避免历史会话灌入记忆库。
132
+
133
+ Hermes(Nous hermes-agent)同理:`mio observe` 以只读方式增量读取
134
+ `<LocalAppData>/hermes/state.db`(`messages.id` 自增游标,无需调用 hermes CLI),
135
+ 把每个用户回合的 `task_outcome` 写入 `MIO_HOME`(`source=hermes-observer`)。
136
+ Hermes 本身有原生记忆,Mio 只负责让 Hermes 经验进入跨 Agent 生态;仅当会话
137
+ 发生在真实 git 仓库时才写回该仓库的 `AGENTS.md`,避免污染用户主目录。
138
+
139
+ ## MIO_HOME
140
+
141
+ 默认 `~/.mio-intelligence`,可通过环境变量 `MIO_HOME` 覆盖。
142
+
143
+ ## Package layout
144
+
145
+ ```text
146
+ packages/mio-cli
147
+ ├── bin/mio.js
148
+ ├── adapters/
149
+ │ ├── codex.js
150
+ │ ├── hermes.js
151
+ │ ├── opencode.js
152
+ │ └── workbuddy.js
153
+ ├── observe/
154
+ │ └── observer.js
155
+ ├── server/
156
+ │ ├── host-capabilities.js
157
+ │ ├── evolution-cutover.js
158
+ │ ├── runtime-modules.js
159
+ │ └── mio-intelligence-mcp/index.js
160
+ └── package.json
224
161
  ```
225
-
226
- | | Standalone Agent Memory | Mio Control Plane |
227
- |---|---|---|
228
- | **Scope** | Single agent | Multi-agent ecosystem |
229
- | **Collection** | Agent must call tools | Passive observation + active calls |
230
- | **Knowledge flow** | Isolated | Shared with reuse evidence |
231
- | **Risk management** | None | Unified policy checks |
232
- | **Optimization** | Per-agent | Ecosystem-wide |
233
-
234
- ---
235
-
236
- ## Version History
237
-
238
- | Version | Milestone |
239
- |---------|-----------|
240
- | < 0.3.6 | Active MCP rule injection only |
241
- | >= 0.3.6 | Passive observation + seamless read/write |
242
- | >= 0.5.0 | Idea generation + evolution suggestions |
243
- | >= 0.5.6 | Rewritten documentation |
244
- | >= 0.5.9 | `mio uninstall <host>` command |
245
- | >= 0.5.10 | `mio upgrade` and `mio doctor` commands |
246
- | >= 0.5.11 | Live web dashboard (`mio dashboard` + `mio.dashboard.*`) |
247
- | >= 0.5.12 | Observer reliability: all outcomes write back (not just success); Hermes cwd hardening; write-back diagnostics |
248
-
249
- ---
250
-
251
- ## License
252
-
253
- MIT
package/adapters/codex.js CHANGED
@@ -1,134 +1,122 @@
1
- 'use strict'
2
-
3
- const fs = require('fs')
4
- const path = require('path')
5
- const os = require('os')
6
- const { restartWarning } = require('./host-utils')
7
-
8
- const SERVER_SECTION = '[mcp_servers.mio-intelligence]'
9
- const ENV_SECTION = '[mcp_servers.mio-intelligence.env]'
10
-
11
- function configPath() {
12
- return path.join(os.homedir(), '.codex', 'config.toml')
13
- }
14
-
15
- function isInstalled() {
16
- const target = configPath()
17
- if (!fs.existsSync(target)) return false
18
- return fs.readFileSync(target, 'utf8').includes(SERVER_SECTION)
19
- }
20
-
21
- // Legacy installs pointed args at the worktree copy of the server
22
- // (server/mio-intelligence-mcp/index.js) instead of this package's copy
23
- // (server/mio-intelligence-mcp/index.js). Detect staleness by comparing the
24
- // configured args path with the server path this package ships.
25
- function configNeedsRepair(serverScript) {
26
- const target = configPath()
27
- if (!fs.existsSync(target)) return false
28
- try {
29
- const src = fs.readFileSync(target, 'utf8')
30
- if (!src.includes(SERVER_SECTION)) return false
31
- const needle = serverScript.replace(/\\/g, '/').toLowerCase()
32
- return !src.replace(/\\/g, '/').toLowerCase().includes(needle)
33
- } catch (_) {
34
- return false
35
- }
36
- }
37
-
38
- function installMessage(installed, restartHint) {
39
- let message = installed
40
- ? 'Codex Mio MCP repaired (server path updated). Restart Codex to load it.'
41
- : 'Codex Mio MCP installed. Restart Codex to load it.'
42
- if (restartHint) message += ' ' + restartHint
43
- return message
44
- }
45
-
46
- function restartWarningMessage() {
47
- return restartWarning({
48
- hostLabel: 'Codex',
49
- exeName: 'codex.exe',
50
- exePathFragment: 'OpenAI.Codex',
51
- configPath: configPath(),
52
- })
53
- }
54
-
55
- function stripMioSection(toml) {
56
- const lines = toml.replace(/\r\n/g, '\n').split('\n')
57
- const out = []
58
- let skipping = false
59
- for (const line of lines) {
60
- const trimmed = line.trim()
61
- if (trimmed === SERVER_SECTION || trimmed === ENV_SECTION) {
62
- skipping = true
63
- continue
64
- }
65
- if (skipping && /^\[.*\]$/.test(trimmed)) {
66
- skipping = false
67
- }
68
- if (!skipping) out.push(line)
69
- }
70
- return out.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n'
71
- }
72
-
73
- function install({ node, serverScript, home, workspace }) {
74
- const target = configPath()
75
- const backup = `${target}.bak-mio-${Date.now()}`
76
- const installed = isInstalled()
77
- if (installed && !configNeedsRepair(serverScript)) {
78
- return {
79
- changed: false,
80
- configPath: target,
81
- message: 'Codex Mio MCP already installed.',
82
- }
83
- }
84
-
85
- if (fs.existsSync(target)) {
86
- fs.copyFileSync(target, backup)
87
- }
88
-
89
- const context = JSON.stringify({
90
- agentId: 'codex',
91
- workspace: workspace || process.cwd(),
92
- sessionId: 'codex-session',
93
- })
94
-
95
- fs.mkdirSync(path.dirname(target), { recursive: true })
96
- let toml = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : ''
97
- toml = stripMioSection(toml)
98
- toml = toml.replace(/\n{3,}/g, '\n\n')
99
- if (!toml.endsWith('\n')) toml += '\n'
100
- const section = [
101
- '',
102
- SERVER_SECTION,
103
- `command = '${node}'`,
104
- `args = ['${serverScript}']`,
105
- 'startup_timeout_sec = 30',
106
- '',
107
- ENV_SECTION,
108
- `MIO_DATA_DIR = '${home}'`,
109
- `MIO_CONTEXT = '${context}'`,
110
- '',
111
- ].join('\n')
112
- fs.writeFileSync(target, `${toml}${section}\n`, 'utf8')
113
-
114
- return {
115
- changed: true,
116
- configPath: target,
117
- backup,
118
- message: installMessage(installed, restartWarningMessage()),
119
- }
120
- }
121
-
122
- function uninstall({ serverScript }) {
123
- const target = configPath()
124
- if (!fs.existsSync(target) || !fs.readFileSync(target, 'utf8').includes(SERVER_SECTION)) {
125
- return { changed: false, configPath: target, message: 'Codex Mio MCP not installed.' }
126
- }
127
- const backup = target + '.bak-mio-' + Date.now()
128
- fs.copyFileSync(target, backup)
129
- const toml = stripMioSection(fs.readFileSync(target, 'utf8'))
130
- fs.writeFileSync(target, toml, 'utf8')
131
- return { changed: true, configPath: target, backup, message: 'Codex Mio MCP removed. Restart Codex to apply.' }
132
- }
133
-
134
- module.exports = { configPath, isInstalled, install, uninstall, restartWarningMessage }
1
+ 'use strict'
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+ const os = require('os')
6
+ const { restartWarning } = require('./host-utils')
7
+
8
+ const SERVER_SECTION = '[mcp_servers.mio-intelligence]'
9
+ const ENV_SECTION = '[mcp_servers.mio-intelligence.env]'
10
+
11
+ function configPath() {
12
+ return path.join(os.homedir(), '.codex', 'config.toml')
13
+ }
14
+
15
+ function isInstalled() {
16
+ const target = configPath()
17
+ if (!fs.existsSync(target)) return false
18
+ return fs.readFileSync(target, 'utf8').includes(SERVER_SECTION)
19
+ }
20
+
21
+ // Legacy installs pointed args at the worktree copy of the server
22
+ // (server/mio-intelligence-mcp/index.js) instead of this package's copy
23
+ // (server/mio-intelligence-mcp/index.js). Detect staleness by comparing the
24
+ // configured args path with the server path this package ships.
25
+ function configNeedsRepair(serverScript) {
26
+ const target = configPath()
27
+ if (!fs.existsSync(target)) return false
28
+ try {
29
+ const src = fs.readFileSync(target, 'utf8')
30
+ if (!src.includes(SERVER_SECTION)) return false
31
+ const needle = serverScript.replace(/\\/g, '/').toLowerCase()
32
+ return !src.replace(/\\/g, '/').toLowerCase().includes(needle)
33
+ } catch (_) {
34
+ return false
35
+ }
36
+ }
37
+
38
+ function installMessage(installed, restartHint) {
39
+ let message = installed
40
+ ? 'Codex Mio MCP repaired (server path updated). Restart Codex to load it.'
41
+ : 'Codex Mio MCP installed. Restart Codex to load it.'
42
+ if (restartHint) message += ' ' + restartHint
43
+ return message
44
+ }
45
+
46
+ function restartWarningMessage() {
47
+ return restartWarning({
48
+ hostLabel: 'Codex',
49
+ exeName: 'codex.exe',
50
+ exePathFragment: 'OpenAI.Codex',
51
+ configPath: configPath(),
52
+ })
53
+ }
54
+
55
+ function stripMioSection(toml) {
56
+ const lines = toml.replace(/\r\n/g, '\n').split('\n')
57
+ const out = []
58
+ let skipping = false
59
+ for (const line of lines) {
60
+ const trimmed = line.trim()
61
+ if (trimmed === SERVER_SECTION || trimmed === ENV_SECTION) {
62
+ skipping = true
63
+ continue
64
+ }
65
+ if (skipping && /^\[.*\]$/.test(trimmed)) {
66
+ skipping = false
67
+ }
68
+ if (!skipping) out.push(line)
69
+ }
70
+ return out.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n'
71
+ }
72
+
73
+ function install({ node, serverScript, home, workspace }) {
74
+ const target = configPath()
75
+ const backup = `${target}.bak-mio-${Date.now()}`
76
+ const installed = isInstalled()
77
+ if (installed && !configNeedsRepair(serverScript)) {
78
+ return {
79
+ changed: false,
80
+ configPath: target,
81
+ message: 'Codex Mio MCP already installed.',
82
+ }
83
+ }
84
+
85
+ if (fs.existsSync(target)) {
86
+ fs.copyFileSync(target, backup)
87
+ }
88
+
89
+ const context = JSON.stringify({
90
+ agentId: 'codex',
91
+ workspace: workspace || process.cwd(),
92
+ sessionId: 'codex-session',
93
+ })
94
+
95
+ fs.mkdirSync(path.dirname(target), { recursive: true })
96
+ let toml = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : ''
97
+ toml = stripMioSection(toml)
98
+ toml = toml.replace(/\n{3,}/g, '\n\n')
99
+ if (!toml.endsWith('\n')) toml += '\n'
100
+ const section = [
101
+ '',
102
+ SERVER_SECTION,
103
+ `command = '${node}'`,
104
+ `args = ['${serverScript}']`,
105
+ 'startup_timeout_sec = 30',
106
+ '',
107
+ ENV_SECTION,
108
+ `MIO_DATA_DIR = '${home}'`,
109
+ `MIO_CONTEXT = '${context}'`,
110
+ '',
111
+ ].join('\n')
112
+ fs.writeFileSync(target, `${toml}${section}\n`, 'utf8')
113
+
114
+ return {
115
+ changed: true,
116
+ configPath: target,
117
+ backup,
118
+ message: installMessage(installed, restartWarningMessage()),
119
+ }
120
+ }
121
+
122
+ module.exports = { configPath, isInstalled, install, restartWarningMessage }