@monoes/monomindcli 2.14.1 → 2.15.0
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/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 +90 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
|
@@ -421,12 +421,9 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
421
421
|
.oad-doc strong { color: var(--text-hi); font-weight: 600; }
|
|
422
422
|
.oad-doc pre { background: var(--surface-hi); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; overflow-x: auto; margin: 0 0 10px; }
|
|
423
423
|
.oad-doc pre code { background: none; padding: 0; font-size: 11.5px; color: var(--text-mid); }
|
|
424
|
-
/* clickable org chart nodes
|
|
424
|
+
/* clickable org chart nodes */
|
|
425
425
|
.v2oc-node { cursor: pointer; }
|
|
426
426
|
.v2oc-node.selected > circle { stroke-width: 3.4; }
|
|
427
|
-
.role-v2 { cursor: pointer; transition: border-color 0.12s, transform 0.12s; }
|
|
428
|
-
.role-v2:hover { transform: translateY(-1px); }
|
|
429
|
-
.rv2-more { margin-top: 9px; font-size: 10.5px; color: var(--accent); opacity: 0.85; }
|
|
430
427
|
|
|
431
428
|
/* org meta strip (cells reused inside #org-meta-modal) */
|
|
432
429
|
.oms-cell { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 10px 12px; }
|
|
@@ -513,20 +510,6 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
513
510
|
.org-chart-legend { display: flex; gap: 14px; flex-wrap: wrap; padding: 0 2px; }
|
|
514
511
|
.ocl-item { display: flex; align-items: center; gap: 5px; font-size: 10px; color: var(--text-lo); }
|
|
515
512
|
|
|
516
|
-
/* roles in v2 */
|
|
517
|
-
.roles-v2-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; }
|
|
518
|
-
.role-v2 { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 12px 14px; }
|
|
519
|
-
.rv2-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 6px; }
|
|
520
|
-
.rv2-avatar { width: 48px; height: 48px; border-radius: 50%; flex-shrink: 0; object-fit: cover; }
|
|
521
|
-
.rv2-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; padding-top: 2px; }
|
|
522
|
-
.rv2-title { font-size: 13px; font-weight: 500; color: var(--text-hi); line-height: 1.3; }
|
|
523
|
-
.rv2-agent { font-size: 10px; padding: 1px 7px; border-radius: 8px; background: var(--accent-dim); color: var(--accent); white-space: nowrap; align-self: flex-start; }
|
|
524
|
-
.rv2-id { font-size: 10px; color: var(--text-lo); font-family: var(--mono); margin-bottom: 4px; }
|
|
525
|
-
.rv2-reports { font-size: 11px; color: var(--text-lo); margin-bottom: 6px; }
|
|
526
|
-
.rv2-reports span { color: var(--text-mid); }
|
|
527
|
-
.rv2-resps { }
|
|
528
|
-
.rv2-resp { font-size: 11px; color: var(--text-lo); line-height: 1.6; display: flex; gap: 6px; }
|
|
529
|
-
.rv2-resp::before { content: '–'; opacity: 0.4; flex-shrink: 0; }
|
|
530
513
|
/* role avatars in org list */
|
|
531
514
|
.oi-avatars { display: flex; margin-top: 5px; }
|
|
532
515
|
.oi-av { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; margin-right: -5px; border: 1.5px solid var(--bg); }
|
|
@@ -542,15 +525,6 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
542
525
|
.av2-type { color: var(--accent); font-weight: 500; }
|
|
543
526
|
.av2-msg { color: var(--text-lo); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
544
527
|
|
|
545
|
-
/* health in v2 */
|
|
546
|
-
.health-v2-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 8px; margin-bottom: 12px; }
|
|
547
|
-
.hv2-cell { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 10px 12px; }
|
|
548
|
-
.hv2-lbl { font-size: 10px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-lo); margin-bottom: 4px; }
|
|
549
|
-
.hv2-val { font-size: 20px; font-weight: 700; color: var(--text-hi); font-family: var(--mono); }
|
|
550
|
-
.hv2-val.green { color: var(--green); }
|
|
551
|
-
.hv2-val.amber { color: var(--accent); }
|
|
552
|
-
.hv2-val.red { color: var(--red); }
|
|
553
|
-
|
|
554
528
|
/* ── alerts rail ─────────────────────────────────────────── */
|
|
555
529
|
#alerts-rail { display: none; }
|
|
556
530
|
#alerts-rail.has-alerts { display: none; }
|
|
@@ -2804,7 +2778,6 @@ function switchProject(path) {
|
|
|
2804
2778
|
_v2SelOrg = null;
|
|
2805
2779
|
_v2OrgData = null;
|
|
2806
2780
|
_v2Orgs = [];
|
|
2807
|
-
Object.keys(_v2OrgEventLog).forEach(k => delete _v2OrgEventLog[k]);
|
|
2808
2781
|
document.getElementById('org-detail-pane')?.classList.add('empty-state');
|
|
2809
2782
|
document.getElementById('org-detail-inner') && (document.getElementById('org-detail-inner').style.display = 'none');
|
|
2810
2783
|
const _noSel = document.querySelector('#org-detail-pane .orgs-no-sel');
|
|
@@ -4498,7 +4471,15 @@ async function renderHumanInputView() {
|
|
|
4498
4471
|
let _hiReloadTimer = null;
|
|
4499
4472
|
function _hiScheduleReload() {
|
|
4500
4473
|
clearTimeout(_hiReloadTimer);
|
|
4501
|
-
_hiReloadTimer = setTimeout(
|
|
4474
|
+
_hiReloadTimer = setTimeout(() => {
|
|
4475
|
+
// Drafts survive the rebuild (renderHiCurrentTab); still wait while one is
|
|
4476
|
+
// being typed, or while its row has focus (a press on its Answer button),
|
|
4477
|
+
// so the row is not replaced under the cursor or the click.
|
|
4478
|
+
const typing = [...document.querySelectorAll('#hi-content .hi-answer-input')]
|
|
4479
|
+
.some(i => i.value.trim() && i.closest('.sess-row')?.contains(document.activeElement));
|
|
4480
|
+
if (typing) _hiScheduleReload();
|
|
4481
|
+
else renderHumanInputView();
|
|
4482
|
+
}, 400);
|
|
4502
4483
|
}
|
|
4503
4484
|
|
|
4504
4485
|
function _hiSwitchTab(tab) {
|
|
@@ -4581,7 +4562,12 @@ function renderHiCurrentTab(projectCount, errors) {
|
|
|
4581
4562
|
el.innerHTML = `${errHtml}<div class="empty"><div class="empty-ico">❓</div><div>${_hiTab === 'answered' ? 'Nothing resolved yet' : 'Nothing waiting on you'}</div></div>`;
|
|
4582
4563
|
return;
|
|
4583
4564
|
}
|
|
4565
|
+
// Rebuilding the list must not lose an answer or note typed into a row.
|
|
4566
|
+
const draftKey = i => { const b = i.closest('.sess-row')?.querySelector('[data-id]'); return b ? [b.dataset.kind, b.dataset.dir, b.dataset.org, b.dataset.id].join('\n') : null; };
|
|
4567
|
+
const drafts = new Map();
|
|
4568
|
+
el.querySelectorAll('.hi-answer-input').forEach(i => { const k = draftKey(i); if (k && i.value) drafts.set(k, i.value); });
|
|
4584
4569
|
el.innerHTML = `${errHtml}<div class="sess-list">${entries.map(_hiEntryHtml).join('')}</div>`;
|
|
4570
|
+
if (drafts.size) el.querySelectorAll('.hi-answer-input').forEach(i => { const k = draftKey(i); if (k && drafts.has(k)) i.value = drafts.get(k); });
|
|
4585
4571
|
}
|
|
4586
4572
|
|
|
4587
4573
|
function updateHumanInputBadge(count) {
|
|
@@ -5805,7 +5791,6 @@ let _v2Orgs = [];
|
|
|
5805
5791
|
let _v2SelOrg = null;
|
|
5806
5792
|
let _v2OrgData = null;
|
|
5807
5793
|
let _v2OrgTab = 'chart';
|
|
5808
|
-
const _v2OrgEventLog = {};
|
|
5809
5794
|
let _v2ChartLayout = { pos: {}, edgeIdByPair: new Map() };
|
|
5810
5795
|
|
|
5811
5796
|
function _orgActivityMs(o) {
|
|
@@ -5880,7 +5865,6 @@ async function v2SelectOrg(name) {
|
|
|
5880
5865
|
_v2SelOrg = name;
|
|
5881
5866
|
_pushHash();
|
|
5882
5867
|
if (typeof v2CloseAgent === 'function') v2CloseAgent();
|
|
5883
|
-
if (_orgLiveInterval) { clearInterval(_orgLiveInterval); _orgLiveInterval = null; }
|
|
5884
5868
|
const requested = name;
|
|
5885
5869
|
const requestedDir = DIR;
|
|
5886
5870
|
document.querySelectorAll('.org-item').forEach(el => {
|
|
@@ -5905,30 +5889,17 @@ async function v2SelectOrg(name) {
|
|
|
5905
5889
|
_v2OrgData = null;
|
|
5906
5890
|
const _enc = encodeURIComponent(name);
|
|
5907
5891
|
try {
|
|
5908
|
-
const
|
|
5909
|
-
apiFetch(`/api/org/${_enc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`),
|
|
5910
|
-
fetch(`/api/org/${_enc}/activity${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]),
|
|
5911
|
-
fetch(`/api/org/${_enc}/health${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():null).catch(()=>null),
|
|
5912
|
-
]);
|
|
5892
|
+
const mainR = await apiFetch(`/api/org/${_enc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`);
|
|
5913
5893
|
if (_v2SelOrg !== requested || DIR !== requestedDir) return;
|
|
5914
5894
|
// API returns {config, state, goals, ...} — flatten config; config wins for schema fields
|
|
5915
5895
|
_v2OrgData = mainR.config ? { ...mainR, ...mainR.config, running: mainR.running } : mainR;
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
// Fetch supplemental data for new tabs in parallel
|
|
5919
|
-
const [agentsR, membersR, issuesR] = await Promise.all([
|
|
5920
|
-
fetch(`/api/org/${_enc}/agents${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]),
|
|
5921
|
-
fetch(`/api/org/${_enc}/members${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]),
|
|
5922
|
-
fetch(`/api/org/${_enc}/issues${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]),
|
|
5923
|
-
]);
|
|
5896
|
+
// The chart's running dots read the agent roster
|
|
5897
|
+
const agentsR = await fetch(`/api/org/${_enc}/agents${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
|
|
5924
5898
|
if (_v2SelOrg !== requested || DIR !== requestedDir) return;
|
|
5925
5899
|
_v2OrgData._agents = Array.isArray(agentsR) ? agentsR : (agentsR?.agents || []);
|
|
5926
|
-
_v2OrgData._members = Array.isArray(membersR) ? membersR : (membersR?.members || []);
|
|
5927
|
-
_v2OrgData._joinRequests = (membersR?.join_requests) || [];
|
|
5928
|
-
_v2OrgData._issues = Array.isArray(issuesR) ? issuesR : (issuesR?.issues || []);
|
|
5929
5900
|
} catch (_) {
|
|
5930
5901
|
if (_v2SelOrg !== requested || DIR !== requestedDir) return;
|
|
5931
|
-
_v2OrgData = { ...listOrg, communication: [],
|
|
5902
|
+
_v2OrgData = { ...listOrg, communication: [], _agents: [] };
|
|
5932
5903
|
}
|
|
5933
5904
|
|
|
5934
5905
|
v2UpdateOrgHeader(listOrg, _v2OrgData);
|
|
@@ -5963,11 +5934,6 @@ window.v2MarkOrgComplete = async () => {
|
|
|
5963
5934
|
};
|
|
5964
5935
|
|
|
5965
5936
|
window.v2SwitchOrgTab = (tab) => {
|
|
5966
|
-
// Clear live interval when switching away from live tab
|
|
5967
|
-
if (tab !== 'live' && _orgLiveInterval) {
|
|
5968
|
-
clearInterval(_orgLiveInterval);
|
|
5969
|
-
_orgLiveInterval = null;
|
|
5970
|
-
}
|
|
5971
5937
|
_v2OrgTab = tab;
|
|
5972
5938
|
document.querySelectorAll('#org-detail-tabs .odt-btn').forEach(b => {
|
|
5973
5939
|
b.classList.toggle('active', b.dataset.tab === tab);
|
|
@@ -5986,30 +5952,8 @@ function v2RenderOrgTab(tab) {
|
|
|
5986
5952
|
}
|
|
5987
5953
|
if (tab === 'chart') v2RenderOrgChart();
|
|
5988
5954
|
else if (tab === 'runtime') v2RenderOrgRuntime();
|
|
5989
|
-
else if (tab === 'roles') v2RenderOrgRoles();
|
|
5990
|
-
else if (tab === 'health') v2RenderOrgHealth();
|
|
5991
|
-
else if (tab === 'heartbeats') v2RenderOrgHeartbeats();
|
|
5992
|
-
else if (tab === 'tasks') v2RenderOrgTasks();
|
|
5993
|
-
else if (tab === 'members') v2RenderOrgMembers();
|
|
5994
|
-
else if (tab === 'goals') v2RenderOrgGoals();
|
|
5995
|
-
else if (tab === 'board') v2RenderOrgBoard();
|
|
5996
|
-
else if (tab === 'live') v2RenderOrgLive();
|
|
5997
5955
|
else if (tab === 'approvals') v2RenderOrgApprovals();
|
|
5998
|
-
else if (tab === 'secrets') v2RenderOrgSecrets();
|
|
5999
|
-
else if (tab === 'settings') v2RenderOrgSettings();
|
|
6000
|
-
else if (tab === 'routines') v2RenderOrgRoutines();
|
|
6001
|
-
else if (tab === 'myissues') v2RenderOrgMyIssues();
|
|
6002
5956
|
else if (tab === 'decisions') v2RenderOrgDecisions();
|
|
6003
|
-
else if (tab === 'plugins') v2RenderOrgPlugins();
|
|
6004
|
-
else if (tab === 'charts') v2RenderOrgCharts();
|
|
6005
|
-
else if (tab === 'projects') v2RenderOrgProjects();
|
|
6006
|
-
else if (tab === 'workspaces') v2RenderOrgWorkspaces();
|
|
6007
|
-
else if (tab === 'invites') v2RenderOrgInvites();
|
|
6008
|
-
else if (tab === 'environments') v2RenderOrgEnvironments();
|
|
6009
|
-
else if (tab === 'access') v2RenderOrgAccess();
|
|
6010
|
-
else if (tab === 'issues-full') v2RenderOrgIssuesFull();
|
|
6011
|
-
else if (tab === 'join-requests') v2RenderOrgJoinRequests();
|
|
6012
|
-
else if (tab === 'threads') v2RenderOrgThreads();
|
|
6013
5957
|
else if (tab === 'chat') v2RenderOrgChat();
|
|
6014
5958
|
else if (tab === 'config') v2RenderOrgConfig();
|
|
6015
5959
|
else if (tab === 'files') v2RenderOrgFiles();
|
|
@@ -6758,47 +6702,6 @@ function v2RoleAvatar(role, avatarMap) {
|
|
|
6758
6702
|
return `data/avatars/${_v2AvatarCandidates(role)[0]}.svg`;
|
|
6759
6703
|
}
|
|
6760
6704
|
|
|
6761
|
-
// ── org roles ───────────────────────────────
|
|
6762
|
-
function v2RenderOrgRoles() {
|
|
6763
|
-
if (!_v2SelOrg) return;
|
|
6764
|
-
const roles = Array.isArray(_v2OrgData?.roles) ? _v2OrgData.roles : [];
|
|
6765
|
-
const V2_ROLE_COLORS = ['oklch(72% 0.18 75)','oklch(65% 0.12 240)','oklch(65% 0.15 150)','oklch(65% 0.13 290)','oklch(65% 0.14 35)','oklch(62% 0.12 195)'];
|
|
6766
|
-
const pane = document.getElementById('odt-roles');
|
|
6767
|
-
if (!pane) return;
|
|
6768
|
-
if (!roles.length) {
|
|
6769
|
-
pane.innerHTML = '<div class="empty">No roles defined</div>';
|
|
6770
|
-
return;
|
|
6771
|
-
}
|
|
6772
|
-
// Determine leader: explicit reports_to=undefined + type=planner/coordinator, or first role, or id=boss
|
|
6773
|
-
const leaderIds = new Set(roles.filter(r => r.id === 'boss' || r.type === 'coordinator' || r.type === 'planner').map(r => r.id));
|
|
6774
|
-
if (!leaderIds.size) leaderIds.add(roles[0]?.id);
|
|
6775
|
-
// Overlay live running status from in-memory agent SSE state
|
|
6776
|
-
const _rolesRunning = new Set((_v2OrgData?._agents || []).filter(a => a.status === 'running').map(a => a.id));
|
|
6777
|
-
const _rolesAvMap = v2AvatarMapFor(roles);
|
|
6778
|
-
pane.innerHTML = `<div class="roles-v2-grid">${roles.map((r, i) => {
|
|
6779
|
-
const color = V2_ROLE_COLORS[i % V2_ROLE_COLORS.length];
|
|
6780
|
-
const isLeader = leaderIds.has(r.id);
|
|
6781
|
-
const isRunning = _rolesRunning.has(r.id);
|
|
6782
|
-
const resps = Array.isArray(r.responsibilities) ? r.responsibilities : [];
|
|
6783
|
-
const displayName = r.name || r.title || r.id;
|
|
6784
|
-
const roleType = r.type || r.agent_type || '';
|
|
6785
|
-
const avatarSrc = v2RoleAvatar(r, _rolesAvMap);
|
|
6786
|
-
return `<div class="role-v2" style="border-top:2px solid ${color}" onclick="v2OpenAgent('${String(r.id).replace(/['\\]/g,'\\$&')}')" title="View agent characteristics, skills & instructions">
|
|
6787
|
-
<div class="rv2-head">
|
|
6788
|
-
<img class="rv2-avatar" src="${esc(avatarSrc)}" alt="${esc(displayName)}" loading="lazy" width="48" height="48" onerror="this.src='data/avatars/coder.svg'"/>
|
|
6789
|
-
<div class="rv2-info">
|
|
6790
|
-
<div class="rv2-title">${esc(displayName)}${isLeader ? ' <span style="font-size:10px;color:var(--accent);font-weight:400;opacity:0.8">lead</span>' : ''}${isRunning ? ' <span class="live-dot" style="display:inline-block;vertical-align:middle;margin-left:4px"></span>' : ''}</div>
|
|
6791
|
-
${roleType ? `<span class="rv2-agent">${esc(roleType)}</span>` : ''}
|
|
6792
|
-
</div>
|
|
6793
|
-
</div>
|
|
6794
|
-
<div class="rv2-id">${esc(r.id)}</div>
|
|
6795
|
-
${r.reports_to ? `<div class="rv2-reports">reports to <span>${esc(r.reports_to)}</span></div>` : ''}
|
|
6796
|
-
${resps.length ? `<div class="rv2-resps">${resps.map(s=>`<div class="rv2-resp">${esc(s)}</div>`).join('')}</div>` : ''}
|
|
6797
|
-
<div class="rv2-more">View characteristics, skills & instructions →</div>
|
|
6798
|
-
</div>`;
|
|
6799
|
-
}).join('')}</div>`;
|
|
6800
|
-
}
|
|
6801
|
-
|
|
6802
6705
|
// ── agent detail drawer ──────────────────────
|
|
6803
6706
|
function v2HighlightAgentNode(roleId) {
|
|
6804
6707
|
document.querySelectorAll('#v2oc-nodes .v2oc-node').forEach(n =>
|
|
@@ -6915,7 +6818,6 @@ async function _v2HandleAvatarFile(inputEl) {
|
|
|
6915
6818
|
const imgEl = document.getElementById('oad-avatar-img');
|
|
6916
6819
|
if (imgEl) imgEl.src = dataUrl;
|
|
6917
6820
|
showToast('Avatar updated', roleId, 'ok');
|
|
6918
|
-
if (document.getElementById('odt-roles')) v2RenderOrgRoles();
|
|
6919
6821
|
if (_v2SelOrg) v2RenderOrgChart();
|
|
6920
6822
|
} catch (e) { showToast('Error', e.message, 'err'); }
|
|
6921
6823
|
}
|
|
@@ -6977,226 +6879,6 @@ function v2RenderMarkdown(md) {
|
|
|
6977
6879
|
return html;
|
|
6978
6880
|
}
|
|
6979
6881
|
|
|
6980
|
-
// ── org activity ────────────────────────────
|
|
6981
|
-
function fmtOrgEvDetail(ev) {
|
|
6982
|
-
if (ev.type === 'org:comms' && (ev.from || ev.role || ev.to || ev.msg)) {
|
|
6983
|
-
const sender = ev.from || ev.role || '';
|
|
6984
|
-
const route = [sender, ev.to].filter(Boolean).join(' → ');
|
|
6985
|
-
return route ? route + (ev.msg ? `: ${ev.msg}` : '') : ev.msg || '';
|
|
6986
|
-
}
|
|
6987
|
-
if (ev.type === 'org:checkpoint') return ev.summary || ev.progress || '';
|
|
6988
|
-
if (ev.type === 'agent:result') return (ev.agent || ev.from || '') + (ev.result || ev.msg || ev.message || ev.summary ? `: ${ev.result || ev.msg || ev.message || ev.summary}` : '');
|
|
6989
|
-
if (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline') return ev.title || ev.role || ev.agent || ev.id || ev.from || '';
|
|
6990
|
-
if (ev.type === 'org:error') return ev.error || ev.msg || ev.message || '';
|
|
6991
|
-
if (ev.type === 'file:write') return ev.path || ev.file || ev.msg || '';
|
|
6992
|
-
return ev.role||ev.result||ev.msg||ev.message||ev.progress||ev.agent||ev.status||'';
|
|
6993
|
-
}
|
|
6994
|
-
|
|
6995
|
-
// ── org health ──────────────────────────────
|
|
6996
|
-
function v2RenderOrgHealth() {
|
|
6997
|
-
if (!_v2SelOrg) return;
|
|
6998
|
-
const listOrg = _v2Orgs.find(o => o.name === _v2SelOrg) || {};
|
|
6999
|
-
const health = _v2OrgData?._health;
|
|
7000
|
-
const roles = Array.isArray(_v2OrgData?.roles) ? _v2OrgData.roles.length : 0;
|
|
7001
|
-
const pane = document.getElementById('odt-health');
|
|
7002
|
-
if (!pane) return;
|
|
7003
|
-
pane.innerHTML = `
|
|
7004
|
-
<div class="health-v2-grid">
|
|
7005
|
-
<div class="hv2-cell"><div class="hv2-lbl">Status</div><div class="hv2-val ${(()=>{const r=_v2OrgData?.running??listOrg.running;if(!r)return '';const a=_v2OrgData?.lastEventAt??listOrg.lastEventAt;return a&&(Date.now()-a)<1800000?'green':'amber';})()} ">${(()=>{const r=_v2OrgData?.running??listOrg.running;if(!r)return 'IDLE';const a=_v2OrgData?.lastEventAt??listOrg.lastEventAt;if(!a)return '🟡 QUIET';const d=Date.now()-a;return d<1800000?'🟢 LIVE':d<7200000?'🟡 QUIET':'🔴 STALE';})()}</div></div>
|
|
7006
|
-
<div class="hv2-cell"><div class="hv2-lbl">Roles</div><div class="hv2-val">${roles}</div></div>
|
|
7007
|
-
<div class="hv2-cell"><div class="hv2-lbl">Topology</div><div class="hv2-val" style="font-size:14px">${esc((_v2OrgData?.topology||'—').toUpperCase())}</div></div>
|
|
7008
|
-
${health?.agents_active!=null?`<div class="hv2-cell"><div class="hv2-lbl">Agents</div><div class="hv2-val ${health.agents_active>0?'green':''}">${health.agents_active}</div></div>`:''}
|
|
7009
|
-
${health?.tasks_pending!=null?`<div class="hv2-cell"><div class="hv2-lbl">Tasks</div><div class="hv2-val ${health.tasks_pending>5?'amber':''}">${health.tasks_pending}</div></div>`:''}
|
|
7010
|
-
${health?.run_success_rate_7d!=null?`<div class="hv2-cell"><div class="hv2-lbl">7d Success</div><div class="hv2-val ${health.run_success_rate_7d>=80?'green':health.run_success_rate_7d<50?'amber':''}">${health.run_success_rate_7d}%</div></div>`:''}
|
|
7011
|
-
${health?.total_runs_7d!=null?`<div class="hv2-cell"><div class="hv2-lbl">7d Runs</div><div class="hv2-val">${health.total_runs_7d}</div></div>`:''}
|
|
7012
|
-
${health?.budget_used_pct!=null?`<div class="hv2-cell"><div class="hv2-lbl">Budget Used</div><div class="hv2-val ${health.budget_used_pct>=90?'amber':''}">${health.budget_used_pct}%</div></div>`:''}
|
|
7013
|
-
</div>
|
|
7014
|
-
${health?.budget_used_pct!=null&&health?.budget_max_tokens>0?`<div style="margin:10px 0 12px"><div style="height:4px;background:var(--border);border-radius:2px;overflow:hidden"><div style="width:${Math.min(100,health.budget_used_pct)}%;height:100%;background:${health.budget_used_pct>=90?'var(--red)':health.budget_used_pct>=70?'oklch(70% 0.18 60)':'var(--accent)'};border-radius:2px"></div></div><div style="font-size:10px;color:var(--text-lo);margin-top:4px">${(health.budget_used_tokens??0).toLocaleString()} / ${health.budget_max_tokens.toLocaleString()} tokens</div></div>`:''}
|
|
7015
|
-
${health?.errors?.length?`<div style="margin-top:12px"><div class="m-group-title">Errors</div>${health.errors.slice(0,5).map(e=>`<div style="font-size:11px;color:var(--red);padding:4px 0;border-bottom:1px solid var(--border)">${esc(e)}</div>`).join('')}</div>`:`<div style="margin-top:10px;font-size:12px;color:var(--text-lo)">${listOrg.running?'Running — no errors detected':'Org is idle'}</div>`}
|
|
7016
|
-
`;
|
|
7017
|
-
}
|
|
7018
|
-
|
|
7019
|
-
// ── org heartbeats tab ─────────────────────────────────────
|
|
7020
|
-
function v2RenderOrgHeartbeats() {
|
|
7021
|
-
if (!_v2SelOrg) return;
|
|
7022
|
-
const pane = document.getElementById('odt-heartbeats');
|
|
7023
|
-
if (!pane) return;
|
|
7024
|
-
// Derive from the agent roster — each agent carries status + lastHeartbeat
|
|
7025
|
-
let hb = Array.isArray(_v2OrgData?.heartbeats) ? _v2OrgData.heartbeats : [];
|
|
7026
|
-
if (!hb.length && Array.isArray(_v2OrgData?._agents)) {
|
|
7027
|
-
hb = _v2OrgData._agents.map(a => ({
|
|
7028
|
-
agent: a.title || a.id || '—',
|
|
7029
|
-
ts: a.lastHeartbeat ?? a.last_heartbeat ?? a.ts ?? null,
|
|
7030
|
-
status: a.status || 'idle',
|
|
7031
|
-
}));
|
|
7032
|
-
}
|
|
7033
|
-
if (!hb.length) { pane.innerHTML = '<div class="empty">No agents to report</div>'; return; }
|
|
7034
|
-
const cls = (st) => (st === 'active' || st === 'running') ? 'on' : ((st === 'error' || st === 'failed') ? 'warn' : '');
|
|
7035
|
-
pane.innerHTML = `<div class="m-group-title">Agent Heartbeats</div>` +
|
|
7036
|
-
hb.slice(0, 50).map(h => `<div style="display:flex;justify-content:space-between;align-items:center;gap:10px;padding:6px 0;border-bottom:1px solid var(--border);font-size:12px">
|
|
7037
|
-
<span style="color:var(--text-hi);font-family:var(--mono);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(h.agent || '—')}</span>
|
|
7038
|
-
<span style="color:var(--text-lo);white-space:nowrap">${h.ts ? relTime(h.ts) : 'never'}</span>
|
|
7039
|
-
<span class="ss-pill ${cls(h.status)}">${esc(String(h.status || 'idle').toUpperCase())}</span>
|
|
7040
|
-
</div>`).join('');
|
|
7041
|
-
}
|
|
7042
|
-
|
|
7043
|
-
// ── org tasks tab ──────────────────────────────────────────
|
|
7044
|
-
function v2RenderOrgTasks() {
|
|
7045
|
-
if (!_v2SelOrg) return;
|
|
7046
|
-
const pane = document.getElementById('odt-tasks');
|
|
7047
|
-
if (!pane) return;
|
|
7048
|
-
// Normalize: API returns {todo:[],doing:[],done:[]} (columns) or a flat array
|
|
7049
|
-
const raw = _v2OrgData?.tasks;
|
|
7050
|
-
// Server puts completed/failed/cancelled in the 'done' column but keeps original status string
|
|
7051
|
-
const _normTaskStatus = (st, col) => {
|
|
7052
|
-
if (st === 'completed' || st === 'failed' || st === 'cancelled') return 'done';
|
|
7053
|
-
if (st === 'in_progress') return 'doing';
|
|
7054
|
-
return st || col;
|
|
7055
|
-
};
|
|
7056
|
-
let tasks = [];
|
|
7057
|
-
if (Array.isArray(raw)) tasks = raw.map(t => ({ ...t, status: _normTaskStatus(t.status, 'todo') }));
|
|
7058
|
-
else if (raw && typeof raw === 'object') {
|
|
7059
|
-
for (const [col, items] of Object.entries(raw)) {
|
|
7060
|
-
(Array.isArray(items) ? items : []).forEach(t => tasks.push({ ...t, status: _normTaskStatus(t.status, col) }));
|
|
7061
|
-
}
|
|
7062
|
-
}
|
|
7063
|
-
if (!tasks.length) { pane.innerHTML = '<div class="empty">No tasks</div>'; return; }
|
|
7064
|
-
const rank = { running: 0, doing: 0, todo: 1, pending: 1, done: 2 };
|
|
7065
|
-
tasks.sort((a, b) => (rank[a.status] ?? 1) - (rank[b.status] ?? 1));
|
|
7066
|
-
const pill = (st) => st === 'done' ? 'on' : (st === 'running' || st === 'doing' ? 'warn' : '');
|
|
7067
|
-
pane.innerHTML = `<div class="m-group-title">Tasks (${tasks.length})</div>` +
|
|
7068
|
-
tasks.slice(0, 80).map(t => `<div style="display:flex;gap:10px;align-items:baseline;padding:5px 0;border-bottom:1px solid var(--border);font-size:12px">
|
|
7069
|
-
<span class="ss-pill ${pill(t.status)}">${esc(t.status || '?')}</span>
|
|
7070
|
-
<span style="flex:1;color:var(--text-hi);font-family:var(--mono);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(t.description || t.title || t.id || '—')}</span>
|
|
7071
|
-
<span style="color:var(--text-lo);white-space:nowrap">${relTime(t.ts || t.created_at || t.updated_at)}</span>
|
|
7072
|
-
</div>`).join('');
|
|
7073
|
-
}
|
|
7074
|
-
|
|
7075
|
-
// ── org members tab ────────────────────────────────────────
|
|
7076
|
-
function v2RenderOrgMembers() {
|
|
7077
|
-
if (!_v2SelOrg) return;
|
|
7078
|
-
const pane = document.getElementById('odt-members');
|
|
7079
|
-
if (!pane) return;
|
|
7080
|
-
// Prefer actual joined members from the members endpoint; fall back to defined roles
|
|
7081
|
-
const members = Array.isArray(_v2OrgData?._members) ? _v2OrgData._members : [];
|
|
7082
|
-
const roles = Array.isArray(_v2OrgData?.roles) ? _v2OrgData.roles : [];
|
|
7083
|
-
const list = members.length ? members : roles;
|
|
7084
|
-
const joinReqs = Array.isArray(_v2OrgData?._joinRequests) ? _v2OrgData._joinRequests : [];
|
|
7085
|
-
if (!list.length) { pane.innerHTML = '<div class="empty">No members</div>'; return; }
|
|
7086
|
-
const src = members.length ? 'joined members' : 'defined roles';
|
|
7087
|
-
// Overlay live running status from in-memory agent SSE state (updated by org:agent:online/offline events)
|
|
7088
|
-
const _liveRunning = new Set((_v2OrgData?._agents || []).filter(a => a.status === 'running').map(a => a.id));
|
|
7089
|
-
const active = (r) => r.running || r.active || r.status === 'active' || _liveRunning.has(r.id || r.name || r.user);
|
|
7090
|
-
pane.innerHTML = `<div class="m-group-title">Members (${list.length}) · ${src}</div>` +
|
|
7091
|
-
list.map(r => `<div style="display:flex;gap:10px;align-items:center;padding:6px 0;border-bottom:1px solid var(--border);font-size:12px">
|
|
7092
|
-
<span style="width:28px;height:28px;border-radius:50%;background:var(--surface-hi);display:inline-flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0">◈</span>
|
|
7093
|
-
<div style="flex:1;min-width:0">
|
|
7094
|
-
<div style="color:var(--text-hi);font-family:var(--mono);font-size:12px">${esc(r.name || r.id || r.user || '—')}</div>
|
|
7095
|
-
<div style="color:var(--text-lo);font-size:10px">${esc(r.title || r.type || r.role || '')}</div>
|
|
7096
|
-
</div>
|
|
7097
|
-
<span class="ss-pill ${active(r) ? 'on' : ''}">${active(r) ? 'ACTIVE' : 'IDLE'}</span>
|
|
7098
|
-
</div>`).join('') +
|
|
7099
|
-
(joinReqs.length ? `<div class="m-group-title" style="margin-top:14px">Join Requests (${joinReqs.length})</div>` +
|
|
7100
|
-
joinReqs.map(j => `<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border);font-size:12px"><span style="color:var(--text-hi)">${esc(j.user || j.name || j.id || '—')}</span><span style="color:var(--text-lo)">${esc(j.status || 'pending')}</span></div>`).join('') : '');
|
|
7101
|
-
}
|
|
7102
|
-
|
|
7103
|
-
// ── GOALS ──────────────────────────────────────────────────
|
|
7104
|
-
function v2RenderOrgGoals() {
|
|
7105
|
-
const el = document.getElementById('odt-goals');
|
|
7106
|
-
if (!el || !_v2OrgData) return;
|
|
7107
|
-
const goals = _v2OrgData.goals || _v2OrgData.config?.goals || [];
|
|
7108
|
-
if (!goals.length) { el.innerHTML = '<div class="empty">No goals defined</div>'; return; }
|
|
7109
|
-
function renderGoal(g, depth) {
|
|
7110
|
-
if (depth > 20) return ''; // Depth guard to prevent stack overflow
|
|
7111
|
-
const indent = depth * 20;
|
|
7112
|
-
const pct = g.total > 0 ? Math.min(100, Math.round(g.filled / g.total * 100)) : 0;
|
|
7113
|
-
const status = g.status || 'pending';
|
|
7114
|
-
const statusCls = status === 'done' ? 'on' : (status === 'blocked' || status === 'in_progress') ? 'warn' : '';
|
|
7115
|
-
return `<div style="padding:8px 0;border-bottom:1px solid var(--border);padding-left:${indent}px">
|
|
7116
|
-
<div style="display:flex;align-items:center;gap:10px">
|
|
7117
|
-
<span style="flex:1;font-size:13px;color:var(--text-hi)">${esc(g.text || g.goal || g.title || '—')}</span>
|
|
7118
|
-
<span class="ss-pill ${statusCls}">${esc(status)}</span>
|
|
7119
|
-
</div>
|
|
7120
|
-
${g.total > 0 ? `<div style="margin-top:5px;height:4px;background:var(--border);border-radius:2px;overflow:hidden"><div style="width:${pct}%;height:100%;background:${pct >= 100 ? 'var(--green)' : 'var(--accent)'};border-radius:2px"></div></div>` : ''}
|
|
7121
|
-
${(g.children || []).map(c => renderGoal(c, depth + 1)).join('')}
|
|
7122
|
-
</div>`;
|
|
7123
|
-
}
|
|
7124
|
-
el.innerHTML = goals.map(g => renderGoal(g, 0)).join('');
|
|
7125
|
-
}
|
|
7126
|
-
|
|
7127
|
-
// ── BOARD ──────────────────────────────────────────────────
|
|
7128
|
-
function v2RenderOrgBoard() {
|
|
7129
|
-
const el = document.getElementById('odt-board');
|
|
7130
|
-
if (!el || !_v2OrgData) return;
|
|
7131
|
-
const issues = _v2OrgData._issues || [];
|
|
7132
|
-
if (!issues.length) { el.innerHTML = '<div class="empty">No issues</div>'; return; }
|
|
7133
|
-
const cols = ['open', 'in_progress', 'blocked', 'done', 'cancelled'];
|
|
7134
|
-
const PRIORITY = { urgent: '🔴', high: '🟠', medium: '🟡', low: '🟢' };
|
|
7135
|
-
const _normIssueStatus = s => (s === 'closed' || s === 'resolved') ? 'done' : (s || 'open');
|
|
7136
|
-
el.innerHTML = `<div style="display:flex;gap:10px;overflow-x:auto;padding-bottom:8px">` +
|
|
7137
|
-
cols.map(col => {
|
|
7138
|
-
const cards = issues.filter(i => _normIssueStatus(i.status || i.state) === col);
|
|
7139
|
-
return `<div style="min-width:160px;flex:1">
|
|
7140
|
-
<div style="font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.07em;color:var(--text-lo);padding:6px 0;border-bottom:1px solid var(--border);margin-bottom:8px">${esc(col.replace('_', ' '))} <span style="background:var(--surface-hi);padding:1px 6px;border-radius:8px">${cards.length}</span></div>
|
|
7141
|
-
${cards.slice(0, 15).map(i => `<div style="background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:7px 9px;margin-bottom:5px;font-size:12px">
|
|
7142
|
-
<div style="color:var(--text-hi)">${PRIORITY[i.priority] || ''} ${esc((i.title || i.description || '—').slice(0, 60))}</div>
|
|
7143
|
-
${i.assignee ? `<div style="font-size:10px;color:var(--text-lo);margin-top:3px">${esc(i.assignee)}</div>` : ''}
|
|
7144
|
-
</div>`).join('')}
|
|
7145
|
-
</div>`;
|
|
7146
|
-
}).join('') + `</div>`;
|
|
7147
|
-
}
|
|
7148
|
-
|
|
7149
|
-
// ── LIVE ───────────────────────────────────────────────────
|
|
7150
|
-
let _orgLiveInterval = null;
|
|
7151
|
-
function v2RenderOrgLive() {
|
|
7152
|
-
const el = document.getElementById('odt-live');
|
|
7153
|
-
if (!el || !_v2OrgData) return;
|
|
7154
|
-
const agents = _v2OrgData._agents || [];
|
|
7155
|
-
const running = agents.filter(a => a.status === 'running' || a.running);
|
|
7156
|
-
el.innerHTML = `<div style="margin-bottom:14px">
|
|
7157
|
-
${running.length
|
|
7158
|
-
? running.map(a => `<div style="display:flex;align-items:center;gap:8px;padding:6px 0;border-bottom:1px solid var(--border)">
|
|
7159
|
-
<span class="live-dot" style="flex-shrink:0"></span>
|
|
7160
|
-
<span style="font-size:13px;color:var(--text-hi)">${esc(a.title || a.id || '—')}</span>${a.adapterType?`<span style="font-size:10px;color:var(--text-lo);margin-left:4px">[${esc(a.adapterType)}]</span>`:''}
|
|
7161
|
-
<span style="margin-left:auto;font-size:11px;color:var(--text-lo)">${esc(a.adapterModel || a.adapter || '')}</span>
|
|
7162
|
-
</div>`).join('')
|
|
7163
|
-
: '<div style="color:var(--text-lo);font-size:13px;padding:10px 0">No agents currently running</div>'}
|
|
7164
|
-
</div>
|
|
7165
|
-
<div class="m-group-title" style="margin-bottom:6px">Activity Feed</div>
|
|
7166
|
-
<div style="max-height:240px;overflow-y:auto;font-size:11px;font-family:var(--mono)">
|
|
7167
|
-
${(()=>{const _la=_v2OrgData._activity||[];const _ls=_v2OrgEventLog[_v2SelOrg]||[];const _lseen=new Set();const _lmerged=[..._la,..._ls].filter(e=>{const k=`${e.ts||''}|${e.type||''}|${e.runId||e.session||''}|${e.from||e.role||e.agent||''}|${(e.result||e.msg||e.message||e.summary||'').slice(0,20)}`;return _lseen.has(k)?false:(_lseen.add(k),true);}).sort((a,b)=>(a.ts||0)-(b.ts||0));return _lmerged.slice(-30).reverse().map(e=>`<div style="padding:3px 0;border-bottom:1px solid var(--border);color:var(--text-lo)">${esc(relTime(e.ts||e.timestamp||e.created_at))}<span style="color:var(--text-mid);margin-left:6px">${esc(e.type||e.kind||e.event||'—')}</span>${fmtOrgEvDetail(e)?`<span style="color:var(--text-hi);margin-left:6px">${esc(fmtOrgEvDetail(e).slice(0,80))}</span>`:''}</div>`).join('');})()}
|
|
7168
|
-
</div>`;
|
|
7169
|
-
// auto-refresh every 5s while tab is active
|
|
7170
|
-
if (!_orgLiveInterval) {
|
|
7171
|
-
_orgLiveInterval = setInterval(async () => {
|
|
7172
|
-
if (_v2OrgTab !== 'live' || !_v2SelOrg || currentView !== 'orgs') { clearInterval(_orgLiveInterval); _orgLiveInterval = null; return; }
|
|
7173
|
-
const _liveOrg = _v2SelOrg;
|
|
7174
|
-
const _enc = encodeURIComponent(_liveOrg);
|
|
7175
|
-
try {
|
|
7176
|
-
const [actData, agentsData] = await Promise.all([
|
|
7177
|
-
fetch(`/api/org/${_enc}/activity${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []),
|
|
7178
|
-
fetch(`/api/org/${_enc}/agents${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : {}).catch(() => {}),
|
|
7179
|
-
]);
|
|
7180
|
-
if (_v2SelOrg !== _liveOrg || !_v2OrgData) return;
|
|
7181
|
-
// Merge fresh API data with SSE-pushed events to avoid clobbering live events
|
|
7182
|
-
// that haven't flushed to the JSONL file yet when this poll ran.
|
|
7183
|
-
const _freshAct = Array.isArray(actData) ? actData : [];
|
|
7184
|
-
const _sseAct = (_v2OrgData._activity || []).filter(e => e._sseOnly);
|
|
7185
|
-
const _mergeSeen = new Set(_freshAct.map(e => `${e.ts||''}|${e.type||''}|${e.runId||e.session||''}`));
|
|
7186
|
-
_sseAct.forEach(e => { const k=`${e.ts||''}|${e.type||''}|${e.runId||e.session||''}`; if (!_mergeSeen.has(k)) _freshAct.push(e); });
|
|
7187
|
-
_v2OrgData._activity = _freshAct.sort((a,b)=>(a.ts||0)-(b.ts||0));
|
|
7188
|
-
const freshAgents = Array.isArray(agentsData) ? agentsData : (agentsData?.agents || []);
|
|
7189
|
-
if (freshAgents.length) {
|
|
7190
|
-
// Preserve SSE-driven running status: state file lags behind real-time SSE updates
|
|
7191
|
-
const sseRunning = new Set((_v2OrgData._agents || []).filter(a => a.status === 'running').map(a => a.id));
|
|
7192
|
-
_v2OrgData._agents = freshAgents.map(a => sseRunning.has(a.id) ? { ...a, status: 'running' } : a);
|
|
7193
|
-
}
|
|
7194
|
-
v2RenderOrgLive();
|
|
7195
|
-
} catch (_) {}
|
|
7196
|
-
}, 5000);
|
|
7197
|
-
}
|
|
7198
|
-
}
|
|
7199
|
-
|
|
7200
6882
|
// ── APPROVALS ──────────────────────────────────────────────
|
|
7201
6883
|
// Tool/action approvals (<org>/approvals.json): a role asked to run a
|
|
7202
6884
|
// sensitive tool (Bash, WebFetch, org_complete, policy.approvalTools…).
|
|
@@ -7315,243 +6997,6 @@ async function _orgGateAction(id, approved) {
|
|
|
7315
6997
|
if (ok) v2RenderOrgDecisions();
|
|
7316
6998
|
}
|
|
7317
6999
|
|
|
7318
|
-
// ── SECRETS ────────────────────────────────────────────────
|
|
7319
|
-
async function v2RenderOrgSecrets() {
|
|
7320
|
-
const el = document.getElementById('odt-secrets');
|
|
7321
|
-
if (!el || !_v2SelOrg) return;
|
|
7322
|
-
const _rendOrg = _v2SelOrg;
|
|
7323
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
7324
|
-
try {
|
|
7325
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
7326
|
-
const data = await fetch(`/api/org/${_enc}/secrets${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []);
|
|
7327
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
7328
|
-
const secrets = Array.isArray(data) ? data : (data.secrets || []);
|
|
7329
|
-
el.innerHTML = '<div style="font-size:11px;color:var(--text-lo);margin-bottom:10px">Secret values are never transmitted or displayed.</div>' +
|
|
7330
|
-
(secrets.length
|
|
7331
|
-
? secrets.map(s => `<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid var(--border);font-size:12px">
|
|
7332
|
-
<span style="font-family:var(--mono);color:var(--text-hi)">${esc(s.name || s.key || '—')}</span>
|
|
7333
|
-
${s.purpose ? `<span style="color:var(--text-lo)">${esc(s.purpose)}</span>` : ''}
|
|
7334
|
-
<span style="margin-left:auto;font-family:var(--mono);color:var(--border)">••••••••</span>
|
|
7335
|
-
</div>`).join('')
|
|
7336
|
-
: '<div class="empty">No secrets configured</div>');
|
|
7337
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
7338
|
-
}
|
|
7339
|
-
|
|
7340
|
-
// ── SETTINGS ───────────────────────────────────────────────
|
|
7341
|
-
function v2RenderOrgSettings() {
|
|
7342
|
-
const el = document.getElementById('odt-settings');
|
|
7343
|
-
if (!el || !_v2OrgData) return;
|
|
7344
|
-
const d = _v2OrgData;
|
|
7345
|
-
const topos = ['hierarchical', 'mesh', 'hierarchical-mesh', 'adaptive', 'centralized', 'hybrid'];
|
|
7346
|
-
const govs = ['auto', 'board', 'strict'];
|
|
7347
|
-
// governance may be a string or an object {policy,...}; budget lives under run_config
|
|
7348
|
-
const govVal = (d.governance && typeof d.governance === 'object') ? (d.governance.policy || '') : (d.governance || '');
|
|
7349
|
-
const budgetVal = d.run_config?.budget_tokens ?? d.budget_tokens ?? d.budgetTokens ?? '';
|
|
7350
|
-
el.innerHTML = `
|
|
7351
|
-
<div style="font-size:11px;color:var(--text-lo);margin-bottom:14px">Changes generate a CLI command — no direct writes from UI.</div>
|
|
7352
|
-
<div style="display:flex;flex-direction:column;gap:12px;max-width:380px">
|
|
7353
|
-
<div><div class="le-lbl">Goal</div><input id="os-goal" class="filter-input" value="${esc(d.goal || '')}"></div>
|
|
7354
|
-
<div><div class="le-lbl">Topology</div>
|
|
7355
|
-
<select id="os-topo" class="filter-input" style="cursor:pointer">
|
|
7356
|
-
${topos.map(t => `<option ${d.topology === t ? 'selected' : ''}>${esc(t)}</option>`).join('')}
|
|
7357
|
-
</select>
|
|
7358
|
-
</div>
|
|
7359
|
-
<div><div class="le-lbl">Governance</div>
|
|
7360
|
-
<select id="os-gov" class="filter-input" style="cursor:pointer">
|
|
7361
|
-
${govs.map(g => `<option ${govVal === g ? 'selected' : ''}>${esc(g)}</option>`).join('')}
|
|
7362
|
-
</select>
|
|
7363
|
-
</div>
|
|
7364
|
-
<div><div class="le-lbl">Budget (tokens)</div><input id="os-budget" class="filter-input" type="number" value="${esc(String(budgetVal || ''))}"></div>
|
|
7365
|
-
<button class="btn" style="width:fit-content;color:var(--accent);border-color:var(--accent)" onclick="generateOrgSettingsCmd()">Generate CLI Command</button>
|
|
7366
|
-
<div id="os-cmd-out" style="display:none;font-family:var(--mono);font-size:11px;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:10px;word-break:break-all;cursor:pointer;color:var(--text-hi)" title="Click to copy" onclick="navigator.clipboard.writeText(this.textContent).then(()=>showToast('Copied','','ok'))"></div>
|
|
7367
|
-
</div>`;
|
|
7368
|
-
}
|
|
7369
|
-
|
|
7370
|
-
function _generateOrgSettingsCmd() {
|
|
7371
|
-
const org = _v2SelOrg || '';
|
|
7372
|
-
const goal = document.getElementById('os-goal')?.value.trim() || '';
|
|
7373
|
-
const topo = document.getElementById('os-topo')?.value || '';
|
|
7374
|
-
const gov = document.getElementById('os-gov')?.value || '';
|
|
7375
|
-
const budget = document.getElementById('os-budget')?.value.trim() || '';
|
|
7376
|
-
const _shellEsc = s => s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/`/g, '\\`').replace(/\$/g, '\\$').replace(/\n/g, ' ');
|
|
7377
|
-
const parts = [`/mastermind:org-settings --org "${_shellEsc(org)}"`];
|
|
7378
|
-
if (goal) parts.push(`--goal "${_shellEsc(goal)}"`);
|
|
7379
|
-
if (topo) parts.push(`--topology ${topo}`);
|
|
7380
|
-
if (gov) parts.push(`--governance ${gov}`);
|
|
7381
|
-
if (budget) parts.push(`--budget-tokens ${budget}`);
|
|
7382
|
-
const out = document.getElementById('os-cmd-out');
|
|
7383
|
-
if (out) { out.textContent = parts.join(' '); out.style.display = 'block'; }
|
|
7384
|
-
}
|
|
7385
|
-
|
|
7386
|
-
// ── ROUTINES ───────────────────────────────────────────────
|
|
7387
|
-
async function v2RenderOrgRoutines() {
|
|
7388
|
-
const el = document.getElementById('odt-routines');
|
|
7389
|
-
if (!el || !_v2SelOrg) return;
|
|
7390
|
-
const _rendOrg = _v2SelOrg;
|
|
7391
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
7392
|
-
try {
|
|
7393
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
7394
|
-
const data = await fetch(`/api/org/${_enc}/routines${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []);
|
|
7395
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
7396
|
-
const routines = Array.isArray(data) ? data : (data.routines || _v2OrgData?.config?.routines || []);
|
|
7397
|
-
if (!routines.length) { el.innerHTML = '<div class="empty">No routines configured</div>'; return; }
|
|
7398
|
-
el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
7399
|
-
<thead><tr style="color:var(--text-xs);text-align:left">
|
|
7400
|
-
<th style="padding:5px 8px">Name</th><th>Cron</th><th>Last Run</th><th>Status</th>
|
|
7401
|
-
</tr></thead><tbody>` +
|
|
7402
|
-
routines.map(r => `<tr style="border-top:1px solid var(--border)">
|
|
7403
|
-
<td style="padding:6px 8px;color:var(--text-hi)">${esc(r.name || '—')}</td>
|
|
7404
|
-
<td style="padding:6px 8px;font-family:var(--mono);color:var(--text-lo)">${esc(r.cron || r.schedule || '—')}</td>
|
|
7405
|
-
<td style="padding:6px 8px;color:var(--text-lo)">${(r.lastRun || r.last_run) ? relTime(r.lastRun || r.last_run) : '—'}</td>
|
|
7406
|
-
<td style="padding:6px 8px"><span class="ss-pill ${r.enabled || r.active || r.status === 'active' ? 'on' : ''}">${esc(r.status || (r.enabled != null ? (r.enabled ? 'active' : 'inactive') : '—'))}</span></td>
|
|
7407
|
-
</tr>`).join('') + '</tbody></table>';
|
|
7408
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
7409
|
-
}
|
|
7410
|
-
|
|
7411
|
-
// ── MY ISSUES ──────────────────────────────────────────────
|
|
7412
|
-
async function v2RenderOrgMyIssues() {
|
|
7413
|
-
const el = document.getElementById('odt-myissues');
|
|
7414
|
-
if (!el || !_v2SelOrg) return;
|
|
7415
|
-
const _rendOrg = _v2SelOrg;
|
|
7416
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
7417
|
-
try {
|
|
7418
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
7419
|
-
const data = await fetch(`/api/org/${_enc}/my-issues${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []);
|
|
7420
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
7421
|
-
const issues = Array.isArray(data) ? data : (data.issues || []);
|
|
7422
|
-
if (!issues.length) { el.innerHTML = '<div class="empty">No issues assigned to you</div>'; return; }
|
|
7423
|
-
const PRIORITY = { urgent: '🔴', high: '🟠', medium: '🟡', low: '🟢' };
|
|
7424
|
-
el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
7425
|
-
<thead><tr style="color:var(--text-xs);text-align:left">
|
|
7426
|
-
<th style="padding:5px 4px">P</th><th style="padding:5px 8px">Title</th><th>Status</th><th>Updated</th>
|
|
7427
|
-
</tr></thead><tbody>` +
|
|
7428
|
-
issues.slice(0, 50).map(i => {
|
|
7429
|
-
const cls = i.status === 'done' || i.status === 'closed' || i.status === 'resolved' ? 'on' : (i.status === 'blocked' || i.status === 'in_progress') ? 'warn' : '';
|
|
7430
|
-
return `<tr style="border-top:1px solid var(--border)">
|
|
7431
|
-
<td style="padding:5px 4px">${PRIORITY[i.priority] || '·'}</td>
|
|
7432
|
-
<td style="padding:5px 8px;color:var(--text-hi);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc((i.title || i.description || '—').slice(0, 80))}</td>
|
|
7433
|
-
<td style="padding:5px 8px"><span class="ss-pill ${cls}">${esc(i.status || 'open')}</span></td>
|
|
7434
|
-
<td style="padding:5px 8px;color:var(--text-xs);font-size:11px;font-family:var(--mono)">${relTime(i.updated_at || i.ts)}</td>
|
|
7435
|
-
</tr>`;
|
|
7436
|
-
}).join('') + '</tbody></table>';
|
|
7437
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
7438
|
-
}
|
|
7439
|
-
|
|
7440
|
-
// ── PLUGINS ────────────────────────────────────────────────
|
|
7441
|
-
async function v2RenderOrgPlugins() {
|
|
7442
|
-
const el = document.getElementById('odt-plugins');
|
|
7443
|
-
if (!el || !_v2SelOrg) return;
|
|
7444
|
-
const _rendOrg = _v2SelOrg;
|
|
7445
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
7446
|
-
try {
|
|
7447
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
7448
|
-
const data = await fetch(`/api/org/${_enc}/plugins${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`).then(r => r.ok ? r.json() : []).catch(() => []);
|
|
7449
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
7450
|
-
const plugins = Array.isArray(data) ? data : (data.plugins || []);
|
|
7451
|
-
if (!plugins.length) { el.innerHTML = '<div class="empty">No plugins installed</div>'; return; }
|
|
7452
|
-
el.innerHTML = `<div class="proj-grid">` +
|
|
7453
|
-
plugins.map(p => {
|
|
7454
|
-
const status = p.status || 'installed';
|
|
7455
|
-
const col = status === 'error' ? 'var(--red)' : status === 'installed' ? 'var(--accent)' : 'var(--text-lo)';
|
|
7456
|
-
return `<div class="proj-card">
|
|
7457
|
-
<div class="proj-card-name">${esc(p.name || '—')}</div>
|
|
7458
|
-
<div class="proj-card-path">${esc((p.description || '').slice(0, 80))}</div>
|
|
7459
|
-
<div style="margin-top:8px"><span class="ss-pill" style="color:${esc(col)};border-color:${esc(col)}44;background:${esc(col)}18">${esc(status)}</span></div>
|
|
7460
|
-
</div>`;
|
|
7461
|
-
}).join('') + `</div>`;
|
|
7462
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
7463
|
-
}
|
|
7464
|
-
|
|
7465
|
-
// ── CHARTS ─────────────────────────────────────────────────
|
|
7466
|
-
function v2RenderOrgCharts() {
|
|
7467
|
-
const el = document.getElementById('odt-charts');
|
|
7468
|
-
if (!el || !_v2OrgData) return;
|
|
7469
|
-
const activity = _v2OrgData._activity || [];
|
|
7470
|
-
const agents = _v2OrgData._agents || [];
|
|
7471
|
-
|
|
7472
|
-
// Build 14-day buckets from activity
|
|
7473
|
-
const now = Date.now();
|
|
7474
|
-
const DAY = 86400000;
|
|
7475
|
-
const buckets = Array.from({length: 14}, (_, i) => ({
|
|
7476
|
-
day: new Date(now - (13 - i) * DAY).toLocaleDateString('en', {month:'short', day:'numeric'}),
|
|
7477
|
-
ts: now - (14 - i) * DAY,
|
|
7478
|
-
events: 0, errors: 0,
|
|
7479
|
-
}));
|
|
7480
|
-
activity.forEach(e => {
|
|
7481
|
-
const ts = new Date(e.ts || e.timestamp || e.created_at || 0).getTime();
|
|
7482
|
-
const idx = buckets.findIndex((b, i) =>
|
|
7483
|
-
ts >= b.ts && (i === 13 || ts < buckets[i + 1].ts));
|
|
7484
|
-
if (idx >= 0) {
|
|
7485
|
-
buckets[idx].events++;
|
|
7486
|
-
if (e.type === 'error' || e.status === 'error') buckets[idx].errors++;
|
|
7487
|
-
}
|
|
7488
|
-
});
|
|
7489
|
-
const maxEvents = Math.max(...buckets.map(b => b.events), 1);
|
|
7490
|
-
|
|
7491
|
-
// Heatmap bar chart
|
|
7492
|
-
const heatmap = buckets.map(b => {
|
|
7493
|
-
const h = Math.max(4, Math.round((b.events / maxEvents) * 80));
|
|
7494
|
-
const col = b.errors > 0 ? 'var(--red)' : b.events > 0 ? 'var(--accent)' : 'var(--border)';
|
|
7495
|
-
return `<div style="display:flex;flex-direction:column;align-items:center;gap:3px;flex:1;min-width:0">
|
|
7496
|
-
<div style="height:80px;display:flex;align-items:flex-end">
|
|
7497
|
-
<div style="width:100%;height:${h}px;background:${col};border-radius:2px 2px 0 0;min-width:8px" title="${b.events} events${b.errors ? `, ${b.errors} errors` : ''}"></div>
|
|
7498
|
-
</div>
|
|
7499
|
-
<div style="font-size:9px;color:var(--text-xs);transform:rotate(-45deg);transform-origin:top left;white-space:nowrap;margin-top:6px;margin-left:4px">${b.day}</div>
|
|
7500
|
-
</div>`;
|
|
7501
|
-
}).join('');
|
|
7502
|
-
|
|
7503
|
-
// Per-agent run bars
|
|
7504
|
-
const agentRuns = agents.slice(0, 10).map(a => {
|
|
7505
|
-
const runs = activity.filter(e => e.agentId === a.id || e.agent === a.id).length;
|
|
7506
|
-
return { name: (a.type || a.title || a.id || '—').toString().slice(0, 20), runs };
|
|
7507
|
-
}).filter(a => a.runs > 0).sort((x, y) => y.runs - x.runs);
|
|
7508
|
-
const maxRuns = Math.max(...agentRuns.map(a => a.runs), 1);
|
|
7509
|
-
|
|
7510
|
-
const totalEvents = activity.length;
|
|
7511
|
-
const errorCount = activity.filter(e => e.type === 'error' || e.status === 'error').length;
|
|
7512
|
-
const successRate = totalEvents > 0 ? Math.round((1 - errorCount / totalEvents) * 100) : 100;
|
|
7513
|
-
const activeCount = agents.filter(a => a.status === 'running' || a.running).length;
|
|
7514
|
-
|
|
7515
|
-
el.innerHTML = `
|
|
7516
|
-
<div style="display:flex;gap:20px;flex-wrap:wrap;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid var(--border)">
|
|
7517
|
-
<div class="chunk-stat"><div class="chunk-stat-val">${totalEvents}</div><div class="chunk-stat-lbl">Events</div></div>
|
|
7518
|
-
<div class="chunk-stat"><div class="chunk-stat-val" style="color:${errorCount>0?'var(--red)':'var(--green)'}">${errorCount}</div><div class="chunk-stat-lbl">Errors</div></div>
|
|
7519
|
-
<div class="chunk-stat"><div class="chunk-stat-val" style="color:var(--green)">${successRate}%</div><div class="chunk-stat-lbl">Success</div></div>
|
|
7520
|
-
<div class="chunk-stat"><div class="chunk-stat-val" style="color:${activeCount>0?'var(--green)':'var(--text-lo)'}">${activeCount}</div><div class="chunk-stat-lbl">Active</div></div>
|
|
7521
|
-
</div>
|
|
7522
|
-
<div class="m-group-title" style="margin-bottom:8px">14-Day Activity</div>
|
|
7523
|
-
<div style="display:flex;gap:2px;align-items:flex-end;padding-bottom:28px;margin-bottom:16px;overflow-x:auto">${heatmap}</div>
|
|
7524
|
-
${agentRuns.length ? `<div class="m-group-title" style="margin-bottom:6px">Per-Agent Runs</div>
|
|
7525
|
-
${agentRuns.map(a => `<div style="display:flex;align-items:center;gap:8px;padding:3px 0;font-size:12px">
|
|
7526
|
-
<div style="width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-hi);font-family:var(--mono);font-size:11px">${esc(a.name)}</div>
|
|
7527
|
-
<div style="flex:1;height:7px;background:var(--border);border-radius:2px;overflow:hidden"><div style="width:${Math.round(a.runs/maxRuns*100)}%;height:100%;background:var(--accent);border-radius:2px"></div></div>
|
|
7528
|
-
<div style="width:30px;text-align:right;color:var(--text-lo);font-family:var(--mono);font-size:11px">${a.runs}</div>
|
|
7529
|
-
</div>`).join('')}` : ''}
|
|
7530
|
-
`;
|
|
7531
|
-
}
|
|
7532
|
-
|
|
7533
|
-
// ── PROJECTS ───────────────────────────────────────────────
|
|
7534
|
-
async function v2RenderOrgProjects() {
|
|
7535
|
-
const el = document.getElementById('odt-projects');
|
|
7536
|
-
if (!el || !_v2SelOrg) return;
|
|
7537
|
-
const _rendOrg = _v2SelOrg;
|
|
7538
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
7539
|
-
try {
|
|
7540
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
7541
|
-
const data = await fetch(`/api/org/${_enc}/projects${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
|
|
7542
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
7543
|
-
const projects = Array.isArray(data) ? data : (data.projects || []);
|
|
7544
|
-
if (!projects.length) { el.innerHTML = '<div class="empty">No projects configured</div>'; return; }
|
|
7545
|
-
el.innerHTML = `<div class="proj-grid">${projects.map(p => `<div class="proj-card">
|
|
7546
|
-
<div class="proj-card-name">${esc(p.name || p.title || '—')}</div>
|
|
7547
|
-
<div class="proj-card-path">${esc((p.description || p.path || '').slice(0,80))}</div>
|
|
7548
|
-
<div class="proj-card-stats" style="margin-top:8px">
|
|
7549
|
-
${p.status ? `<span class="ss-pill ${p.status==='active'?'on':''}">${esc(p.status)}</span>` : ''}
|
|
7550
|
-
</div>
|
|
7551
|
-
</div>`).join('')}</div>`;
|
|
7552
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
7553
|
-
}
|
|
7554
|
-
|
|
7555
7000
|
// ── ORG CHAT ───────────────────────────────────────────────
|
|
7556
7001
|
let _odtChatSessions = [];
|
|
7557
7002
|
let _odtChatCurrentId = null;
|
|
@@ -8041,6 +7486,7 @@ window.odtSendChatMessage = async () => {
|
|
|
8041
7486
|
}
|
|
8042
7487
|
} catch(e) {
|
|
8043
7488
|
inp.value = text;
|
|
7489
|
+
showToast('Not delivered', e.message, 'err');
|
|
8044
7490
|
} finally {
|
|
8045
7491
|
btn.disabled = false;
|
|
8046
7492
|
inp.focus();
|
|
@@ -8788,7 +8234,7 @@ async function v2RenderOrgRuntime() {
|
|
|
8788
8234
|
const statusSub = v.live
|
|
8789
8235
|
? `${esc(v.run || '')}${v.startedAt ? ` · up ${fmtDur(Date.now() - v.startedAt)}` : ''}`
|
|
8790
8236
|
: `${esc(v.run || 'no run yet')}${v.closedBy ? ` · ${esc(_RT_CLOSED_BY[v.closedBy] || v.closedBy)}` : ''}`;
|
|
8791
|
-
let idle = '—', idleSub = s.idle_minutes === 0 ? 'watchdog disabled' : `window ${s.idle_minutes}m`;
|
|
8237
|
+
let idle = '—', idleSub = s.idle_minutes === 0 ? 'watchdog disabled' : `window ${esc(s.idle_minutes)}m`;
|
|
8792
8238
|
if (v.idle) {
|
|
8793
8239
|
if (v.idle.idle_hold && v.idle.idle_hold !== 'unknown') {
|
|
8794
8240
|
idle = `held: ${esc(v.idle.idle_hold)}`;
|
|
@@ -8805,7 +8251,7 @@ async function v2RenderOrgRuntime() {
|
|
|
8805
8251
|
const budgetSub = `${esc(b.basis)} basis${pct !== null ? ` · ${pct.toFixed(1)}%` : ''}${budgetWhy} · ${fmtCost(v.totals?.cost_usd)}`;
|
|
8806
8252
|
const gate = s.completion_evidence ? 'evidence required' : 'off';
|
|
8807
8253
|
const gateSub = s.completion_evidence
|
|
8808
|
-
? `escalate after ${s.max_evidence_attempts} refusals · org_complete: ${esc(s.completion)}`
|
|
8254
|
+
? `escalate after ${esc(s.max_evidence_attempts)} refusals · org_complete: ${esc(s.completion)}`
|
|
8809
8255
|
: `org_complete gate: ${esc(s.completion)}`;
|
|
8810
8256
|
|
|
8811
8257
|
let html = '<div style="padding:16px">';
|
|
@@ -8816,7 +8262,7 @@ async function v2RenderOrgRuntime() {
|
|
|
8816
8262
|
${_rtCard('IDLE WATCHDOG', idle, idleSub)}
|
|
8817
8263
|
${_rtCard('BUDGET (THIS RUN)', budget, budgetSub)}
|
|
8818
8264
|
${_rtCard('COMPLETION GATE', gate, gateSub)}
|
|
8819
|
-
${_rtCard('SESSIONS', `per ${esc(s.session_scope)}`, `workspace: ${esc(s.workspace)} · max ${s.max_concurrent_agents} agents`)}
|
|
8265
|
+
${_rtCard('SESSIONS', `per ${esc(s.session_scope)}`, `workspace: ${esc(s.workspace)} · max ${esc(s.max_concurrent_agents)} agents`)}
|
|
8820
8266
|
</div>`;
|
|
8821
8267
|
|
|
8822
8268
|
// Roles
|
|
@@ -8848,7 +8294,7 @@ async function v2RenderOrgRuntime() {
|
|
|
8848
8294
|
`<span style="color:var(--text-hi)">${esc(t.title || t.id)}</span><div style="font-size:10px;color:var(--text-xs)">${esc(t.id)}</div>`,
|
|
8849
8295
|
esc(t.assignee || '—'),
|
|
8850
8296
|
`<span class="ss-pill ${t.status === 'done' ? 'on' : t.status === 'failed' || t.status === 'blocked' ? 'warn' : ''}">${esc(t.status)}</span>`,
|
|
8851
|
-
t.evidenceFailures ? `<span style="color:${t.evidenceFailures >= s.max_evidence_attempts ? 'var(--red)' : 'var(--amber, #d29922)'}">${t.evidenceFailures} / ${s.max_evidence_attempts}</span>` : '—',
|
|
8297
|
+
t.evidenceFailures ? `<span style="color:${t.evidenceFailures >= s.max_evidence_attempts ? 'var(--red)' : 'var(--amber, #d29922)'}">${esc(t.evidenceFailures)} / ${esc(s.max_evidence_attempts)}</span>` : '—',
|
|
8852
8298
|
esc(t.loadout || '—'),
|
|
8853
8299
|
t.blockedUntil ? `until ${_rtClock(t.blockedUntil)}${t.blockedReason ? ` — ${esc(t.blockedReason)}` : ''}` : '—',
|
|
8854
8300
|
]), 'No task graph for this run (the boss has not used org_task)');
|
|
@@ -8872,7 +8318,7 @@ async function v2RenderOrgRuntime() {
|
|
|
8872
8318
|
h.outcome
|
|
8873
8319
|
? `<span class="ss-pill ${h.outcome.status === 'achieved' ? 'on' : 'warn'}">${esc(h.outcome.status)}</span>${h.blocker ? ` blocker: ${esc(h.blocker)}` : ''}<div style="font-size:11px;color:var(--text-lo);max-width:320px;white-space:pre-wrap;max-height:80px;overflow:auto">${esc(h.blockerDetail || h.outcome.summary || '')}</div>`
|
|
8874
8320
|
: '<span style="color:var(--text-xs)">none claimed</span>',
|
|
8875
|
-
h.runnableTasksAtStop ? `<span style="color:var(--amber, #d29922)">${h.runnableTasksAtStop}</span>` : '0',
|
|
8321
|
+
h.runnableTasksAtStop ? `<span style="color:var(--amber, #d29922)">${esc(h.runnableTasksAtStop)}</span>` : '0',
|
|
8876
8322
|
mono(_rtTok(h.totalTokens)),
|
|
8877
8323
|
mono(fmtCost(h.totalCostUsd)),
|
|
8878
8324
|
]), 'No finished runs yet');
|
|
@@ -9177,177 +8623,6 @@ function _ofSaveImage(defaultName) {
|
|
|
9177
8623
|
a.click();
|
|
9178
8624
|
}
|
|
9179
8625
|
|
|
9180
|
-
// ── WORKSPACES ─────────────────────────────────────────────
|
|
9181
|
-
async function v2RenderOrgWorkspaces() {
|
|
9182
|
-
const el = document.getElementById('odt-workspaces');
|
|
9183
|
-
if (!el || !_v2SelOrg) return;
|
|
9184
|
-
const _rendOrg = _v2SelOrg;
|
|
9185
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
9186
|
-
try {
|
|
9187
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
9188
|
-
const data = await fetch(`/api/org/${_enc}/workspaces${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
|
|
9189
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
9190
|
-
const ws = Array.isArray(data) ? data : (data.workspaces || []);
|
|
9191
|
-
if (!ws.length) { el.innerHTML = '<div class="empty">No workspaces configured</div>'; return; }
|
|
9192
|
-
el.innerHTML = ws.map(w => `<div style="padding:10px 0;border-bottom:1px solid var(--border)">
|
|
9193
|
-
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
|
9194
|
-
<span style="font-size:13px;color:var(--text-hi);font-weight:500">${esc(w.name || w.id || '—')}</span>
|
|
9195
|
-
${w.branch ? `<span class="ss-pill">${esc(w.branch)}</span>` : ''}
|
|
9196
|
-
</div>
|
|
9197
|
-
${w.path ? `<div style="font-size:11px;color:var(--text-lo);font-family:var(--mono)">${esc(w.path)}</div>` : ''}
|
|
9198
|
-
${w.services?.length ? `<div style="font-size:11px;color:var(--text-lo);margin-top:3px">${w.services.map(s=>esc(s)).join(', ')}</div>` : ''}
|
|
9199
|
-
</div>`).join('');
|
|
9200
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
9201
|
-
}
|
|
9202
|
-
|
|
9203
|
-
// ── INVITES ────────────────────────────────────────────────
|
|
9204
|
-
async function v2RenderOrgInvites() {
|
|
9205
|
-
const el = document.getElementById('odt-invites');
|
|
9206
|
-
if (!el || !_v2SelOrg) return;
|
|
9207
|
-
const _rendOrg = _v2SelOrg;
|
|
9208
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
9209
|
-
try {
|
|
9210
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
9211
|
-
const data = await fetch(`/api/org/${_enc}/invites${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
|
|
9212
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
9213
|
-
const invites = Array.isArray(data) ? data : (data.invites || []);
|
|
9214
|
-
if (!invites.length) { el.innerHTML = '<div class="empty">No active invite tokens</div>'; return; }
|
|
9215
|
-
el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
9216
|
-
<thead><tr style="color:var(--text-xs);text-align:left">
|
|
9217
|
-
<th style="padding:5px 8px">Token</th><th>Role</th><th>Expires</th><th>Created</th>
|
|
9218
|
-
</tr></thead>
|
|
9219
|
-
<tbody>${invites.map(i => `<tr style="border-top:1px solid var(--border)">
|
|
9220
|
-
<td style="padding:5px 8px;color:var(--text-hi);font-family:var(--mono);font-size:11px">${esc((i.token||i.id||'—').slice(0,16))}…</td>
|
|
9221
|
-
<td style="padding:5px 8px"><span class="ss-pill">${esc(i.role||'viewer')}</span></td>
|
|
9222
|
-
<td style="padding:5px 8px;color:var(--text-lo)">${i.expiresAt ? relTime(i.expiresAt) : '—'}</td>
|
|
9223
|
-
<td style="padding:5px 8px;color:var(--text-lo)">${i.createdAt ? relTime(i.createdAt) : '—'}</td>
|
|
9224
|
-
</tr>`).join('')}</tbody>
|
|
9225
|
-
</table>`;
|
|
9226
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
9227
|
-
}
|
|
9228
|
-
|
|
9229
|
-
// ── ENVIRONMENTS ───────────────────────────────────────────
|
|
9230
|
-
async function v2RenderOrgEnvironments() {
|
|
9231
|
-
const el = document.getElementById('odt-environments');
|
|
9232
|
-
if (!el || !_v2SelOrg) return;
|
|
9233
|
-
const _rendOrg = _v2SelOrg;
|
|
9234
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
9235
|
-
try {
|
|
9236
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
9237
|
-
const data = await fetch(`/api/org/${_enc}/environments${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
|
|
9238
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
9239
|
-
const envs = Array.isArray(data) ? data : (data.environments || []);
|
|
9240
|
-
if (!envs.length) { el.innerHTML = '<div class="empty">No environments configured</div>'; return; }
|
|
9241
|
-
el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
9242
|
-
<thead><tr style="color:var(--text-xs);text-align:left">
|
|
9243
|
-
<th style="padding:5px 8px">Name</th><th>Driver</th><th>Host</th><th>Status</th>
|
|
9244
|
-
</tr></thead>
|
|
9245
|
-
<tbody>${envs.map(e => `<tr style="border-top:1px solid var(--border)">
|
|
9246
|
-
<td style="padding:5px 8px;color:var(--text-hi);font-weight:500">${esc(e.name||e.id||'—')}</td>
|
|
9247
|
-
<td style="padding:5px 8px;color:var(--text-lo);font-family:var(--mono)">${esc(e.driver||e.type||'—')}</td>
|
|
9248
|
-
<td style="padding:5px 8px;color:var(--text-lo);font-family:var(--mono)">${esc(e.host||'—')}</td>
|
|
9249
|
-
<td style="padding:5px 8px"><span class="ss-pill ${e.status==='active'||e.active?'on':''}">${esc(e.status||'—')}</span></td>
|
|
9250
|
-
</tr>`).join('')}</tbody>
|
|
9251
|
-
</table>`;
|
|
9252
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
9253
|
-
}
|
|
9254
|
-
|
|
9255
|
-
// ── ACCESS ─────────────────────────────────────────────────
|
|
9256
|
-
function v2RenderOrgAccess() {
|
|
9257
|
-
const el = document.getElementById('odt-access');
|
|
9258
|
-
if (!el || !_v2OrgData) return;
|
|
9259
|
-
const members = _v2OrgData._members || [];
|
|
9260
|
-
if (!members.length) { el.innerHTML = '<div class="empty">No members found</div>'; return; }
|
|
9261
|
-
const TIER = { owner: 0, admin: 1, operator: 2, viewer: 3 };
|
|
9262
|
-
const byRole = {};
|
|
9263
|
-
members.forEach(m => { const r = m.role || 'viewer'; (byRole[r] || (byRole[r] = [])).push(m); });
|
|
9264
|
-
const sorted = Object.entries(byRole).sort((a,b) => (TIER[a[0]]??9) - (TIER[b[0]]??9));
|
|
9265
|
-
el.innerHTML = sorted.map(([role, mems]) => `
|
|
9266
|
-
<div style="margin-bottom:14px">
|
|
9267
|
-
<div class="m-group-title" style="margin-bottom:6px;text-transform:capitalize">${esc(role)}</div>
|
|
9268
|
-
${mems.map(m => `<div style="display:flex;align-items:center;gap:10px;padding:6px 0;border-bottom:1px solid var(--border);font-size:12px">
|
|
9269
|
-
<div style="width:28px;height:28px;border-radius:50%;background:var(--accent-dim);color:var(--accent);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;flex-shrink:0">${esc((m.name||m.username||m.id||'?')[0].toUpperCase())}</div>
|
|
9270
|
-
<span style="color:var(--text-hi);flex:1">${esc(m.name || m.username || m.id || '—')}</span>
|
|
9271
|
-
${m.grants?.length ? m.grants.map(g => `<span class="ss-pill" style="font-size:10px">${esc(g)}</span>`).join('') : ''}
|
|
9272
|
-
${m.suspended ? '<span class="ss-pill warn">suspended</span>' : ''}
|
|
9273
|
-
</div>`).join('')}
|
|
9274
|
-
</div>`).join('');
|
|
9275
|
-
}
|
|
9276
|
-
|
|
9277
|
-
// ── ISSUES (FULL) ──────────────────────────────────────────
|
|
9278
|
-
function v2RenderOrgIssuesFull() {
|
|
9279
|
-
const el = document.getElementById('odt-issues-full');
|
|
9280
|
-
if (!el || !_v2OrgData) return;
|
|
9281
|
-
const issues = _v2OrgData._issues || [];
|
|
9282
|
-
if (!issues.length) { el.innerHTML = '<div class="empty">No issues found</div>'; return; }
|
|
9283
|
-
const PRIORITY = { urgent:'🔴', high:'🟠', medium:'🟡', low:'🟢' };
|
|
9284
|
-
el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
9285
|
-
<thead><tr style="color:var(--text-xs);text-align:left">
|
|
9286
|
-
<th style="padding:5px 4px">P</th><th style="padding:5px 8px">Title</th><th>Assignee</th><th>Status</th><th>Updated</th>
|
|
9287
|
-
</tr></thead>
|
|
9288
|
-
<tbody>${issues.slice(0,100).map(i => `<tr style="border-top:1px solid var(--border)">
|
|
9289
|
-
<td style="padding:5px 4px">${PRIORITY[i.priority]||'·'}</td>
|
|
9290
|
-
<td style="padding:5px 8px;color:var(--text-hi);max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc((i.title||i.description||'—').slice(0,80))}</td>
|
|
9291
|
-
<td style="padding:5px 8px;color:var(--text-lo)">${esc(i.assignee||'—')}</td>
|
|
9292
|
-
<td style="padding:5px 8px"><span class="ss-pill ${i.status==='done'||i.status==='closed'||i.status==='resolved'?'on':(i.status==='blocked'||i.status==='in_progress')?'warn':''}">${esc(i.status||'open')}</span></td>
|
|
9293
|
-
<td style="padding:5px 8px;color:var(--text-xs);font-family:var(--mono);font-size:11px">${relTime(i.updated_at||i.ts)}</td>
|
|
9294
|
-
</tr>`).join('')}</tbody>
|
|
9295
|
-
</table>`;
|
|
9296
|
-
}
|
|
9297
|
-
|
|
9298
|
-
// ── JOIN REQUESTS ──────────────────────────────────────────
|
|
9299
|
-
async function v2RenderOrgJoinRequests() {
|
|
9300
|
-
const el = document.getElementById('odt-join-requests');
|
|
9301
|
-
if (!el || !_v2SelOrg) return;
|
|
9302
|
-
const _rendOrg = _v2SelOrg;
|
|
9303
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
9304
|
-
try {
|
|
9305
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
9306
|
-
const data = await fetch(`/api/org/${_enc}/join-requests${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
|
|
9307
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
9308
|
-
const reqs = Array.isArray(data) ? data : (data.joinRequests || data.join_requests || []);
|
|
9309
|
-
if (!reqs.length) { el.innerHTML = '<div class="empty">No pending join requests</div>'; return; }
|
|
9310
|
-
el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
9311
|
-
<thead><tr style="color:var(--text-xs);text-align:left">
|
|
9312
|
-
<th style="padding:5px 8px">Requester</th><th>Type</th><th>Status</th><th>Created</th>
|
|
9313
|
-
</tr></thead>
|
|
9314
|
-
<tbody>${reqs.map(r => `<tr style="border-top:1px solid var(--border)">
|
|
9315
|
-
<td style="padding:5px 8px;color:var(--text-hi)">${esc(r.name||r.username||r.id||'—')}</td>
|
|
9316
|
-
<td style="padding:5px 8px;color:var(--text-lo)">${r.type==='agent'?'🤖 agent':'👤 human'}</td>
|
|
9317
|
-
<td style="padding:5px 8px"><span class="ss-pill ${r.status==='approved'?'on':r.status==='rejected'?'warn':''}">${esc(r.status||'pending')}</span></td>
|
|
9318
|
-
<td style="padding:5px 8px;color:var(--text-lo)">${r.createdAt ? relTime(r.createdAt) : '—'}</td>
|
|
9319
|
-
</tr>`).join('')}</tbody>
|
|
9320
|
-
</table>`;
|
|
9321
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
9322
|
-
}
|
|
9323
|
-
|
|
9324
|
-
// ── THREADS ────────────────────────────────────────────────
|
|
9325
|
-
async function v2RenderOrgThreads() {
|
|
9326
|
-
const el = document.getElementById('odt-threads');
|
|
9327
|
-
if (!el || !_v2SelOrg) return;
|
|
9328
|
-
const _rendOrg = _v2SelOrg;
|
|
9329
|
-
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
9330
|
-
try {
|
|
9331
|
-
const _enc = encodeURIComponent(_rendOrg);
|
|
9332
|
-
const data = await fetch(`/api/org/${_enc}/threads${DIR?`?dir=${encodeURIComponent(DIR)}`:''}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
|
|
9333
|
-
if (_v2SelOrg !== _rendOrg) return;
|
|
9334
|
-
const threads = Array.isArray(data) ? data : (data.threads || []);
|
|
9335
|
-
if (!threads.length) { el.innerHTML = '<div class="empty">No threads found</div>'; return; }
|
|
9336
|
-
el.innerHTML = `<table style="width:100%;border-collapse:collapse;font-size:12px">
|
|
9337
|
-
<thead><tr style="color:var(--text-xs);text-align:left">
|
|
9338
|
-
<th style="padding:5px 8px">Subject</th><th>Author</th><th>Messages</th><th>Issue</th><th>Created</th>
|
|
9339
|
-
</tr></thead>
|
|
9340
|
-
<tbody>${threads.map(t => `<tr style="border-top:1px solid var(--border)">
|
|
9341
|
-
<td style="padding:5px 8px;color:var(--text-hi);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc((t.subject||t.title||'—').slice(0,50))}</td>
|
|
9342
|
-
<td style="padding:5px 8px;color:var(--text-lo)">${esc(t.author||t.createdBy||'—')}</td>
|
|
9343
|
-
<td style="padding:5px 8px;color:var(--text-lo);text-align:center">${t.messageCount ?? t.messages ?? '—'}</td>
|
|
9344
|
-
<td style="padding:5px 8px;color:var(--text-lo);font-family:var(--mono);font-size:11px">${t.issueId ? `#${esc(t.issueId.toString().slice(0,8))}` : '—'}</td>
|
|
9345
|
-
<td style="padding:5px 8px;color:var(--text-lo)">${t.createdAt ? relTime(t.createdAt) : '—'}</td>
|
|
9346
|
-
</tr>`).join('')}</tbody>
|
|
9347
|
-
</table>`;
|
|
9348
|
-
} catch (e) { el.innerHTML = `<div class="empty">Failed: ${esc(e.message)}</div>`; }
|
|
9349
|
-
}
|
|
9350
|
-
|
|
9351
8626
|
// stop org
|
|
9352
8627
|
window.v2StopOrg = async () => {
|
|
9353
8628
|
if (!_v2SelOrg) return;
|
|
@@ -9497,7 +8772,7 @@ function _filterOrgList(q) {
|
|
|
9497
8772
|
if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
|
|
9498
8773
|
if (src) src.close();
|
|
9499
8774
|
// Do NOT reset seenKeys here — the server replays ~50 events on every reconnect and
|
|
9500
|
-
// clearing the set lets all replays through
|
|
8775
|
+
// clearing the set lets all replays through a second time.
|
|
9501
8776
|
src = window.mmEventSource(`/api/mastermind-stream${DIR ? `?project=${encodeURIComponent(DIR)}` : ''}`);
|
|
9502
8777
|
src.onmessage = e => {
|
|
9503
8778
|
try {
|
|
@@ -9513,10 +8788,7 @@ function _filterOrgList(q) {
|
|
|
9513
8788
|
// Events without a project field are treated as belonging to the current project.
|
|
9514
8789
|
const evProject = ev.project || '';
|
|
9515
8790
|
if (DIR && evProject && !evProject.startsWith(DIR) && !DIR.startsWith(evProject)) return;
|
|
9516
|
-
|
|
9517
|
-
_v2OrgEventLog[n].push(ev);
|
|
9518
|
-
if (_v2OrgEventLog[n].length > 50) _v2OrgEventLog[n].shift();
|
|
9519
|
-
// Update in-memory agent status so Live tab running-agents section reflects reality.
|
|
8791
|
+
// Update in-memory agent status so the org chart's running dots reflect reality.
|
|
9520
8792
|
// org:agent:online → mark that agent running; org:complete/run:complete → reset all to idle.
|
|
9521
8793
|
if (n === _v2SelOrg && _v2OrgData && Array.isArray(_v2OrgData._agents)) {
|
|
9522
8794
|
if (ev.type === 'org:agent:online' && ev.role) {
|
|
@@ -9529,34 +8801,7 @@ function _filterOrgList(q) {
|
|
|
9529
8801
|
} else if (ev.type === 'org:complete' || ev.type === 'run:complete') {
|
|
9530
8802
|
_v2OrgData._agents.forEach(a => { a.status = 'idle'; });
|
|
9531
8803
|
}
|
|
9532
|
-
if (_v2OrgTab === '
|
|
9533
|
-
else if (_v2OrgTab === 'heartbeats' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2RenderOrgHeartbeats();
|
|
9534
|
-
else if (_v2OrgTab === 'members' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2RenderOrgMembers();
|
|
9535
|
-
else if (_v2OrgTab === 'roles' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2RenderOrgRoles();
|
|
9536
|
-
else if (_v2OrgTab === 'chart' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2UpdateChartRunningDots();
|
|
9537
|
-
// Health tab: update KPI counters live from SSE events so agents_active and
|
|
9538
|
-
// tasks_pending reflect current state without waiting for a run-event re-fetch.
|
|
9539
|
-
if (_v2OrgTab === 'health' && _v2OrgData._health) {
|
|
9540
|
-
if (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete') {
|
|
9541
|
-
_v2OrgData._health.agents_active = _v2OrgData._agents.filter(a => a.status === 'running').length;
|
|
9542
|
-
v2RenderOrgHealth();
|
|
9543
|
-
}
|
|
9544
|
-
if (ev.type === 'org:checkpoint' && ev.pending_tasks != null) {
|
|
9545
|
-
_v2OrgData._health.tasks_pending = ev.pending_tasks;
|
|
9546
|
-
v2RenderOrgHealth();
|
|
9547
|
-
}
|
|
9548
|
-
if (ev.type === 'run:cycle:complete' && ev.pending != null) {
|
|
9549
|
-
_v2OrgData._health.tasks_pending = ev.pending;
|
|
9550
|
-
v2RenderOrgHealth();
|
|
9551
|
-
}
|
|
9552
|
-
}
|
|
9553
|
-
}
|
|
9554
|
-
// Also push live org:comms / org:checkpoint events directly into the Live tab
|
|
9555
|
-
// activity feed so they appear in real-time without waiting for the 5s poll.
|
|
9556
|
-
if (n === _v2SelOrg && _v2OrgTab === 'live' && _v2OrgData) {
|
|
9557
|
-
if (!Array.isArray(_v2OrgData._activity)) _v2OrgData._activity = [];
|
|
9558
|
-
_v2OrgData._activity.push({ ...ev, _sseOnly: true });
|
|
9559
|
-
v2RenderOrgLive();
|
|
8804
|
+
if (_v2OrgTab === 'chart' && (ev.type === 'org:agent:online' || ev.type === 'org:agent:offline' || ev.type === 'org:complete' || ev.type === 'run:complete')) v2UpdateChartRunningDots();
|
|
9560
8805
|
}
|
|
9561
8806
|
// Pulse the org list dot on comms/checkpoint events so the user can see the org is active
|
|
9562
8807
|
// without switching to the activity tab
|
|
@@ -9586,78 +8831,14 @@ function _filterOrgList(q) {
|
|
|
9586
8831
|
const listOrg = _v2Orgs.find(o => o.name === _v2SelOrg) || {};
|
|
9587
8832
|
if (_v2OrgData) _v2OrgData.running = listOrg.running;
|
|
9588
8833
|
v2UpdateOrgHeader(listOrg, _v2OrgData);
|
|
9589
|
-
if (_v2OrgTab === 'health') {
|
|
9590
|
-
// Re-fetch health after run events so stats (agents_active, success_rate, total_runs) are current
|
|
9591
|
-
const _hEnc = encodeURIComponent(_v2SelOrg);
|
|
9592
|
-
fetch(`/api/org/${_hEnc}/health${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
|
|
9593
|
-
.then(r => r.ok ? r.json() : null)
|
|
9594
|
-
.then(h => { if (h && _v2OrgData && _v2SelOrg === n) { _v2OrgData._health = h; v2RenderOrgHealth(); } })
|
|
9595
|
-
.catch(() => { v2RenderOrgHealth(); });
|
|
9596
|
-
}
|
|
9597
|
-
if (_v2OrgTab === 'tasks') {
|
|
9598
|
-
// Re-fetch org data after run events so tasks (todo/doing/done) reflect current store state
|
|
9599
|
-
const _tEnc = encodeURIComponent(_v2SelOrg);
|
|
9600
|
-
fetch(`/api/org/${_tEnc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
|
|
9601
|
-
.then(r => r.ok ? r.json() : null)
|
|
9602
|
-
.then(d => { if (d && _v2OrgData && _v2SelOrg === n && d.tasks !== undefined) { _v2OrgData.tasks = d.tasks; v2RenderOrgTasks(); } })
|
|
9603
|
-
.catch(() => { v2RenderOrgTasks(); });
|
|
9604
|
-
}
|
|
9605
|
-
if (_v2OrgTab === 'board' || _v2OrgTab === 'issues-full') {
|
|
9606
|
-
// Re-fetch issues after run events so board/issues-full reflect current state
|
|
9607
|
-
const _iEnc = encodeURIComponent(_v2SelOrg);
|
|
9608
|
-
fetch(`/api/org/${_iEnc}/issues${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
|
|
9609
|
-
.then(r => r.ok ? r.json() : null)
|
|
9610
|
-
.then(d => { if (d && _v2OrgData && _v2SelOrg === n) { _v2OrgData._issues = Array.isArray(d) ? d : (d?.issues || []); _v2OrgTab === 'board' ? v2RenderOrgBoard() : v2RenderOrgIssuesFull(); } })
|
|
9611
|
-
.catch(() => { _v2OrgTab === 'board' ? v2RenderOrgBoard() : v2RenderOrgIssuesFull(); });
|
|
9612
|
-
}
|
|
9613
|
-
if (_v2OrgTab === 'goals') {
|
|
9614
|
-
// Re-fetch org data after run events so goal progress (filled/total/status) reflects current state
|
|
9615
|
-
const _glEnc = encodeURIComponent(_v2SelOrg);
|
|
9616
|
-
fetch(`/api/org/${_glEnc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
|
|
9617
|
-
.then(r => r.ok ? r.json() : null)
|
|
9618
|
-
.then(d => { if (d && _v2OrgData && _v2SelOrg === n && d.goals !== undefined) { _v2OrgData.goals = d.goals; v2RenderOrgGoals(); } })
|
|
9619
|
-
.catch(() => { v2RenderOrgGoals(); });
|
|
9620
|
-
}
|
|
9621
8834
|
if (_v2OrgTab === 'chat') {
|
|
9622
8835
|
// Re-load chat run list after run events so cycleCount/eventCount in dropdown reflect disk state
|
|
9623
8836
|
_odtLoadChatSessions().catch(() => {});
|
|
9624
8837
|
}
|
|
9625
|
-
if (_v2OrgTab === 'heartbeats') {
|
|
9626
|
-
// Re-fetch agents after run events to get fresh tokensIn/tokensOut/lastHeartbeat
|
|
9627
|
-
// Heartbeats tab derives from _v2OrgData._agents.
|
|
9628
|
-
const _agEnc = encodeURIComponent(_v2SelOrg);
|
|
9629
|
-
fetch(`/api/org/${_agEnc}/agents${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
|
|
9630
|
-
.then(r => r.ok ? r.json() : null)
|
|
9631
|
-
.then(d => { if (d && _v2OrgData && _v2SelOrg === n) { _v2OrgData._agents = Array.isArray(d) ? d : (d?.agents || []); v2RenderOrgHeartbeats(); } })
|
|
9632
|
-
.catch(() => { v2RenderOrgHeartbeats(); });
|
|
9633
|
-
}
|
|
9634
|
-
if (_v2OrgTab === 'myissues') {
|
|
9635
|
-
// Re-render myissues after run events — it fetches its own data on every render
|
|
9636
|
-
v2RenderOrgMyIssues();
|
|
9637
|
-
}
|
|
9638
|
-
if (_v2OrgTab === 'charts') {
|
|
9639
|
-
// Re-fetch activity after run events so the 14-day heatmap reflects the completed run
|
|
9640
|
-
const _chEnc = encodeURIComponent(_v2SelOrg);
|
|
9641
|
-
fetch(`/api/org/${_chEnc}/activity${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
|
|
9642
|
-
.then(r => r.ok ? r.json() : [])
|
|
9643
|
-
.then(d => { if (_v2OrgData && _v2SelOrg === n) { _v2OrgData._activity = Array.isArray(d) ? d : []; v2RenderOrgCharts(); } })
|
|
9644
|
-
.catch(() => { v2RenderOrgCharts(); });
|
|
9645
|
-
}
|
|
9646
8838
|
}
|
|
9647
8839
|
}
|
|
9648
8840
|
}, 400);
|
|
9649
8841
|
}
|
|
9650
|
-
// run:cycle:complete: lightweight re-fetch for tasks without renderOrgs()
|
|
9651
|
-
// — cycles fire frequently and only affect task state, not org-list status.
|
|
9652
|
-
if (ev.type === 'run:cycle:complete' && _v2SelOrg === n && _v2OrgData) {
|
|
9653
|
-
if (_v2OrgTab === 'tasks') {
|
|
9654
|
-
const _tcEnc = encodeURIComponent(_v2SelOrg);
|
|
9655
|
-
fetch(`/api/org/${_tcEnc}${DIR ? `?dir=${encodeURIComponent(DIR)}` : ''}`)
|
|
9656
|
-
.then(r => r.ok ? r.json() : null)
|
|
9657
|
-
.then(d => { if (d && _v2OrgData && _v2SelOrg === n && d.tasks !== undefined) { _v2OrgData.tasks = d.tasks; v2RenderOrgTasks(); } })
|
|
9658
|
-
.catch(() => { v2RenderOrgTasks(); });
|
|
9659
|
-
}
|
|
9660
|
-
}
|
|
9661
8842
|
} catch(_) {}
|
|
9662
8843
|
};
|
|
9663
8844
|
src.onerror = () => {
|
|
@@ -14513,7 +13694,6 @@ Object.assign(window, {
|
|
|
14513
13694
|
v2HandleAvatarFile: _v2HandleAvatarFile,
|
|
14514
13695
|
orgApprovalAction: _orgApprovalAction,
|
|
14515
13696
|
orgGateAction: _orgGateAction,
|
|
14516
|
-
generateOrgSettingsCmd: _generateOrgSettingsCmd,
|
|
14517
13697
|
setOdtChatMode: _setOdtChatMode,
|
|
14518
13698
|
v2SaveOrgConfig: _v2SaveOrgConfig,
|
|
14519
13699
|
setHeatmapFilter: _setHeatmapFilter,
|