@monoes/monomindcli 2.10.0 → 2.10.2
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/.claude/skills/monodesign/reference/hooks.md +2 -2
- package/.claude/skills/monodesign/scripts/hook-admin.mjs +51 -18
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +58 -82
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/init/codex-generator.d.ts.map +1 -1
- package/dist/src/init/codex-generator.js +21 -1
- package/dist/src/init/codex-generator.js.map +1 -1
- package/dist/src/init/mcp-generator.d.ts +7 -0
- package/dist/src/init/mcp-generator.d.ts.map +1 -1
- package/dist/src/init/mcp-generator.js +29 -0
- package/dist/src/init/mcp-generator.js.map +1 -1
- package/dist/src/knowledge/eval/golden-set.d.ts.map +1 -1
- package/dist/src/knowledge/eval/golden-set.js +0 -22
- package/dist/src/knowledge/eval/golden-set.js.map +1 -1
- package/dist/src/orgrt/kimicode-runner.d.ts.map +1 -1
- package/dist/src/orgrt/kimicode-runner.js +8 -2
- package/dist/src/orgrt/kimicode-runner.js.map +1 -1
- package/dist/src/orgrt/pi-runner.d.ts.map +1 -1
- package/dist/src/orgrt/pi-runner.js +25 -16
- package/dist/src/orgrt/pi-runner.js.map +1 -1
- package/dist/src/ui/dashboard.html +51 -0
- package/dist/src/ui/data/mastermind-sessions.json +1 -0
- package/dist/src/ui/orgs.html +40 -0
- package/dist/src/ui/routes-monoes.mjs +336 -0
- package/dist/src/ui/server.mjs +19 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi-runner.d.ts","sourceRoot":"","sources":["../../../src/orgrt/pi-runner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pi-runner.d.ts","sourceRoot":"","sources":["../../../src/orgrt/pi-runner.ts"],"names":[],"mappings":"AAgDA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAgCjF;2BAC2B;AAC3B,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG;IAC9C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAgCA;AAeD,qBAAa,aAAc,YAAW,WAAW;IACnC,OAAO,CAAC,KAAK,CAAC;IAA1B,YAAoB,KAAK,CAAC,EAAE,MAAM,YAAA,EAAI;IAE/B,GAAG,CAAC,IAAI,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC,CAoF1D;IAED,OAAO,CAAC,OAAO;CAoIhB"}
|
|
@@ -9,20 +9,23 @@
|
|
|
9
9
|
* Auth: pi's own provider login (per-provider API key or subscription,
|
|
10
10
|
* configured via `pi` itself). No env vars set here.
|
|
11
11
|
*
|
|
12
|
-
* Subprocess protocol —
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* tool
|
|
12
|
+
* Subprocess protocol — byte-verified against a running `pi` 0.73.1 binary
|
|
13
|
+
* (2026-08-25, see doc/agent-exec-protocol testing).
|
|
14
|
+
* - Invocation: `pi --mode json --session-dir <dir> "<prompt>"`. The
|
|
15
|
+
* prompt is POSITIONAL (matching pi's own interactive-mode CLI shape and
|
|
16
|
+
* the same convention codex uses) — an earlier revision of this runner
|
|
17
|
+
* passed it via a `-p` flag, which a second-source cross-check against
|
|
18
|
+
* another public agentic-CLI wrapper's tool table indicated was wrong;
|
|
19
|
+
* corrected here. An even earlier revision also passed `--approve`
|
|
20
|
+
* (meant to accept the cwd as trusted so pi doesn't stop to ask) — that
|
|
21
|
+
* flag does not exist in 0.73.1's `--help` output at all and made every
|
|
22
|
+
* turn fail immediately with "Unknown option: --approve"; removed.
|
|
23
|
+
* `--mode json` alone was confirmed NOT to block on an interactive
|
|
24
|
+
* trust prompt, so no replacement flag is needed (pi has no single
|
|
25
|
+
* "yolo" flag; tool auto-approval for individual actions is a separate,
|
|
26
|
+
* not-yet-wired concern for this runner — org tool calls go through
|
|
27
|
+
* canUseTool regardless, since they use the tool-fence protocol, not
|
|
28
|
+
* pi's native tool surface).
|
|
26
29
|
* (`--session-dir` also gives turn-to-turn continuity: pi persists
|
|
27
30
|
* sessions in that directory and resumes the latest one by default —
|
|
28
31
|
* there is no confirmed explicit `--resume <id>` flag for headless use,
|
|
@@ -164,8 +167,14 @@ export class PiAgentRunner {
|
|
|
164
167
|
runTurn(bin, prompt, sessionDir, args) {
|
|
165
168
|
return new Promise((resolve, reject) => {
|
|
166
169
|
// Prompt is positional (see file header) — always LAST so no later flag
|
|
167
|
-
// is mistaken for part of it.
|
|
168
|
-
|
|
170
|
+
// is mistaken for part of it. NOTE ⓥ: `--approve` does not exist in the
|
|
171
|
+
// installed pi CLI (0.73.1) — confirmed live via `pi --help`, which
|
|
172
|
+
// lists no approve/trust/yolo flag at all; passing it made every turn
|
|
173
|
+
// fail immediately with "Unknown option: --approve" before pi ever ran.
|
|
174
|
+
// `--mode json` alone (no `--print`) was verified NOT to block on an
|
|
175
|
+
// interactive trust prompt, matching this file's original assumption —
|
|
176
|
+
// removing `--approve` was the only fix needed.
|
|
177
|
+
const cliArgs = ['--mode', 'json', '--session-dir', sessionDir];
|
|
169
178
|
if (args.model)
|
|
170
179
|
cliArgs.push('--model', args.model);
|
|
171
180
|
cliArgs.push(prompt);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi-runner.js","sourceRoot":"","sources":["../../../src/orgrt/pi-runner.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD
|
|
1
|
+
{"version":3,"file":"pi-runner.js","sourceRoot":"","sources":["../../../src/orgrt/pi-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -62,6 +62,7 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
62
62
|
.nav-glob-items { display: none; flex-direction: column; }
|
|
63
63
|
.nav-glob-items.open { display: flex; }
|
|
64
64
|
#sb-footer { padding: 10px 14px; border-top: 1px solid var(--border); }
|
|
65
|
+
#sb-monoes { margin-bottom: 8px; }
|
|
65
66
|
#sb-user { font-size: 12px; font-weight: 500; color: var(--text-mid); }
|
|
66
67
|
#sb-path { font-size: 10px; color: var(--text-lo); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--mono); }
|
|
67
68
|
|
|
@@ -1657,6 +1658,9 @@ select.pb-cfg-inp { padding: 4px 7px; cursor: pointer; }
|
|
|
1657
1658
|
</div>
|
|
1658
1659
|
</div>
|
|
1659
1660
|
<div id="sb-footer">
|
|
1661
|
+
<div id="sb-monoes">
|
|
1662
|
+
<button class="btn" id="sb-monoes-btn" onclick="toggleMonoesConnection()" style="width:100%;font-size:11px">Connect to monoes.me</button>
|
|
1663
|
+
</div>
|
|
1660
1664
|
<div id="sb-user">—</div>
|
|
1661
1665
|
<div id="sb-path">—</div>
|
|
1662
1666
|
</div>
|
|
@@ -2664,6 +2668,7 @@ async function init() {
|
|
|
2664
2668
|
restoreURLParams();
|
|
2665
2669
|
viewRendered.now = true;
|
|
2666
2670
|
updateBudgetBtnStyle();
|
|
2671
|
+
refreshMonoesStatus();
|
|
2667
2672
|
await refreshNow();
|
|
2668
2673
|
if (sessParam) setTimeout(() => jumpToSession(sessParam), 300);
|
|
2669
2674
|
initSSE();
|
|
@@ -2713,6 +2718,49 @@ async function apiFetch(url, timeout = 15000) {
|
|
|
2713
2718
|
} finally { clearTimeout(tid); }
|
|
2714
2719
|
}
|
|
2715
2720
|
|
|
2721
|
+
// ── monoes.me connection ────────────────────────────────────
|
|
2722
|
+
async function refreshMonoesStatus() {
|
|
2723
|
+
const btn = document.getElementById('sb-monoes-btn');
|
|
2724
|
+
try {
|
|
2725
|
+
const status = await apiFetch('/api/monoes/status');
|
|
2726
|
+
if (status.connected) {
|
|
2727
|
+
btn.textContent = `⬡ ${status.username || 'Connected'} — Disconnect`;
|
|
2728
|
+
btn.title = 'Connected to monoes.me. Click to disconnect.';
|
|
2729
|
+
btn.dataset.connected = 'true';
|
|
2730
|
+
} else {
|
|
2731
|
+
btn.textContent = 'Connect to monoes.me';
|
|
2732
|
+
btn.title = 'Connect your monoes.me community account';
|
|
2733
|
+
btn.dataset.connected = 'false';
|
|
2734
|
+
}
|
|
2735
|
+
} catch (_) {}
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
async function toggleMonoesConnection() {
|
|
2739
|
+
const btn = document.getElementById('sb-monoes-btn');
|
|
2740
|
+
if (btn.dataset.connected === 'true') {
|
|
2741
|
+
if (!confirm('Disconnect from monoes.me?')) return;
|
|
2742
|
+
try {
|
|
2743
|
+
await fetch('/api/monoes/disconnect', { method: 'POST' });
|
|
2744
|
+
} catch (_) {}
|
|
2745
|
+
await refreshMonoesStatus();
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
try {
|
|
2749
|
+
const res = await fetch('/api/monoes/connect', { method: 'POST' });
|
|
2750
|
+
const { authorizeUrl, error } = await res.json();
|
|
2751
|
+
if (error || !authorizeUrl) { alert(`Could not start monoes.me connection: ${error || 'unknown error'}`); return; }
|
|
2752
|
+
const popup = window.open(authorizeUrl, 'monoes-connect', 'width=520,height=680');
|
|
2753
|
+
const poll = setInterval(async () => {
|
|
2754
|
+
if (popup && popup.closed) {
|
|
2755
|
+
clearInterval(poll);
|
|
2756
|
+
await refreshMonoesStatus();
|
|
2757
|
+
}
|
|
2758
|
+
}, 1000);
|
|
2759
|
+
} catch (e) {
|
|
2760
|
+
alert(`Could not start monoes.me connection: ${e.message}`);
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2716
2764
|
// ── project switching ──────────────────────────────────────
|
|
2717
2765
|
function _showNavProjectCtx(path) {
|
|
2718
2766
|
const projName = baseName(path);
|
|
@@ -6712,6 +6760,9 @@ async function _v2OpenAgent(roleId) {
|
|
|
6712
6760
|
if (_v2SelOrg === _agentOrg) bodyEl.innerHTML = '<div class="oad-loading">Could not load agent detail.</div>';
|
|
6713
6761
|
}
|
|
6714
6762
|
}
|
|
6763
|
+
// Role nodes and cards use inline handlers, which resolve names on `window`.
|
|
6764
|
+
// Keep the public entrypoint while the implementation remains privately named.
|
|
6765
|
+
window.v2OpenAgent = _v2OpenAgent;
|
|
6715
6766
|
function v2RenderAgentDrawer(data) {
|
|
6716
6767
|
const role = (data?.role) || {};
|
|
6717
6768
|
const def = (data?.definition) || { found: false };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[]
|
package/dist/src/ui/orgs.html
CHANGED
|
@@ -849,6 +849,13 @@ html, body {
|
|
|
849
849
|
<div class="hdr-chip">TOPOLOGY <span id="hdr-topology">—</span></div>
|
|
850
850
|
<div class="hdr-chip" id="hdr-created-wrap" style="display:none">SINCE <span id="hdr-created">—</span></div>
|
|
851
851
|
</div>
|
|
852
|
+
<button id="upload-monoes-btn" onclick="uploadCurrentOrgToMonoes()"
|
|
853
|
+
style="font-size:9px; font-family:var(--mono); letter-spacing:1px; color:var(--text-mid);
|
|
854
|
+
border:1px solid var(--border); background:none; padding:4px 12px;
|
|
855
|
+
border-radius:3px; cursor:pointer; text-transform:uppercase; transition:background 0.12s;"
|
|
856
|
+
onmouseover="this.style.background='var(--bg-hover, rgba(255,255,255,0.06))'"
|
|
857
|
+
onmouseout="this.style.background='none'"
|
|
858
|
+
title="Upload this org to your monoes.me community account">⬡ UPLOAD TO MONOES.ME</button>
|
|
852
859
|
<button id="stop-btn" onclick="stopCurrentOrg()" style="display:none;
|
|
853
860
|
font-size:9px; font-family:var(--mono); letter-spacing:1px; color:var(--red);
|
|
854
861
|
border:1px solid oklch(62% 0.22 25 / 0.35); background:none; padding:4px 12px;
|
|
@@ -2233,6 +2240,39 @@ function handleMmEvent(ev) {
|
|
|
2233
2240
|
}
|
|
2234
2241
|
}
|
|
2235
2242
|
|
|
2243
|
+
// ── Upload to monoes.me action ───────────────────────────────────────
|
|
2244
|
+
window.uploadCurrentOrgToMonoes = async () => {
|
|
2245
|
+
if (!selectedOrg) return;
|
|
2246
|
+
const btn = document.getElementById('upload-monoes-btn');
|
|
2247
|
+
const originalLabel = btn.textContent;
|
|
2248
|
+
btn.disabled = true;
|
|
2249
|
+
btn.textContent = 'UPLOADING…';
|
|
2250
|
+
try {
|
|
2251
|
+
const res = await fetch('/api/monoes/upload-org', {
|
|
2252
|
+
method: 'POST',
|
|
2253
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2254
|
+
body: JSON.stringify({ orgName: selectedOrg }),
|
|
2255
|
+
});
|
|
2256
|
+
const body = await res.json();
|
|
2257
|
+
if (!res.ok) {
|
|
2258
|
+
if (body.error === 'not_connected') {
|
|
2259
|
+
alert('Connect to monoes.me first (button in the sidebar) before uploading an org.');
|
|
2260
|
+
} else {
|
|
2261
|
+
alert(`Upload failed: ${body.error || res.statusText}`);
|
|
2262
|
+
}
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
if (confirm(`Uploaded to monoes.me. Open it now?`)) {
|
|
2266
|
+
window.open(body.url, '_blank');
|
|
2267
|
+
}
|
|
2268
|
+
} catch (e) {
|
|
2269
|
+
alert(`Upload failed: ${e.message}`);
|
|
2270
|
+
} finally {
|
|
2271
|
+
btn.disabled = false;
|
|
2272
|
+
btn.textContent = originalLabel;
|
|
2273
|
+
}
|
|
2274
|
+
};
|
|
2275
|
+
|
|
2236
2276
|
// ── Stop org action ────────────────────────────────────────────────
|
|
2237
2277
|
window.stopCurrentOrg = async () => {
|
|
2238
2278
|
if (!selectedOrg) return;
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// ── monoes.me connection (OAuth) ────────────────────────────────────────
|
|
2
|
+
// Lets the dashboard connect a monoes.me community account: upload local
|
|
3
|
+
// orgs directly (routes-monoes-upload, added alongside this), and register
|
|
4
|
+
// monoes.me's MCP server into .mcp.json (mcp-generator.ts) so agent
|
|
5
|
+
// sessions in this project get monoes.me's community tools automatically.
|
|
6
|
+
//
|
|
7
|
+
// Mirrors the existing dashboard-token file convention (one JSON file
|
|
8
|
+
// under MONOMIND_HOME/.monomind/, plain fs read/write, no external auth
|
|
9
|
+
// library) rather than introducing new dependencies for a single OAuth
|
|
10
|
+
// client flow.
|
|
11
|
+
import crypto from 'node:crypto';
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
|
|
15
|
+
const MONOES_BASE_URL = process.env.MONOMIND_MONOES_URL || 'https://monoes.me';
|
|
16
|
+
const MONOES_SCOPE = 'community:read community:write';
|
|
17
|
+
|
|
18
|
+
// state -> { codeVerifier, createdAt } — short-lived, in-memory only. The
|
|
19
|
+
// user completes the browser redirect within a few minutes of clicking
|
|
20
|
+
// "Connect"; losing this on a dashboard restart just means the in-flight
|
|
21
|
+
// connect attempt has to be retried, not a security issue.
|
|
22
|
+
const _pendingStates = new Map();
|
|
23
|
+
const STATE_TTL_MS = 10 * 60 * 1000;
|
|
24
|
+
|
|
25
|
+
function _connectionFile(monomindHome) {
|
|
26
|
+
return path.join(monomindHome, '.monomind', 'monoes-connection.json');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function readMonoesConnection(monomindHome) {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(fs.readFileSync(_connectionFile(monomindHome), 'utf8'));
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function _writeMonoesConnection(monomindHome, data) {
|
|
38
|
+
const file = _connectionFile(monomindHome);
|
|
39
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
40
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function _deleteMonoesConnection(monomindHome) {
|
|
44
|
+
try {
|
|
45
|
+
fs.unlinkSync(_connectionFile(monomindHome));
|
|
46
|
+
} catch {}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function _base64url(buffer) {
|
|
50
|
+
return buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function createPkcePair() {
|
|
54
|
+
const verifier = _base64url(crypto.randomBytes(32));
|
|
55
|
+
const challenge = _base64url(crypto.createHash('sha256').update(verifier).digest());
|
|
56
|
+
return { verifier, challenge };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function _pruneExpiredStates() {
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
for (const [state, entry] of _pendingStates) {
|
|
62
|
+
if (now - entry.createdAt > STATE_TTL_MS) _pendingStates.delete(state);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function _registerClient(redirectUri) {
|
|
67
|
+
const res = await fetch(`${MONOES_BASE_URL}/api/auth/oauth2/register`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
redirect_uris: [redirectUri],
|
|
72
|
+
token_endpoint_auth_method: 'none',
|
|
73
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
74
|
+
// The dashboard's redirect_uri is http://127.0.0.1:<port>/... — the
|
|
75
|
+
// OAuth server only allows plain-http loopback redirects for native
|
|
76
|
+
// clients (RFC 8252 §7.3); "web" (the DCR default) requires https.
|
|
77
|
+
application_type: 'native',
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
if (!res.ok) throw new Error(`monoes.me client registration failed: ${res.status}`);
|
|
81
|
+
const data = await res.json();
|
|
82
|
+
return data.client_id;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function _getOrRegisterClientId(monomindHome, redirectUri) {
|
|
86
|
+
const existing = readMonoesConnection(monomindHome);
|
|
87
|
+
if (existing?.clientId) return existing.clientId;
|
|
88
|
+
const clientId = await _registerClient(redirectUri);
|
|
89
|
+
_writeMonoesConnection(monomindHome, { ...(existing || {}), clientId });
|
|
90
|
+
return clientId;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function _buildMonoesMcpEntry(accessToken) {
|
|
94
|
+
return {
|
|
95
|
+
type: 'http',
|
|
96
|
+
url: `${MONOES_BASE_URL}/api/mcp`,
|
|
97
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Merges (or removes) the `monoes` entry in the project's existing
|
|
102
|
+
* .mcp.json. Only touches that one key — never creates or rewrites the
|
|
103
|
+
* rest of the file, since .mcp.json is otherwise owned by `init`/the user.
|
|
104
|
+
* No-ops if .mcp.json doesn't exist yet (nothing to merge into). */
|
|
105
|
+
function _syncMonoesMcpEntry(projectDir, accessToken) {
|
|
106
|
+
const mcpPath = path.join(projectDir, '.mcp.json');
|
|
107
|
+
let config;
|
|
108
|
+
try {
|
|
109
|
+
config = JSON.parse(fs.readFileSync(mcpPath, 'utf8'));
|
|
110
|
+
} catch {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
config.mcpServers = config.mcpServers || {};
|
|
114
|
+
if (accessToken) {
|
|
115
|
+
config.mcpServers.monoes = _buildMonoesMcpEntry(accessToken);
|
|
116
|
+
} else {
|
|
117
|
+
delete config.mcpServers.monoes;
|
|
118
|
+
}
|
|
119
|
+
fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function _json(res, corsOrigin, status, body) {
|
|
123
|
+
res.writeHead(status, {
|
|
124
|
+
'Content-Type': 'application/json',
|
|
125
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
126
|
+
});
|
|
127
|
+
res.end(JSON.stringify(body));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Given a stored connection, returns a still-valid access token — silently
|
|
131
|
+
* refreshing via the refresh_token grant if the current one is expired or
|
|
132
|
+
* about to expire. Returns null (and deletes the connection) if refresh
|
|
133
|
+
* itself fails, treating that identically to "never connected". */
|
|
134
|
+
export async function getValidMonoesToken(monomindHome) {
|
|
135
|
+
const conn = readMonoesConnection(monomindHome);
|
|
136
|
+
if (!conn?.accessToken) return null;
|
|
137
|
+
|
|
138
|
+
const expiresSoon = !conn.expiresAt || conn.expiresAt - Date.now() < 60_000;
|
|
139
|
+
if (!expiresSoon) return conn.accessToken;
|
|
140
|
+
if (!conn.refreshToken) {
|
|
141
|
+
_deleteMonoesConnection(monomindHome);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const res = await fetch(`${MONOES_BASE_URL}/api/auth/oauth2/token`, {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
149
|
+
body: new URLSearchParams({
|
|
150
|
+
grant_type: 'refresh_token',
|
|
151
|
+
refresh_token: /* value */ conn.refreshToken,
|
|
152
|
+
client_id: conn.clientId,
|
|
153
|
+
}),
|
|
154
|
+
});
|
|
155
|
+
if (!res.ok) throw new Error(`refresh failed: ${res.status}`);
|
|
156
|
+
const data = await res.json();
|
|
157
|
+
const updated = {
|
|
158
|
+
...conn,
|
|
159
|
+
accessToken: /* value */ data.access_token,
|
|
160
|
+
refreshToken: /* value */ data.refresh_token || conn.refreshToken,
|
|
161
|
+
expiresAt: Date.now() + (data.expires_in || 3600) * 1000,
|
|
162
|
+
};
|
|
163
|
+
_writeMonoesConnection(monomindHome, updated);
|
|
164
|
+
return updated.accessToken;
|
|
165
|
+
} catch {
|
|
166
|
+
_deleteMonoesConnection(monomindHome);
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function handleMonoesRoutes(req, res, url, corsOrigin, ctx) {
|
|
172
|
+
const { MONOMIND_HOME, dashboardPort, projectDir } = ctx;
|
|
173
|
+
const redirectUri = `http://127.0.0.1:${dashboardPort}/api/monoes/callback`;
|
|
174
|
+
|
|
175
|
+
// --------------------------------------------------- POST /api/monoes/connect
|
|
176
|
+
if (req.method === 'POST' && url === '/api/monoes/connect') {
|
|
177
|
+
try {
|
|
178
|
+
_pruneExpiredStates();
|
|
179
|
+
const clientId = await _getOrRegisterClientId(MONOMIND_HOME, redirectUri);
|
|
180
|
+
const { verifier, challenge } = createPkcePair();
|
|
181
|
+
const state = _base64url(crypto.randomBytes(16));
|
|
182
|
+
_pendingStates.set(state, { codeVerifier: verifier, createdAt: Date.now() });
|
|
183
|
+
|
|
184
|
+
const authorizeUrl =
|
|
185
|
+
`${MONOES_BASE_URL}/api/auth/oauth2/authorize?client_id=${encodeURIComponent(clientId)}` +
|
|
186
|
+
`&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code` +
|
|
187
|
+
`&scope=${encodeURIComponent(MONOES_SCOPE)}` +
|
|
188
|
+
`&code_challenge=${challenge}&code_challenge_method=S256&state=${state}`;
|
|
189
|
+
|
|
190
|
+
_json(res, corsOrigin, 200, { authorizeUrl });
|
|
191
|
+
} catch (err) {
|
|
192
|
+
_json(res, corsOrigin, 500, { error: err.message });
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// -------------------------------------------------- GET /api/monoes/callback
|
|
198
|
+
if (req.method === 'GET' && url.startsWith('/api/monoes/callback')) {
|
|
199
|
+
const qs = new URL(req.url, 'http://localhost').searchParams;
|
|
200
|
+
const code = qs.get('code');
|
|
201
|
+
const state = qs.get('state');
|
|
202
|
+
const pending = state ? _pendingStates.get(state) : null;
|
|
203
|
+
|
|
204
|
+
const closeTab = (message) => {
|
|
205
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
206
|
+
res.end(
|
|
207
|
+
`<!doctype html><html><body style="font-family:sans-serif;padding:2rem;">${message}</body></html>`,
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
if (!code || !pending) {
|
|
212
|
+
closeTab('Connection failed — the request was invalid or expired. You can close this tab and try again.');
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
_pendingStates.delete(state);
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const existing = readMonoesConnection(MONOMIND_HOME);
|
|
219
|
+
const tokenRes = await fetch(`${MONOES_BASE_URL}/api/auth/oauth2/token`, {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
222
|
+
body: new URLSearchParams({
|
|
223
|
+
grant_type: 'authorization_code',
|
|
224
|
+
code,
|
|
225
|
+
redirect_uri: redirectUri,
|
|
226
|
+
client_id: existing.clientId,
|
|
227
|
+
code_verifier: pending.codeVerifier,
|
|
228
|
+
}),
|
|
229
|
+
});
|
|
230
|
+
if (!tokenRes.ok) throw new Error(`token exchange failed: ${tokenRes.status}`);
|
|
231
|
+
const tokenData = await tokenRes.json();
|
|
232
|
+
|
|
233
|
+
const meRes = await fetch(`${MONOES_BASE_URL}/api/community/me`, {
|
|
234
|
+
headers: { Authorization: `Bearer ${tokenData.access_token}` },
|
|
235
|
+
});
|
|
236
|
+
const me = meRes.ok ? await meRes.json() : { username: null };
|
|
237
|
+
|
|
238
|
+
_writeMonoesConnection(MONOMIND_HOME, {
|
|
239
|
+
clientId: existing.clientId,
|
|
240
|
+
accessToken: /* value */ tokenData.access_token,
|
|
241
|
+
refreshToken: /* value */ tokenData.refresh_token || null,
|
|
242
|
+
expiresAt: Date.now() + (tokenData.expires_in || 3600) * 1000,
|
|
243
|
+
connectedUsername: me.username || null,
|
|
244
|
+
});
|
|
245
|
+
_syncMonoesMcpEntry(path.resolve(projectDir || process.cwd()), tokenData.access_token);
|
|
246
|
+
|
|
247
|
+
closeTab('Connected to monoes.me — you can close this tab.');
|
|
248
|
+
} catch (err) {
|
|
249
|
+
closeTab(`Connection failed: ${err.message}. You can close this tab and try again.`);
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ---------------------------------------------------- GET /api/monoes/status
|
|
255
|
+
// Polled on every dashboard load — piggybacks the silent-refresh check so
|
|
256
|
+
// .mcp.json's embedded access token self-heals (refreshed or removed)
|
|
257
|
+
// roughly as often as the dashboard is opened, instead of only on the
|
|
258
|
+
// explicit connect/disconnect actions.
|
|
259
|
+
if (req.method === 'GET' && url === '/api/monoes/status') {
|
|
260
|
+
const beforeConn = readMonoesConnection(MONOMIND_HOME);
|
|
261
|
+
const validToken = /* value */ beforeConn?.accessToken ? await getValidMonoesToken(MONOMIND_HOME) : null;
|
|
262
|
+
const afterConn = readMonoesConnection(MONOMIND_HOME);
|
|
263
|
+
if (validToken !== (beforeConn?.accessToken || null)) {
|
|
264
|
+
_syncMonoesMcpEntry(path.resolve(projectDir || process.cwd()), validToken);
|
|
265
|
+
}
|
|
266
|
+
_json(res, corsOrigin, 200, {
|
|
267
|
+
connected: !!validToken,
|
|
268
|
+
username: validToken ? afterConn?.connectedUsername || null : null,
|
|
269
|
+
});
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ------------------------------------------------ POST /api/monoes/disconnect
|
|
274
|
+
if (req.method === 'POST' && url === '/api/monoes/disconnect') {
|
|
275
|
+
_deleteMonoesConnection(MONOMIND_HOME);
|
|
276
|
+
_syncMonoesMcpEntry(path.resolve(projectDir || process.cwd()), null);
|
|
277
|
+
_json(res, corsOrigin, 200, { connected: false });
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ----------------------------------------------- POST /api/monoes/upload-org
|
|
282
|
+
// Reads the org's existing on-disk definition (the same
|
|
283
|
+
// .monomind/orgs/<name>.json file GET /api/orgs/:name already serves —
|
|
284
|
+
// no new read path) and forwards it to monoes.me's real upload endpoint.
|
|
285
|
+
if (req.method === 'POST' && url === '/api/monoes/upload-org') {
|
|
286
|
+
let body = '';
|
|
287
|
+
req.on('data', (c) => {
|
|
288
|
+
body += c;
|
|
289
|
+
if (body.length > 1e6) req.destroy();
|
|
290
|
+
});
|
|
291
|
+
req.on('end', async () => {
|
|
292
|
+
try {
|
|
293
|
+
const { orgName, dir } = JSON.parse(body || '{}');
|
|
294
|
+
if (!orgName || orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
295
|
+
_json(res, corsOrigin, 400, { error: 'Invalid org name' });
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const token = await getValidMonoesToken(MONOMIND_HOME);
|
|
300
|
+
if (!token) {
|
|
301
|
+
_json(res, corsOrigin, 401, { error: 'not_connected' });
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const root = path.resolve(dir || ctx.projectDir || process.cwd());
|
|
306
|
+
const projDir = ctx._resolveOrgProjectDir(orgName, root) || root;
|
|
307
|
+
const orgFile = path.join(projDir, '.monomind', 'orgs', `${orgName}.json`);
|
|
308
|
+
if (!fs.existsSync(orgFile)) {
|
|
309
|
+
_json(res, corsOrigin, 404, { error: 'Org not found' });
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const orgJson = fs.readFileSync(orgFile, 'utf8');
|
|
313
|
+
|
|
314
|
+
const uploadRes = await fetch(`${MONOES_BASE_URL}/api/community/orgs`, {
|
|
315
|
+
method: 'POST',
|
|
316
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
317
|
+
body: JSON.stringify({ orgJson }),
|
|
318
|
+
});
|
|
319
|
+
const uploadBody = await uploadRes.json().catch(() => ({}));
|
|
320
|
+
if (!uploadRes.ok) {
|
|
321
|
+
_json(res, corsOrigin, uploadRes.status, uploadBody);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
_json(res, corsOrigin, 201, {
|
|
325
|
+
...uploadBody,
|
|
326
|
+
url: `${MONOES_BASE_URL}/community/orgs/${uploadBody.id}`,
|
|
327
|
+
});
|
|
328
|
+
} catch (err) {
|
|
329
|
+
_json(res, corsOrigin, 500, { error: err.message });
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return false;
|
|
336
|
+
}
|
package/dist/src/ui/server.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
collectTokens,
|
|
20
20
|
} from './collector.mjs';
|
|
21
21
|
import { handleMonographRoutes } from './routes-monograph.mjs';
|
|
22
|
+
import { handleMonoesRoutes } from './routes-monoes.mjs';
|
|
22
23
|
import { handleOrgRoutes } from './routes-org.mjs';
|
|
23
24
|
import {
|
|
24
25
|
addMmClient,
|
|
@@ -1778,7 +1779,13 @@ export async function startServer({
|
|
|
1778
1779
|
]);
|
|
1779
1780
|
const _isOpenRoute = (url, method) =>
|
|
1780
1781
|
(method === 'GET' || method === 'HEAD' || method === 'OPTIONS') &&
|
|
1781
|
-
(_OPEN_ROUTES.has(url) ||
|
|
1782
|
+
(_OPEN_ROUTES.has(url) ||
|
|
1783
|
+
/^\/data\/avatars\/[A-Za-z0-9._-]+\.svg$/.test(url) ||
|
|
1784
|
+
// The browser hits this directly via monoes.me's OAuth redirect — it
|
|
1785
|
+
// never has this dashboard's own auth token to attach. Protected
|
|
1786
|
+
// instead by the OAuth `state` parameter (routes-monoes.mjs rejects
|
|
1787
|
+
// any code/state pair it didn't itself generate).
|
|
1788
|
+
url.startsWith('/api/monoes/callback'));
|
|
1782
1789
|
|
|
1783
1790
|
const server = http.createServer(async (req, res) => {
|
|
1784
1791
|
const url = req.url.split('?')[0];
|
|
@@ -5296,6 +5303,17 @@ export async function startServer({
|
|
|
5296
5303
|
return;
|
|
5297
5304
|
}
|
|
5298
5305
|
|
|
5306
|
+
// ── monoes.me connection routes (extracted to routes-monoes.mjs) ───────
|
|
5307
|
+
if (
|
|
5308
|
+
await handleMonoesRoutes(req, res, url, corsOrigin, {
|
|
5309
|
+
MONOMIND_HOME,
|
|
5310
|
+
dashboardPort: _boundPortForCors,
|
|
5311
|
+
projectDir,
|
|
5312
|
+
_resolveOrgProjectDir,
|
|
5313
|
+
})
|
|
5314
|
+
)
|
|
5315
|
+
return;
|
|
5316
|
+
|
|
5299
5317
|
// ── Org/mastermind routes (extracted to routes-org.mjs) ────────────────
|
|
5300
5318
|
if (
|
|
5301
5319
|
await handleOrgRoutes(req, res, url, corsOrigin, {
|