mio-agent-runtime 0.1.0 → 0.2.1
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 +8 -1
- package/adapters/codex.js +2 -1
- package/adapters/opencode.js +67 -5
- package/adapters/workbuddy.js +119 -5
- package/bin/mio.js +30 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,6 +22,13 @@ mio --json status Machine-readable status
|
|
|
22
22
|
mio --json agents Machine-readable agents
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## Host notes
|
|
26
|
+
|
|
27
|
+
- WorkBuddy uses `~/.workbuddy/mcp.json` and requires each custom MCP server to
|
|
28
|
+
be approved in `~/.workbuddy/mcp-approvals.json`. `mio install workbuddy`
|
|
29
|
+
writes both files. If WorkBuddy is already running, restart it once so it
|
|
30
|
+
reloads the approval file.
|
|
31
|
+
|
|
25
32
|
## MIO_HOME
|
|
26
33
|
|
|
27
34
|
默认 `~/.mio-intelligence`,可通过环境变量 `MIO_HOME` 覆盖。
|
|
@@ -38,4 +45,4 @@ packages/mio-cli
|
|
|
38
45
|
├── server/
|
|
39
46
|
│ └── mio-intelligence-mcp/index.js
|
|
40
47
|
└── package.json
|
|
41
|
-
```
|
|
48
|
+
```
|
package/adapters/codex.js
CHANGED
|
@@ -14,7 +14,7 @@ function isInstalled() {
|
|
|
14
14
|
return fs.readFileSync(target, 'utf8').includes('[mcp_servers.mio-intelligence]')
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function install({ node, serverScript, home, workspace }) {
|
|
17
|
+
function install({ node, serverScript, home, workspace, project }) {
|
|
18
18
|
const target = configPath()
|
|
19
19
|
const backup = `${target}.bak-mio-${Date.now()}`
|
|
20
20
|
if (isInstalled()) {
|
|
@@ -31,6 +31,7 @@ function install({ node, serverScript, home, workspace }) {
|
|
|
31
31
|
|
|
32
32
|
const context = JSON.stringify({
|
|
33
33
|
agentId: 'codex',
|
|
34
|
+
project: project || path.basename(workspace || process.cwd()),
|
|
34
35
|
workspace: workspace || process.cwd(),
|
|
35
36
|
sessionId: 'codex-session',
|
|
36
37
|
})
|
package/adapters/opencode.js
CHANGED
|
@@ -1,14 +1,76 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const os = require('os')
|
|
6
|
+
|
|
7
|
+
function configPath() {
|
|
8
|
+
return path.join(os.homedir(), '.config', 'opencode', 'opencode.json')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function readConfig() {
|
|
12
|
+
const target = configPath()
|
|
13
|
+
if (!fs.existsSync(target)) return null
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(fs.readFileSync(target, 'utf8'))
|
|
16
|
+
} catch (_) {
|
|
17
|
+
return null
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
3
21
|
function isInstalled() {
|
|
4
|
-
|
|
22
|
+
const config = readConfig()
|
|
23
|
+
return Boolean(config && config.mcp && config.mcp['mio-intelligence'])
|
|
5
24
|
}
|
|
6
25
|
|
|
7
|
-
function install() {
|
|
26
|
+
function install({ node, serverScript, home, workspace, project }) {
|
|
27
|
+
const target = configPath()
|
|
28
|
+
if (isInstalled()) {
|
|
29
|
+
return {
|
|
30
|
+
changed: false,
|
|
31
|
+
configPath: target,
|
|
32
|
+
message: 'OpenCode Mio MCP already installed.',
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const backup = `${target}.bak-mio-${Date.now()}`
|
|
37
|
+
if (fs.existsSync(target)) {
|
|
38
|
+
fs.copyFileSync(target, backup)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const config = readConfig() || {}
|
|
42
|
+
const existingMcp = config.mcp && typeof config.mcp === 'object' ? config.mcp : {}
|
|
43
|
+
const context = JSON.stringify({
|
|
44
|
+
agentId: 'opencode',
|
|
45
|
+
project: project || path.basename(workspace || process.cwd()),
|
|
46
|
+
workspace: workspace || process.cwd(),
|
|
47
|
+
sessionId: 'opencode-session',
|
|
48
|
+
})
|
|
49
|
+
const next = {
|
|
50
|
+
...config,
|
|
51
|
+
mcp: {
|
|
52
|
+
...existingMcp,
|
|
53
|
+
'mio-intelligence': {
|
|
54
|
+
enabled: true,
|
|
55
|
+
type: 'local',
|
|
56
|
+
command: [node, serverScript],
|
|
57
|
+
environment: {
|
|
58
|
+
MIO_DATA_DIR: home,
|
|
59
|
+
MIO_CONTEXT: context,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
66
|
+
fs.writeFileSync(target, `${JSON.stringify(next, null, 2)}\n`, 'utf8')
|
|
67
|
+
|
|
8
68
|
return {
|
|
9
|
-
changed:
|
|
10
|
-
|
|
69
|
+
changed: true,
|
|
70
|
+
configPath: target,
|
|
71
|
+
backup,
|
|
72
|
+
message: 'OpenCode Mio MCP installed. Restart OpenCode to load it.',
|
|
11
73
|
}
|
|
12
74
|
}
|
|
13
75
|
|
|
14
|
-
module.exports = { isInstalled, install }
|
|
76
|
+
module.exports = { configPath, isInstalled, install }
|
package/adapters/workbuddy.js
CHANGED
|
@@ -1,14 +1,128 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const os = require('os')
|
|
6
|
+
const crypto = require('crypto')
|
|
7
|
+
|
|
8
|
+
function configDir() {
|
|
9
|
+
return (
|
|
10
|
+
process.env.WORKBUDDY_CONFIG_DIR ||
|
|
11
|
+
process.env.CODEBUDDY_CONFIG_DIR ||
|
|
12
|
+
path.join(os.homedir(), '.workbuddy')
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function configPath() {
|
|
17
|
+
return path.join(configDir(), 'mcp.json')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function legacyConfigPath() {
|
|
21
|
+
return path.join(configDir(), '.mcp.json')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function approvalsPath() {
|
|
25
|
+
return path.join(configDir(), 'mcp-approvals.json')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readJson(file) {
|
|
29
|
+
if (!fs.existsSync(file)) return null
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
32
|
+
} catch (_) {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
3
37
|
function isInstalled() {
|
|
4
|
-
|
|
38
|
+
const config = readJson(configPath())
|
|
39
|
+
const entry = config && config.mcpServers && config.mcpServers['mio-intelligence']
|
|
40
|
+
if (!entry) return false
|
|
41
|
+
|
|
42
|
+
const approvals = readJson(approvalsPath()) || {}
|
|
43
|
+
return Boolean(approvals[approvalKey('mio-intelligence', entry)])
|
|
5
44
|
}
|
|
6
45
|
|
|
7
|
-
function
|
|
46
|
+
function approvalKey(name, entry) {
|
|
47
|
+
let input
|
|
48
|
+
if (entry.command) {
|
|
49
|
+
input = `${entry.command || ''}|${(entry.args || []).map(String).sort().join(',')}|${Object.keys(entry.env || {}).sort().join(',')}`
|
|
50
|
+
} else if (entry.url) {
|
|
51
|
+
try {
|
|
52
|
+
input = new URL(entry.url).origin
|
|
53
|
+
} catch (_) {
|
|
54
|
+
input = entry.url
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
input = JSON.stringify(entry)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const hash = crypto.createHash('sha256').update(input).digest('hex')
|
|
61
|
+
return `${hash}::${name}`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function writeApproval(name, entry) {
|
|
65
|
+
const target = approvalsPath()
|
|
66
|
+
const approvals = readJson(target) || {}
|
|
67
|
+
approvals[approvalKey(name, entry)] = Date.now()
|
|
68
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
69
|
+
fs.writeFileSync(target, `${JSON.stringify(approvals, null, 2)}\n`, 'utf8')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function install({ node, serverScript, home, workspace, project }) {
|
|
73
|
+
const target = configPath()
|
|
74
|
+
const canonical = readJson(target) || {}
|
|
75
|
+
const legacy = readJson(legacyConfigPath()) || {}
|
|
76
|
+
const canonicalServers =
|
|
77
|
+
canonical.mcpServers && typeof canonical.mcpServers === 'object' ? canonical.mcpServers : {}
|
|
78
|
+
const legacyServers =
|
|
79
|
+
legacy.mcpServers && typeof legacy.mcpServers === 'object' ? legacy.mcpServers : {}
|
|
80
|
+
|
|
81
|
+
const config = {
|
|
82
|
+
...legacy,
|
|
83
|
+
...canonical,
|
|
84
|
+
mcpServers: {
|
|
85
|
+
...legacyServers,
|
|
86
|
+
...canonicalServers,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const context = JSON.stringify({
|
|
91
|
+
agentId: 'workbuddy',
|
|
92
|
+
project: project || path.basename(workspace || process.cwd()),
|
|
93
|
+
workspace: workspace || process.cwd(),
|
|
94
|
+
sessionId: 'workbuddy-session',
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
config.mcpServers = {
|
|
98
|
+
...config.mcpServers,
|
|
99
|
+
'mio-intelligence': {
|
|
100
|
+
command: node,
|
|
101
|
+
args: [serverScript],
|
|
102
|
+
env: {
|
|
103
|
+
MIO_DATA_DIR: home,
|
|
104
|
+
MIO_CONTEXT: context,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
writeApproval('mio-intelligence', config.mcpServers['mio-intelligence'])
|
|
110
|
+
|
|
111
|
+
const backup = `${target}.bak-mio-${Date.now()}`
|
|
112
|
+
if (fs.existsSync(target)) {
|
|
113
|
+
fs.copyFileSync(target, backup)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
117
|
+
fs.writeFileSync(target, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
|
|
118
|
+
|
|
8
119
|
return {
|
|
9
|
-
changed:
|
|
10
|
-
|
|
120
|
+
changed: true,
|
|
121
|
+
configPath: target,
|
|
122
|
+
approvalsPath: approvalsPath(),
|
|
123
|
+
backup,
|
|
124
|
+
message: 'WorkBuddy Mio MCP installed and approved. WorkBuddy should auto-load it; restart if it was already running.',
|
|
11
125
|
}
|
|
12
126
|
}
|
|
13
127
|
|
|
14
|
-
module.exports = { isInstalled, install }
|
|
128
|
+
module.exports = { configDir, configPath, approvalsPath, approvalKey, isInstalled, install }
|
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 { execFileSync } = 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')
|
|
@@ -15,6 +16,23 @@ const adapters = {
|
|
|
15
16
|
workbuddy: require('../adapters/workbuddy.js'),
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
function projectFromWorkspace(workspace) {
|
|
20
|
+
try {
|
|
21
|
+
const commonDir = execFileSync(
|
|
22
|
+
'git',
|
|
23
|
+
['-C', workspace, 'rev-parse', '--path-format=absolute', '--git-common-dir'],
|
|
24
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
|
|
25
|
+
).trim()
|
|
26
|
+
if (commonDir) {
|
|
27
|
+
const name = path.basename(path.dirname(commonDir))
|
|
28
|
+
if (name) return name
|
|
29
|
+
}
|
|
30
|
+
} catch (_) {
|
|
31
|
+
// fall through to workspace basename
|
|
32
|
+
}
|
|
33
|
+
return path.basename(workspace)
|
|
34
|
+
}
|
|
35
|
+
|
|
18
36
|
function ensureHome() {
|
|
19
37
|
fs.mkdirSync(MIO_HOME, { recursive: true })
|
|
20
38
|
fs.mkdirSync(path.join(MIO_HOME, 'logs'), { recursive: true })
|
|
@@ -53,15 +71,20 @@ function initCommand() {
|
|
|
53
71
|
|
|
54
72
|
function statusCommand(useJson) {
|
|
55
73
|
const config = readConfig()
|
|
56
|
-
const
|
|
57
|
-
|
|
74
|
+
const installed = {
|
|
75
|
+
codex: adapters.codex.isInstalled(),
|
|
76
|
+
opencode: adapters.opencode.isInstalled(),
|
|
77
|
+
workbuddy: adapters.workbuddy.isInstalled(),
|
|
78
|
+
}
|
|
58
79
|
const payload = {
|
|
59
80
|
version: config.version,
|
|
60
81
|
home: MIO_HOME,
|
|
61
82
|
serverScript: SERVER_SCRIPT,
|
|
62
83
|
serverScriptExists: fs.existsSync(SERVER_SCRIPT),
|
|
63
84
|
agents: config.agents || {},
|
|
64
|
-
codexInstalled: installed,
|
|
85
|
+
codexInstalled: installed.codex,
|
|
86
|
+
opencodeInstalled: installed.opencode,
|
|
87
|
+
workbuddyInstalled: installed.workbuddy,
|
|
65
88
|
}
|
|
66
89
|
if (useJson) {
|
|
67
90
|
jsonOrText(payload, true)
|
|
@@ -69,7 +92,9 @@ function statusCommand(useJson) {
|
|
|
69
92
|
}
|
|
70
93
|
console.log(`MIO_HOME: ${MIO_HOME}`)
|
|
71
94
|
console.log(`MCP Server: ${SERVER_SCRIPT}`)
|
|
72
|
-
console.log(`Codex MCP: ${installed ? 'installed' : 'not installed'}`)
|
|
95
|
+
console.log(`Codex MCP: ${installed.codex ? 'installed' : 'not installed'}`)
|
|
96
|
+
console.log(`OpenCode MCP: ${installed.opencode ? 'installed' : 'not installed'}`)
|
|
97
|
+
console.log(`WorkBuddy MCP: ${installed.workbuddy ? 'installed' : 'not installed'}`)
|
|
73
98
|
}
|
|
74
99
|
|
|
75
100
|
function agentsCommand(useJson) {
|
|
@@ -110,6 +135,7 @@ function installCommand(host, useJson) {
|
|
|
110
135
|
serverScript: SERVER_SCRIPT,
|
|
111
136
|
home: MIO_HOME,
|
|
112
137
|
workspace: process.cwd(),
|
|
138
|
+
project: projectFromWorkspace(process.cwd()),
|
|
113
139
|
})
|
|
114
140
|
} catch (error) {
|
|
115
141
|
console.error(`Failed to install ${host}: ${error.message || error}`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mio-agent-runtime",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Mio Agent Runtime installer and MCP intelligence layer",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -20,4 +20,4 @@
|
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
}
|
|
23
|
-
}
|
|
23
|
+
}
|