mio-agent-runtime 0.2.1 → 0.2.3
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 +50 -44
- package/adapters/codex.js +61 -62
- package/adapters/opencode.js +107 -35
- package/adapters/workbuddy.js +14 -128
- package/bin/mio.js +164 -183
- package/package.json +22 -22
package/README.md
CHANGED
|
@@ -1,48 +1,54 @@
|
|
|
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
|
-
## Commands
|
|
14
|
-
|
|
15
|
-
```text
|
|
16
|
-
mio init Initialize MIO_HOME
|
|
17
|
-
mio mcp Start Mio MCP server (stdio)
|
|
18
|
-
mio install <host> Install Mio into a host (codex|opencode|workbuddy)
|
|
19
|
-
mio status Show runtime and adapter status
|
|
20
|
-
mio agents List installed agents
|
|
21
|
-
mio --json status Machine-readable status
|
|
22
|
-
mio --json agents Machine-readable agents
|
|
23
|
-
```
|
|
24
|
-
|
|
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
|
+
## Commands
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
mio init Initialize MIO_HOME
|
|
17
|
+
mio mcp Start Mio MCP server (stdio)
|
|
18
|
+
mio install <host> Install Mio into a host (codex|opencode|workbuddy)
|
|
19
|
+
mio status Show runtime and adapter status
|
|
20
|
+
mio agents List installed agents
|
|
21
|
+
mio --json status Machine-readable status
|
|
22
|
+
mio --json agents Machine-readable agents
|
|
23
|
+
```
|
|
24
|
+
|
|
25
25
|
## Host notes
|
|
26
26
|
|
|
27
|
+
- OpenCode uses `~/.config/opencode/opencode.json` for MCP registration and
|
|
28
|
+
`~/.config/opencode/AGENTS.md` for global instructions. `mio install opencode`
|
|
29
|
+
writes both files and injects Mio usage rules into `AGENTS.md` so OpenCode
|
|
30
|
+
calls Mio memory/observer/policy automatically. If OpenCode is already
|
|
31
|
+
running, restart it once so it reloads both files.
|
|
27
32
|
- WorkBuddy uses `~/.workbuddy/mcp.json` and requires each custom MCP server to
|
|
28
33
|
be approved in `~/.workbuddy/mcp-approvals.json`. `mio install workbuddy`
|
|
29
|
-
writes both files
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
├──
|
|
42
|
-
|
|
43
|
-
│ ├──
|
|
44
|
-
│
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
└──
|
|
48
|
-
|
|
34
|
+
writes both files and injects Mio usage rules into `~/.workbuddy/MEMORY.md`
|
|
35
|
+
so WorkBuddy calls Mio memory/observer/policy automatically. If WorkBuddy is
|
|
36
|
+
already running, restart it once so it reloads the approval and memory files.
|
|
37
|
+
|
|
38
|
+
## MIO_HOME
|
|
39
|
+
|
|
40
|
+
默认 `~/.mio-intelligence`,可通过环境变量 `MIO_HOME` 覆盖。
|
|
41
|
+
|
|
42
|
+
## Package layout
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
packages/mio-cli
|
|
46
|
+
├── bin/mio.js
|
|
47
|
+
├── adapters/
|
|
48
|
+
│ ├── codex.js
|
|
49
|
+
│ ├── opencode.js
|
|
50
|
+
│ └── workbuddy.js
|
|
51
|
+
├── server/
|
|
52
|
+
│ └── mio-intelligence-mcp/index.js
|
|
53
|
+
└── package.json
|
|
54
|
+
```
|
package/adapters/codex.js
CHANGED
|
@@ -1,63 +1,62 @@
|
|
|
1
|
-
'use strict'
|
|
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(), '.codex', 'config.toml')
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function isInstalled() {
|
|
12
|
-
const target = configPath()
|
|
13
|
-
if (!fs.existsSync(target)) return false
|
|
14
|
-
return fs.readFileSync(target, 'utf8').includes('[mcp_servers.mio-intelligence]')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function install({ node, serverScript, home, workspace
|
|
18
|
-
const target = configPath()
|
|
19
|
-
const backup = `${target}.bak-mio-${Date.now()}`
|
|
20
|
-
if (isInstalled()) {
|
|
21
|
-
return {
|
|
22
|
-
changed: false,
|
|
23
|
-
configPath: target,
|
|
24
|
-
message: 'Codex Mio MCP already installed.',
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (fs.existsSync(target)) {
|
|
29
|
-
fs.copyFileSync(target, backup)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const context = JSON.stringify({
|
|
33
|
-
agentId: 'codex',
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
fs.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
'
|
|
44
|
-
`
|
|
45
|
-
|
|
46
|
-
'
|
|
47
|
-
'',
|
|
48
|
-
'
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
'use strict'
|
|
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(), '.codex', 'config.toml')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isInstalled() {
|
|
12
|
+
const target = configPath()
|
|
13
|
+
if (!fs.existsSync(target)) return false
|
|
14
|
+
return fs.readFileSync(target, 'utf8').includes('[mcp_servers.mio-intelligence]')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function install({ node, serverScript, home, workspace }) {
|
|
18
|
+
const target = configPath()
|
|
19
|
+
const backup = `${target}.bak-mio-${Date.now()}`
|
|
20
|
+
if (isInstalled()) {
|
|
21
|
+
return {
|
|
22
|
+
changed: false,
|
|
23
|
+
configPath: target,
|
|
24
|
+
message: 'Codex Mio MCP already installed.',
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (fs.existsSync(target)) {
|
|
29
|
+
fs.copyFileSync(target, backup)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const context = JSON.stringify({
|
|
33
|
+
agentId: 'codex',
|
|
34
|
+
workspace: workspace || process.cwd(),
|
|
35
|
+
sessionId: 'codex-session',
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
39
|
+
let toml = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : ''
|
|
40
|
+
if (!toml.endsWith('\n')) toml += '\n'
|
|
41
|
+
const section = [
|
|
42
|
+
'[mcp_servers.mio-intelligence]',
|
|
43
|
+
`command = '${node}'`,
|
|
44
|
+
`args = ['${serverScript}']`,
|
|
45
|
+
'startup_timeout_sec = 30',
|
|
46
|
+
'',
|
|
47
|
+
'[mcp_servers.mio-intelligence.env]',
|
|
48
|
+
`MIO_DATA_DIR = '${home}'`,
|
|
49
|
+
`MIO_CONTEXT = '${context}'`,
|
|
50
|
+
'',
|
|
51
|
+
].join('\n')
|
|
52
|
+
fs.writeFileSync(target, `${toml}${section}\n`, 'utf8')
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
changed: true,
|
|
56
|
+
configPath: target,
|
|
57
|
+
backup,
|
|
58
|
+
message: 'Codex Mio MCP installed. Restart Codex to load it.',
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
63
62
|
module.exports = { configPath, isInstalled, install }
|
package/adapters/opencode.js
CHANGED
|
@@ -4,10 +4,29 @@ const fs = require('fs')
|
|
|
4
4
|
const path = require('path')
|
|
5
5
|
const os = require('os')
|
|
6
6
|
|
|
7
|
+
const INSTRUCTIONS_BEGIN = '<!-- MIO_INTELLIGENCE_BEGIN -->'
|
|
8
|
+
const INSTRUCTIONS_END = '<!-- MIO_INTELLIGENCE_END -->'
|
|
9
|
+
const INSTRUCTIONS_BLOCK = `${INSTRUCTIONS_BEGIN}
|
|
10
|
+
## Mio Intelligence Layer
|
|
11
|
+
|
|
12
|
+
Mio MCP tools are available. Treat these as mandatory runtime rules for every workspace.
|
|
13
|
+
|
|
14
|
+
- Before designing or implementing a solution, or when unsure about prior decisions, call \`mio-intelligence_mio_memory_query\` first. Omit \`project\` unless a specific project is required; Mio derives it from the current Git repository.
|
|
15
|
+
- After a decision affecting architecture, deployment, dependencies, or long-term behavior, call \`mio-intelligence_mio_memory_record\` with \`kind\`: \`decision\`, \`context\`, \`problem\`, or \`note\`.
|
|
16
|
+
- When a tool call fails, retries, or returns an error, call \`mio-intelligence_mio_observer_ingest\` with a stable \`trace_id\`, relevant \`event_type\`, \`payload\`, and \`outcome\`.
|
|
17
|
+
- Before a high-risk operation such as deletion, migration, credential change, or production change, call \`mio-intelligence_mio_policy_check\` and follow its suggestion when available.
|
|
18
|
+
- If Mio MCP is unavailable, do not block the task. Continue normally and note the missed memory or observation.
|
|
19
|
+
- Do not store secrets, credentials, or raw sensitive content in Mio memory.
|
|
20
|
+
${INSTRUCTIONS_END}`
|
|
21
|
+
|
|
7
22
|
function configPath() {
|
|
8
23
|
return path.join(os.homedir(), '.config', 'opencode', 'opencode.json')
|
|
9
24
|
}
|
|
10
25
|
|
|
26
|
+
function globalInstructionsPath() {
|
|
27
|
+
return path.join(os.homedir(), '.config', 'opencode', 'AGENTS.md')
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
function readConfig() {
|
|
12
31
|
const target = configPath()
|
|
13
32
|
if (!fs.existsSync(target)) return null
|
|
@@ -18,59 +37,112 @@ function readConfig() {
|
|
|
18
37
|
}
|
|
19
38
|
}
|
|
20
39
|
|
|
21
|
-
function
|
|
22
|
-
const
|
|
23
|
-
|
|
40
|
+
function readGlobalInstructions() {
|
|
41
|
+
const target = globalInstructionsPath()
|
|
42
|
+
if (!fs.existsSync(target)) return ''
|
|
43
|
+
try {
|
|
44
|
+
return fs.readFileSync(target, 'utf8')
|
|
45
|
+
} catch (_) {
|
|
46
|
+
return ''
|
|
47
|
+
}
|
|
24
48
|
}
|
|
25
49
|
|
|
26
|
-
function
|
|
27
|
-
const target =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
50
|
+
function ensureGlobalInstructions() {
|
|
51
|
+
const target = globalInstructionsPath()
|
|
52
|
+
let content = readGlobalInstructions()
|
|
53
|
+
const original = content
|
|
54
|
+
const start = content.indexOf(INSTRUCTIONS_BEGIN)
|
|
55
|
+
if (start !== -1) {
|
|
56
|
+
const end = content.indexOf(INSTRUCTIONS_END, start)
|
|
57
|
+
content = end === -1 ? content.slice(0, start) : content.slice(0, start) + content.slice(end + INSTRUCTIONS_END.length)
|
|
58
|
+
content = content.trimEnd()
|
|
59
|
+
}
|
|
60
|
+
if (content) content += '\n\n'
|
|
61
|
+
content += INSTRUCTIONS_BLOCK + '\n'
|
|
62
|
+
if (content === original) {
|
|
63
|
+
return { changed: false, target, backup: null }
|
|
34
64
|
}
|
|
35
65
|
|
|
36
66
|
const backup = `${target}.bak-mio-${Date.now()}`
|
|
37
67
|
if (fs.existsSync(target)) {
|
|
38
68
|
fs.copyFileSync(target, backup)
|
|
39
69
|
}
|
|
70
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
71
|
+
fs.writeFileSync(target, content, 'utf8')
|
|
72
|
+
return { changed: true, target, backup }
|
|
73
|
+
}
|
|
40
74
|
|
|
75
|
+
function isInstalled() {
|
|
76
|
+
const config = readConfig()
|
|
77
|
+
if (!config || !config.mcp || !config.mcp['mio-intelligence']) return false
|
|
78
|
+
const instructions = readGlobalInstructions()
|
|
79
|
+
return instructions.includes(INSTRUCTIONS_BEGIN) && instructions.includes(INSTRUCTIONS_END)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function install({ node, serverScript, home, workspace, project }) {
|
|
83
|
+
const target = configPath()
|
|
41
84
|
const config = readConfig() || {}
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
85
|
+
const configInstalled = Boolean(config.mcp && config.mcp['mio-intelligence'])
|
|
86
|
+
|
|
87
|
+
let configChanged = false
|
|
88
|
+
let backup = null
|
|
89
|
+
|
|
90
|
+
if (!configInstalled) {
|
|
91
|
+
backup = `${target}.bak-mio-${Date.now()}`
|
|
92
|
+
if (fs.existsSync(target)) {
|
|
93
|
+
fs.copyFileSync(target, backup)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const existingMcp = config.mcp && typeof config.mcp === 'object' ? config.mcp : {}
|
|
97
|
+
const context = JSON.stringify({
|
|
98
|
+
agentId: 'opencode',
|
|
99
|
+
project: project || path.basename(workspace || process.cwd()),
|
|
100
|
+
workspace: workspace || process.cwd(),
|
|
101
|
+
sessionId: 'opencode-session',
|
|
102
|
+
})
|
|
103
|
+
const next = {
|
|
104
|
+
...config,
|
|
105
|
+
mcp: {
|
|
106
|
+
...existingMcp,
|
|
107
|
+
'mio-intelligence': {
|
|
108
|
+
enabled: true,
|
|
109
|
+
type: 'local',
|
|
110
|
+
command: [node, serverScript],
|
|
111
|
+
environment: {
|
|
112
|
+
MIO_DATA_DIR: home,
|
|
113
|
+
MIO_CONTEXT: context,
|
|
114
|
+
},
|
|
60
115
|
},
|
|
61
116
|
},
|
|
62
|
-
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
120
|
+
fs.writeFileSync(target, `${JSON.stringify(next, null, 2)}\n`, 'utf8')
|
|
121
|
+
configChanged = true
|
|
63
122
|
}
|
|
64
123
|
|
|
65
|
-
|
|
66
|
-
|
|
124
|
+
const instructionsResult = ensureGlobalInstructions()
|
|
125
|
+
const changed = configChanged || instructionsResult.changed
|
|
126
|
+
|
|
127
|
+
let message
|
|
128
|
+
if (configChanged && instructionsResult.changed) {
|
|
129
|
+
message = 'OpenCode Mio MCP installed and global Mio rules injected into AGENTS.md. Restart OpenCode to load them.'
|
|
130
|
+
} else if (instructionsResult.changed) {
|
|
131
|
+
message = 'OpenCode Mio MCP already installed. Injected global Mio rules into AGENTS.md. Restart OpenCode to load them.'
|
|
132
|
+
} else {
|
|
133
|
+
message = 'OpenCode Mio MCP already installed.'
|
|
134
|
+
}
|
|
67
135
|
|
|
68
136
|
return {
|
|
69
|
-
changed
|
|
137
|
+
changed,
|
|
70
138
|
configPath: target,
|
|
139
|
+
globalInstructionsPath: instructionsResult.target,
|
|
140
|
+
configChanged,
|
|
141
|
+
instructionsChanged: instructionsResult.changed,
|
|
71
142
|
backup,
|
|
72
|
-
|
|
143
|
+
backupInstructions: instructionsResult.backup,
|
|
144
|
+
message,
|
|
73
145
|
}
|
|
74
146
|
}
|
|
75
147
|
|
|
76
|
-
module.exports = { configPath, isInstalled, install }
|
|
148
|
+
module.exports = { configPath, globalInstructionsPath, isInstalled, install }
|
package/adapters/workbuddy.js
CHANGED
|
@@ -1,128 +1,14 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
37
|
-
function isInstalled() {
|
|
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)])
|
|
44
|
-
}
|
|
45
|
-
|
|
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
|
-
|
|
119
|
-
return {
|
|
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.',
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
module.exports = { configDir, configPath, approvalsPath, approvalKey, isInstalled, install }
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function isInstalled() {
|
|
4
|
+
return false
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function install() {
|
|
8
|
+
return {
|
|
9
|
+
changed: false,
|
|
10
|
+
message: 'WorkBuddy adapter is not implemented yet.',
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = { isInstalled, install }
|
package/bin/mio.js
CHANGED
|
@@ -1,74 +1,56 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict'
|
|
3
|
-
|
|
4
|
-
const fs = require('fs')
|
|
5
|
-
const path = require('path')
|
|
6
|
-
const os = require('os')
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function jsonOrText(payload, useJson) {
|
|
57
|
-
if (useJson) {
|
|
58
|
-
console.log(JSON.stringify(payload, null, 2))
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
return payload
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function initCommand() {
|
|
65
|
-
ensureHome()
|
|
66
|
-
const config = readConfig()
|
|
67
|
-
if (!config.createdAt) config.createdAt = new Date().toISOString()
|
|
68
|
-
writeConfig(config)
|
|
69
|
-
console.log(`MIO_HOME ready: ${MIO_HOME}`)
|
|
70
|
-
}
|
|
71
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const os = require('os')
|
|
7
|
+
|
|
8
|
+
const SERVER_SCRIPT = path.resolve(__dirname, '..', 'server', 'mio-intelligence-mcp', 'index.js')
|
|
9
|
+
const MIO_HOME = process.env.MIO_HOME || path.join(os.homedir(), '.mio-intelligence')
|
|
10
|
+
const CONFIG_FILE = path.join(MIO_HOME, 'config.json')
|
|
11
|
+
|
|
12
|
+
const adapters = {
|
|
13
|
+
codex: require('../adapters/codex.js'),
|
|
14
|
+
opencode: require('../adapters/opencode.js'),
|
|
15
|
+
workbuddy: require('../adapters/workbuddy.js'),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function ensureHome() {
|
|
19
|
+
fs.mkdirSync(MIO_HOME, { recursive: true })
|
|
20
|
+
fs.mkdirSync(path.join(MIO_HOME, 'logs'), { recursive: true })
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readConfig() {
|
|
24
|
+
const fallback = { version: 1, home: MIO_HOME, agents: {} }
|
|
25
|
+
if (!fs.existsSync(CONFIG_FILE)) return fallback
|
|
26
|
+
try {
|
|
27
|
+
return { ...fallback, ...JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')) }
|
|
28
|
+
} catch (_) {
|
|
29
|
+
return fallback
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function writeConfig(config) {
|
|
34
|
+
ensureHome()
|
|
35
|
+
fs.writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function jsonOrText(payload, useJson) {
|
|
39
|
+
if (useJson) {
|
|
40
|
+
console.log(JSON.stringify(payload, null, 2))
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
return payload
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function initCommand() {
|
|
47
|
+
ensureHome()
|
|
48
|
+
const config = readConfig()
|
|
49
|
+
if (!config.createdAt) config.createdAt = new Date().toISOString()
|
|
50
|
+
writeConfig(config)
|
|
51
|
+
console.log(`MIO_HOME ready: ${MIO_HOME}`)
|
|
52
|
+
}
|
|
53
|
+
|
|
72
54
|
function statusCommand(useJson) {
|
|
73
55
|
const config = readConfig()
|
|
74
56
|
const installed = {
|
|
@@ -86,121 +68,120 @@ function statusCommand(useJson) {
|
|
|
86
68
|
opencodeInstalled: installed.opencode,
|
|
87
69
|
workbuddyInstalled: installed.workbuddy,
|
|
88
70
|
}
|
|
89
|
-
if (useJson) {
|
|
90
|
-
jsonOrText(payload, true)
|
|
91
|
-
return
|
|
92
|
-
}
|
|
71
|
+
if (useJson) {
|
|
72
|
+
jsonOrText(payload, true)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
93
75
|
console.log(`MIO_HOME: ${MIO_HOME}`)
|
|
94
76
|
console.log(`MCP Server: ${SERVER_SCRIPT}`)
|
|
95
77
|
console.log(`Codex MCP: ${installed.codex ? 'installed' : 'not installed'}`)
|
|
96
78
|
console.log(`OpenCode MCP: ${installed.opencode ? 'installed' : 'not installed'}`)
|
|
97
79
|
console.log(`WorkBuddy MCP: ${installed.workbuddy ? 'installed' : 'not installed'}`)
|
|
98
80
|
}
|
|
99
|
-
|
|
100
|
-
function agentsCommand(useJson) {
|
|
101
|
-
const config = readConfig()
|
|
102
|
-
const agents = config.agents || {}
|
|
103
|
-
if (useJson) {
|
|
104
|
-
jsonOrText(agents, true)
|
|
105
|
-
return
|
|
106
|
-
}
|
|
107
|
-
const names = Object.keys(agents)
|
|
108
|
-
if (names.length === 0) {
|
|
109
|
-
console.log('No agents installed.')
|
|
110
|
-
return
|
|
111
|
-
}
|
|
112
|
-
for (const name of names) {
|
|
113
|
-
console.log(`${name}\t${agents[name].installedAt || ''}`)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function installCommand(host, useJson) {
|
|
118
|
-
if (!host) {
|
|
119
|
-
console.error('Usage: mio install <codex|opencode|workbuddy>')
|
|
120
|
-
process.exitCode = 1
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
const adapter = adapters[host]
|
|
124
|
-
if (!adapter) {
|
|
125
|
-
console.error(`Unknown host: ${host}`)
|
|
126
|
-
process.exitCode = 1
|
|
127
|
-
return
|
|
128
|
-
}
|
|
129
|
-
ensureHome()
|
|
130
|
-
const config = readConfig()
|
|
131
|
-
let result
|
|
132
|
-
try {
|
|
133
|
-
result = adapter.install({
|
|
134
|
-
node: process.execPath,
|
|
135
|
-
serverScript: SERVER_SCRIPT,
|
|
136
|
-
home: MIO_HOME,
|
|
137
|
-
workspace: process.cwd(),
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
config.agents =
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
mio
|
|
169
|
-
mio
|
|
170
|
-
mio
|
|
171
|
-
mio
|
|
172
|
-
mio
|
|
173
|
-
mio --json
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
case 'help':
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
main()
|
|
81
|
+
|
|
82
|
+
function agentsCommand(useJson) {
|
|
83
|
+
const config = readConfig()
|
|
84
|
+
const agents = config.agents || {}
|
|
85
|
+
if (useJson) {
|
|
86
|
+
jsonOrText(agents, true)
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
const names = Object.keys(agents)
|
|
90
|
+
if (names.length === 0) {
|
|
91
|
+
console.log('No agents installed.')
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
for (const name of names) {
|
|
95
|
+
console.log(`${name}\t${agents[name].installedAt || ''}`)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function installCommand(host, useJson) {
|
|
100
|
+
if (!host) {
|
|
101
|
+
console.error('Usage: mio install <codex|opencode|workbuddy>')
|
|
102
|
+
process.exitCode = 1
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
const adapter = adapters[host]
|
|
106
|
+
if (!adapter) {
|
|
107
|
+
console.error(`Unknown host: ${host}`)
|
|
108
|
+
process.exitCode = 1
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
ensureHome()
|
|
112
|
+
const config = readConfig()
|
|
113
|
+
let result
|
|
114
|
+
try {
|
|
115
|
+
result = adapter.install({
|
|
116
|
+
node: process.execPath,
|
|
117
|
+
serverScript: SERVER_SCRIPT,
|
|
118
|
+
home: MIO_HOME,
|
|
119
|
+
workspace: process.cwd(),
|
|
120
|
+
})
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(`Failed to install ${host}: ${error.message || error}`)
|
|
123
|
+
process.exitCode = 1
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
if (result.changed) {
|
|
127
|
+
config.agents = config.agents || {}
|
|
128
|
+
config.agents[host] = {
|
|
129
|
+
installedAt: new Date().toISOString(),
|
|
130
|
+
configPath: result.configPath || null,
|
|
131
|
+
}
|
|
132
|
+
writeConfig(config)
|
|
133
|
+
}
|
|
134
|
+
if (useJson) {
|
|
135
|
+
jsonOrText(result, true)
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
console.log(result.message || `${host} adapter completed.`)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function mcpCommand() {
|
|
142
|
+
require(SERVER_SCRIPT)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function help() {
|
|
146
|
+
console.log(`Mio Agent Runtime CLI
|
|
147
|
+
|
|
148
|
+
Usage:
|
|
149
|
+
mio init Initialize MIO_HOME
|
|
150
|
+
mio mcp Start Mio MCP server (stdio)
|
|
151
|
+
mio install <host> Install Mio into a host (codex|opencode|workbuddy)
|
|
152
|
+
mio status Show runtime and adapter status
|
|
153
|
+
mio agents List installed agents
|
|
154
|
+
mio --json status Machine-readable status
|
|
155
|
+
mio --json agents Machine-readable agents
|
|
156
|
+
`)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function main() {
|
|
160
|
+
const args = process.argv.slice(2)
|
|
161
|
+
const jsonIndex = args.indexOf('--json')
|
|
162
|
+
const useJson = jsonIndex !== -1
|
|
163
|
+
if (useJson) args.splice(jsonIndex, 1)
|
|
164
|
+
const command = args[0] || 'help'
|
|
165
|
+
|
|
166
|
+
switch (command) {
|
|
167
|
+
case 'init':
|
|
168
|
+
return initCommand()
|
|
169
|
+
case 'mcp':
|
|
170
|
+
return mcpCommand()
|
|
171
|
+
case 'install':
|
|
172
|
+
return installCommand(args[1], useJson)
|
|
173
|
+
case 'status':
|
|
174
|
+
return statusCommand(useJson)
|
|
175
|
+
case 'agents':
|
|
176
|
+
return agentsCommand(useJson)
|
|
177
|
+
case 'help':
|
|
178
|
+
case '--help':
|
|
179
|
+
return help()
|
|
180
|
+
default:
|
|
181
|
+
console.error(`Unknown command: ${command}`)
|
|
182
|
+
help()
|
|
183
|
+
process.exitCode = 1
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
main()
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mio-agent-runtime",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Mio Agent Runtime installer and MCP intelligence layer",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "commonjs",
|
|
7
|
-
"bin": {
|
|
8
|
-
"mio": "bin/mio.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"bin/",
|
|
12
|
-
"server/",
|
|
13
|
-
"adapters/",
|
|
14
|
-
"README.md"
|
|
15
|
-
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"check": "node --check bin/mio.js && node --check adapters/codex.js && node --check adapters/opencode.js && node --check adapters/workbuddy.js && node --check server/mio-intelligence-mcp/index.js",
|
|
18
|
-
"prepack": "npm run check"
|
|
19
|
-
},
|
|
20
|
-
"engines": {
|
|
21
|
-
"node": ">=18"
|
|
22
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "mio-agent-runtime",
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "Mio Agent Runtime installer and MCP intelligence layer",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mio": "bin/mio.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"server/",
|
|
13
|
+
"adapters/",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"check": "node --check bin/mio.js && node --check adapters/codex.js && node --check adapters/opencode.js && node --check adapters/workbuddy.js && node --check server/mio-intelligence-mcp/index.js",
|
|
18
|
+
"prepack": "npm run check"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
}
|
|
23
23
|
}
|