monomind 2.1.3 → 2.1.4
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 +18 -17
- package/package.json +1 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/createorg.md +8 -9
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/createorg.md +91 -701
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/org-settings.md +46 -47
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/runorg.md +12 -1
- package/packages/@monomind/cli/README.md +18 -17
- package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +76 -1
- package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +7 -3
- package/packages/@monomind/cli/dist/src/orgrt/server.js +24 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.js +8 -0
- package/packages/@monomind/cli/dist/src/orgrt/types.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/pricing/model-pricing.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/ui/collector.mjs +20 -5
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +270 -499
- package/packages/@monomind/cli/dist/src/ui/orgs-files.js +1 -1
- package/packages/@monomind/cli/dist/src/ui/server.mjs +132 -158
- package/packages/@monomind/cli/package.json +3 -3
- package/packages/@monomind/cli/scripts/publish.sh +6 -0
- package/scripts/generate-agent-avatars.mjs +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// packages/@monomind/cli/
|
|
1
|
+
// packages/@monomind/cli/src/ui/orgs-files.js
|
|
2
2
|
// Files tab + diff-view feature, split out of orgs.html to keep that file under
|
|
3
3
|
// the CLAUDE.md 500-line guideline. Loaded as a classic <script> right where this
|
|
4
4
|
// code used to live inline — shares the same global scope as orgs.html's main
|
|
@@ -481,7 +481,9 @@ function _detectMimeType(filePath) {
|
|
|
481
481
|
'.json': 'application/json', '.md': 'text/markdown', '.txt': 'text/plain',
|
|
482
482
|
'.html': 'text/html', '.css': 'text/css', '.py': 'text/x-python',
|
|
483
483
|
'.sh': 'text/x-shellscript', '.yaml': 'text/yaml', '.yml': 'text/yaml',
|
|
484
|
-
'.toml': 'text/plain', '.env': 'text/plain', '.xml': 'text/xml'
|
|
484
|
+
'.toml': 'text/plain', '.env': 'text/plain', '.xml': 'text/xml',
|
|
485
|
+
'.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
|
|
486
|
+
'.gif': 'image/gif', '.svg': 'image/svg+xml', '.webp': 'image/webp' };
|
|
485
487
|
return map[ext] || 'application/octet-stream';
|
|
486
488
|
}
|
|
487
489
|
|
|
@@ -1030,7 +1032,13 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1030
1032
|
if (req.method === 'GET' && url === '/') {
|
|
1031
1033
|
const htmlPath = path.join(__dirname, 'dashboard.html');
|
|
1032
1034
|
try {
|
|
1033
|
-
|
|
1035
|
+
let html = fs.readFileSync(htmlPath, 'utf8');
|
|
1036
|
+
// Inject this process's auth credential so the page's own fetch() calls can
|
|
1037
|
+
// attach it (see the fetch-wrapper near the top of dashboard.html's <script>
|
|
1038
|
+
// block) — every non-GET route above requires it, but the served HTML was
|
|
1039
|
+
// previously never given a way to know it, so every write action 401'd from
|
|
1040
|
+
// the actual browser UI.
|
|
1041
|
+
html = html.replace('<head>', `<head>\n<meta name="mm-token" content="${dashboardAuthValue}">`);
|
|
1034
1042
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
1035
1043
|
res.end(html);
|
|
1036
1044
|
} catch (err) {
|
|
@@ -4740,6 +4748,122 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
4740
4748
|
return;
|
|
4741
4749
|
}
|
|
4742
4750
|
|
|
4751
|
+
// GET /api/questions?dir=<projectDir> — list unanswered ask_human questions for
|
|
4752
|
+
// every org in one project. Mirrors the existing -approvals.json sidecar convention,
|
|
4753
|
+
// but reads this feature's .monomind/orgs/<org>/questions.json files (one per org dir).
|
|
4754
|
+
if (req.method === 'GET' && url === '/api/questions') {
|
|
4755
|
+
try {
|
|
4756
|
+
const _qDir = new URL(req.url, 'http://localhost').searchParams.get('dir') || projectDir || process.cwd();
|
|
4757
|
+
const base = path.join(path.resolve(_qDir), '.monomind', 'orgs');
|
|
4758
|
+
const out = [];
|
|
4759
|
+
if (fs.existsSync(base)) {
|
|
4760
|
+
for (const orgName of fs.readdirSync(base)) {
|
|
4761
|
+
const qFile = path.join(base, orgName, 'questions.json');
|
|
4762
|
+
if (!fs.existsSync(qFile)) continue;
|
|
4763
|
+
let data = { questions: [] };
|
|
4764
|
+
try { data = JSON.parse(fs.readFileSync(qFile, 'utf8')); } catch (_) {}
|
|
4765
|
+
for (const q of (data.questions || [])) {
|
|
4766
|
+
if (q.answer === null || q.answer === undefined) out.push({ org: orgName, ...q });
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
res.writeHead(200, { 'Content-Type': 'application/json', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
4771
|
+
res.end(JSON.stringify({ questions: out }));
|
|
4772
|
+
} catch (_e) { res.writeHead(500); res.end('{"questions":[]}'); }
|
|
4773
|
+
return;
|
|
4774
|
+
}
|
|
4775
|
+
|
|
4776
|
+
// POST /api/questions/answer — forward a human's answer to the org's live process
|
|
4777
|
+
// (looked up via the same file-based broker registry orgrt's cross-process delivery
|
|
4778
|
+
// already uses), or fail with a clear error if no process anywhere hosts it.
|
|
4779
|
+
if (req.method === 'POST' && url === '/api/questions/answer') {
|
|
4780
|
+
let body = '';
|
|
4781
|
+
for await (const chunk of req) { body += chunk; if (body.length > 2097152) { req.destroy(); break; } }
|
|
4782
|
+
try {
|
|
4783
|
+
const parsed = JSON.parse(body);
|
|
4784
|
+
const { dir, org, role, questionId, answer } = parsed;
|
|
4785
|
+
if (!org || !role || !questionId || answer === undefined) {
|
|
4786
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
4787
|
+
res.end(JSON.stringify({ ok: false, error: 'org, role, questionId, answer are required' }));
|
|
4788
|
+
return;
|
|
4789
|
+
}
|
|
4790
|
+
if (String(org).length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(String(org))) {
|
|
4791
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
4792
|
+
res.end(JSON.stringify({ ok: false, error: 'Invalid org name' }));
|
|
4793
|
+
return;
|
|
4794
|
+
}
|
|
4795
|
+
const brokerDir = path.join(os.homedir(), '.monomind', 'orgrt-broker');
|
|
4796
|
+
const entryPath = path.join(brokerDir, `${org}.json`);
|
|
4797
|
+
let hostUrl = null;
|
|
4798
|
+
try {
|
|
4799
|
+
const entry = JSON.parse(fs.readFileSync(entryPath, 'utf8'));
|
|
4800
|
+
if (Date.now() - entry.updatedAt < 90000) hostUrl = entry.url;
|
|
4801
|
+
} catch (_) {}
|
|
4802
|
+
if (!hostUrl) {
|
|
4803
|
+
// No live process to forward to — the control server has no daemon instance of
|
|
4804
|
+
// its own to call autoWake() on. If the org's definition still exists on disk,
|
|
4805
|
+
// queue the answer the same way inbox.ts's queueMessage()/drainInbox() already
|
|
4806
|
+
// do for offline cross-org messages, so it's delivered whenever the org next
|
|
4807
|
+
// starts (manually or via its own schedule) — matching the offline-delivery goal
|
|
4808
|
+
// for the dashboard path, not just the direct-daemon path Task 4 already covers.
|
|
4809
|
+
const projDir = path.resolve(dir || projectDir || process.cwd());
|
|
4810
|
+
const orgDefFile = path.join(projDir, '.monomind', 'orgs', `${org}.json`);
|
|
4811
|
+
if (!fs.existsSync(orgDefFile)) {
|
|
4812
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
4813
|
+
res.end(JSON.stringify({ ok: false, error: `org "${org}" not found — no running process and no saved definition` }));
|
|
4814
|
+
return;
|
|
4815
|
+
}
|
|
4816
|
+
const qFile = path.join(projDir, '.monomind', 'orgs', org, 'questions.json');
|
|
4817
|
+
let qData = { questions: [] };
|
|
4818
|
+
try { qData = JSON.parse(fs.readFileSync(qFile, 'utf8')); } catch (_) {}
|
|
4819
|
+
const qIdx = (qData.questions || []).findIndex(q => q.questionId === questionId);
|
|
4820
|
+
if (qIdx === -1) {
|
|
4821
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
4822
|
+
res.end(JSON.stringify({ ok: false, error: `question "${questionId}" not found for org "${org}"` }));
|
|
4823
|
+
return;
|
|
4824
|
+
}
|
|
4825
|
+
if (qData.questions[qIdx].answer !== null && qData.questions[qIdx].answer !== undefined) {
|
|
4826
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
4827
|
+
res.end(JSON.stringify({ ok: true, alreadyAnswered: true }));
|
|
4828
|
+
return;
|
|
4829
|
+
}
|
|
4830
|
+
const answeredQuestion = qData.questions[qIdx];
|
|
4831
|
+
qData.questions[qIdx] = { ...answeredQuestion, answer, answeredAt: Date.now() };
|
|
4832
|
+
const qTmp = `${qFile}.tmp`;
|
|
4833
|
+
fs.writeFileSync(qTmp, JSON.stringify(qData, null, 2));
|
|
4834
|
+
fs.renameSync(qTmp, qFile);
|
|
4835
|
+
const inboxDir = path.join(projDir, '.monomind', 'orgs', org);
|
|
4836
|
+
fs.mkdirSync(inboxDir, { recursive: true });
|
|
4837
|
+
fs.appendFileSync(path.join(inboxDir, 'inbox.jsonl'), JSON.stringify({
|
|
4838
|
+
fromQualified: 'human', toRole: role, subject: `answer:${questionId}`, body: `question: ${answeredQuestion.question}\n\nanswer: ${answer}`, ts: Date.now(),
|
|
4839
|
+
}) + '\n');
|
|
4840
|
+
const queuedEvent = { type: 'org:question-answered', org, role, questionId, ts: Date.now(), queued: true };
|
|
4841
|
+
appendToFile(path.join(projDir, 'data', 'mastermind-events.jsonl'), JSON.stringify(queuedEvent) + '\n').catch(() => {});
|
|
4842
|
+
broadcastMm(queuedEvent);
|
|
4843
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
4844
|
+
res.end(JSON.stringify({ ok: true, queued: true }));
|
|
4845
|
+
return;
|
|
4846
|
+
}
|
|
4847
|
+
const fwd = await fetch(`${hostUrl}/api/answer-question`, {
|
|
4848
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
4849
|
+
body: JSON.stringify({ org, role, questionId, answer }),
|
|
4850
|
+
signal: AbortSignal.timeout(5000),
|
|
4851
|
+
});
|
|
4852
|
+
const fwdData = await fwd.json().catch(() => ({}));
|
|
4853
|
+
if (fwd.ok && fwdData.ok) {
|
|
4854
|
+
const event = { type: 'org:question-answered', org, role, questionId, ts: Date.now() };
|
|
4855
|
+
appendToFile(path.join(path.resolve(dir || projectDir || process.cwd()), 'data', 'mastermind-events.jsonl'), JSON.stringify(event) + '\n').catch(() => {});
|
|
4856
|
+
broadcastMm(event);
|
|
4857
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
4858
|
+
res.end(JSON.stringify({ ok: true }));
|
|
4859
|
+
} else {
|
|
4860
|
+
res.writeHead(fwd.status || 500, { 'Content-Type': 'application/json' });
|
|
4861
|
+
res.end(JSON.stringify({ ok: false, error: fwdData.error || 'answer delivery failed' }));
|
|
4862
|
+
}
|
|
4863
|
+
} catch (_e) { res.writeHead(500); res.end('{"ok":false}'); }
|
|
4864
|
+
return;
|
|
4865
|
+
}
|
|
4866
|
+
|
|
4743
4867
|
// POST /api/org/:name/approvals/:id — approve or reject a pending approval request
|
|
4744
4868
|
// Body: { action: "approve" | "reject" | "revision_requested" }
|
|
4745
4869
|
if (req.method === 'POST' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/approvals\/[^/]+$/i)) {
|
|
@@ -5132,6 +5256,12 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5132
5256
|
const stat = fs.statSync(resolved);
|
|
5133
5257
|
if (!stat.isFile()) { res.writeHead(400); res.end('Not a file'); return; }
|
|
5134
5258
|
if (stat.size > 524288) { res.writeHead(413); res.end('File too large'); return; }
|
|
5259
|
+
const _fcMime = _detectMimeType(resolved);
|
|
5260
|
+
if (_fcMime.startsWith('image/')) {
|
|
5261
|
+
res.writeHead(200, { 'Content-Type': _fcMime, ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5262
|
+
res.end(fs.readFileSync(resolved));
|
|
5263
|
+
return;
|
|
5264
|
+
}
|
|
5135
5265
|
const content = fs.readFileSync(resolved, 'utf8');
|
|
5136
5266
|
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8', ...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}) });
|
|
5137
5267
|
res.end(content);
|
|
@@ -5704,162 +5834,6 @@ new Sigma(g,document.getElementById('g'),{renderEdgeLabels:false,labelColor:{col
|
|
|
5704
5834
|
return;
|
|
5705
5835
|
}
|
|
5706
5836
|
|
|
5707
|
-
// ----------------------------------------------- GET /api/monoagent/platforms
|
|
5708
|
-
// Returns all supported platforms from `monoes connect list --all --json`
|
|
5709
|
-
if (req.method === 'GET' && url === '/api/monoagent/platforms') {
|
|
5710
|
-
try {
|
|
5711
|
-
const { execFile } = await import('child_process');
|
|
5712
|
-
const out = await new Promise((resolve, reject) => {
|
|
5713
|
-
execFile('monoes', ['connect', 'list', '--all', '--json'], { timeout: 8000 }, (err, stdout) => {
|
|
5714
|
-
if (err) reject(err); else resolve(stdout);
|
|
5715
|
-
});
|
|
5716
|
-
});
|
|
5717
|
-
// Parse + re-serialize to ensure only valid JSON reaches the client
|
|
5718
|
-
// (monoes may emit warning lines before the JSON array)
|
|
5719
|
-
let parsed; try { parsed = JSON.parse(out); } catch (_) { parsed = []; }
|
|
5720
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5721
|
-
res.end(JSON.stringify(Array.isArray(parsed) ? parsed : []));
|
|
5722
|
-
} catch (e) { res.writeHead(200); res.end('[]'); }
|
|
5723
|
-
return;
|
|
5724
|
-
}
|
|
5725
|
-
|
|
5726
|
-
// ----------------------------------------------- GET /api/monoagent/connections
|
|
5727
|
-
// Returns active API/OAuth connections + browser sessions merged
|
|
5728
|
-
if (req.method === 'GET' && url === '/api/monoagent/connections') {
|
|
5729
|
-
try {
|
|
5730
|
-
const { execFile } = await import('child_process');
|
|
5731
|
-
const [connsOut, sessOut] = await Promise.all([
|
|
5732
|
-
new Promise((resolve) => {
|
|
5733
|
-
execFile('monoes', ['connect', 'list', '--json'], { timeout: 8000 }, (err, stdout) => resolve(err ? '[]' : stdout));
|
|
5734
|
-
}),
|
|
5735
|
-
new Promise((resolve) => {
|
|
5736
|
-
execFile('monoes', ['--json', 'login', 'status'], { timeout: 8000 }, (err, stdout) => resolve(err ? '[]' : stdout));
|
|
5737
|
-
}),
|
|
5738
|
-
]);
|
|
5739
|
-
let connections = []; try { connections = JSON.parse(connsOut); } catch (_) {}
|
|
5740
|
-
let sessions = []; try { sessions = JSON.parse(sessOut); } catch (_) {}
|
|
5741
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5742
|
-
res.end(JSON.stringify({ connections, sessions }));
|
|
5743
|
-
} catch (e) { res.writeHead(200); res.end(JSON.stringify({ connections: [], sessions: [] })); }
|
|
5744
|
-
return;
|
|
5745
|
-
}
|
|
5746
|
-
|
|
5747
|
-
// ----------------------------------------------- POST /api/monoagent/login
|
|
5748
|
-
// Launches browser login for social platforms via `monoes login <platform>`
|
|
5749
|
-
if (req.method === 'POST' && url === '/api/monoagent/login') {
|
|
5750
|
-
try {
|
|
5751
|
-
let body = '';
|
|
5752
|
-
await new Promise((resolve, reject) => { req.on('data', d => { body += d; if (body.length > 65536) { req.destroy(); reject(new Error('body too large')); } }); req.on('end', resolve); req.on('error', reject); });
|
|
5753
|
-
const { id } = JSON.parse(body);
|
|
5754
|
-
if (!id || !/^[a-z][a-z0-9_-]*$/.test(id)) { res.writeHead(400); res.end(JSON.stringify({ error: 'invalid id' })); return; }
|
|
5755
|
-
const { spawn } = await import('child_process');
|
|
5756
|
-
const child = spawn('monoes', ['login', id, '--timeout', '10m'], { detached: true, stdio: 'ignore' });
|
|
5757
|
-
// Defer response until spawn confirms or errors — prevents race where error fires after res.end()
|
|
5758
|
-
child.once('spawn', () => {
|
|
5759
|
-
child.unref();
|
|
5760
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5761
|
-
res.end(JSON.stringify({ ok: true }));
|
|
5762
|
-
});
|
|
5763
|
-
child.once('error', (err) => {
|
|
5764
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
5765
|
-
res.end(JSON.stringify({ error: err.message }));
|
|
5766
|
-
});
|
|
5767
|
-
} catch (e) { res.writeHead(500); res.end(JSON.stringify({ error: e.message })); }
|
|
5768
|
-
return;
|
|
5769
|
-
}
|
|
5770
|
-
|
|
5771
|
-
// ----------------------------------------------- POST /api/monoagent/connect
|
|
5772
|
-
if (req.method === 'POST' && url === '/api/monoagent/connect') {
|
|
5773
|
-
try {
|
|
5774
|
-
let body = '';
|
|
5775
|
-
await new Promise((resolve, reject) => { req.on('data', d => { body += d; if (body.length > 65536) { req.destroy(); reject(new Error('body too large')); } }); req.on('end', resolve); req.on('error', reject); });
|
|
5776
|
-
const { id, method, fields } = JSON.parse(body);
|
|
5777
|
-
if (!id || !/^[a-z][a-z0-9_-]*$/.test(id)) { res.writeHead(400); res.end(JSON.stringify({ error: 'invalid id' })); return; }
|
|
5778
|
-
if (method && !/^[a-z][a-z0-9_-]*$/.test(method)) { res.writeHead(400); res.end(JSON.stringify({ error: 'invalid method' })); return; }
|
|
5779
|
-
const { execFile } = await import('child_process');
|
|
5780
|
-
const args = ['connect', id];
|
|
5781
|
-
if (method) args.push('--method', method);
|
|
5782
|
-
if (fields && typeof fields === 'object') {
|
|
5783
|
-
for (const [k, v] of Object.entries(fields)) {
|
|
5784
|
-
// Only allow simple word keys — prevents --flag injection
|
|
5785
|
-
if (!/^[a-z][a-z0-9_]*$/.test(k)) continue;
|
|
5786
|
-
args.push(`--${k}`, String(v).slice(0, 2048));
|
|
5787
|
-
}
|
|
5788
|
-
}
|
|
5789
|
-
await new Promise((resolve, reject) => {
|
|
5790
|
-
execFile('monoes', args, { timeout: 30000 }, (err, stdout, stderr) => {
|
|
5791
|
-
const ok = !err;
|
|
5792
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5793
|
-
res.end(JSON.stringify({ ok, stdout: (stdout || '').trim(), stderr: (stderr || '').trim() }));
|
|
5794
|
-
resolve();
|
|
5795
|
-
});
|
|
5796
|
-
});
|
|
5797
|
-
} catch (e) { res.writeHead(500); res.end(JSON.stringify({ error: e.message })); }
|
|
5798
|
-
return;
|
|
5799
|
-
}
|
|
5800
|
-
|
|
5801
|
-
// ----------------------------------------------- POST /api/monoagent/test
|
|
5802
|
-
if (req.method === 'POST' && url === '/api/monoagent/test') {
|
|
5803
|
-
try {
|
|
5804
|
-
let body = '';
|
|
5805
|
-
await new Promise((resolve, reject) => { req.on('data', d => { body += d; if (body.length > 65536) { req.destroy(); reject(new Error('body too large')); } }); req.on('end', resolve); req.on('error', reject); });
|
|
5806
|
-
const { id } = JSON.parse(body);
|
|
5807
|
-
if (!id || !/^[a-z0-9][a-z0-9_:-]*$/.test(id)) { res.writeHead(400); res.end(JSON.stringify({ error: 'id required' })); return; }
|
|
5808
|
-
const { execFile } = await import('child_process');
|
|
5809
|
-
await new Promise((resolve, reject) => {
|
|
5810
|
-
execFile('monoes', ['connect', 'test', id], { timeout: 15000 }, (err, stdout, stderr) => {
|
|
5811
|
-
if (err) reject(new Error(stderr || err.message)); else resolve(stdout);
|
|
5812
|
-
});
|
|
5813
|
-
});
|
|
5814
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5815
|
-
res.end(JSON.stringify({ ok: true }));
|
|
5816
|
-
} catch (e) { res.writeHead(200); res.end(JSON.stringify({ ok: false, error: e.message })); }
|
|
5817
|
-
return;
|
|
5818
|
-
}
|
|
5819
|
-
|
|
5820
|
-
// ----------------------------------------------- POST /api/monoagent/disconnect
|
|
5821
|
-
if (req.method === 'POST' && url === '/api/monoagent/disconnect') {
|
|
5822
|
-
try {
|
|
5823
|
-
let body = '';
|
|
5824
|
-
await new Promise((resolve, reject) => { req.on('data', d => { body += d; if (body.length > 65536) { req.destroy(); reject(new Error('body too large')); } }); req.on('end', resolve); req.on('error', reject); });
|
|
5825
|
-
const { id, type } = JSON.parse(body);
|
|
5826
|
-
if (!id || !/^[a-z0-9][a-z0-9_:-]*$/.test(id)) { res.writeHead(400); res.end(JSON.stringify({ error: 'id required' })); return; }
|
|
5827
|
-
if (type !== 'session' && type !== 'connection') { res.writeHead(400); res.end(JSON.stringify({ error: 'invalid type' })); return; }
|
|
5828
|
-
const { execFile } = await import('child_process');
|
|
5829
|
-
const cmd = type === 'session' ? ['logout', id] : ['connect', 'remove', id];
|
|
5830
|
-
await new Promise((resolve, reject) => {
|
|
5831
|
-
execFile('monoes', cmd, { timeout: 10000 }, (err, _stdout, stderr) => {
|
|
5832
|
-
if (err) reject(new Error(stderr || err.message)); else resolve();
|
|
5833
|
-
});
|
|
5834
|
-
});
|
|
5835
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5836
|
-
res.end(JSON.stringify({ ok: true }));
|
|
5837
|
-
} catch (e) { res.writeHead(200); res.end(JSON.stringify({ ok: false, error: e.message })); }
|
|
5838
|
-
return;
|
|
5839
|
-
}
|
|
5840
|
-
|
|
5841
|
-
// ----------------------------------------------- GET /api/monoagent/data
|
|
5842
|
-
if (req.method === 'GET' && url === '/api/monoagent/data') {
|
|
5843
|
-
try {
|
|
5844
|
-
const { execFile } = await import('child_process');
|
|
5845
|
-
const run = (args) => new Promise(resolve => {
|
|
5846
|
-
execFile('monoagent', args, { timeout: 8000 }, (err, stdout) => resolve(err ? '[]' : stdout));
|
|
5847
|
-
});
|
|
5848
|
-
const [wfOut, actOut] = await Promise.all([
|
|
5849
|
-
run(['workflow', 'list', '--json']),
|
|
5850
|
-
run(['action', 'list', '--json']),
|
|
5851
|
-
]);
|
|
5852
|
-
const workflows = JSON.parse(wfOut.trim() || '[]');
|
|
5853
|
-
const actions = JSON.parse(actOut.trim() || '[]');
|
|
5854
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5855
|
-
res.end(JSON.stringify({ workflows, actions }));
|
|
5856
|
-
} catch (e) {
|
|
5857
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
5858
|
-
res.end(JSON.stringify({ workflows: [], actions: [], error: e.message }));
|
|
5859
|
-
}
|
|
5860
|
-
return;
|
|
5861
|
-
}
|
|
5862
|
-
|
|
5863
5837
|
// ------------------------------------------------- GET /api/playbooks
|
|
5864
5838
|
// List playbook definitions from <dir>/.monomind/playbooks/*.json.
|
|
5865
5839
|
// (Previously only POST was registered, so GET /api/playbooks?dir=... fell
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI engine for Monomind — an open-source MCP server that extends Claude Code with a codebase knowledge graph (tree-sitter + SQLite), persistent memory, multi-agent task coordination, and session hooks. MIT licensed, fully local.",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
"README.md"
|
|
80
80
|
],
|
|
81
81
|
"scripts": {
|
|
82
|
-
"build": "tsc && mkdir -p dist/src/browser/dashboard && cp src/browser/dashboard/ui.html dist/src/browser/dashboard/ui.html",
|
|
83
|
-
"build:loose": "tsc --noEmitOnError false || true && mkdir -p dist/src/browser/dashboard && cp src/browser/dashboard/ui.html dist/src/browser/dashboard/ui.html",
|
|
82
|
+
"build": "tsc && mkdir -p dist/src/browser/dashboard && cp src/browser/dashboard/ui.html dist/src/browser/dashboard/ui.html && mkdir -p dist/src/ui && cp src/ui/dashboard.html src/ui/server.mjs src/ui/collector.mjs src/ui/orgs.html src/ui/orgs-files.js src/ui/sse-manager.mjs dist/src/ui/ && rm -rf dist/src/ui/data && cp -r src/ui/data dist/src/ui/data",
|
|
83
|
+
"build:loose": "tsc --noEmitOnError false || true && mkdir -p dist/src/browser/dashboard && cp src/browser/dashboard/ui.html dist/src/browser/dashboard/ui.html && mkdir -p dist/src/ui && cp src/ui/dashboard.html src/ui/server.mjs src/ui/collector.mjs src/ui/orgs.html src/ui/orgs-files.js src/ui/sse-manager.mjs dist/src/ui/ && rm -rf dist/src/ui/data && cp -r src/ui/data dist/src/ui/data",
|
|
84
84
|
"test": "vitest run",
|
|
85
85
|
"test:pattern-store": "npx tsx src/transfer/store/tests/standalone-test.ts",
|
|
86
86
|
"prepublishOnly": "cp ../../../README.md ./README.md",
|
|
@@ -9,6 +9,12 @@ CLI_DIR="$(dirname "$SCRIPT_DIR")"
|
|
|
9
9
|
|
|
10
10
|
cd "$CLI_DIR"
|
|
11
11
|
|
|
12
|
+
# dist/src/ui/* (dashboard, server, etc.) is hand-authored source under src/ui/
|
|
13
|
+
# copied into dist/ by the build step — it is no longer committed to git, so a
|
|
14
|
+
# stale or missing dist/ here would silently ship a broken/absent dashboard.
|
|
15
|
+
echo "=== Building ==="
|
|
16
|
+
npm run build
|
|
17
|
+
|
|
12
18
|
# Get current version
|
|
13
19
|
VERSION=$(node -p "require('./package.json').version")
|
|
14
20
|
echo "Publishing version: $VERSION"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* Generates 120 unique SVG agent avatar files + a sprite-sheet HTML.
|
|
4
|
-
* Output: packages/@monomind/cli/
|
|
4
|
+
* Output: packages/@monomind/cli/src/ui/data/avatars/
|
|
5
5
|
* Run: node scripts/generate-agent-avatars.mjs
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@ import path from 'path';
|
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
11
|
|
|
12
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
const OUT_DIR = path.resolve(__dirname, '../packages/@monomind/cli/
|
|
13
|
+
const OUT_DIR = path.resolve(__dirname, '../packages/@monomind/cli/src/ui/data/avatars');
|
|
14
14
|
fs.mkdirSync(OUT_DIR, { recursive: true });
|
|
15
15
|
|
|
16
16
|
// ─── Palettes ────────────────────────────────────────────────────────────────
|
|
@@ -489,4 +489,4 @@ const spritePath = path.join(OUT_DIR, '../agent-avatars.html');
|
|
|
489
489
|
fs.writeFileSync(spritePath, spriteHtml, 'utf8');
|
|
490
490
|
console.log(`✅ Sprite-sheet viewer: data/agent-avatars.html`);
|
|
491
491
|
console.log(`\n🎉 Done! ${AGENTS120.length} avatars ready.`);
|
|
492
|
-
console.log(` Open: packages/@monomind/cli/
|
|
492
|
+
console.log(` Open: packages/@monomind/cli/src/ui/data/agent-avatars.html`);
|