mio-agent-runtime 0.5.8 → 0.5.10
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 +5 -0
- package/adapters/codex.js +13 -1
- package/adapters/hermes.js +19 -1
- package/adapters/opencode.js +40 -1
- package/adapters/workbuddy.js +47 -1
- package/bin/mio.js +146 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,6 +53,9 @@ Restart your agent after install. That's it.
|
|
|
53
53
|
|---------|-------------|
|
|
54
54
|
| `mio init` | Create `~/.mio-intelligence` and default config |
|
|
55
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 |
|
|
56
59
|
| `mio mcp` | Start MCP server (stdio, for agent integration) |
|
|
57
60
|
| `mio status` | Show runtime state, adapter status, observer PID |
|
|
58
61
|
| `mio agents` | List installed hosts |
|
|
@@ -237,6 +240,8 @@ WorkBuddy ─┘ + Observer ......... automatic data collection
|
|
|
237
240
|
| >= 0.3.6 | Passive observation + seamless read/write |
|
|
238
241
|
| >= 0.5.0 | Idea generation + evolution suggestions |
|
|
239
242
|
| >= 0.5.6 | Rewritten documentation |
|
|
243
|
+
| >= 0.5.9 | `mio uninstall <host>` command |
|
|
244
|
+
| >= 0.5.10 | `mio upgrade` and `mio doctor` commands |
|
|
240
245
|
|
|
241
246
|
---
|
|
242
247
|
|
package/adapters/codex.js
CHANGED
|
@@ -119,4 +119,16 @@ function install({ node, serverScript, home, workspace }) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
|
|
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 }
|
package/adapters/hermes.js
CHANGED
|
@@ -119,4 +119,22 @@ function isHermesOnPath() {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
function uninstall({ serverScript }) {
|
|
123
|
+
if (!hermesBin() && !isHermesOnPath()) {
|
|
124
|
+
return { changed: false, message: 'Hermes binary not found. Nothing to remove.', configPath: null }
|
|
125
|
+
}
|
|
126
|
+
if (!isInstalled()) {
|
|
127
|
+
return { changed: false, message: 'Hermes Mio MCP not installed.', configPath: null }
|
|
128
|
+
}
|
|
129
|
+
runHermes(['mcp', 'remove', SERVER_NAME])
|
|
130
|
+
const removed = !isInstalled()
|
|
131
|
+
return {
|
|
132
|
+
changed: removed,
|
|
133
|
+
configPath: null,
|
|
134
|
+
message: removed
|
|
135
|
+
? 'Hermes Mio MCP removed. Start a new Hermes session to apply.'
|
|
136
|
+
: 'Hermes mcp remove returned but Mio is still present. Run `hermes mcp remove ' + SERVER_NAME + '` manually.'
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = { hermesBin, isInstalled, install, uninstall }
|
package/adapters/opencode.js
CHANGED
|
@@ -173,4 +173,43 @@ function install({ node, serverScript, home, workspace, project }) {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
function removeGlobalInstructions() {
|
|
177
|
+
const target = globalInstructionsPath()
|
|
178
|
+
if (!fs.existsSync(target)) return { changed: false, target, backup: null }
|
|
179
|
+
const content = fs.readFileSync(target, 'utf8')
|
|
180
|
+
const start = content.indexOf(INSTRUCTIONS_BEGIN)
|
|
181
|
+
if (start === -1) return { changed: false, target, backup: null }
|
|
182
|
+
const end = content.indexOf(INSTRUCTIONS_END, start)
|
|
183
|
+
let next = end === -1 ? content.slice(0, start) : content.slice(0, start) + content.slice(end + INSTRUCTIONS_END.length)
|
|
184
|
+
next = next.trimEnd() + '\n'
|
|
185
|
+
const backup = target + '.bak-mio-' + Date.now()
|
|
186
|
+
fs.copyFileSync(target, backup)
|
|
187
|
+
fs.writeFileSync(target, next, 'utf8')
|
|
188
|
+
return { changed: true, target, backup }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function uninstall({ serverScript }) {
|
|
192
|
+
const target = configPath()
|
|
193
|
+
const config = readConfig()
|
|
194
|
+
const hadMcp = Boolean(config && config.mcp && config.mcp['mio-intelligence'])
|
|
195
|
+
let configChanged = false
|
|
196
|
+
let backup = null
|
|
197
|
+
if (hadMcp) {
|
|
198
|
+
backup = target + '.bak-mio-' + Date.now()
|
|
199
|
+
if (fs.existsSync(target)) fs.copyFileSync(target, backup)
|
|
200
|
+
delete config.mcp['mio-intelligence']
|
|
201
|
+
if (Object.keys(config.mcp).length === 0) delete config.mcp
|
|
202
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
203
|
+
fs.writeFileSync(target, JSON.stringify(config, null, 2) + '\n', 'utf8')
|
|
204
|
+
configChanged = true
|
|
205
|
+
}
|
|
206
|
+
const instr = removeGlobalInstructions()
|
|
207
|
+
const changed = configChanged || instr.changed
|
|
208
|
+
if (!changed) return { changed: false, configPath: target, message: 'OpenCode Mio MCP not installed.' }
|
|
209
|
+
let message = 'OpenCode Mio MCP removed'
|
|
210
|
+
const restartHint = restartWarningMessage()
|
|
211
|
+
if (restartHint) message += '. ' + restartHint
|
|
212
|
+
return { changed: true, configPath: target, globalInstructionsPath: instr.target, backup, message }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
module.exports = { configPath, globalInstructionsPath, isInstalled, install, uninstall, configNeedsRepair, restartWarningMessage }
|
package/adapters/workbuddy.js
CHANGED
|
@@ -193,4 +193,50 @@ function install({ node, serverScript, home, workspace, project }) {
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
function removeBlock(target) {
|
|
197
|
+
if (!fs.existsSync(target)) return { changed: false, target, backup: null }
|
|
198
|
+
const content = fs.readFileSync(target, 'utf8')
|
|
199
|
+
const start = content.indexOf(MEMORY_BEGIN)
|
|
200
|
+
if (start === -1) return { changed: false, target, backup: null }
|
|
201
|
+
const end = content.indexOf(MEMORY_END, start)
|
|
202
|
+
let next = end === -1 ? content.slice(0, start) : content.slice(0, start) + content.slice(end + MEMORY_END.length)
|
|
203
|
+
next = next.trimEnd() + '\n'
|
|
204
|
+
const backup = target + '.bak-mio-' + Date.now()
|
|
205
|
+
fs.copyFileSync(target, backup)
|
|
206
|
+
fs.writeFileSync(target, next, 'utf8')
|
|
207
|
+
return { changed: true, target, backup }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function uninstall({ serverScript, workspace }) {
|
|
211
|
+
const target = configPath()
|
|
212
|
+
const config = readJson(target)
|
|
213
|
+
const entry = config && config.mcpServers && config.mcpServers['mio-intelligence']
|
|
214
|
+
if (!entry) return { changed: false, configPath: target, message: 'WorkBuddy Mio MCP not installed.' }
|
|
215
|
+
const backup = target + '.bak-mio-' + Date.now()
|
|
216
|
+
if (fs.existsSync(target)) fs.copyFileSync(target, backup)
|
|
217
|
+
delete config.mcpServers['mio-intelligence']
|
|
218
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
219
|
+
fs.writeFileSync(target, JSON.stringify(config, null, 2) + '\n', 'utf8')
|
|
220
|
+
const approvalsTarget = approvalsPath()
|
|
221
|
+
const approvals = readJson(approvalsTarget) || {}
|
|
222
|
+
const key = approvalKey('mio-intelligence', entry)
|
|
223
|
+
if (approvals[key]) {
|
|
224
|
+
delete approvals[key]
|
|
225
|
+
fs.writeFileSync(approvalsTarget, JSON.stringify(approvals, null, 2) + '\n', 'utf8')
|
|
226
|
+
}
|
|
227
|
+
const userRules = removeBlock(memoryRulesPath())
|
|
228
|
+
let projectRules = null
|
|
229
|
+
const workspaceDir = workspace || process.cwd()
|
|
230
|
+
if (workspaceDir && path.isAbsolute(workspaceDir)) projectRules = removeBlock(projectInstructionsPath(workspaceDir))
|
|
231
|
+
const changed = userRules.changed || Boolean(projectRules && projectRules.changed)
|
|
232
|
+
return {
|
|
233
|
+
changed: true,
|
|
234
|
+
configPath: target,
|
|
235
|
+
approvalsPath: approvalsTarget,
|
|
236
|
+
memoryRulesPath: userRules.target,
|
|
237
|
+
projectInstructionsPath: projectRules ? projectRules.target : null,
|
|
238
|
+
message: 'WorkBuddy Mio MCP removed, approval revoked, and Mio rules stripped from CODEBUDDY.md and workspace AGENTS.md. Restart WorkBuddy to apply.'
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = { configDir, configPath, approvalsPath, memoryRulesPath, projectInstructionsPath, approvalKey, isInstalled, install, uninstall }
|
package/bin/mio.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const fs = require('fs')
|
|
5
5
|
const path = require('path')
|
|
6
6
|
const os = require('os')
|
|
7
|
+
const { spawnSync } = require('child_process')
|
|
7
8
|
|
|
8
9
|
const SERVER_SCRIPT = path.resolve(__dirname, '..', 'server', 'mio-intelligence-mcp', 'index.js')
|
|
9
10
|
const MIO_HOME = process.env.MIO_HOME || path.join(os.homedir(), '.mio-intelligence')
|
|
@@ -219,6 +220,142 @@ function observeCommand(args) {
|
|
|
219
220
|
observer.startForeground(MIO_HOME)
|
|
220
221
|
}
|
|
221
222
|
|
|
223
|
+
function uninstallCommand(host, useJson) {
|
|
224
|
+
if (!host) {
|
|
225
|
+
console.error('Usage: mio uninstall <codex|opencode|workbuddy|hermes>')
|
|
226
|
+
process.exitCode = 1
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
const adapter = adapters[host]
|
|
230
|
+
if (!adapter) {
|
|
231
|
+
console.error(`Unknown host: ${host}`)
|
|
232
|
+
process.exitCode = 1
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
let result
|
|
236
|
+
try {
|
|
237
|
+
result = adapter.uninstall({
|
|
238
|
+
node: process.execPath,
|
|
239
|
+
serverScript: SERVER_SCRIPT,
|
|
240
|
+
home: MIO_HOME,
|
|
241
|
+
workspace: process.cwd(),
|
|
242
|
+
})
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error(`Failed to uninstall ${host}: ${error.message || error}`)
|
|
245
|
+
process.exitCode = 1
|
|
246
|
+
return
|
|
247
|
+
}
|
|
248
|
+
if (result.changed) {
|
|
249
|
+
const config = readConfig()
|
|
250
|
+
if (config.agents && config.agents[host]) {
|
|
251
|
+
delete config.agents[host]
|
|
252
|
+
writeConfig(config)
|
|
253
|
+
}
|
|
254
|
+
const remaining = Object.keys(config.agents || {}).length
|
|
255
|
+
if (remaining === 0 && observer.isRunning(MIO_HOME)) {
|
|
256
|
+
observer.stopBackground(MIO_HOME)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (useJson) {
|
|
260
|
+
jsonOrText(result, true)
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
console.log(result.message || `${host} adapter removed.`)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function runNpm(args) {
|
|
267
|
+
try {
|
|
268
|
+
const res = spawnSync('npm', args, { encoding: 'utf8', shell: true, windowsHide: true })
|
|
269
|
+
if (res.error) return { ok: false, error: res.error.message, stdout: '', stderr: '' }
|
|
270
|
+
return { ok: true, status: res.status, stdout: res.stdout || '', stderr: res.stderr || '' }
|
|
271
|
+
} catch (e) {
|
|
272
|
+
return { ok: false, error: e.message, stdout: '', stderr: '' }
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function npmGlobalVersion() {
|
|
277
|
+
const r = runNpm(['ls', '-g', 'mio-agent-runtime'])
|
|
278
|
+
const m = r.stdout && r.stdout.match(/mio-agent-runtime@(\d+\.\d+\.\d+)/)
|
|
279
|
+
return m ? m[1] : null
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function npmLatestVersion() {
|
|
283
|
+
const r = runNpm(['view', 'mio-agent-runtime', 'version'])
|
|
284
|
+
const m = r.stdout && r.stdout.trim().match(/(\d+\.\d+\.\d+)/)
|
|
285
|
+
return m ? m[1] : null
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function upgradeCommand(useJson) {
|
|
289
|
+
const before = npmGlobalVersion()
|
|
290
|
+
const installRes = runNpm(['install', '-g', 'mio-agent-runtime@latest'])
|
|
291
|
+
if (!installRes.ok || installRes.status !== 0) {
|
|
292
|
+
const msg = 'npm install failed: ' + (installRes.error || installRes.stderr || 'unknown error')
|
|
293
|
+
if (useJson) return jsonOrText({ ok: false, error: msg }, true)
|
|
294
|
+
console.error(msg)
|
|
295
|
+
process.exitCode = 1
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
const after = npmGlobalVersion()
|
|
299
|
+
const prefix = (() => { const r = runNpm(['config', 'get', 'prefix']); return r.stdout ? r.stdout.trim() : '' })()
|
|
300
|
+
const isWin = process.platform === 'win32'
|
|
301
|
+
const globalServer = prefix ? path.join(prefix, 'node_modules', 'mio-agent-runtime', 'server', 'mio-intelligence-mcp', 'index.js') : SERVER_SCRIPT
|
|
302
|
+
const globalNode = prefix ? (isWin ? path.join(prefix, 'node.exe') : path.join(prefix, 'bin', 'node')) : process.execPath
|
|
303
|
+
const nodeBin = fs.existsSync(globalNode) ? globalNode : process.execPath
|
|
304
|
+
const config = readConfig()
|
|
305
|
+
const hosts = Object.keys(config.agents || {})
|
|
306
|
+
const rewired = []
|
|
307
|
+
for (const host of hosts) {
|
|
308
|
+
const adapter = adapters[host]
|
|
309
|
+
if (!adapter) continue
|
|
310
|
+
try {
|
|
311
|
+
const result = adapter.install({ node: nodeBin, serverScript: globalServer, home: MIO_HOME, workspace: process.cwd() })
|
|
312
|
+
if (result.changed) rewired.push(host)
|
|
313
|
+
} catch (_) {}
|
|
314
|
+
}
|
|
315
|
+
let observerStarted = false
|
|
316
|
+
try { observerStarted = observer.startBackground(MIO_HOME).started } catch (_) {}
|
|
317
|
+
const payload = { ok: true, before, after, rewiredHosts: rewired, observerStarted }
|
|
318
|
+
if (useJson) return jsonOrText(payload, true)
|
|
319
|
+
console.log('Upgraded Mio: ' + (before || '?') + ' -> ' + (after || '?'))
|
|
320
|
+
if (rewired.length) console.log('Re-wired hosts: ' + rewired.join(', '))
|
|
321
|
+
else console.log('All installed hosts already up to date.')
|
|
322
|
+
console.log(observerStarted ? 'Observer started.' : 'Observer already running.')
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function doctorCommand(useJson) {
|
|
326
|
+
const checks = []
|
|
327
|
+
function add(name, ok, detail) { checks.push({ name: name, ok: Boolean(ok), detail: detail || '' }) }
|
|
328
|
+
add('MIO_HOME exists', fs.existsSync(MIO_HOME), MIO_HOME)
|
|
329
|
+
if (fs.existsSync(MIO_HOME)) {
|
|
330
|
+
try { fs.accessSync(MIO_HOME, fs.constants.W_OK); add('MIO_HOME writable', true) }
|
|
331
|
+
catch (_) { add('MIO_HOME writable', false, 'no write permission') }
|
|
332
|
+
}
|
|
333
|
+
add('MCP server present', fs.existsSync(SERVER_SCRIPT), SERVER_SCRIPT)
|
|
334
|
+
for (const host of ['codex', 'opencode', 'workbuddy', 'hermes']) {
|
|
335
|
+
const adapter = adapters[host]
|
|
336
|
+
const installed = adapter ? adapter.isInstalled() : false
|
|
337
|
+
add('host:' + host + ' installed', installed)
|
|
338
|
+
}
|
|
339
|
+
const obsRunning = observer.isRunning(MIO_HOME)
|
|
340
|
+
add('observer running', obsRunning, obsRunning ? 'pid ' + observer.readPid(MIO_HOME) : 'not running')
|
|
341
|
+
const installed = npmGlobalVersion()
|
|
342
|
+
const latest = npmLatestVersion()
|
|
343
|
+
add('npm up to date', installed === latest, 'installed ' + installed + ' / latest ' + latest)
|
|
344
|
+
const prefix = (() => { const r = runNpm(['config', 'get', 'prefix']); return r.stdout ? r.stdout.trim() : '' })()
|
|
345
|
+
const execPrefix = path.dirname(process.execPath)
|
|
346
|
+
add('global bin on PATH', prefix && execPrefix && prefix.toLowerCase() === execPrefix.toLowerCase(), 'npm prefix ' + prefix + ' / node ' + execPrefix)
|
|
347
|
+
const stray0 = path.join(process.cwd(), '0')
|
|
348
|
+
add('no stray 0 file', !fs.existsSync(stray0), fs.existsSync(stray0) ? stray0 : '')
|
|
349
|
+
const failed = checks.filter(function (c) { return !c.ok })
|
|
350
|
+
const payload = { ok: failed.length === 0, checks: checks, failedCount: failed.length }
|
|
351
|
+
if (useJson) return jsonOrText(payload, true)
|
|
352
|
+
for (const c of checks) {
|
|
353
|
+
console.log((c.ok ? 'OK ' : 'FAIL ') + c.name + (c.detail ? ' (' + c.detail + ')' : ''))
|
|
354
|
+
}
|
|
355
|
+
console.log('')
|
|
356
|
+
console.log(failed.length === 0 ? 'All checks passed.' : failed.length + ' check(s) failed. Run `mio upgrade` or reinstall affected hosts.')
|
|
357
|
+
}
|
|
358
|
+
|
|
222
359
|
function help() {
|
|
223
360
|
console.log(`Mio Agent Runtime CLI
|
|
224
361
|
|
|
@@ -227,6 +364,9 @@ Usage:
|
|
|
227
364
|
mio init Initialize MIO_HOME
|
|
228
365
|
mio mcp Start Mio MCP server (stdio)
|
|
229
366
|
mio install <host> Install Mio into a host (codex|opencode|workbuddy|hermes)
|
|
367
|
+
mio uninstall <host> Remove Mio from a host
|
|
368
|
+
mio upgrade Upgrade Mio and re-wire all installed hosts
|
|
369
|
+
mio doctor Diagnose install and runtime issues
|
|
230
370
|
mio status Show runtime and adapter status
|
|
231
371
|
mio agents List installed agents
|
|
232
372
|
mio observe Watch WorkBuddy transcripts and auto-ingest task outcomes
|
|
@@ -253,6 +393,12 @@ function main() {
|
|
|
253
393
|
return mcpCommand()
|
|
254
394
|
case 'install':
|
|
255
395
|
return installCommand(args[1], useJson)
|
|
396
|
+
case 'uninstall':
|
|
397
|
+
return uninstallCommand(args[1], useJson)
|
|
398
|
+
case 'upgrade':
|
|
399
|
+
return upgradeCommand(useJson)
|
|
400
|
+
case 'doctor':
|
|
401
|
+
return doctorCommand(useJson)
|
|
256
402
|
case 'status':
|
|
257
403
|
return statusCommand(useJson)
|
|
258
404
|
case 'agents':
|