@monoes/monomindcli 2.14.1 → 2.15.1
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/helpers/control-start.cjs +10 -13
- package/.claude/skills/monodesign/scripts/context.mjs +4 -4
- package/.claude/skills/monodesign/scripts/critique-storage.mjs +1 -1
- package/.claude/skills/monodesign/scripts/detect-csp.mjs +1 -6
- package/.claude/skills/monodesign/scripts/detector/browser/injected/index.mjs +16 -18
- package/.claude/skills/monodesign/scripts/detector/cli/main.mjs +3 -3
- package/.claude/skills/monodesign/scripts/detector/detect-antipatterns-browser.js +67 -47
- package/.claude/skills/monodesign/scripts/detector/engines/regex/detect-text.mjs +6 -6
- package/.claude/skills/monodesign/scripts/detector/engines/static-html/css-cascade.mjs +6 -5
- package/.claude/skills/monodesign/scripts/detector/engines/static-html/detect-html.mjs +6 -6
- package/.claude/skills/monodesign/scripts/detector/fix/index.mjs +3 -3
- package/.claude/skills/monodesign/scripts/detector/node/file-system.mjs +1 -1
- package/.claude/skills/monodesign/scripts/detector/registry/antipatterns.mjs +1 -1
- package/.claude/skills/monodesign/scripts/detector/rules/checks.mjs +24 -25
- package/.claude/skills/monodesign/scripts/detector/shared/color.mjs +2 -2
- package/.claude/skills/monodesign/scripts/detector/shared/constants.mjs +1 -1
- package/.claude/skills/monodesign/scripts/detector/shared/inline-ignores.mjs +2 -2
- package/.claude/skills/monodesign/scripts/hook-admin.mjs +6 -6
- package/.claude/skills/monodesign/scripts/hook-before-edit.mjs +4 -4
- package/.claude/skills/monodesign/scripts/hook-lib.mjs +20 -11
- package/.claude/skills/monodesign/scripts/hook.mjs +1 -1
- package/.claude/skills/monodesign/scripts/lib/design-parser.mjs +9 -42
- package/.claude/skills/monodesign/scripts/lib/is-generated.mjs +1 -1
- package/.claude/skills/monodesign/scripts/lib/monodesign-config.mjs +5 -5
- package/.claude/skills/monodesign/scripts/lib/monodesign-paths.mjs +1 -1
- package/.claude/skills/monodesign/scripts/live/event-validation.mjs +13 -13
- package/.claude/skills/monodesign/scripts/live/manual-apply.mjs +4 -4
- package/.claude/skills/monodesign/scripts/live/manual-edits-buffer.mjs +1 -1
- package/.claude/skills/monodesign/scripts/live/session-store.mjs +5 -5
- package/.claude/skills/monodesign/scripts/live/svelte-component.mjs +13 -13
- package/.claude/skills/monodesign/scripts/live/sveltekit-adapter.mjs +4 -4
- package/.claude/skills/monodesign/scripts/live/ui-core.mjs +1 -1
- package/.claude/skills/monodesign/scripts/live-accept.mjs +24 -24
- package/.claude/skills/monodesign/scripts/live-browser-dom.js +8 -7
- package/.claude/skills/monodesign/scripts/live-browser-session.js +8 -7
- package/.claude/skills/monodesign/scripts/live-browser.js +405 -432
- package/.claude/skills/monodesign/scripts/live-commit-manual-edits.mjs +6 -6
- package/.claude/skills/monodesign/scripts/live-copy-edit-agent.mjs +2 -2
- package/.claude/skills/monodesign/scripts/live-discard-manual-edits.mjs +1 -1
- package/.claude/skills/monodesign/scripts/live-inject.mjs +7 -7
- package/.claude/skills/monodesign/scripts/live-insert.mjs +11 -11
- package/.claude/skills/monodesign/scripts/live-manual-edit-evidence.mjs +4 -4
- package/.claude/skills/monodesign/scripts/live-poll.mjs +5 -5
- package/.claude/skills/monodesign/scripts/live-server.mjs +10 -10
- package/.claude/skills/monodesign/scripts/live-wrap.mjs +31 -41
- package/.claude/skills/monodesign/scripts/live.mjs +5 -5
- package/.claude/skills/monodesign/scripts/palette.mjs +3 -1
- package/.claude/skills/monodesign/scripts/pin.mjs +1 -1
- package/dist/src/commands/doc-filters.d.ts.map +1 -1
- package/dist/src/commands/doc-filters.js +6 -0
- package/dist/src/commands/doc-filters.js.map +1 -1
- package/dist/src/commands/doc-library.d.ts +2 -1
- package/dist/src/commands/doc-library.d.ts.map +1 -1
- package/dist/src/commands/doc-library.js +66 -2
- package/dist/src/commands/doc-library.js.map +1 -1
- package/dist/src/commands/doc.d.ts.map +1 -1
- package/dist/src/commands/doc.js +2 -0
- package/dist/src/commands/doc.js.map +1 -1
- package/dist/src/commands/org.d.ts.map +1 -1
- package/dist/src/commands/org.js +42 -4
- package/dist/src/commands/org.js.map +1 -1
- package/dist/src/commands/ui.d.ts.map +1 -1
- package/dist/src/commands/ui.js +60 -0
- package/dist/src/commands/ui.js.map +1 -1
- package/dist/src/knowledge/capture-envelope.d.ts +5 -0
- package/dist/src/knowledge/capture-envelope.d.ts.map +1 -1
- package/dist/src/knowledge/capture-envelope.js +2 -0
- package/dist/src/knowledge/capture-envelope.js.map +1 -1
- package/dist/src/knowledge/document-chunking.d.ts +42 -0
- package/dist/src/knowledge/document-chunking.d.ts.map +1 -0
- package/dist/src/knowledge/document-chunking.js +288 -0
- package/dist/src/knowledge/document-chunking.js.map +1 -0
- package/dist/src/knowledge/document-index.d.ts +124 -0
- package/dist/src/knowledge/document-index.d.ts.map +1 -0
- package/dist/src/knowledge/document-index.js +314 -0
- package/dist/src/knowledge/document-index.js.map +1 -0
- package/dist/src/knowledge/document-ingest.d.ts +23 -0
- package/dist/src/knowledge/document-ingest.d.ts.map +1 -0
- package/dist/src/knowledge/document-ingest.js +368 -0
- package/dist/src/knowledge/document-ingest.js.map +1 -0
- package/dist/src/knowledge/document-pipeline.d.ts +26 -208
- package/dist/src/knowledge/document-pipeline.d.ts.map +1 -1
- package/dist/src/knowledge/document-pipeline.js +25 -1104
- package/dist/src/knowledge/document-pipeline.js.map +1 -1
- package/dist/src/knowledge/document-search.d.ts +27 -0
- package/dist/src/knowledge/document-search.d.ts.map +1 -0
- package/dist/src/knowledge/document-search.js +138 -0
- package/dist/src/knowledge/document-search.js.map +1 -0
- package/dist/src/knowledge/document-store.d.ts +28 -0
- package/dist/src/knowledge/document-store.d.ts.map +1 -0
- package/dist/src/knowledge/document-store.js +61 -0
- package/dist/src/knowledge/document-store.js.map +1 -0
- package/dist/src/knowledge/document-types.d.ts +103 -0
- package/dist/src/knowledge/document-types.d.ts.map +1 -0
- package/dist/src/knowledge/document-types.js +12 -0
- package/dist/src/knowledge/document-types.js.map +1 -0
- package/dist/src/knowledge/highlights.d.ts +168 -0
- package/dist/src/knowledge/highlights.d.ts.map +1 -0
- package/dist/src/knowledge/highlights.js +311 -0
- package/dist/src/knowledge/highlights.js.map +1 -0
- package/dist/src/knowledge/lookup.d.ts +80 -0
- package/dist/src/knowledge/lookup.d.ts.map +1 -0
- package/dist/src/knowledge/lookup.js +156 -0
- package/dist/src/knowledge/lookup.js.map +1 -0
- package/dist/src/knowledge/okf-bundle.d.ts +19 -0
- package/dist/src/knowledge/okf-bundle.d.ts.map +1 -0
- package/dist/src/knowledge/okf-bundle.js +106 -0
- package/dist/src/knowledge/okf-bundle.js.map +1 -0
- package/dist/src/knowledge/profile-store.d.ts +134 -0
- package/dist/src/knowledge/profile-store.d.ts.map +1 -0
- package/dist/src/knowledge/profile-store.js +237 -0
- package/dist/src/knowledge/profile-store.js.map +1 -0
- package/dist/src/orgrt/agent-runner.d.ts +4 -0
- package/dist/src/orgrt/agent-runner.d.ts.map +1 -1
- package/dist/src/orgrt/agent-runner.js +22 -0
- package/dist/src/orgrt/agent-runner.js.map +1 -1
- package/dist/src/orgrt/antigravity-runner.d.ts.map +1 -1
- package/dist/src/orgrt/antigravity-runner.js +2 -1
- package/dist/src/orgrt/antigravity-runner.js.map +1 -1
- package/dist/src/orgrt/authority-mask.d.ts +32 -0
- package/dist/src/orgrt/authority-mask.d.ts.map +1 -0
- package/dist/src/orgrt/authority-mask.js +135 -0
- package/dist/src/orgrt/authority-mask.js.map +1 -0
- package/dist/src/orgrt/codex-runner.d.ts.map +1 -1
- package/dist/src/orgrt/codex-runner.js +2 -1
- package/dist/src/orgrt/codex-runner.js.map +1 -1
- package/dist/src/orgrt/copilot-runner.d.ts.map +1 -1
- package/dist/src/orgrt/copilot-runner.js +2 -1
- package/dist/src/orgrt/copilot-runner.js.map +1 -1
- package/dist/src/orgrt/crush-runner.d.ts.map +1 -1
- package/dist/src/orgrt/crush-runner.js +6 -1
- package/dist/src/orgrt/crush-runner.js.map +1 -1
- package/dist/src/orgrt/daemon.d.ts +4 -0
- package/dist/src/orgrt/daemon.d.ts.map +1 -1
- package/dist/src/orgrt/daemon.js +13 -2
- package/dist/src/orgrt/daemon.js.map +1 -1
- package/dist/src/orgrt/decisions.d.ts +9 -0
- package/dist/src/orgrt/decisions.d.ts.map +1 -1
- package/dist/src/orgrt/decisions.js +32 -5
- package/dist/src/orgrt/decisions.js.map +1 -1
- package/dist/src/orgrt/file-roots.d.ts +2 -1
- package/dist/src/orgrt/file-roots.d.ts.map +1 -1
- package/dist/src/orgrt/file-roots.js +7 -3
- package/dist/src/orgrt/file-roots.js.map +1 -1
- package/dist/src/orgrt/grok-runner.d.ts.map +1 -1
- package/dist/src/orgrt/grok-runner.js +2 -1
- package/dist/src/orgrt/grok-runner.js.map +1 -1
- package/dist/src/orgrt/hermes-runner.d.ts.map +1 -1
- package/dist/src/orgrt/hermes-runner.js +2 -1
- package/dist/src/orgrt/hermes-runner.js.map +1 -1
- package/dist/src/orgrt/inbox.d.ts.map +1 -1
- package/dist/src/orgrt/inbox.js +70 -4
- package/dist/src/orgrt/inbox.js.map +1 -1
- package/dist/src/orgrt/kimicode-runner.d.ts.map +1 -1
- package/dist/src/orgrt/kimicode-runner.js +2 -1
- package/dist/src/orgrt/kimicode-runner.js.map +1 -1
- package/dist/src/orgrt/opencode-runner.d.ts.map +1 -1
- package/dist/src/orgrt/opencode-runner.js +3 -2
- package/dist/src/orgrt/opencode-runner.js.map +1 -1
- package/dist/src/orgrt/pi-rpc-runner.d.ts.map +1 -1
- package/dist/src/orgrt/pi-rpc-runner.js +3 -2
- package/dist/src/orgrt/pi-rpc-runner.js.map +1 -1
- package/dist/src/orgrt/pi-runner.d.ts.map +1 -1
- package/dist/src/orgrt/pi-runner.js +2 -1
- package/dist/src/orgrt/pi-runner.js.map +1 -1
- package/dist/src/orgrt/policy.d.ts.map +1 -1
- package/dist/src/orgrt/policy.js +3 -0
- package/dist/src/orgrt/policy.js.map +1 -1
- package/dist/src/orgrt/qwen-rpc-runner.d.ts.map +1 -1
- package/dist/src/orgrt/qwen-rpc-runner.js +3 -2
- package/dist/src/orgrt/qwen-rpc-runner.js.map +1 -1
- package/dist/src/orgrt/qwen-runner.d.ts.map +1 -1
- package/dist/src/orgrt/qwen-runner.js +2 -1
- package/dist/src/orgrt/qwen-runner.js.map +1 -1
- package/dist/src/orgrt/role-sandbox.d.ts +21 -0
- package/dist/src/orgrt/role-sandbox.d.ts.map +1 -1
- package/dist/src/orgrt/role-sandbox.js +31 -6
- package/dist/src/orgrt/role-sandbox.js.map +1 -1
- package/dist/src/orgrt/session.d.ts.map +1 -1
- package/dist/src/orgrt/session.js +21 -2
- package/dist/src/orgrt/session.js.map +1 -1
- package/dist/src/orgrt/task-dag.d.ts +3 -0
- package/dist/src/orgrt/task-dag.d.ts.map +1 -1
- package/dist/src/orgrt/task-dag.js +1 -0
- package/dist/src/orgrt/task-dag.js.map +1 -1
- package/dist/src/orgrt/types.d.ts +5 -0
- package/dist/src/orgrt/types.d.ts.map +1 -1
- package/dist/src/orgrt/types.js +23 -1
- package/dist/src/orgrt/types.js.map +1 -1
- package/dist/src/ui/dashboard.html +28 -848
- package/dist/src/ui/human-auth.mjs +103 -0
- package/dist/src/ui/org-hil.mjs +86 -37
- package/dist/src/ui/org-runtime.mjs +21 -13
- package/dist/src/ui/routes-org.mjs +49 -445
- package/dist/src/ui/server.mjs +103 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
|
@@ -29,23 +29,43 @@ function hilRoot(req, ctx) {
|
|
|
29
29
|
return path.resolve(dir || ctx.projectDir || process.cwd());
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
const HIL_BODY_MAX = 65536;
|
|
33
|
+
|
|
34
|
+
/** JSON body (≤64KB); null when malformed. Call inside hilRespond: an
|
|
35
|
+
* oversized body throws 413 and an aborted upload 400, instead of an
|
|
36
|
+
* unhandled rejection that would take the dashboard process down. Bytes are
|
|
37
|
+
* decoded once at the end, so a character split across chunks survives. */
|
|
33
38
|
async function readHilBody(req) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
const chunks = [];
|
|
40
|
+
let size = 0;
|
|
41
|
+
try {
|
|
42
|
+
// Past the cap, keep draining (without keeping) so the 413 still reaches
|
|
43
|
+
// the client — leaving the iterator early destroys the socket.
|
|
44
|
+
for await (const chunk of req) {
|
|
45
|
+
size += chunk.length;
|
|
46
|
+
if (size <= HIL_BODY_MAX) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
40
47
|
}
|
|
48
|
+
} catch (err) {
|
|
49
|
+
throw Object.assign(new Error(`request body not received: ${err.message}`), { status: 400 });
|
|
41
50
|
}
|
|
51
|
+
if (size > HIL_BODY_MAX)
|
|
52
|
+
throw Object.assign(new Error('request body is over 64KB'), { status: 413 });
|
|
42
53
|
try {
|
|
43
|
-
return JSON.parse(
|
|
54
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}');
|
|
44
55
|
} catch (_) {
|
|
45
56
|
return null;
|
|
46
57
|
}
|
|
47
58
|
}
|
|
48
59
|
|
|
60
|
+
/** decodeURIComponent for a path segment; a malformed escape is a 400. */
|
|
61
|
+
function decodeHilSegment(seg) {
|
|
62
|
+
try {
|
|
63
|
+
return decodeURIComponent(seg);
|
|
64
|
+
} catch (_) {
|
|
65
|
+
throw Object.assign(new Error(`malformed path segment: ${seg}`), { status: 400 });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
49
69
|
/** Run `fn` and answer with its JSON result, or the HIL error's status. */
|
|
50
70
|
async function hilRespond(res, corsOrigin, fn) {
|
|
51
71
|
const cors = corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {};
|
|
@@ -524,74 +544,6 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
524
544
|
return true;
|
|
525
545
|
}
|
|
526
546
|
|
|
527
|
-
// GET /api/org/:name/projects — org projects from projects json file
|
|
528
|
-
if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/projects$/i.test(url)) {
|
|
529
|
-
try {
|
|
530
|
-
const parts = url.split('/');
|
|
531
|
-
const orgName = decodeURIComponent(parts[3]);
|
|
532
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
533
|
-
res.writeHead(400);
|
|
534
|
-
res.end('[]');
|
|
535
|
-
return true;
|
|
536
|
-
}
|
|
537
|
-
const _projsQs = new URL(req.url, 'http://localhost').searchParams;
|
|
538
|
-
const d = path.resolve(_projsQs.get('dir') || ctx.projectDir || process.cwd());
|
|
539
|
-
const projFile = path.join(d, '.monomind', 'orgs', `${orgName}-projects.json`);
|
|
540
|
-
if (!fs.existsSync(projFile)) {
|
|
541
|
-
res.writeHead(200, {
|
|
542
|
-
'Content-Type': 'application/json',
|
|
543
|
-
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
544
|
-
});
|
|
545
|
-
res.end('[]');
|
|
546
|
-
return true;
|
|
547
|
-
}
|
|
548
|
-
const data = JSON.parse(fs.readFileSync(projFile, 'utf8'));
|
|
549
|
-
res.writeHead(200, {
|
|
550
|
-
'Content-Type': 'application/json',
|
|
551
|
-
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
552
|
-
});
|
|
553
|
-
res.end(JSON.stringify(data.projects || []));
|
|
554
|
-
} catch (_) {
|
|
555
|
-
res.writeHead(500);
|
|
556
|
-
res.end('[]');
|
|
557
|
-
}
|
|
558
|
-
return true;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// GET /api/org/:name/members — org member list and join requests
|
|
562
|
-
if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/members$/i.test(url)) {
|
|
563
|
-
try {
|
|
564
|
-
const parts = url.split('/');
|
|
565
|
-
const orgName = decodeURIComponent(parts[3]);
|
|
566
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
567
|
-
res.writeHead(400);
|
|
568
|
-
res.end('{}');
|
|
569
|
-
return true;
|
|
570
|
-
}
|
|
571
|
-
const _membersQs = new URL(req.url, 'http://localhost').searchParams;
|
|
572
|
-
const d = path.resolve(_membersQs.get('dir') || ctx.projectDir || process.cwd());
|
|
573
|
-
const membersFile = path.join(d, '.monomind', 'orgs', `${orgName}-members.json`);
|
|
574
|
-
if (!fs.existsSync(membersFile)) {
|
|
575
|
-
res.writeHead(200, {
|
|
576
|
-
'Content-Type': 'application/json',
|
|
577
|
-
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
578
|
-
});
|
|
579
|
-
res.end('{"members":[],"join_requests":[]}');
|
|
580
|
-
return true;
|
|
581
|
-
}
|
|
582
|
-
const data = JSON.parse(fs.readFileSync(membersFile, 'utf8'));
|
|
583
|
-
res.writeHead(200, {
|
|
584
|
-
'Content-Type': 'application/json',
|
|
585
|
-
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
586
|
-
});
|
|
587
|
-
res.end(JSON.stringify(data));
|
|
588
|
-
} catch (_) {
|
|
589
|
-
res.writeHead(500);
|
|
590
|
-
res.end('{}');
|
|
591
|
-
}
|
|
592
|
-
return true;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
547
|
// GET /api/org/:name/adapters — org adapter registry
|
|
596
548
|
if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/adapters$/i.test(url)) {
|
|
597
549
|
try {
|
|
@@ -1039,47 +991,6 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1039
991
|
return true;
|
|
1040
992
|
}
|
|
1041
993
|
|
|
1042
|
-
// GET /api/org/:name/issues — org task/issue list from issues file
|
|
1043
|
-
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/issues$/i)) {
|
|
1044
|
-
try {
|
|
1045
|
-
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
1046
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
1047
|
-
res.writeHead(400);
|
|
1048
|
-
res.end('Invalid org name');
|
|
1049
|
-
return true;
|
|
1050
|
-
}
|
|
1051
|
-
const _issuesQs = new URL(req.url, 'http://localhost').searchParams;
|
|
1052
|
-
const _issuesDir = path.resolve(_issuesQs.get('dir') || ctx.projectDir || process.cwd());
|
|
1053
|
-
const issuesPath = path.join(_issuesDir, '.monomind', 'orgs', `${orgName}-issues.json`);
|
|
1054
|
-
const payload = { issues: [] };
|
|
1055
|
-
try {
|
|
1056
|
-
const raw = JSON.parse(fs.readFileSync(issuesPath, 'utf8'));
|
|
1057
|
-
payload.issues = (raw.issues || []).map((i) => ({
|
|
1058
|
-
id: i.id,
|
|
1059
|
-
slug: i.slug,
|
|
1060
|
-
title: i.title,
|
|
1061
|
-
description: i.description || null,
|
|
1062
|
-
status: i.status || 'open',
|
|
1063
|
-
priority: i.priority || 'medium',
|
|
1064
|
-
assignee_id: i.assignee_id || null,
|
|
1065
|
-
assignee: i.assignee || i.assignee_id || null,
|
|
1066
|
-
project_id: i.project_id || null,
|
|
1067
|
-
parent_id: i.parent_id || null,
|
|
1068
|
-
created_at: i.created_at,
|
|
1069
|
-
updated_at: i.updated_at,
|
|
1070
|
-
}));
|
|
1071
|
-
} catch (_) {
|
|
1072
|
-
/* file missing is fine */
|
|
1073
|
-
}
|
|
1074
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1075
|
-
res.end(JSON.stringify(payload));
|
|
1076
|
-
} catch (_) {
|
|
1077
|
-
res.writeHead(500);
|
|
1078
|
-
res.end('{}');
|
|
1079
|
-
}
|
|
1080
|
-
return true;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
994
|
// GET /api/org/:name/health — aggregate org health, from the same runtime
|
|
1084
995
|
// sources as /runtime: live role sessions, the task DAG, this run's usage on
|
|
1085
996
|
// the budget's enforcement basis, and how the last 7 days of runs ended.
|
|
@@ -1094,7 +1005,12 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1094
1005
|
).length;
|
|
1095
1006
|
const activeTasks = v.tasks.filter((t) => t.status === 'running').length;
|
|
1096
1007
|
const weekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
1097
|
-
|
|
1008
|
+
// A resumed run keeps its run id and appends a history line per stop:
|
|
1009
|
+
// count each run once, by its latest line (history is newest first).
|
|
1010
|
+
const seenRuns = new Set();
|
|
1011
|
+
const week = v.history.filter(
|
|
1012
|
+
(h) => (h.endedAt || 0) >= weekAgo && !seenRuns.has(h.run) && seenRuns.add(h.run),
|
|
1013
|
+
);
|
|
1098
1014
|
// Only a boss's gate-checked org_complete with 'achieved' counts as success.
|
|
1099
1015
|
const achieved = week.filter(
|
|
1100
1016
|
(h) => h.closedBy === 'org-complete' && h.outcome?.status === 'achieved',
|
|
@@ -1118,149 +1034,6 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1118
1034
|
});
|
|
1119
1035
|
}
|
|
1120
1036
|
|
|
1121
|
-
// GET /api/org/:name/environments — org execution environments (strips key material)
|
|
1122
|
-
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/environments$/i)) {
|
|
1123
|
-
try {
|
|
1124
|
-
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
1125
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
1126
|
-
res.writeHead(400);
|
|
1127
|
-
res.end('Invalid org name');
|
|
1128
|
-
return true;
|
|
1129
|
-
}
|
|
1130
|
-
const _envsQs = new URL(req.url, 'http://localhost').searchParams;
|
|
1131
|
-
const envsPath = path.join(
|
|
1132
|
-
path.resolve(_envsQs.get('dir') || ctx.projectDir || process.cwd()),
|
|
1133
|
-
'.monomind',
|
|
1134
|
-
'orgs',
|
|
1135
|
-
`${orgName}-environments.json`,
|
|
1136
|
-
);
|
|
1137
|
-
const payload = { environments: [], default_env: null };
|
|
1138
|
-
try {
|
|
1139
|
-
const raw = JSON.parse(fs.readFileSync(envsPath, 'utf8'));
|
|
1140
|
-
// Strip any accidental key_material or private_key fields — never send to browser
|
|
1141
|
-
payload.default_env = raw.default_env || null;
|
|
1142
|
-
payload.environments = (raw.environments || []).map((e) => {
|
|
1143
|
-
const safe = { ...e };
|
|
1144
|
-
delete safe.key_material;
|
|
1145
|
-
delete safe.private_key;
|
|
1146
|
-
delete safe.ssh_key;
|
|
1147
|
-
delete safe.password;
|
|
1148
|
-
return safe;
|
|
1149
|
-
});
|
|
1150
|
-
} catch (_) {
|
|
1151
|
-
/* file missing is fine — return empty */
|
|
1152
|
-
}
|
|
1153
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1154
|
-
res.end(JSON.stringify(payload));
|
|
1155
|
-
} catch (_) {
|
|
1156
|
-
res.writeHead(500);
|
|
1157
|
-
res.end('{}');
|
|
1158
|
-
}
|
|
1159
|
-
return true;
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
// GET /api/org/:name/workspaces — org workspaces cross-referenced with worktree registry
|
|
1163
|
-
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/workspaces$/i)) {
|
|
1164
|
-
try {
|
|
1165
|
-
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
1166
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
1167
|
-
res.writeHead(400);
|
|
1168
|
-
res.end('Invalid org name');
|
|
1169
|
-
return true;
|
|
1170
|
-
}
|
|
1171
|
-
const _wsQs = new URL(req.url, 'http://localhost').searchParams;
|
|
1172
|
-
const base = path.join(
|
|
1173
|
-
path.resolve(_wsQs.get('dir') || ctx.projectDir || process.cwd()),
|
|
1174
|
-
'.monomind',
|
|
1175
|
-
'orgs',
|
|
1176
|
-
);
|
|
1177
|
-
const payload = { workspaces: [] };
|
|
1178
|
-
try {
|
|
1179
|
-
const wsRaw = JSON.parse(
|
|
1180
|
-
fs.readFileSync(path.join(base, `${orgName}-workspaces.json`), 'utf8'),
|
|
1181
|
-
);
|
|
1182
|
-
const workspaces = wsRaw.workspaces || [];
|
|
1183
|
-
// Optionally cross-reference worktree registry for branch/status enrichment
|
|
1184
|
-
const worktreeMap = {};
|
|
1185
|
-
try {
|
|
1186
|
-
const wtRaw = JSON.parse(
|
|
1187
|
-
fs.readFileSync(path.join(base, `${orgName}-worktrees.json`), 'utf8'),
|
|
1188
|
-
);
|
|
1189
|
-
(wtRaw.worktrees || []).forEach((wt) => {
|
|
1190
|
-
worktreeMap[wt.path] = wt;
|
|
1191
|
-
});
|
|
1192
|
-
} catch (_) {
|
|
1193
|
-
/* no worktree registry, that's fine */
|
|
1194
|
-
}
|
|
1195
|
-
payload.workspaces = workspaces.map((w) => {
|
|
1196
|
-
const wt = w.worktree_path ? worktreeMap[w.worktree_path] : null;
|
|
1197
|
-
return wt ? { ...w, branch: w.branch || wt.branch || w.branch } : w;
|
|
1198
|
-
});
|
|
1199
|
-
} catch (_) {
|
|
1200
|
-
/* file missing is fine */
|
|
1201
|
-
}
|
|
1202
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1203
|
-
res.end(JSON.stringify(payload));
|
|
1204
|
-
} catch (_) {
|
|
1205
|
-
res.writeHead(500);
|
|
1206
|
-
res.end('{}');
|
|
1207
|
-
}
|
|
1208
|
-
return true;
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
// GET /api/org/:name/invites — active invites + pending join requests
|
|
1212
|
-
if (
|
|
1213
|
-
req.method === 'GET' &&
|
|
1214
|
-
url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/invites(\?.*)?$/i)
|
|
1215
|
-
) {
|
|
1216
|
-
try {
|
|
1217
|
-
const orgName = decodeURIComponent(url.split('/')[3].split('?')[0]);
|
|
1218
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
1219
|
-
res.writeHead(400);
|
|
1220
|
-
res.end('Invalid org name');
|
|
1221
|
-
return true;
|
|
1222
|
-
}
|
|
1223
|
-
const _invitesQs = new URL(req.url, 'http://localhost').searchParams;
|
|
1224
|
-
const base = path.join(
|
|
1225
|
-
path.resolve(_invitesQs.get('dir') || ctx.projectDir || process.cwd()),
|
|
1226
|
-
'.monomind',
|
|
1227
|
-
'orgs',
|
|
1228
|
-
);
|
|
1229
|
-
const payload = { invites: [], join_requests: [] };
|
|
1230
|
-
try {
|
|
1231
|
-
const raw = JSON.parse(fs.readFileSync(path.join(base, `${orgName}-members.json`), 'utf8'));
|
|
1232
|
-
const all = raw.join_requests || [];
|
|
1233
|
-
payload.invites = all
|
|
1234
|
-
.filter((r) => r.type === 'invite' && r.status === 'pending')
|
|
1235
|
-
.map((r) => ({
|
|
1236
|
-
id: r.id,
|
|
1237
|
-
token: r.token ? `${r.token.slice(0, 8)}…` : r.id,
|
|
1238
|
-
role: r.role || 'operator',
|
|
1239
|
-
createdAt: r.createdAt || null,
|
|
1240
|
-
expiresAt: r.expiresAt || null,
|
|
1241
|
-
status: r.status,
|
|
1242
|
-
}));
|
|
1243
|
-
payload.join_requests = all
|
|
1244
|
-
.filter((r) => r.type !== 'invite' && r.status === 'pending_approval')
|
|
1245
|
-
.map((r) => ({
|
|
1246
|
-
id: r.id,
|
|
1247
|
-
requestType: r.requestType || 'human',
|
|
1248
|
-
role: r.role || 'viewer',
|
|
1249
|
-
createdAt: r.createdAt || null,
|
|
1250
|
-
message: r.message || '',
|
|
1251
|
-
}));
|
|
1252
|
-
} catch (_) {
|
|
1253
|
-
/* members file missing */
|
|
1254
|
-
}
|
|
1255
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1256
|
-
res.end(JSON.stringify(payload));
|
|
1257
|
-
} catch (_) {
|
|
1258
|
-
res.writeHead(500);
|
|
1259
|
-
res.end('{}');
|
|
1260
|
-
}
|
|
1261
|
-
return true;
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
1037
|
// GET /api/org/:name/plugins — plugins from registry filtered/merged with org overrides
|
|
1265
1038
|
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/plugins$/i)) {
|
|
1266
1039
|
try {
|
|
@@ -1322,52 +1095,6 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1322
1095
|
return true;
|
|
1323
1096
|
}
|
|
1324
1097
|
|
|
1325
|
-
// GET /api/org/:name/my-issues — open + in_progress issues (self-assignable queue)
|
|
1326
|
-
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/my-issues$/i)) {
|
|
1327
|
-
try {
|
|
1328
|
-
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
1329
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
1330
|
-
res.writeHead(400);
|
|
1331
|
-
res.end('Invalid org name');
|
|
1332
|
-
return true;
|
|
1333
|
-
}
|
|
1334
|
-
const _myIssuesQs = new URL(req.url, 'http://localhost').searchParams;
|
|
1335
|
-
const base = path.join(
|
|
1336
|
-
path.resolve(_myIssuesQs.get('dir') || ctx.projectDir || process.cwd()),
|
|
1337
|
-
'.monomind',
|
|
1338
|
-
'orgs',
|
|
1339
|
-
);
|
|
1340
|
-
const payload = { issues: [] };
|
|
1341
|
-
try {
|
|
1342
|
-
const raw = JSON.parse(fs.readFileSync(path.join(base, `${orgName}-issues.json`), 'utf8'));
|
|
1343
|
-
// Return open + in_progress issues — the "my issues" queue for the operator
|
|
1344
|
-
payload.issues = (raw.issues || [])
|
|
1345
|
-
.filter((i) => i.status === 'open' || i.status === 'in_progress')
|
|
1346
|
-
.map((i) => ({
|
|
1347
|
-
id: i.id,
|
|
1348
|
-
title: i.title || null,
|
|
1349
|
-
description: i.description || null,
|
|
1350
|
-
status: i.status || 'open',
|
|
1351
|
-
priority: i.priority || 'medium',
|
|
1352
|
-
assigneeId: i.assigneeId || i.assigned_to || null,
|
|
1353
|
-
projectId: i.projectId || i.project_id || null,
|
|
1354
|
-
createdAt: i.createdAt || null,
|
|
1355
|
-
lastActivityAt: i.lastActivityAt || null,
|
|
1356
|
-
updated_at: i.updated_at || i.lastActivityAt || i.updatedAt || i.ts || null,
|
|
1357
|
-
ts: i.ts || i.updated_at || i.lastActivityAt || null,
|
|
1358
|
-
}));
|
|
1359
|
-
} catch (_) {
|
|
1360
|
-
/* issues file missing */
|
|
1361
|
-
}
|
|
1362
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1363
|
-
res.end(JSON.stringify(payload));
|
|
1364
|
-
} catch (_) {
|
|
1365
|
-
res.writeHead(500);
|
|
1366
|
-
res.end('{}');
|
|
1367
|
-
}
|
|
1368
|
-
return true;
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
1098
|
// GET /api/org/:name/runtime — what the org is doing now, from the runtime's
|
|
1372
1099
|
// own sources (live daemon status, run bus, idle watchdog, history).
|
|
1373
1100
|
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/runtime$/i)) {
|
|
@@ -1385,9 +1112,9 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1385
1112
|
// same OrgDefSchema parse `org run` does.
|
|
1386
1113
|
if (req.method === 'POST' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/config$/i)) {
|
|
1387
1114
|
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
1388
|
-
const body = await readHilBody(req);
|
|
1389
1115
|
const root = hilRoot(req, ctx);
|
|
1390
|
-
return hilRespond(res, corsOrigin, () => {
|
|
1116
|
+
return hilRespond(res, corsOrigin, async () => {
|
|
1117
|
+
const body = await readHilBody(req);
|
|
1391
1118
|
if (!body || typeof body !== 'object')
|
|
1392
1119
|
throw Object.assign(new Error('JSON body required'), { status: 400 });
|
|
1393
1120
|
try {
|
|
@@ -1500,16 +1227,14 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1500
1227
|
// POST /api/questions/answer { dir, org, questionId, answer } — live to the
|
|
1501
1228
|
// hosting daemon (operator credential), else queued for the org's next run.
|
|
1502
1229
|
if (req.method === 'POST' && url === '/api/questions/answer') {
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1230
|
+
return hilRespond(res, corsOrigin, async () => {
|
|
1231
|
+
const body = await readHilBody(req);
|
|
1232
|
+
const { org, questionId, answer } = body || {};
|
|
1233
|
+
if (!validOrgName(org) || !questionId || typeof answer !== 'string' || !answer.trim())
|
|
1507
1234
|
throw Object.assign(new Error('org, questionId and a non-empty answer are required'), {
|
|
1508
1235
|
status: 400,
|
|
1509
1236
|
});
|
|
1510
|
-
|
|
1511
|
-
const root = path.resolve(body.dir || ctx.projectDir || process.cwd());
|
|
1512
|
-
return hilRespond(res, corsOrigin, async () => {
|
|
1237
|
+
const root = path.resolve(body.dir || ctx.projectDir || process.cwd());
|
|
1513
1238
|
const r = await hil.answerQuestion(root, org, String(questionId), answer.trim());
|
|
1514
1239
|
hilEvent(ctx, root, {
|
|
1515
1240
|
type: 'org:question-answered',
|
|
@@ -1530,14 +1255,14 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1530
1255
|
) {
|
|
1531
1256
|
const parts = url.split('?')[0].split('/');
|
|
1532
1257
|
const orgName = decodeURIComponent(parts[3]);
|
|
1533
|
-
const gateId = decodeURIComponent(parts[5]);
|
|
1534
|
-
const body = await readHilBody(req);
|
|
1535
|
-
const resolution =
|
|
1536
|
-
typeof body?.resolution === 'string' && body.resolution.trim()
|
|
1537
|
-
? body.resolution.trim().slice(0, 4000)
|
|
1538
|
-
: undefined;
|
|
1539
1258
|
const root = hilRoot(req, ctx);
|
|
1540
1259
|
return hilRespond(res, corsOrigin, async () => {
|
|
1260
|
+
const gateId = decodeHilSegment(parts[5]);
|
|
1261
|
+
const body = await readHilBody(req);
|
|
1262
|
+
const resolution =
|
|
1263
|
+
typeof body?.resolution === 'string' && body.resolution.trim()
|
|
1264
|
+
? body.resolution.trim().slice(0, 4000)
|
|
1265
|
+
: undefined;
|
|
1541
1266
|
if (typeof body?.approved !== 'boolean')
|
|
1542
1267
|
throw Object.assign(new Error('approved (boolean) is required'), { status: 400 });
|
|
1543
1268
|
const r = await hil.resolveGate(root, orgName, gateId, body.approved, resolution);
|
|
@@ -1561,10 +1286,10 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1561
1286
|
) {
|
|
1562
1287
|
const parts = url.split('?')[0].split('/');
|
|
1563
1288
|
const orgName = decodeURIComponent(parts[3]);
|
|
1564
|
-
const requestId = decodeURIComponent(parts[5]);
|
|
1565
|
-
const body = await readHilBody(req);
|
|
1566
1289
|
const root = hilRoot(req, ctx);
|
|
1567
1290
|
return hilRespond(res, corsOrigin, async () => {
|
|
1291
|
+
const requestId = decodeHilSegment(parts[5]);
|
|
1292
|
+
const body = await readHilBody(req);
|
|
1568
1293
|
if (body?.action !== 'approve' && body?.action !== 'reject')
|
|
1569
1294
|
throw Object.assign(new Error('action must be approve or reject'), { status: 400 });
|
|
1570
1295
|
const approved = body.action === 'approve';
|
|
@@ -1581,54 +1306,6 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1581
1306
|
});
|
|
1582
1307
|
}
|
|
1583
1308
|
|
|
1584
|
-
// GET /api/org/:name/secrets — masked secrets list (NEVER exposes values)
|
|
1585
|
-
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/secrets$/i)) {
|
|
1586
|
-
try {
|
|
1587
|
-
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
1588
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
1589
|
-
res.writeHead(400);
|
|
1590
|
-
res.end('Invalid org name');
|
|
1591
|
-
return true;
|
|
1592
|
-
}
|
|
1593
|
-
const _secretsQs = new URL(req.url, 'http://localhost').searchParams;
|
|
1594
|
-
const base = path.join(
|
|
1595
|
-
path.resolve(_secretsQs.get('dir') || ctx.projectDir || process.cwd()),
|
|
1596
|
-
'.monomind',
|
|
1597
|
-
'orgs',
|
|
1598
|
-
);
|
|
1599
|
-
const secretsDir = path.join(base, '.secrets');
|
|
1600
|
-
const readJsonSafe = (f) => {
|
|
1601
|
-
try {
|
|
1602
|
-
return JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
1603
|
-
} catch (_) {
|
|
1604
|
-
return null;
|
|
1605
|
-
}
|
|
1606
|
-
};
|
|
1607
|
-
// Read secrets index — NEVER expose actual values
|
|
1608
|
-
const indexFile = path.join(secretsDir, `${orgName}-index.json`);
|
|
1609
|
-
const data = readJsonSafe(indexFile) || { secrets: [] };
|
|
1610
|
-
const secrets = (data.secrets || []).map((s) => ({
|
|
1611
|
-
name: s.name,
|
|
1612
|
-
purpose: s.purpose || null,
|
|
1613
|
-
maskedRef: s.maskedRef || `${(s.name || '').substring(0, 4)}***`,
|
|
1614
|
-
status: s.status || 'active',
|
|
1615
|
-
createdAt: s.createdAt || null,
|
|
1616
|
-
rotatedAt: s.rotatedAt || null,
|
|
1617
|
-
lastUsedAt: s.lastUsedAt || null,
|
|
1618
|
-
usageCount: s.usageCount || 0,
|
|
1619
|
-
}));
|
|
1620
|
-
res.writeHead(200, {
|
|
1621
|
-
'Content-Type': 'application/json',
|
|
1622
|
-
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
1623
|
-
});
|
|
1624
|
-
res.end(JSON.stringify({ secrets }));
|
|
1625
|
-
} catch (_) {
|
|
1626
|
-
res.writeHead(500);
|
|
1627
|
-
res.end('{"secrets":[]}');
|
|
1628
|
-
}
|
|
1629
|
-
return true;
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
1309
|
// GET /api/org/:name/budgets — org and per-agent budget data
|
|
1633
1310
|
// Returns: { org_budget: {limit_tokens, limit_usd}, agent_budgets: {agentId: {limit_usd}}, agents: [{id, title, tokens_in, tokens_out, total_cost_usd}] }
|
|
1634
1311
|
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/budgets$/i)) {
|
|
@@ -1847,79 +1524,6 @@ export async function handleOrgRoutes(req, res, url, corsOrigin, ctx) {
|
|
|
1847
1524
|
return true;
|
|
1848
1525
|
}
|
|
1849
1526
|
|
|
1850
|
-
// GET /api/org/:name/join-requests — pending join requests for this org
|
|
1851
|
-
// Returns: { requests: [{id, requesterId, requesterName, type, status, createdAt, resolvedAt}], pending: N }
|
|
1852
|
-
if (
|
|
1853
|
-
req.method === 'GET' &&
|
|
1854
|
-
url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/join-requests(\?.*)?$/i)
|
|
1855
|
-
) {
|
|
1856
|
-
try {
|
|
1857
|
-
const orgName = decodeURIComponent(url.split('/')[3].split('?')[0]);
|
|
1858
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
1859
|
-
res.writeHead(400);
|
|
1860
|
-
res.end('Invalid org name');
|
|
1861
|
-
return true;
|
|
1862
|
-
}
|
|
1863
|
-
const _joinQs = new URL(req.url, 'http://localhost').searchParams;
|
|
1864
|
-
const joinFile = path.join(
|
|
1865
|
-
path.resolve(_joinQs.get('dir') || ctx.projectDir || process.cwd()),
|
|
1866
|
-
'.monomind',
|
|
1867
|
-
'orgs',
|
|
1868
|
-
`${orgName}-join-requests.json`,
|
|
1869
|
-
);
|
|
1870
|
-
let requests = [];
|
|
1871
|
-
try {
|
|
1872
|
-
const raw = fs.readFileSync(joinFile, 'utf8');
|
|
1873
|
-
const data = JSON.parse(raw);
|
|
1874
|
-
requests = (data.requests || []).map((r) => ({
|
|
1875
|
-
id: r.id,
|
|
1876
|
-
requesterId: r.requesterId,
|
|
1877
|
-
requesterName: r.requesterName || r.requesterId,
|
|
1878
|
-
type: r.type || 'human',
|
|
1879
|
-
status: r.status || 'pending_approval',
|
|
1880
|
-
createdAt: r.createdAt,
|
|
1881
|
-
resolvedAt: r.resolvedAt || null,
|
|
1882
|
-
}));
|
|
1883
|
-
} catch (_) {}
|
|
1884
|
-
const pending = requests.filter((r) => r.status === 'pending_approval').length;
|
|
1885
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1886
|
-
res.end(JSON.stringify({ requests, pending }));
|
|
1887
|
-
} catch (_) {
|
|
1888
|
-
res.writeHead(500);
|
|
1889
|
-
res.end('{"requests":[],"pending":0}');
|
|
1890
|
-
}
|
|
1891
|
-
return true;
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
// GET /api/org/:name/goals — read org goals
|
|
1895
|
-
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/goals$/i)) {
|
|
1896
|
-
try {
|
|
1897
|
-
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
1898
|
-
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
1899
|
-
res.writeHead(400);
|
|
1900
|
-
res.end('Invalid org name');
|
|
1901
|
-
return true;
|
|
1902
|
-
}
|
|
1903
|
-
const _goalsQs = new URL(req.url, 'http://localhost').searchParams;
|
|
1904
|
-
const _goalsRoot = path.resolve(_goalsQs.get('dir') || ctx.projectDir || process.cwd());
|
|
1905
|
-
const _goalsProjDir = ctx._resolveOrgProjectDir(orgName, _goalsRoot) || _goalsRoot;
|
|
1906
|
-
const goalsFile = path.join(_goalsProjDir, '.monomind', 'orgs', `${orgName}-goals.json`);
|
|
1907
|
-
let data = { goals: [] };
|
|
1908
|
-
try {
|
|
1909
|
-
data = JSON.parse(fs.readFileSync(goalsFile, 'utf8'));
|
|
1910
|
-
} catch (_) {}
|
|
1911
|
-
res.writeHead(200, {
|
|
1912
|
-
'Content-Type': 'application/json',
|
|
1913
|
-
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
1914
|
-
});
|
|
1915
|
-
res.end(JSON.stringify({ goals: data.goals || [] }));
|
|
1916
|
-
} catch (_) {
|
|
1917
|
-
res.writeHead(500);
|
|
1918
|
-
res.end('{"goals":[]}');
|
|
1919
|
-
}
|
|
1920
|
-
return true;
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
1527
|
// GET /api/org/:name/routines — read org routines (falls back to synthesizing from org config's loop object)
|
|
1924
1528
|
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/routines$/i)) {
|
|
1925
1529
|
try {
|