@monoes/monomindcli 2.16.11 → 2.16.13
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/agents/core/coder.md +1 -1
- package/.claude/agents/engineering/engineering-devops-automator.md +1 -1
- package/.claude/agents/github/repo-architect.md +1 -1
- package/.claude/agents/testing/production-validator.md +1 -1
- package/.claude/agents/testing/testing-tool-evaluator.md +1 -1
- package/.claude/commands/github/pr-manager.md +1 -1
- package/.claude/helpers/pick-rank.cjs +34 -4
- package/.claude/skills/monolean-audit/SKILL.md +1 -1
- package/dist/src/commands/cleanup-data.d.ts +30 -0
- package/dist/src/commands/cleanup-data.d.ts.map +1 -0
- package/dist/src/commands/cleanup-data.js +138 -0
- package/dist/src/commands/cleanup-data.js.map +1 -0
- package/dist/src/commands/cleanup-origin.d.ts +24 -0
- package/dist/src/commands/cleanup-origin.d.ts.map +1 -0
- package/dist/src/commands/cleanup-origin.js +67 -0
- package/dist/src/commands/cleanup-origin.js.map +1 -0
- package/dist/src/commands/cleanup-scratch.d.ts +43 -0
- package/dist/src/commands/cleanup-scratch.d.ts.map +1 -0
- package/dist/src/commands/cleanup-scratch.js +125 -0
- package/dist/src/commands/cleanup-scratch.js.map +1 -0
- package/dist/src/commands/cleanup.d.ts +2 -39
- package/dist/src/commands/cleanup.d.ts.map +1 -1
- package/dist/src/commands/cleanup.js +9 -296
- package/dist/src/commands/cleanup.js.map +1 -1
- package/dist/src/commands/doctor-gitignore-checks.d.ts +10 -0
- package/dist/src/commands/doctor-gitignore-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-gitignore-checks.js +141 -0
- package/dist/src/commands/doctor-gitignore-checks.js.map +1 -0
- package/dist/src/commands/doctor-helpers-checks.d.ts +10 -0
- package/dist/src/commands/doctor-helpers-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-helpers-checks.js +292 -0
- package/dist/src/commands/doctor-helpers-checks.js.map +1 -0
- package/dist/src/commands/doctor-memory-checks.d.ts +47 -0
- package/dist/src/commands/doctor-memory-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-memory-checks.js +298 -0
- package/dist/src/commands/doctor-memory-checks.js.map +1 -0
- package/dist/src/commands/doctor-monograph-checks.d.ts +9 -0
- package/dist/src/commands/doctor-monograph-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-monograph-checks.js +331 -0
- package/dist/src/commands/doctor-monograph-checks.js.map +1 -0
- package/dist/src/commands/doctor-project-checks.d.ts +7 -66
- package/dist/src/commands/doctor-project-checks.d.ts.map +1 -1
- package/dist/src/commands/doctor-project-checks.js +10 -1331
- package/dist/src/commands/doctor-project-checks.js.map +1 -1
- package/dist/src/commands/doctor-routing-checks.d.ts +10 -0
- package/dist/src/commands/doctor-routing-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-routing-checks.js +194 -0
- package/dist/src/commands/doctor-routing-checks.js.map +1 -0
- package/dist/src/commands/doctor-worker-checks.d.ts +17 -0
- package/dist/src/commands/doctor-worker-checks.d.ts.map +1 -0
- package/dist/src/commands/doctor-worker-checks.js +122 -0
- package/dist/src/commands/doctor-worker-checks.js.map +1 -0
- package/dist/src/commands/hooks-routing-commands.js +1 -1
- package/dist/src/commands/hooks-routing-commands.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +6 -15
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/init/never-commit.d.ts +3 -3
- package/dist/src/init/types.d.ts +1 -1
- package/dist/src/init/types.js +1 -1
- package/dist/src/knowledge/document-store.js +1 -1
- package/dist/src/knowledge/document-store.js.map +1 -1
- package/dist/src/mcp-tools/hooks-edit-command.d.ts +10 -0
- package/dist/src/mcp-tools/hooks-edit-command.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-edit-command.js +247 -0
- package/dist/src/mcp-tools/hooks-edit-command.js.map +1 -0
- package/dist/src/mcp-tools/hooks-learning.d.ts +9 -0
- package/dist/src/mcp-tools/hooks-learning.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-learning.js +408 -0
- package/dist/src/mcp-tools/hooks-learning.js.map +1 -0
- package/dist/src/mcp-tools/hooks-metrics-list.d.ts +8 -0
- package/dist/src/mcp-tools/hooks-metrics-list.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-metrics-list.js +205 -0
- package/dist/src/mcp-tools/hooks-metrics-list.js.map +1 -0
- package/dist/src/mcp-tools/hooks-route.d.ts +12 -0
- package/dist/src/mcp-tools/hooks-route.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-route.js +251 -0
- package/dist/src/mcp-tools/hooks-route.js.map +1 -0
- package/dist/src/mcp-tools/hooks-routing.d.ts +6 -27
- package/dist/src/mcp-tools/hooks-routing.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-routing.js +6 -1727
- package/dist/src/mcp-tools/hooks-routing.js.map +1 -1
- package/dist/src/mcp-tools/hooks-session.d.ts +9 -0
- package/dist/src/mcp-tools/hooks-session.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-session.js +243 -0
- package/dist/src/mcp-tools/hooks-session.js.map +1 -0
- package/dist/src/mcp-tools/hooks-task.d.ts +17 -0
- package/dist/src/mcp-tools/hooks-task.d.ts.map +1 -0
- package/dist/src/mcp-tools/hooks-task.js +427 -0
- package/dist/src/mcp-tools/hooks-task.js.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts +2 -1
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +2 -1
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/memory-tools.js +1 -1
- package/dist/src/mcp-tools/memory-tools.js.map +1 -1
- package/dist/src/memory/bm25-index.d.ts +2 -2
- package/dist/src/memory/bm25-index.js +2 -2
- package/dist/src/memory/embedding-operations.js +2 -2
- package/dist/src/memory/embedding-operations.js.map +1 -1
- package/dist/src/memory/entity-name-key.d.ts +1 -1
- package/dist/src/memory/entity-name-key.js +1 -1
- package/dist/src/memory/hnsw-operations.d.ts +1 -1
- package/dist/src/memory/hnsw-operations.js +1 -1
- package/dist/src/memory/memory-bridge-backend.d.ts +43 -0
- package/dist/src/memory/memory-bridge-backend.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-backend.js +363 -0
- package/dist/src/memory/memory-bridge-backend.js.map +1 -0
- package/dist/src/memory/memory-bridge-core.d.ts +42 -0
- package/dist/src/memory/memory-bridge-core.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-core.js +126 -0
- package/dist/src/memory/memory-bridge-core.js.map +1 -0
- package/dist/src/memory/memory-bridge-entries.d.ts +131 -0
- package/dist/src/memory/memory-bridge-entries.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-entries.js +227 -0
- package/dist/src/memory/memory-bridge-entries.js.map +1 -0
- package/dist/src/memory/memory-bridge-feedback.d.ts +60 -0
- package/dist/src/memory/memory-bridge-feedback.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-feedback.js +118 -0
- package/dist/src/memory/memory-bridge-feedback.js.map +1 -0
- package/dist/src/memory/memory-bridge-paths.d.ts +123 -0
- package/dist/src/memory/memory-bridge-paths.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-paths.js +335 -0
- package/dist/src/memory/memory-bridge-paths.js.map +1 -0
- package/dist/src/memory/memory-bridge-search.d.ts +48 -0
- package/dist/src/memory/memory-bridge-search.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-search.js +383 -0
- package/dist/src/memory/memory-bridge-search.js.map +1 -0
- package/dist/src/memory/memory-bridge-sessions.d.ts +38 -0
- package/dist/src/memory/memory-bridge-sessions.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-sessions.js +109 -0
- package/dist/src/memory/memory-bridge-sessions.js.map +1 -0
- package/dist/src/memory/memory-bridge-store.d.ts +58 -0
- package/dist/src/memory/memory-bridge-store.d.ts.map +1 -0
- package/dist/src/memory/memory-bridge-store.js +201 -0
- package/dist/src/memory/memory-bridge-store.js.map +1 -0
- package/dist/src/memory/memory-bridge.d.ts +10 -438
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +14 -1787
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/memory/memory-kg-claims.d.ts +52 -0
- package/dist/src/memory/memory-kg-claims.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-claims.js +99 -0
- package/dist/src/memory/memory-kg-claims.js.map +1 -0
- package/dist/src/memory/memory-kg-index.d.ts +86 -0
- package/dist/src/memory/memory-kg-index.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-index.js +256 -0
- package/dist/src/memory/memory-kg-index.js.map +1 -0
- package/dist/src/memory/memory-kg-ingest.d.ts +44 -0
- package/dist/src/memory/memory-kg-ingest.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-ingest.js +361 -0
- package/dist/src/memory/memory-kg-ingest.js.map +1 -0
- package/dist/src/memory/memory-kg-model.d.ts +168 -0
- package/dist/src/memory/memory-kg-model.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-model.js +158 -0
- package/dist/src/memory/memory-kg-model.js.map +1 -0
- package/dist/src/memory/memory-kg-names.d.ts +64 -0
- package/dist/src/memory/memory-kg-names.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-names.js +187 -0
- package/dist/src/memory/memory-kg-names.js.map +1 -0
- package/dist/src/memory/memory-kg-promote.d.ts +79 -0
- package/dist/src/memory/memory-kg-promote.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-promote.js +248 -0
- package/dist/src/memory/memory-kg-promote.js.map +1 -0
- package/dist/src/memory/memory-kg-rebuild.d.ts +77 -0
- package/dist/src/memory/memory-kg-rebuild.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-rebuild.js +299 -0
- package/dist/src/memory/memory-kg-rebuild.js.map +1 -0
- package/dist/src/memory/memory-kg-rollback.d.ts +47 -0
- package/dist/src/memory/memory-kg-rollback.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-rollback.js +193 -0
- package/dist/src/memory/memory-kg-rollback.js.map +1 -0
- package/dist/src/memory/memory-kg-rules.d.ts +64 -0
- package/dist/src/memory/memory-kg-rules.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-rules.js +251 -0
- package/dist/src/memory/memory-kg-rules.js.map +1 -0
- package/dist/src/memory/memory-kg-scan.d.ts +118 -0
- package/dist/src/memory/memory-kg-scan.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-scan.js +209 -0
- package/dist/src/memory/memory-kg-scan.js.map +1 -0
- package/dist/src/memory/memory-kg-search.d.ts +80 -0
- package/dist/src/memory/memory-kg-search.d.ts.map +1 -0
- package/dist/src/memory/memory-kg-search.js +239 -0
- package/dist/src/memory/memory-kg-search.js.map +1 -0
- package/dist/src/memory/memory-kg.d.ts +19 -547
- package/dist/src/memory/memory-kg.d.ts.map +1 -1
- package/dist/src/memory/memory-kg.js +10 -2389
- package/dist/src/memory/memory-kg.js.map +1 -1
- package/dist/src/orgrt/sandbox-stubs.d.ts +4 -0
- package/dist/src/orgrt/sandbox-stubs.d.ts.map +1 -1
- package/dist/src/orgrt/sandbox-stubs.js +12 -2
- package/dist/src/orgrt/sandbox-stubs.js.map +1 -1
- package/dist/src/ui/org-hil.mjs +1 -1
- package/dist/src/ui/routes-org-agents.mjs +318 -0
- package/dist/src/ui/routes-org-approvals.mjs +133 -0
- package/dist/src/ui/routes-org-config.mjs +459 -0
- package/dist/src/ui/routes-org-control.mjs +300 -0
- package/dist/src/ui/routes-org-files.mjs +172 -0
- package/dist/src/ui/routes-org-helpers.mjs +84 -0
- package/dist/src/ui/routes-org-knowledge.mjs +262 -0
- package/dist/src/ui/routes-org-lifecycle.mjs +224 -0
- package/dist/src/ui/routes-org-live.mjs +278 -0
- package/dist/src/ui/routes-org-mastermind.mjs +346 -0
- package/dist/src/ui/routes-org-planning.mjs +398 -0
- package/dist/src/ui/routes-org-runs.mjs +430 -0
- package/dist/src/ui/routes-org-status.mjs +342 -0
- package/dist/src/ui/routes-org-workflows.mjs +212 -0
- package/dist/src/ui/routes-org.mjs +26 -3847
- package/dist/src/ui/server.mjs +1 -1
- package/dist/src/utils/native-error.d.ts +1 -1
- package/dist/src/utils/native-error.js +1 -1
- package/dist/src/utils/paths.d.ts +2 -1
- package/dist/src/utils/paths.d.ts.map +1 -1
- package/dist/src/utils/paths.js +2 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/org-skills/adversarial-reviewer/SKILL.md +1 -1
- package/org-skills/board-deck-builder/SKILL.md +1 -1
- package/org-skills/code-reviewer/SKILL.md +1 -1
- package/org-skills/debugging-wizard/SKILL.md +1 -1
- package/org-skills/scientific-thinking-literature-review/SKILL.md +1 -1
- package/org-skills/scientific-thinking-scholar-evaluation/SKILL.md +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
// Org dashboard routes: adapters, agent detail and agent avatar.
|
|
5
|
+
// Registered, in order, by handleOrgRoutes in routes-org.mjs.
|
|
6
|
+
export async function handleOrgAgentRoutes(req, res, url, corsOrigin, ctx) {
|
|
7
|
+
// GET /api/org/:name/adapters — org adapter registry
|
|
8
|
+
if (req.method === 'GET' && /^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/adapters$/i.test(url)) {
|
|
9
|
+
try {
|
|
10
|
+
const parts = url.split('/');
|
|
11
|
+
const orgName = decodeURIComponent(parts[3]);
|
|
12
|
+
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
13
|
+
res.writeHead(400);
|
|
14
|
+
res.end('{}');
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
const _adaptersQs = new URL(req.url, 'http://localhost').searchParams;
|
|
18
|
+
const _adaptersRoot = path.resolve(_adaptersQs.get('dir') || ctx.projectDir || process.cwd());
|
|
19
|
+
const d = ctx._resolveOrgProjectDir(orgName, _adaptersRoot) || _adaptersRoot;
|
|
20
|
+
const adaptersFile = path.join(d, '.monomind', 'orgs', `${orgName}-adapters.json`);
|
|
21
|
+
if (!fs.existsSync(adaptersFile)) {
|
|
22
|
+
// Return defaults derived from org config if available
|
|
23
|
+
const orgFile = path.join(d, '.monomind', 'orgs', `${orgName}.json`);
|
|
24
|
+
// claude-sonnet-5 = DEFAULT_CLAUDE_MODEL (src/orgrt/vercel-providers.ts); .mjs can't import TS.
|
|
25
|
+
let defaultAdapter = 'claude-sonnet-5';
|
|
26
|
+
try {
|
|
27
|
+
defaultAdapter =
|
|
28
|
+
JSON.parse(fs.readFileSync(orgFile, 'utf8'))?.run_config?.ceo_adapter || defaultAdapter;
|
|
29
|
+
} catch (_) {}
|
|
30
|
+
res.writeHead(200, {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
33
|
+
});
|
|
34
|
+
// This list mirrors the runtimes orgrt/daemon.ts actually registers
|
|
35
|
+
// (resolveRunner()'s if-chain + the ClaudeAgentRunner default it falls
|
|
36
|
+
// through to) rather than an independent, hand-maintained catalog — a
|
|
37
|
+
// standalone 'gemini-local' entry used to be advertised here even
|
|
38
|
+
// though no runner is ever registered for it (autoRuntimeFromProvider
|
|
39
|
+
// has no 'gemini' case, so a role requesting it silently falls back to
|
|
40
|
+
// Claude — see daemon.ts#startOrg's fail-fast warning). Gemini is only
|
|
41
|
+
// actually reachable via the 'vercel' runtime (vendor: 'google') or via
|
|
42
|
+
// 'antigravity' (Google-account CLI), both listed below. 'http' maps to
|
|
43
|
+
// provider.kind === 'base-url' (ANTHROPIC_BASE_URL + optional auth
|
|
44
|
+
// token) — a real, usable path, not disabled.
|
|
45
|
+
res.end(
|
|
46
|
+
JSON.stringify({
|
|
47
|
+
default_adapter: defaultAdapter,
|
|
48
|
+
adapters: [
|
|
49
|
+
{
|
|
50
|
+
type: 'claude-local',
|
|
51
|
+
label: 'Claude (local CLI)',
|
|
52
|
+
source: 'built-in',
|
|
53
|
+
disabled: false,
|
|
54
|
+
modelsCount: 3,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'codex-local',
|
|
58
|
+
label: 'Codex CLI',
|
|
59
|
+
source: 'built-in',
|
|
60
|
+
disabled: false,
|
|
61
|
+
modelsCount: 1,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
type: 'antigravity',
|
|
65
|
+
label: 'Antigravity (Google CLI)',
|
|
66
|
+
source: 'built-in',
|
|
67
|
+
disabled: false,
|
|
68
|
+
modelsCount: 1,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: 'grok',
|
|
72
|
+
label: 'Grok CLI',
|
|
73
|
+
source: 'built-in',
|
|
74
|
+
disabled: false,
|
|
75
|
+
modelsCount: 1,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'qwen',
|
|
79
|
+
label: 'Qwen CLI',
|
|
80
|
+
source: 'built-in',
|
|
81
|
+
disabled: false,
|
|
82
|
+
modelsCount: 1,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: 'crush',
|
|
86
|
+
label: 'Crush CLI',
|
|
87
|
+
source: 'built-in',
|
|
88
|
+
disabled: false,
|
|
89
|
+
modelsCount: 1,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: 'copilot',
|
|
93
|
+
label: 'GitHub Copilot CLI',
|
|
94
|
+
source: 'built-in',
|
|
95
|
+
disabled: false,
|
|
96
|
+
modelsCount: 1,
|
|
97
|
+
},
|
|
98
|
+
{ type: 'pi', label: 'Pi CLI', source: 'built-in', disabled: false, modelsCount: 1 },
|
|
99
|
+
{
|
|
100
|
+
type: 'opencode',
|
|
101
|
+
label: 'OpenCode CLI',
|
|
102
|
+
source: 'built-in',
|
|
103
|
+
disabled: false,
|
|
104
|
+
modelsCount: 1,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
type: 'kimicode',
|
|
108
|
+
label: 'KimiCode CLI',
|
|
109
|
+
source: 'built-in',
|
|
110
|
+
disabled: false,
|
|
111
|
+
modelsCount: 1,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: 'vercel',
|
|
115
|
+
label: 'Vercel AI SDK (multi-vendor, incl. Gemini/OpenAI)',
|
|
116
|
+
source: 'built-in',
|
|
117
|
+
disabled: false,
|
|
118
|
+
modelsCount: 14,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
type: 'http',
|
|
122
|
+
label: 'Custom HTTP (base-url provider)',
|
|
123
|
+
source: 'built-in',
|
|
124
|
+
disabled: false,
|
|
125
|
+
modelsCount: 0,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
const data = JSON.parse(fs.readFileSync(adaptersFile, 'utf8'));
|
|
133
|
+
res.writeHead(200, {
|
|
134
|
+
'Content-Type': 'application/json',
|
|
135
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
136
|
+
});
|
|
137
|
+
res.end(JSON.stringify(data));
|
|
138
|
+
} catch (_) {
|
|
139
|
+
res.writeHead(500);
|
|
140
|
+
res.end('{}');
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// GET /api/org/:name/agent/:roleId — full agent detail: org role + .claude/agents definition
|
|
146
|
+
// (characteristics, responsibilities, instructions document)
|
|
147
|
+
if (
|
|
148
|
+
req.method === 'GET' &&
|
|
149
|
+
/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/agent\/[a-z0-9][a-z0-9_-]{0,63}$/i.test(url)
|
|
150
|
+
) {
|
|
151
|
+
try {
|
|
152
|
+
const parts = url.split('/');
|
|
153
|
+
const orgName = decodeURIComponent(parts[3]);
|
|
154
|
+
const roleId = decodeURIComponent(parts[5]);
|
|
155
|
+
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
156
|
+
res.writeHead(400);
|
|
157
|
+
res.end('{}');
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
if (roleId.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(roleId)) {
|
|
161
|
+
res.writeHead(400);
|
|
162
|
+
res.end('{}');
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
const _agentQs = new URL(req.url, 'http://localhost').searchParams;
|
|
166
|
+
const d = path.resolve(_agentQs.get('dir') || ctx.projectDir || process.cwd());
|
|
167
|
+
const orgFile = path.join(d, '.monomind', 'orgs', `${orgName}.json`);
|
|
168
|
+
if (!fs.existsSync(orgFile)) {
|
|
169
|
+
res.writeHead(404);
|
|
170
|
+
res.end('{}');
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
const config = JSON.parse(fs.readFileSync(orgFile, 'utf8'));
|
|
174
|
+
const role = (config.roles || []).find((r) => r.id === roleId);
|
|
175
|
+
if (!role) {
|
|
176
|
+
res.writeHead(404);
|
|
177
|
+
res.end('{}');
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const agentType = String(role.agent_type || role.type || '').toLowerCase();
|
|
182
|
+
const wanted = [agentType, String(role.id).toLowerCase()].filter(Boolean);
|
|
183
|
+
|
|
184
|
+
// Find a matching agent definition under .claude/agents (recursive); match frontmatter name then filename.
|
|
185
|
+
const agentsDir = path.join(d, '.claude', 'agents');
|
|
186
|
+
let definition = { found: false };
|
|
187
|
+
if (wanted.length && fs.existsSync(agentsDir)) {
|
|
188
|
+
const stack = [agentsDir];
|
|
189
|
+
let nameMatch = null,
|
|
190
|
+
slugMatch = null;
|
|
191
|
+
while (stack.length && !nameMatch) {
|
|
192
|
+
const dir = stack.pop();
|
|
193
|
+
let entries = [];
|
|
194
|
+
try {
|
|
195
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
196
|
+
} catch (_) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
for (const e of entries) {
|
|
200
|
+
const full = path.join(dir, e.name);
|
|
201
|
+
if (e.isDirectory()) {
|
|
202
|
+
stack.push(full);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (!e.name.endsWith('.md') || e.name.startsWith('_')) continue;
|
|
206
|
+
const slug = e.name.replace(/\.md$/, '').toLowerCase();
|
|
207
|
+
let raw = '';
|
|
208
|
+
try {
|
|
209
|
+
raw = fs.readFileSync(full, 'utf8');
|
|
210
|
+
} catch (_) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const fmName = ((raw.match(/^name:\s*(.+)$/m) || [])[1] || '').trim().toLowerCase();
|
|
214
|
+
if (fmName && wanted.includes(fmName)) {
|
|
215
|
+
nameMatch = { full, raw };
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
if (!slugMatch && wanted.includes(slug)) slugMatch = { full, raw };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const match = nameMatch || slugMatch;
|
|
222
|
+
if (match) {
|
|
223
|
+
definition = ctx.parseAgentDef(match.raw);
|
|
224
|
+
definition.found = true;
|
|
225
|
+
definition.file = path.relative(d, match.full);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
res.writeHead(200, {
|
|
230
|
+
'Content-Type': 'application/json',
|
|
231
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
232
|
+
'Cache-Control': 'no-cache',
|
|
233
|
+
});
|
|
234
|
+
res.end(JSON.stringify({ role, definition }));
|
|
235
|
+
} catch (_) {
|
|
236
|
+
res.writeHead(500);
|
|
237
|
+
res.end('{}');
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// POST /api/org/:name/agent/:roleId/avatar — set (or clear) a custom avatar for one
|
|
243
|
+
// role, stored inline as a data URL on the role itself so it travels with the org
|
|
244
|
+
// config export/import. Body: { avatarDataUrl } — null/omitted avatarDataUrl clears
|
|
245
|
+
// the custom avatar and reverts to the built-in library picture. `dir` (like every
|
|
246
|
+
// other dir-accepting route in this file, POST included — see /goals above) comes
|
|
247
|
+
// from the query string, not the body.
|
|
248
|
+
if (
|
|
249
|
+
req.method === 'POST' &&
|
|
250
|
+
/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/agent\/[a-z0-9][a-z0-9_-]{0,63}\/avatar$/i.test(url)
|
|
251
|
+
) {
|
|
252
|
+
let body = '';
|
|
253
|
+
for await (const chunk of req) {
|
|
254
|
+
body += chunk;
|
|
255
|
+
if (body.length > 8388608) {
|
|
256
|
+
req.destroy();
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
const parts = url.split('/');
|
|
262
|
+
const orgName = decodeURIComponent(parts[3]);
|
|
263
|
+
const roleId = decodeURIComponent(parts[5]);
|
|
264
|
+
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) {
|
|
265
|
+
res.writeHead(400);
|
|
266
|
+
res.end('{"ok":false,"error":"Invalid org name"}');
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
if (roleId.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(roleId)) {
|
|
270
|
+
res.writeHead(400);
|
|
271
|
+
res.end('{"ok":false,"error":"Invalid role id"}');
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
const parsed = JSON.parse(body);
|
|
275
|
+
const avatarDataUrl = parsed.avatarDataUrl;
|
|
276
|
+
if (
|
|
277
|
+
avatarDataUrl != null &&
|
|
278
|
+
(typeof avatarDataUrl !== 'string' ||
|
|
279
|
+
avatarDataUrl.length > 2_000_000 ||
|
|
280
|
+
!/^data:image\/(png|jpe?g|webp|gif);base64,[A-Za-z0-9+/=]+$/.test(avatarDataUrl))
|
|
281
|
+
) {
|
|
282
|
+
res.writeHead(400);
|
|
283
|
+
res.end('{"ok":false,"error":"Invalid or oversized avatarDataUrl"}');
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
const _avatarQs = new URL(req.url, 'http://localhost').searchParams;
|
|
287
|
+
const d = path.resolve(_avatarQs.get('dir') || ctx.projectDir || process.cwd());
|
|
288
|
+
const orgFile = path.join(d, '.monomind', 'orgs', `${orgName}.json`);
|
|
289
|
+
if (!fs.existsSync(orgFile)) {
|
|
290
|
+
res.writeHead(404);
|
|
291
|
+
res.end('{"ok":false,"error":"org not found"}');
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
const config = JSON.parse(fs.readFileSync(orgFile, 'utf8'));
|
|
295
|
+
const role = (config.roles || []).find((r) => r.id === roleId);
|
|
296
|
+
if (!role) {
|
|
297
|
+
res.writeHead(404);
|
|
298
|
+
res.end('{"ok":false,"error":"role not found"}');
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
if (avatarDataUrl) role.avatar = avatarDataUrl;
|
|
302
|
+
else delete role.avatar;
|
|
303
|
+
const tmp = `${orgFile}.tmp`;
|
|
304
|
+
fs.writeFileSync(tmp, JSON.stringify(config, null, 2), 'utf-8');
|
|
305
|
+
fs.renameSync(tmp, orgFile);
|
|
306
|
+
res.writeHead(200, {
|
|
307
|
+
'Content-Type': 'application/json',
|
|
308
|
+
...(corsOrigin ? { 'Access-Control-Allow-Origin': corsOrigin } : {}),
|
|
309
|
+
});
|
|
310
|
+
res.end(JSON.stringify({ ok: true }));
|
|
311
|
+
} catch (e) {
|
|
312
|
+
res.writeHead(500);
|
|
313
|
+
res.end(JSON.stringify({ ok: false, error: String(e.message || e) }));
|
|
314
|
+
}
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import * as hil from './org-hil.mjs';
|
|
3
|
+
import {
|
|
4
|
+
decodeHilSegment,
|
|
5
|
+
hilEvent,
|
|
6
|
+
hilRespond,
|
|
7
|
+
hilRoot,
|
|
8
|
+
readHilBody,
|
|
9
|
+
validOrgName,
|
|
10
|
+
} from './routes-org-helpers.mjs';
|
|
11
|
+
|
|
12
|
+
// Org dashboard routes: human-in-the-loop approvals, gates and questions.
|
|
13
|
+
// Registered, in order, by handleOrgRoutes in routes-org.mjs.
|
|
14
|
+
export async function handleOrgApprovalRoutes(req, res, url, corsOrigin, ctx) {
|
|
15
|
+
// GET /api/org/:name/approvals — the org's tool/action approvals
|
|
16
|
+
// (<org>/approvals.json, written by orgrt/approvals.ts), newest first.
|
|
17
|
+
if (
|
|
18
|
+
req.method === 'GET' &&
|
|
19
|
+
url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/approvals(\?.*)?$/i)
|
|
20
|
+
) {
|
|
21
|
+
const orgName = decodeURIComponent(url.split('/')[3].split('?')[0]);
|
|
22
|
+
return hilRespond(res, corsOrigin, () => {
|
|
23
|
+
const approvals = hil.listApprovals(hilRoot(req, ctx), orgName);
|
|
24
|
+
return { approvals, pending: approvals.filter((a) => a.status === 'pending').length };
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// GET /api/org/:name/gates — the org's decision gates (<org>/gates.json).
|
|
29
|
+
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/gates(\?.*)?$/i)) {
|
|
30
|
+
const orgName = decodeURIComponent(url.split('/')[3].split('?')[0]);
|
|
31
|
+
return hilRespond(res, corsOrigin, () => {
|
|
32
|
+
const gates = hil.listGates(hilRoot(req, ctx), orgName);
|
|
33
|
+
return { gates, pending: gates.filter((g) => g.status === 'pending').length };
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// GET /api/questions?dir= — ask_human questions (pending and answered) for
|
|
38
|
+
// every org in one project, each with its `blocking` flag.
|
|
39
|
+
if (req.method === 'GET' && (url === '/api/questions' || url.startsWith('/api/questions?'))) {
|
|
40
|
+
return hilRespond(res, corsOrigin, () => ({
|
|
41
|
+
questions: hil.pendingForProject(hilRoot(req, ctx)).questions,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// GET /api/hil?dir= — everything in one project that waits on a human:
|
|
46
|
+
// questions, approvals and decision gates across all its orgs.
|
|
47
|
+
if (req.method === 'GET' && (url === '/api/hil' || url.startsWith('/api/hil?'))) {
|
|
48
|
+
return hilRespond(res, corsOrigin, () => hil.pendingForProject(hilRoot(req, ctx)));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// POST /api/questions/answer { dir, org, questionId, answer } — live to the
|
|
52
|
+
// hosting daemon (operator credential), else queued for the org's next run.
|
|
53
|
+
if (req.method === 'POST' && url === '/api/questions/answer') {
|
|
54
|
+
return hilRespond(res, corsOrigin, async () => {
|
|
55
|
+
const body = await readHilBody(req);
|
|
56
|
+
const { org, questionId, answer } = body || {};
|
|
57
|
+
if (!validOrgName(org) || !questionId || typeof answer !== 'string' || !answer.trim())
|
|
58
|
+
throw Object.assign(new Error('org, questionId and a non-empty answer are required'), {
|
|
59
|
+
status: 400,
|
|
60
|
+
});
|
|
61
|
+
const root = path.resolve(body.dir || ctx.projectDir || process.cwd());
|
|
62
|
+
const r = await hil.answerQuestion(root, org, String(questionId), answer.trim());
|
|
63
|
+
hilEvent(ctx, root, {
|
|
64
|
+
type: 'org:question-answered',
|
|
65
|
+
org,
|
|
66
|
+
role: r.role,
|
|
67
|
+
questionId,
|
|
68
|
+
delivery: r.delivery,
|
|
69
|
+
});
|
|
70
|
+
return { ok: true, ...r };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// POST /api/org/:name/gates/:id { approved: boolean, resolution? } — resolve
|
|
75
|
+
// a decision gate, live or (org not running) in gates.json.
|
|
76
|
+
if (
|
|
77
|
+
req.method === 'POST' &&
|
|
78
|
+
url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/gates\/[^/?]+(\?.*)?$/i)
|
|
79
|
+
) {
|
|
80
|
+
const parts = url.split('?')[0].split('/');
|
|
81
|
+
const orgName = decodeURIComponent(parts[3]);
|
|
82
|
+
const root = hilRoot(req, ctx);
|
|
83
|
+
return hilRespond(res, corsOrigin, async () => {
|
|
84
|
+
const gateId = decodeHilSegment(parts[5]);
|
|
85
|
+
const body = await readHilBody(req);
|
|
86
|
+
const resolution =
|
|
87
|
+
typeof body?.resolution === 'string' && body.resolution.trim()
|
|
88
|
+
? body.resolution.trim().slice(0, 4000)
|
|
89
|
+
: undefined;
|
|
90
|
+
if (typeof body?.approved !== 'boolean')
|
|
91
|
+
throw Object.assign(new Error('approved (boolean) is required'), { status: 400 });
|
|
92
|
+
const r = await hil.resolveGate(root, orgName, gateId, body.approved, resolution);
|
|
93
|
+
const status = body.approved ? 'approved' : 'rejected';
|
|
94
|
+
hilEvent(ctx, root, {
|
|
95
|
+
type: 'org:gate:resolved',
|
|
96
|
+
org: orgName,
|
|
97
|
+
gateId,
|
|
98
|
+
status,
|
|
99
|
+
delivery: r.delivery,
|
|
100
|
+
});
|
|
101
|
+
return { ok: true, status, ...r };
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// POST /api/org/:name/approvals/:requestId { action: "approve" | "reject" } —
|
|
106
|
+
// resolve one tool/action approval, live or (org not running) in approvals.json.
|
|
107
|
+
if (
|
|
108
|
+
req.method === 'POST' &&
|
|
109
|
+
url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/approvals\/[^/?]+(\?.*)?$/i)
|
|
110
|
+
) {
|
|
111
|
+
const parts = url.split('?')[0].split('/');
|
|
112
|
+
const orgName = decodeURIComponent(parts[3]);
|
|
113
|
+
const root = hilRoot(req, ctx);
|
|
114
|
+
return hilRespond(res, corsOrigin, async () => {
|
|
115
|
+
const requestId = decodeHilSegment(parts[5]);
|
|
116
|
+
const body = await readHilBody(req);
|
|
117
|
+
if (body?.action !== 'approve' && body?.action !== 'reject')
|
|
118
|
+
throw Object.assign(new Error('action must be approve or reject'), { status: 400 });
|
|
119
|
+
const approved = body.action === 'approve';
|
|
120
|
+
const r = await hil.resolveApproval(root, orgName, requestId, approved);
|
|
121
|
+
const status = approved ? 'approved' : 'denied';
|
|
122
|
+
hilEvent(ctx, root, {
|
|
123
|
+
type: 'org:approval:resolved',
|
|
124
|
+
org: orgName,
|
|
125
|
+
requestId,
|
|
126
|
+
status,
|
|
127
|
+
delivery: r.delivery,
|
|
128
|
+
});
|
|
129
|
+
return { ok: true, status, ...r };
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|