mio-agent-runtime 0.5.15 → 0.5.17
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 +144 -236
- package/adapters/codex.js +122 -134
- package/adapters/hermes.js +122 -140
- package/adapters/host-utils.js +61 -61
- package/adapters/opencode.js +176 -215
- package/adapters/workbuddy.js +196 -242
- package/bin/mio.js +423 -424
- package/observe/observer.js +1326 -1335
- package/package.json +38 -25
- package/scripts/verify-packed-runtime.js +371 -0
- package/server/creativity-engine.js +397 -0
- package/server/evolution-cutover.js +119 -0
- package/server/host-capabilities.js +50 -0
- package/server/mio-intelligence-mcp/README.md +184 -173
- package/server/mio-intelligence-mcp/index.js +621 -300
- package/server/mio-intelligence-mcp/package.json +17 -17
- package/server/mio-intelligence-mcp/phase0.js +381 -381
- package/server/runtime-modules.js +111 -0
- package/server/mio-intelligence-mcp/dashboard-server.js +0 -171
package/adapters/hermes.js
CHANGED
|
@@ -1,140 +1,122 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const fs = require('fs')
|
|
4
|
-
const path = require('path')
|
|
5
|
-
const os = require('os')
|
|
6
|
-
const { spawnSync } = require('child_process')
|
|
7
|
-
|
|
8
|
-
const SERVER_NAME = 'mio-intelligence'
|
|
9
|
-
|
|
10
|
-
function hermesHome() {
|
|
11
|
-
return process.env.HERMES_HOME || path.join(os.homedir(), 'AppData', 'Local', 'hermes')
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function hermesConfigPath() {
|
|
15
|
-
return path.join(hermesHome(), 'config.yaml')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function configNeedsRepair() {
|
|
19
|
-
const cfg = hermesConfigPath()
|
|
20
|
-
if (!fs.existsSync(cfg)) return false
|
|
21
|
-
try {
|
|
22
|
-
const src = fs.readFileSync(cfg, 'utf8')
|
|
23
|
-
// Legacy installs stored the env pairs as plain args (--env KEY=VALUE)
|
|
24
|
-
// because --env came after --args; Hermes now stores them under env:.
|
|
25
|
-
return /- MIO_DATA_DIR=/.test(src) || /- MIO_CONTEXT=/.test(src)
|
|
26
|
-
} catch (_) {
|
|
27
|
-
return false
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function hermesBin() {
|
|
32
|
-
if (process.env.HERMES_BIN && fs.existsSync(process.env.HERMES_BIN)) return process.env.HERMES_BIN
|
|
33
|
-
const home = hermesHome()
|
|
34
|
-
const candidates = [
|
|
35
|
-
// Official Windows native install: venv script registered on User PATH
|
|
36
|
-
path.join(home, 'hermes-agent', 'venv', 'Scripts', 'hermes.exe'),
|
|
37
|
-
// Newer installer shim under <LocalAppData>/hermes/bin
|
|
38
|
-
path.join(home, 'bin', 'hermes.cmd'),
|
|
39
|
-
// Legacy layout
|
|
40
|
-
path.join(home, 'hermes-agent', 'bin', 'hermes.exe'),
|
|
41
|
-
path.join(os.homedir(), '.hermes', 'bin', 'hermes.exe'),
|
|
42
|
-
]
|
|
43
|
-
for (const c of candidates) {
|
|
44
|
-
if (fs.existsSync(c)) return c
|
|
45
|
-
}
|
|
46
|
-
return null
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function runHermes(args) {
|
|
50
|
-
const bin = hermesBin()
|
|
51
|
-
// `hermes mcp add` asks to confirm tool enablement interactively; pre-answer
|
|
52
|
-
// it so the install is non-interactive.
|
|
53
|
-
const input = args[0] === 'mcp' && args[1] === 'add' ? 'Y\n' : undefined
|
|
54
|
-
let res
|
|
55
|
-
try {
|
|
56
|
-
const isCmdShim = Boolean(bin && /\.(cmd|bat)$/i.test(bin))
|
|
57
|
-
res = bin
|
|
58
|
-
? spawnSync(bin, args, { encoding: 'utf8', timeout: 60000, windowsHide: true, input: input, shell: isCmdShim
|
|
59
|
-
: spawnSync('hermes', args, { encoding: 'utf8', timeout: 60000, shell: true, windowsHide: true, input: input
|
|
60
|
-
} catch (_) {
|
|
61
|
-
return null
|
|
62
|
-
}
|
|
63
|
-
if (res.error || res.status !== 0) return null
|
|
64
|
-
return (res.stdout || '') + (res.stderr || '')
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function isInstalled() {
|
|
68
|
-
const out = runHermes(['mcp', 'list'])
|
|
69
|
-
return Boolean(out && out.indexOf(SERVER_NAME) !== -1)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function install({ node, serverScript, home, workspace, project }) {
|
|
73
|
-
if (!hermesBin() && !isHermesOnPath()) {
|
|
74
|
-
return {
|
|
75
|
-
changed: false,
|
|
76
|
-
message: 'Hermes binary not found. Install hermes-agent first.',
|
|
77
|
-
configPath: null,
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (isInstalled()) {
|
|
81
|
-
if (!configNeedsRepair()) {
|
|
82
|
-
return { changed: false, message: 'Hermes Mio MCP already installed.', configPath: null }
|
|
83
|
-
}
|
|
84
|
-
runHermes(['mcp', 'remove', SERVER_NAME])
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const context = JSON.stringify({
|
|
88
|
-
agentId: 'hermes',
|
|
89
|
-
project: project || path.basename(workspace || process.cwd()),
|
|
90
|
-
workspace: workspace || process.cwd(),
|
|
91
|
-
sessionId: 'hermes-session',
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
// Hermes requires --env before --args: --args swallows everything after it.
|
|
95
|
-
const args = [
|
|
96
|
-
'mcp', 'add', SERVER_NAME,
|
|
97
|
-
'--command', node,
|
|
98
|
-
'--env', 'MIO_DATA_DIR=' + home, 'MIO_CONTEXT=' + context,
|
|
99
|
-
'--args', serverScript,
|
|
100
|
-
]
|
|
101
|
-
const out = runHermes(args)
|
|
102
|
-
const installed = isInstalled()
|
|
103
|
-
|
|
104
|
-
let message
|
|
105
|
-
if (installed) {
|
|
106
|
-
message = 'Hermes Mio MCP installed (config: <LocalAppData>/hermes/config.yaml). Start a new Hermes session to load it.'
|
|
107
|
-
} else {
|
|
108
|
-
message = 'Hermes mcp add returned but Mio was not detected. Run `hermes mcp add ' + SERVER_NAME + ' --command ' + node + ' --args ' + serverScript + '` manually and answer Y to the prompt.'
|
|
109
|
-
}
|
|
110
|
-
return { changed: installed, configPath: null, message }
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function isHermesOnPath() {
|
|
114
|
-
try {
|
|
115
|
-
const res = spawnSync('where', ['hermes'], { encoding: 'utf8', shell: true })
|
|
116
|
-
return res.status === 0 && /hermes/.test(res.stdout || '')
|
|
117
|
-
} catch (_) {
|
|
118
|
-
return false
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
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 }
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const os = require('os')
|
|
6
|
+
const { spawnSync } = require('child_process')
|
|
7
|
+
|
|
8
|
+
const SERVER_NAME = 'mio-intelligence'
|
|
9
|
+
|
|
10
|
+
function hermesHome() {
|
|
11
|
+
return process.env.HERMES_HOME || path.join(os.homedir(), 'AppData', 'Local', 'hermes')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function hermesConfigPath() {
|
|
15
|
+
return path.join(hermesHome(), 'config.yaml')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function configNeedsRepair() {
|
|
19
|
+
const cfg = hermesConfigPath()
|
|
20
|
+
if (!fs.existsSync(cfg)) return false
|
|
21
|
+
try {
|
|
22
|
+
const src = fs.readFileSync(cfg, 'utf8')
|
|
23
|
+
// Legacy installs stored the env pairs as plain args (--env KEY=VALUE)
|
|
24
|
+
// because --env came after --args; Hermes now stores them under env:.
|
|
25
|
+
return /- MIO_DATA_DIR=/.test(src) || /- MIO_CONTEXT=/.test(src)
|
|
26
|
+
} catch (_) {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hermesBin() {
|
|
32
|
+
if (process.env.HERMES_BIN && fs.existsSync(process.env.HERMES_BIN)) return process.env.HERMES_BIN
|
|
33
|
+
const home = hermesHome()
|
|
34
|
+
const candidates = [
|
|
35
|
+
// Official Windows native install: venv script registered on User PATH
|
|
36
|
+
path.join(home, 'hermes-agent', 'venv', 'Scripts', 'hermes.exe'),
|
|
37
|
+
// Newer installer shim under <LocalAppData>/hermes/bin
|
|
38
|
+
path.join(home, 'bin', 'hermes.cmd'),
|
|
39
|
+
// Legacy layout
|
|
40
|
+
path.join(home, 'hermes-agent', 'bin', 'hermes.exe'),
|
|
41
|
+
path.join(os.homedir(), '.hermes', 'bin', 'hermes.exe'),
|
|
42
|
+
]
|
|
43
|
+
for (const c of candidates) {
|
|
44
|
+
if (fs.existsSync(c)) return c
|
|
45
|
+
}
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function runHermes(args) {
|
|
50
|
+
const bin = hermesBin()
|
|
51
|
+
// `hermes mcp add` asks to confirm tool enablement interactively; pre-answer
|
|
52
|
+
// it so the install is non-interactive.
|
|
53
|
+
const input = args[0] === 'mcp' && args[1] === 'add' ? 'Y\n' : undefined
|
|
54
|
+
let res
|
|
55
|
+
try {
|
|
56
|
+
const isCmdShim = Boolean(bin && /\.(cmd|bat)$/i.test(bin))
|
|
57
|
+
res = bin
|
|
58
|
+
? spawnSync(bin, args, { encoding: 'utf8', timeout: 60000, windowsHide: true, input: input, shell: isCmdShim })
|
|
59
|
+
: spawnSync('hermes', args, { encoding: 'utf8', timeout: 60000, shell: true, windowsHide: true, input: input })
|
|
60
|
+
} catch (_) {
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
if (res.error || res.status !== 0) return null
|
|
64
|
+
return (res.stdout || '') + (res.stderr || '')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isInstalled() {
|
|
68
|
+
const out = runHermes(['mcp', 'list'])
|
|
69
|
+
return Boolean(out && out.indexOf(SERVER_NAME) !== -1)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function install({ node, serverScript, home, workspace, project }) {
|
|
73
|
+
if (!hermesBin() && !isHermesOnPath()) {
|
|
74
|
+
return {
|
|
75
|
+
changed: false,
|
|
76
|
+
message: 'Hermes binary not found. Install hermes-agent first.',
|
|
77
|
+
configPath: null,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (isInstalled()) {
|
|
81
|
+
if (!configNeedsRepair()) {
|
|
82
|
+
return { changed: false, message: 'Hermes Mio MCP already installed.', configPath: null }
|
|
83
|
+
}
|
|
84
|
+
runHermes(['mcp', 'remove', SERVER_NAME])
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const context = JSON.stringify({
|
|
88
|
+
agentId: 'hermes',
|
|
89
|
+
project: project || path.basename(workspace || process.cwd()),
|
|
90
|
+
workspace: workspace || process.cwd(),
|
|
91
|
+
sessionId: 'hermes-session',
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// Hermes requires --env before --args: --args swallows everything after it.
|
|
95
|
+
const args = [
|
|
96
|
+
'mcp', 'add', SERVER_NAME,
|
|
97
|
+
'--command', node,
|
|
98
|
+
'--env', 'MIO_DATA_DIR=' + home, 'MIO_CONTEXT=' + context,
|
|
99
|
+
'--args', serverScript,
|
|
100
|
+
]
|
|
101
|
+
const out = runHermes(args)
|
|
102
|
+
const installed = isInstalled()
|
|
103
|
+
|
|
104
|
+
let message
|
|
105
|
+
if (installed) {
|
|
106
|
+
message = 'Hermes Mio MCP installed (config: <LocalAppData>/hermes/config.yaml). Start a new Hermes session to load it.'
|
|
107
|
+
} else {
|
|
108
|
+
message = 'Hermes mcp add returned but Mio was not detected. Run `hermes mcp add ' + SERVER_NAME + ' --command ' + node + ' --args ' + serverScript + '` manually and answer Y to the prompt.'
|
|
109
|
+
}
|
|
110
|
+
return { changed: installed, configPath: null, message }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isHermesOnPath() {
|
|
114
|
+
try {
|
|
115
|
+
const res = spawnSync('where', ['hermes'], { encoding: 'utf8', shell: true })
|
|
116
|
+
return res.status === 0 && /hermes/.test(res.stdout || '')
|
|
117
|
+
} catch (_) {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = { hermesBin, isInstalled, install }
|
package/adapters/host-utils.js
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const fs = require('fs')
|
|
4
|
-
const { spawnSync } = require('child_process')
|
|
5
|
-
|
|
6
|
-
// Windows only: returns the earliest creation time (epoch ms) of a running
|
|
7
|
-
// process whose ExecutablePath contains `exePathFragment`, or null when the
|
|
8
|
-
// host app is not running / detection is unavailable.
|
|
9
|
-
function earliestRunningSince(exePathFragment) {
|
|
10
|
-
if (process.platform !== 'win32') return null
|
|
11
|
-
try {
|
|
12
|
-
const escaped = String(exePathFragment).replace(/'/g, "''")
|
|
13
|
-
const script =
|
|
14
|
-
"Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -like '*" +
|
|
15
|
-
escaped +
|
|
16
|
-
"*' } | ForEach-Object { [long]($_.CreationDate.ToUniversalTime() - [datetime]'1970-01-01').TotalMilliseconds }"
|
|
17
|
-
const res = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
18
|
-
encoding: 'utf8',
|
|
19
|
-
timeout: 10000,
|
|
20
|
-
windowsHide: true,
|
|
21
|
-
})
|
|
22
|
-
if (res.error || res.status !== 0) return null
|
|
23
|
-
const times = (res.stdout || '')
|
|
24
|
-
.split(/\r?\n/)
|
|
25
|
-
.map((line) => line.trim())
|
|
26
|
-
.filter(Boolean)
|
|
27
|
-
.map(Number)
|
|
28
|
-
.filter((value) => Number.isFinite(value) && value > 0)
|
|
29
|
-
if (times.length === 0) return null
|
|
30
|
-
return Math.min(...times)
|
|
31
|
-
} catch (_) {
|
|
32
|
-
return null
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Returns a human-readable warning when the host app is currently running from
|
|
37
|
-
// a config that predates its process start (i.e. a restart is required to load
|
|
38
|
-
// the updated Mio MCP config). Returns null when no restart is needed.
|
|
39
|
-
function restartWarning({ hostLabel, exeName, exePathFragment, configPath }) {
|
|
40
|
-
const since = earliestRunningSince(exePathFragment)
|
|
41
|
-
if (since === null) return null
|
|
42
|
-
let mtime = null
|
|
43
|
-
try {
|
|
44
|
-
mtime = fs.statSync(configPath).mtimeMs
|
|
45
|
-
} catch (_) {
|
|
46
|
-
return null
|
|
47
|
-
}
|
|
48
|
-
if (!(mtime > since)) return null
|
|
49
|
-
const started = new Date(since).toISOString().replace('T', ' ').slice(0, 19) + ' UTC'
|
|
50
|
-
return (
|
|
51
|
-
hostLabel +
|
|
52
|
-
' is running with an older Mio config (app started ' +
|
|
53
|
-
started +
|
|
54
|
-
', config updated after). Fully quit ' +
|
|
55
|
-
hostLabel +
|
|
56
|
-
' (end all ' +
|
|
57
|
-
exeName +
|
|
58
|
-
' processes) and relaunch to load the updated Mio MCP config.'
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const { spawnSync } = require('child_process')
|
|
5
|
+
|
|
6
|
+
// Windows only: returns the earliest creation time (epoch ms) of a running
|
|
7
|
+
// process whose ExecutablePath contains `exePathFragment`, or null when the
|
|
8
|
+
// host app is not running / detection is unavailable.
|
|
9
|
+
function earliestRunningSince(exePathFragment) {
|
|
10
|
+
if (process.platform !== 'win32') return null
|
|
11
|
+
try {
|
|
12
|
+
const escaped = String(exePathFragment).replace(/'/g, "''")
|
|
13
|
+
const script =
|
|
14
|
+
"Get-CimInstance Win32_Process | Where-Object { $_.ExecutablePath -like '*" +
|
|
15
|
+
escaped +
|
|
16
|
+
"*' } | ForEach-Object { [long]($_.CreationDate.ToUniversalTime() - [datetime]'1970-01-01').TotalMilliseconds }"
|
|
17
|
+
const res = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
timeout: 10000,
|
|
20
|
+
windowsHide: true,
|
|
21
|
+
})
|
|
22
|
+
if (res.error || res.status !== 0) return null
|
|
23
|
+
const times = (res.stdout || '')
|
|
24
|
+
.split(/\r?\n/)
|
|
25
|
+
.map((line) => line.trim())
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.map(Number)
|
|
28
|
+
.filter((value) => Number.isFinite(value) && value > 0)
|
|
29
|
+
if (times.length === 0) return null
|
|
30
|
+
return Math.min(...times)
|
|
31
|
+
} catch (_) {
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Returns a human-readable warning when the host app is currently running from
|
|
37
|
+
// a config that predates its process start (i.e. a restart is required to load
|
|
38
|
+
// the updated Mio MCP config). Returns null when no restart is needed.
|
|
39
|
+
function restartWarning({ hostLabel, exeName, exePathFragment, configPath }) {
|
|
40
|
+
const since = earliestRunningSince(exePathFragment)
|
|
41
|
+
if (since === null) return null
|
|
42
|
+
let mtime = null
|
|
43
|
+
try {
|
|
44
|
+
mtime = fs.statSync(configPath).mtimeMs
|
|
45
|
+
} catch (_) {
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
if (!(mtime > since)) return null
|
|
49
|
+
const started = new Date(since).toISOString().replace('T', ' ').slice(0, 19) + ' UTC'
|
|
50
|
+
return (
|
|
51
|
+
hostLabel +
|
|
52
|
+
' is running with an older Mio config (app started ' +
|
|
53
|
+
started +
|
|
54
|
+
', config updated after). Fully quit ' +
|
|
55
|
+
hostLabel +
|
|
56
|
+
' (end all ' +
|
|
57
|
+
exeName +
|
|
58
|
+
' processes) and relaunch to load the updated Mio MCP config.'
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
62
|
module.exports = { earliestRunningSince, restartWarning }
|