@yemi33/minions 0.1.1814 → 0.1.1816
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/CHANGELOG.md +10 -0
- package/bin/minions.js +17 -1
- package/dashboard/js/command-center.js +34 -4
- package/dashboard.js +40 -2
- package/engine/copilot-models.json +1 -1
- package/engine/restart-health.js +169 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/bin/minions.js
CHANGED
|
@@ -40,6 +40,7 @@ const { spawn, spawnSync, execSync } = require('child_process');
|
|
|
40
40
|
|
|
41
41
|
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
42
42
|
const shared = require(path.join(PKG_ROOT, 'engine', 'shared'));
|
|
43
|
+
const { waitForRestartHealth, formatRestartHealthError } = require(path.join(PKG_ROOT, 'engine', 'restart-health'));
|
|
43
44
|
const DASH_PORT = 7331;
|
|
44
45
|
const DASHBOARD_BROWSER_PRESENCE_MAX_AGE_MS = 45000;
|
|
45
46
|
|
|
@@ -708,7 +709,22 @@ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
|
708
709
|
console.log(`\n Engine started (PID: ${engineProc.pid})`);
|
|
709
710
|
const dashProc = spawnDashboard(suppressDashboardOpen);
|
|
710
711
|
console.log(` Dashboard started (PID: ${dashProc.pid})`);
|
|
711
|
-
console.log(` Dashboard: http://localhost:${DASH_PORT}
|
|
712
|
+
console.log(` Dashboard: http://localhost:${DASH_PORT}`);
|
|
713
|
+
console.log(' Verifying restart health...');
|
|
714
|
+
void (async () => {
|
|
715
|
+
const result = await waitForRestartHealth({
|
|
716
|
+
minionsHome: MINIONS_HOME,
|
|
717
|
+
dashboardUrl: `http://127.0.0.1:${DASH_PORT}/api/health`,
|
|
718
|
+
});
|
|
719
|
+
if (!result.ok) {
|
|
720
|
+
console.error(formatRestartHealthError(result));
|
|
721
|
+
process.exit(1);
|
|
722
|
+
}
|
|
723
|
+
console.log(` Restart verified: engine PID ${result.engine.pid}; dashboard healthy.\n`);
|
|
724
|
+
})().catch(err => {
|
|
725
|
+
console.error(`\n ERROR: Restart verification failed: ${err.message}\n`);
|
|
726
|
+
process.exit(1);
|
|
727
|
+
});
|
|
712
728
|
} else if (cmd === 'nuke') {
|
|
713
729
|
ensureInstalled();
|
|
714
730
|
if (!rest.includes('--confirm')) {
|
|
@@ -826,11 +826,15 @@ async function _ccDoSend(message, skipUserMsg, forceTabId, intentMetadata) {
|
|
|
826
826
|
var failures = evt.actionResults.filter(function(r) { return r && r.error; });
|
|
827
827
|
var warnings = evt.actionResults.filter(function(r) { return r && r.warning; });
|
|
828
828
|
if (failures.length > 0) {
|
|
829
|
-
var failHtml = failures.map(function(r) {
|
|
829
|
+
var failHtml = failures.map(function(r) {
|
|
830
|
+
return '<li>' + escHtml(r.type || (r.actionContext && r.actionContext.type) || 'action') + ': ' + escHtml(r.error) + _ccActionContextSuffix(r.actionContext) + '</li>';
|
|
831
|
+
}).join('');
|
|
830
832
|
addMsg('system', '<div style="padding:6px 12px;font-size:11px;color:var(--red);background:var(--surface2);border-radius:6px;margin:4px 0">⚠️ ' + failures.length + ' action' + (failures.length > 1 ? 's' : '') + ' failed:<ul style="margin:4px 0 0 16px;padding:0">' + failHtml + '</ul></div>', false, activeTabId);
|
|
831
833
|
}
|
|
832
834
|
if (warnings.length > 0) {
|
|
833
|
-
var warnHtml = warnings.map(function(r) {
|
|
835
|
+
var warnHtml = warnings.map(function(r) {
|
|
836
|
+
return '<li>' + escHtml(r.type || (r.actionContext && r.actionContext.type) || 'action') + ': ' + escHtml(r.warning) + _ccActionContextSuffix(r.actionContext) + '</li>';
|
|
837
|
+
}).join('');
|
|
834
838
|
addMsg('system', '<div style="padding:6px 12px;font-size:11px;color:var(--orange);background:var(--surface2);border-radius:6px;margin:4px 0">ℹ️ ' + warnings.length + ' action' + (warnings.length > 1 ? '' : '') + ' completed with warnings:<ul style="margin:4px 0 0 16px;padding:0">' + warnHtml + '</ul></div>', false, activeTabId);
|
|
835
839
|
}
|
|
836
840
|
}
|
|
@@ -1012,6 +1016,28 @@ async function _ccFetch(url, body, method) {
|
|
|
1012
1016
|
return res;
|
|
1013
1017
|
}
|
|
1014
1018
|
|
|
1019
|
+
var CC_ACTION_CONTEXT_STALE_MS = 2 * 60 * 1000;
|
|
1020
|
+
function _ccActionContextIsStale(ctx, nowMs) {
|
|
1021
|
+
if (!ctx || !ctx.requestedAt) return false;
|
|
1022
|
+
var ts = Date.parse(ctx.requestedAt);
|
|
1023
|
+
if (!Number.isFinite(ts)) return false;
|
|
1024
|
+
var now = Number.isFinite(nowMs) ? nowMs : Date.now();
|
|
1025
|
+
return now - ts > CC_ACTION_CONTEXT_STALE_MS;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
function _ccActionContextSuffix(ctx, nowMs) {
|
|
1029
|
+
if (!ctx || typeof ctx !== 'object') return '';
|
|
1030
|
+
var parts = [];
|
|
1031
|
+
if (ctx.title) parts.push('action "' + escHtml(ctx.title) + '"');
|
|
1032
|
+
if (ctx.request) parts.push('request "' + escHtml(ctx.request) + '"');
|
|
1033
|
+
if (ctx.requestedAt) parts.push('started ' + escHtml(ctx.requestedAt));
|
|
1034
|
+
if (parts.length === 0) return '';
|
|
1035
|
+
var stale = _ccActionContextIsStale(ctx, nowMs)
|
|
1036
|
+
? ' <strong style="color:var(--orange)">possibly stale</strong>'
|
|
1037
|
+
: '';
|
|
1038
|
+
return '<div style="font-size:10px;color:var(--muted);margin-top:2px">Context: ' + parts.join(' | ') + stale + '</div>';
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1015
1041
|
// Tag actions that the server already executed so ccExecuteAction skips the API call
|
|
1016
1042
|
function _tagServerExecuted(actions, actionResults) {
|
|
1017
1043
|
if (!actionResults || !Array.isArray(actionResults)) return;
|
|
@@ -1022,10 +1048,12 @@ function _tagServerExecuted(actions, actionResults) {
|
|
|
1022
1048
|
if (r.id) actions[i]._serverId = r.id;
|
|
1023
1049
|
if (r.warning) actions[i]._serverWarning = r.warning;
|
|
1024
1050
|
if (r.duplicate) actions[i]._serverDuplicate = true;
|
|
1051
|
+
if (r.actionContext) actions[i]._serverContext = r.actionContext;
|
|
1025
1052
|
if (r.reusedFromAction !== undefined) actions[i]._serverHidden = true;
|
|
1026
1053
|
} else if (r && r.error) {
|
|
1027
1054
|
actions[i]._serverExecuted = true;
|
|
1028
1055
|
actions[i]._serverError = r.error;
|
|
1056
|
+
if (r.actionContext) actions[i]._serverContext = r.actionContext;
|
|
1029
1057
|
}
|
|
1030
1058
|
// clientExecuted: false means server didn't handle it — frontend must execute
|
|
1031
1059
|
}
|
|
@@ -1040,7 +1068,8 @@ async function ccExecuteAction(action, targetTabId) {
|
|
|
1040
1068
|
if (action._serverExecuted) {
|
|
1041
1069
|
if (action._serverHidden) return;
|
|
1042
1070
|
if (action._serverError) {
|
|
1043
|
-
status.innerHTML = '✗ ' + escHtml(action.type) + ' failed: ' + escHtml(action._serverError)
|
|
1071
|
+
status.innerHTML = '✗ ' + escHtml(action.type) + ' failed: ' + escHtml(action._serverError) +
|
|
1072
|
+
_ccActionContextSuffix(action._serverContext);
|
|
1044
1073
|
status.style.color = 'var(--red)';
|
|
1045
1074
|
} else {
|
|
1046
1075
|
var label = action._serverId ? escHtml(action._serverId) : escHtml(action.title || action.type);
|
|
@@ -1048,7 +1077,8 @@ async function ccExecuteAction(action, targetTabId) {
|
|
|
1048
1077
|
var successLabel = serverActionType === 'dispatch' ? 'Dispatched' : serverActionType;
|
|
1049
1078
|
status.innerHTML = '✓ ' + escHtml(successLabel) + ': <strong>' + label + '</strong>' +
|
|
1050
1079
|
(action._serverDuplicate ? '<div style="font-size:10px;color:var(--orange);margin-top:2px">Already existed from a previous request; no duplicate work item was created.</div>' : '') +
|
|
1051
|
-
(action._serverWarning ? '<div style="font-size:10px;color:var(--muted);margin-top:2px">' + escHtml(action._serverWarning) + '</div>' : '')
|
|
1080
|
+
(action._serverWarning ? '<div style="font-size:10px;color:var(--muted);margin-top:2px">' + escHtml(action._serverWarning) + '</div>' : '') +
|
|
1081
|
+
_ccActionContextSuffix(action._serverContext);
|
|
1052
1082
|
status.style.color = 'var(--green)';
|
|
1053
1083
|
}
|
|
1054
1084
|
ccAddMessage('action', status.outerHTML, false, targetTabId);
|
package/dashboard.js
CHANGED
|
@@ -2202,6 +2202,34 @@ function _actionsWithIntentFallback(actions, opts = {}) {
|
|
|
2202
2202
|
return [action];
|
|
2203
2203
|
}
|
|
2204
2204
|
|
|
2205
|
+
function _ccActionContextValue(action) {
|
|
2206
|
+
if (!action || typeof action !== 'object') return '';
|
|
2207
|
+
const candidates = [action.title, action.id, action.file, action.target, action.pr, action.endpoint];
|
|
2208
|
+
for (const value of candidates) {
|
|
2209
|
+
const cleaned = _ccCleanIntentString(value, 160);
|
|
2210
|
+
if (cleaned) return cleaned;
|
|
2211
|
+
}
|
|
2212
|
+
return '';
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
function _annotateCCActionResults(actions, actionResults, opts = {}) {
|
|
2216
|
+
if (!Array.isArray(actionResults)) return actionResults;
|
|
2217
|
+
const requestedAt = _ccCleanIntentString(opts.requestedAt || new Date().toISOString(), 80);
|
|
2218
|
+
const request = _ccCleanIntentString(opts.message, 180);
|
|
2219
|
+
return actionResults.map((result, idx) => {
|
|
2220
|
+
if (!result || typeof result !== 'object') return result;
|
|
2221
|
+
const action = Array.isArray(actions) ? normalizeCCAction(actions[idx]) : null;
|
|
2222
|
+
const context = {
|
|
2223
|
+
type: _ccCleanIntentString(result.type || action?.type || 'action', 80) || 'action',
|
|
2224
|
+
};
|
|
2225
|
+
const title = _ccActionContextValue(action);
|
|
2226
|
+
if (title) context.title = title;
|
|
2227
|
+
if (request) context.request = request;
|
|
2228
|
+
if (requestedAt) context.requestedAt = requestedAt;
|
|
2229
|
+
return { ...result, actionContext: context };
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2205
2233
|
function parseCCActions(text) {
|
|
2206
2234
|
let actions = [];
|
|
2207
2235
|
let displayText = stripCCActionsForDisplay(text);
|
|
@@ -7100,6 +7128,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
7100
7128
|
try {
|
|
7101
7129
|
const body = await readBody(req);
|
|
7102
7130
|
if (!body.message) return jsonReply(res, 400, { error: 'message required' });
|
|
7131
|
+
const actionRequestedAt = new Date().toISOString();
|
|
7103
7132
|
|
|
7104
7133
|
// Per-tab concurrency guard
|
|
7105
7134
|
tabId = body.tabId || 'default';
|
|
@@ -7155,7 +7184,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
7155
7184
|
{ message: body.message, intentMetadata: body.intentMetadata, source: 'command-center', answerText: parsed.text, toolUses }
|
|
7156
7185
|
);
|
|
7157
7186
|
if (parsed.actions.length > 0) {
|
|
7158
|
-
parsed.actionResults =
|
|
7187
|
+
parsed.actionResults = _annotateCCActionResults(
|
|
7188
|
+
parsed.actions,
|
|
7189
|
+
await executeCCActions(parsed.actions),
|
|
7190
|
+
{ message: body.message, requestedAt: actionRequestedAt }
|
|
7191
|
+
);
|
|
7159
7192
|
}
|
|
7160
7193
|
// Mirror only user-facing text to Teams; never send the internal action block.
|
|
7161
7194
|
if (!tabId.startsWith('teams-')) {
|
|
@@ -7259,6 +7292,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
7259
7292
|
const body = await readBody(req);
|
|
7260
7293
|
if (!body.message && !body.reconnect) { res.statusCode = 400; res.end('message required'); return; }
|
|
7261
7294
|
tabId = body.tabId || 'default';
|
|
7295
|
+
const actionRequestedAt = new Date().toISOString();
|
|
7262
7296
|
if (body.reconnect) {
|
|
7263
7297
|
const live = _getCcLiveStream(tabId);
|
|
7264
7298
|
if (!live) { res.statusCode = 409; res.end('No live command-center response to reconnect'); return; }
|
|
@@ -7487,7 +7521,11 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
7487
7521
|
);
|
|
7488
7522
|
let actionResults;
|
|
7489
7523
|
if (actions.length > 0) {
|
|
7490
|
-
actionResults =
|
|
7524
|
+
actionResults = _annotateCCActionResults(
|
|
7525
|
+
actions,
|
|
7526
|
+
await executeCCActions(actions),
|
|
7527
|
+
{ message: body.message, requestedAt: actionRequestedAt }
|
|
7528
|
+
);
|
|
7491
7529
|
}
|
|
7492
7530
|
const donePayload = { type: 'done', text: displayText, actions, actionResults, sessionId: responseSessionId, newSession: !wasResume };
|
|
7493
7531
|
// Issue #1834: surface action JSON parse failures so the UI can warn
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const http = require('http');
|
|
4
|
+
const https = require('https');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const DEFAULT_RESTART_HEALTH_TIMEOUT_MS = 15000;
|
|
8
|
+
const DEFAULT_RESTART_HEALTH_INTERVAL_MS = 250;
|
|
9
|
+
|
|
10
|
+
function sleep(ms) {
|
|
11
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readEngineControl(minionsHome) {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(fs.readFileSync(path.join(minionsHome, 'engine', 'control.json'), 'utf8'));
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizePid(pid) {
|
|
23
|
+
const n = Number(pid);
|
|
24
|
+
return Number.isInteger(n) && n > 0 ? n : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isProcessAlive(pid) {
|
|
28
|
+
const n = normalizePid(pid);
|
|
29
|
+
if (!n || n === process.pid) return false;
|
|
30
|
+
try {
|
|
31
|
+
if (process.platform === 'win32') {
|
|
32
|
+
const out = execSync(`tasklist /FI "PID eq ${n}" /NH`, {
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
windowsHide: true,
|
|
35
|
+
timeout: 3000,
|
|
36
|
+
});
|
|
37
|
+
return new RegExp(`\\b${n}\\b`).test(out) && out.toLowerCase().includes('node');
|
|
38
|
+
}
|
|
39
|
+
process.kill(n, 0);
|
|
40
|
+
return true;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function httpGetJson(url, timeoutMs = 1000) {
|
|
47
|
+
return new Promise(resolve => {
|
|
48
|
+
let settled = false;
|
|
49
|
+
const parsed = new URL(url);
|
|
50
|
+
const client = parsed.protocol === 'https:' ? https : http;
|
|
51
|
+
const req = client.get(parsed, { timeout: timeoutMs }, res => {
|
|
52
|
+
let body = '';
|
|
53
|
+
res.setEncoding('utf8');
|
|
54
|
+
res.on('data', chunk => { body += chunk; });
|
|
55
|
+
res.on('end', () => {
|
|
56
|
+
if (settled) return;
|
|
57
|
+
settled = true;
|
|
58
|
+
let json = null;
|
|
59
|
+
try { json = body ? JSON.parse(body) : null; }
|
|
60
|
+
catch (err) {
|
|
61
|
+
resolve({ ok: false, statusCode: res.statusCode, error: err, body });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode, json, body });
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
req.on('timeout', () => {
|
|
68
|
+
if (settled) return;
|
|
69
|
+
settled = true;
|
|
70
|
+
req.destroy();
|
|
71
|
+
resolve({ ok: false, error: new Error(`timed out after ${timeoutMs}ms`) });
|
|
72
|
+
});
|
|
73
|
+
req.on('error', err => {
|
|
74
|
+
if (settled) return;
|
|
75
|
+
settled = true;
|
|
76
|
+
resolve({ ok: false, error: err });
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function checkRestartHealth(options = {}) {
|
|
82
|
+
const {
|
|
83
|
+
minionsHome,
|
|
84
|
+
dashboardUrl = 'http://127.0.0.1:7331/api/health',
|
|
85
|
+
readControl = readEngineControl,
|
|
86
|
+
isProcessAlive: isAlive = isProcessAlive,
|
|
87
|
+
httpGetJson: getJson = httpGetJson,
|
|
88
|
+
} = options;
|
|
89
|
+
|
|
90
|
+
const control = readControl(minionsHome);
|
|
91
|
+
const pid = normalizePid(control && control.pid);
|
|
92
|
+
const engineAlive = pid ? isAlive(pid) : false;
|
|
93
|
+
const engineOk = control && control.state === 'running' && engineAlive;
|
|
94
|
+
|
|
95
|
+
const dashboard = await getJson(dashboardUrl, 1000);
|
|
96
|
+
const dashboardStatus = dashboard && dashboard.json && dashboard.json.status;
|
|
97
|
+
const dashboardOk = !!(dashboard && dashboard.ok && dashboardStatus === 'healthy');
|
|
98
|
+
|
|
99
|
+
const errors = [];
|
|
100
|
+
if (!engineOk) {
|
|
101
|
+
const state = control && control.state ? control.state : 'unknown';
|
|
102
|
+
const pidLabel = pid || 'none';
|
|
103
|
+
errors.push(`Engine is not healthy (state=${state}, pid=${pidLabel}, alive=${engineAlive ? 'yes' : 'no'})`);
|
|
104
|
+
}
|
|
105
|
+
if (!dashboardOk) {
|
|
106
|
+
const detail = dashboard && dashboard.error
|
|
107
|
+
? dashboard.error.message
|
|
108
|
+
: dashboard && dashboard.statusCode
|
|
109
|
+
? `HTTP ${dashboard.statusCode}${dashboardStatus ? `, status=${dashboardStatus}` : ''}`
|
|
110
|
+
: 'no response';
|
|
111
|
+
errors.push(`Dashboard failed health check at ${dashboardUrl} (${detail})`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
ok: engineOk && dashboardOk,
|
|
116
|
+
engine: { state: control && control.state, pid, alive: engineAlive },
|
|
117
|
+
dashboard: { url: dashboardUrl, ok: !!(dashboard && dashboard.ok), statusCode: dashboard && dashboard.statusCode, status: dashboardStatus },
|
|
118
|
+
errors,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function waitForRestartHealth(options = {}) {
|
|
123
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_RESTART_HEALTH_TIMEOUT_MS;
|
|
124
|
+
const intervalMs = options.intervalMs ?? DEFAULT_RESTART_HEALTH_INTERVAL_MS;
|
|
125
|
+
const maxAttempts = normalizePid(options.maxAttempts);
|
|
126
|
+
const started = Date.now();
|
|
127
|
+
let attempts = 0;
|
|
128
|
+
let last = null;
|
|
129
|
+
|
|
130
|
+
while (true) {
|
|
131
|
+
attempts++;
|
|
132
|
+
last = await checkRestartHealth(options);
|
|
133
|
+
last.attempts = attempts;
|
|
134
|
+
last.elapsedMs = Date.now() - started;
|
|
135
|
+
if (last.ok) return last;
|
|
136
|
+
if (maxAttempts && attempts >= maxAttempts) break;
|
|
137
|
+
const remainingMs = timeoutMs - (Date.now() - started);
|
|
138
|
+
if (!maxAttempts && remainingMs <= 0) break;
|
|
139
|
+
await sleep(maxAttempts ? intervalMs : Math.min(intervalMs, remainingMs));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return last || {
|
|
143
|
+
ok: false,
|
|
144
|
+
attempts,
|
|
145
|
+
elapsedMs: Date.now() - started,
|
|
146
|
+
errors: ['Restart health check did not run'],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function formatRestartHealthError(result) {
|
|
151
|
+
const elapsed = typeof result.elapsedMs === 'number' ? `${result.elapsedMs}ms` : 'unknown time';
|
|
152
|
+
const attempts = result.attempts || 0;
|
|
153
|
+
const details = (result.errors || ['Unknown restart verification failure']).map(err => ` - ${err}`).join('\n');
|
|
154
|
+
return `\n ERROR: Restart verification failed after ${elapsed} (${attempts} attempt${attempts === 1 ? '' : 's'}).\n${details}\n`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
DEFAULT_RESTART_HEALTH_TIMEOUT_MS,
|
|
159
|
+
DEFAULT_RESTART_HEALTH_INTERVAL_MS,
|
|
160
|
+
checkRestartHealth,
|
|
161
|
+
waitForRestartHealth,
|
|
162
|
+
formatRestartHealthError,
|
|
163
|
+
_private: {
|
|
164
|
+
httpGetJson,
|
|
165
|
+
isProcessAlive,
|
|
166
|
+
readEngineControl,
|
|
167
|
+
normalizePid,
|
|
168
|
+
},
|
|
169
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1816",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|