@stackmemoryai/stackmemory 1.10.5 → 1.14.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/LICENSE +131 -64
- package/README.md +107 -24
- package/bin/claude-sm +16 -1
- package/bin/claude-smd +16 -1
- package/bin/codex-smd +16 -1
- package/bin/gemini-sm +16 -1
- package/bin/hermes-sm +21 -0
- package/bin/hermes-smd +21 -0
- package/bin/opencode-sm +16 -1
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +225 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/brain.js +206 -0
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/company-os.js +184 -0
- package/dist/src/cli/commands/context.js +5 -0
- package/dist/src/cli/commands/daemon.js +41 -0
- package/dist/src/cli/commands/handoff.js +40 -9
- package/dist/src/cli/commands/onboard.js +70 -3
- package/dist/src/cli/commands/operator.js +127 -0
- package/dist/src/cli/commands/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +232 -5
- package/dist/src/cli/commands/orchestrator.js +315 -26
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/patterns.js +254 -0
- package/dist/src/cli/commands/portal.js +161 -0
- package/dist/src/cli/commands/scaffold.js +92 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +178 -11
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/commands/sync.js +253 -0
- package/dist/src/cli/commands/tasks.js +130 -1
- package/dist/src/cli/commands/vision.js +221 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/hermes-sm.js +224 -0
- package/dist/src/cli/index.js +105 -39
- package/dist/src/cli/opencode-sm.js +38 -21
- package/dist/src/cli/utils/determinism-watcher.js +66 -0
- package/dist/src/cli/utils/real-cli-bin.js +116 -0
- package/dist/src/core/brain/brain-store.js +187 -0
- package/dist/src/core/brain/brain-sync.js +193 -0
- package/dist/src/core/brain/index.js +78 -0
- package/dist/src/core/brain/types.js +10 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/{integrations/diffmem → core/cache}/index.js +5 -5
- package/dist/src/core/cache/token-estimator.js +39 -0
- package/dist/src/core/config/feature-flags.js +2 -6
- package/dist/src/core/context/frame-database.js +79 -27
- package/dist/src/core/context/recursive-context-manager.js +1 -1
- package/dist/src/core/context/rehydration.js +2 -1
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/cross-search/index.js +10 -0
- package/dist/src/core/database/sqlite-adapter.js +14 -84
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +54 -2
- package/dist/src/core/models/provider-pricing.js +58 -4
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -0
- package/dist/src/core/patterns/index.js +22 -0
- package/dist/src/core/patterns/pattern-applier.js +39 -0
- package/dist/src/core/patterns/pattern-observer.js +157 -0
- package/dist/src/core/patterns/pattern-store.js +259 -0
- package/dist/src/core/patterns/types.js +19 -0
- package/dist/src/core/provenance/confidence-scorer.js +128 -0
- package/dist/src/core/provenance/index.js +40 -0
- package/dist/src/core/provenance/provenance-store.js +194 -0
- package/dist/src/core/provenance/types.js +82 -0
- package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
- package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
- package/dist/src/core/session/project-handoff.js +64 -0
- package/dist/src/core/session/session-manager.js +28 -0
- package/dist/src/core/shared-state/canonical-store.js +564 -0
- package/dist/src/core/skill-packs/index.js +18 -0
- package/dist/src/core/skill-packs/parser.js +42 -0
- package/dist/src/core/skill-packs/registry.js +224 -0
- package/dist/src/core/skill-packs/types.js +79 -0
- package/dist/src/core/storage/cloud-sync-manager.js +116 -0
- package/dist/src/core/storage/cloud-sync.js +574 -0
- package/dist/src/core/storage/two-tier-storage.js +5 -1
- package/dist/src/core/tasks/master-tasks-template.js +43 -0
- package/dist/src/core/tasks/md-task-parser.js +138 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/vision/index.js +27 -0
- package/dist/src/core/vision/signals.js +79 -0
- package/dist/src/core/vision/types.js +22 -0
- package/dist/src/core/vision/vision-file.js +146 -0
- package/dist/src/core/vision/vision-loop.js +220 -0
- package/dist/src/core/wiki/wiki-compiler.js +103 -1
- package/dist/src/daemon/daemon-config.js +52 -0
- package/dist/src/daemon/services/desire-path-service.js +566 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/services/research-stream-service.js +320 -0
- package/dist/src/daemon/services/telemetry-service.js +192 -0
- package/dist/src/daemon/unified-daemon.js +58 -1
- package/dist/src/features/browser/cli-browser-agent.js +417 -0
- package/dist/src/features/browser/stagehand-workflows.js +578 -0
- package/dist/src/features/operator/adapter-factory.js +62 -0
- package/dist/src/features/operator/browser-adapter.js +109 -0
- package/dist/src/features/operator/desktop-adapter.js +125 -0
- package/dist/src/features/operator/index.js +39 -0
- package/dist/src/features/operator/llm-decision.js +137 -0
- package/dist/src/features/operator/operator-logger.js +92 -0
- package/dist/src/features/operator/overnight-runner.js +327 -0
- package/dist/src/features/operator/screen-adapter.js +91 -0
- package/dist/src/features/operator/session-manager.js +127 -0
- package/dist/src/features/operator/state-machine.js +227 -0
- package/dist/src/features/operator/task-queue.js +81 -0
- package/dist/src/features/portal/index.js +26 -0
- package/dist/src/features/portal/server.js +240 -0
- package/dist/src/features/portal/types.js +14 -0
- package/dist/src/features/portal/ui.js +195 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/features/tasks/task-aware-context.js +2 -1
- package/dist/src/features/tui/simple-monitor.js +0 -23
- package/dist/src/features/tui/swarm-monitor.js +8 -66
- package/dist/src/features/web/client/hooks/use-socket.js +12 -0
- package/dist/src/{core/merge/index.js → features/web/client/lib/utils.js} +8 -4
- package/dist/src/features/web/client/next-env.d.js +4 -0
- package/dist/src/features/web/client/stores/session-store.js +12 -0
- package/dist/src/features/web/server/gcp-billing.js +76 -0
- package/dist/src/features/web/server/index.js +10 -0
- package/dist/src/features/web/server/spend-calculator.js +228 -0
- package/dist/src/hooks/schemas.js +6 -1
- package/dist/src/integrations/anthropic/client.js +3 -2
- package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
- package/dist/src/integrations/claude-code/subagent-client.js +307 -11
- package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/linear/webhook-retry.js +196 -0
- package/dist/src/integrations/linear/webhook-server.js +18 -22
- package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -84
- package/dist/src/integrations/mcp/server.js +542 -641
- package/dist/src/integrations/mcp/tool-alias-registry.js +297 -0
- package/dist/src/integrations/mcp/tool-definitions.js +152 -682
- package/dist/src/mcp/stackmemory-mcp-server.js +571 -231
- package/dist/src/orchestrators/multimodal/determinism.js +244 -0
- package/dist/src/orchestrators/multimodal/harness.js +149 -78
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
- package/dist/src/utils/hook-installer.js +0 -8
- package/dist/src/utils/process-cleanup.js +1 -7
- package/docs/README.md +42 -0
- package/docs/guides/README_INSTALL.md +208 -0
- package/package.json +27 -9
- package/packs/coding/python-fastapi/instructions.md +60 -0
- package/packs/coding/python-fastapi/pack.yaml +28 -0
- package/packs/coding/typescript-react/instructions.md +47 -0
- package/packs/coding/typescript-react/pack.yaml +28 -0
- package/packs/core/commands/capture.md +32 -0
- package/packs/core/commands/learn.md +73 -0
- package/packs/core/commands/next.md +36 -0
- package/packs/core/commands/restart.md +58 -0
- package/packs/core/commands/restore.md +29 -0
- package/packs/core/commands/start.md +57 -0
- package/packs/core/commands/stop.md +65 -0
- package/packs/core/commands/summary.md +40 -0
- package/packs/core/manifest.json +24 -0
- package/packs/ops/decision-recovery/instructions.md +65 -0
- package/packs/ops/decision-recovery/pack.yaml +89 -0
- package/scripts/claude-code-wrapper.sh +11 -0
- package/scripts/claude-sm-setup.sh +12 -1
- package/scripts/codex-wrapper.sh +11 -0
- package/scripts/git-hooks/branch-context-manager.sh +11 -0
- package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
- package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
- package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
- package/scripts/hooks/cleanup-shell.sh +12 -1
- package/scripts/hooks/task-complete.sh +12 -1
- package/scripts/install-code-execution-hooks.sh +12 -1
- package/scripts/install-sweep-hook.sh +12 -0
- package/scripts/install.sh +11 -0
- package/scripts/opencode-wrapper.sh +11 -0
- package/scripts/portal/cloud-init.yaml +69 -0
- package/scripts/portal/setup.sh +69 -0
- package/scripts/portal/stackmemory-portal.service +34 -0
- package/scripts/setup-claude-integration.sh +12 -1
- package/scripts/smoke-init-db.sh +23 -0
- package/scripts/stackmemory-daemon.sh +11 -0
- package/scripts/verify-dist.cjs +11 -4
- package/dist/src/cli/commands/ralph.js +0 -1053
- package/dist/src/cli/commands/team.js +0 -168
- package/dist/src/core/context/shared-context-layer.js +0 -620
- package/dist/src/core/context/stack-merge-resolver.js +0 -748
- package/dist/src/core/merge/conflict-detector.js +0 -430
- package/dist/src/core/merge/resolution-engine.js +0 -557
- package/dist/src/core/merge/stack-diff.js +0 -531
- package/dist/src/core/merge/unified-merge-resolver.js +0 -302
- package/dist/src/hooks/diffmem-hooks.js +0 -376
- package/dist/src/integrations/diffmem/client.js +0 -208
- package/dist/src/integrations/diffmem/config.js +0 -14
- package/dist/src/integrations/greptile/client.js +0 -101
- package/dist/src/integrations/greptile/config.js +0 -14
- package/dist/src/integrations/greptile/index.js +0 -11
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
- package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
- package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
- package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -391
- package/dist/src/integrations/ralph/index.js +0 -17
- package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -435
- package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
- package/dist/src/integrations/ralph/loopmax.js +0 -488
- package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
- package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
- package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
- package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
- package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1007
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
- package/scripts/ralph-loop-implementation.js +0 -404
- /package/dist/src/core/{merge → cache}/types.js +0 -0
- /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
- /package/dist/src/{integrations/greptile/types.js → core/trace/trace-event.js} +0 -0
- /package/dist/src/{integrations/ralph → features/operator}/types.js +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
const XTERM_VERSION = "5.3.0";
|
|
6
|
+
const FIT_VERSION = "0.10.0";
|
|
7
|
+
const SOCKET_IO_VERSION = "4.7.5";
|
|
8
|
+
function renderPortalPage(opts) {
|
|
9
|
+
return `<!doctype html>
|
|
10
|
+
<html lang="en">
|
|
11
|
+
<head>
|
|
12
|
+
<meta charset="utf-8" />
|
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
14
|
+
<meta name="theme-color" content="#0b0f17" />
|
|
15
|
+
<title>StackMemory Portal \u2014 ${escapeHtml(opts.session)}</title>
|
|
16
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@${XTERM_VERSION}/css/xterm.min.css" />
|
|
17
|
+
<style>
|
|
18
|
+
:root {
|
|
19
|
+
--bg: #0b0f17;
|
|
20
|
+
--panel: #11161f;
|
|
21
|
+
--border: #1e2733;
|
|
22
|
+
--accent: #7c5cff;
|
|
23
|
+
--accent-dim: #4a3a99;
|
|
24
|
+
--ok: #2ecc71;
|
|
25
|
+
--warn: #f1c40f;
|
|
26
|
+
--err: #e74c3c;
|
|
27
|
+
--text: #c7d0dc;
|
|
28
|
+
--muted: #6b7686;
|
|
29
|
+
}
|
|
30
|
+
* { box-sizing: border-box; }
|
|
31
|
+
html, body {
|
|
32
|
+
margin: 0; height: 100%;
|
|
33
|
+
background: var(--bg); color: var(--text);
|
|
34
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
}
|
|
37
|
+
#app { display: flex; flex-direction: column; height: 100dvh; }
|
|
38
|
+
header {
|
|
39
|
+
display: flex; align-items: center; gap: 12px;
|
|
40
|
+
padding: 10px 14px; background: var(--panel);
|
|
41
|
+
border-bottom: 1px solid var(--border);
|
|
42
|
+
-webkit-app-region: drag; user-select: none;
|
|
43
|
+
}
|
|
44
|
+
.brand { display: flex; align-items: center; gap: 9px; font-weight: 700; letter-spacing: .3px; }
|
|
45
|
+
.logo {
|
|
46
|
+
width: 22px; height: 22px; border-radius: 6px;
|
|
47
|
+
background: linear-gradient(135deg, var(--accent), #34d3ff);
|
|
48
|
+
box-shadow: 0 0 18px rgba(124, 92, 255, .55);
|
|
49
|
+
}
|
|
50
|
+
.session { color: var(--muted); font-size: 12px; }
|
|
51
|
+
.session b { color: var(--text); font-weight: 600; }
|
|
52
|
+
.spacer { flex: 1; }
|
|
53
|
+
.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); box-shadow: 0 0 0 0 rgba(0,0,0,0); transition: background .2s; }
|
|
54
|
+
.dot.ok { background: var(--ok); box-shadow: 0 0 10px var(--ok); }
|
|
55
|
+
.dot.err { background: var(--err); box-shadow: 0 0 10px var(--err); }
|
|
56
|
+
.status { font-size: 12px; color: var(--muted); min-width: 92px; text-align: right; }
|
|
57
|
+
button.tool {
|
|
58
|
+
background: transparent; color: var(--muted); border: 1px solid var(--border);
|
|
59
|
+
border-radius: 7px; padding: 5px 10px; font: inherit; font-size: 12px; cursor: pointer;
|
|
60
|
+
transition: all .15s;
|
|
61
|
+
}
|
|
62
|
+
button.tool:hover { color: var(--text); border-color: var(--accent-dim); }
|
|
63
|
+
#term-wrap { flex: 1; padding: 8px 10px 10px; min-height: 0; }
|
|
64
|
+
#terminal { height: 100%; }
|
|
65
|
+
#overlay {
|
|
66
|
+
position: fixed; inset: 0; display: none; place-items: center;
|
|
67
|
+
background: rgba(5, 8, 13, .85); backdrop-filter: blur(3px); z-index: 10;
|
|
68
|
+
}
|
|
69
|
+
#overlay.show { display: grid; }
|
|
70
|
+
.card {
|
|
71
|
+
background: var(--panel); border: 1px solid var(--border); border-radius: 14px;
|
|
72
|
+
padding: 26px 30px; max-width: 380px; text-align: center;
|
|
73
|
+
box-shadow: 0 24px 60px rgba(0,0,0,.5);
|
|
74
|
+
}
|
|
75
|
+
.card h2 { margin: 0 0 8px; font-size: 17px; }
|
|
76
|
+
.card p { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
|
|
77
|
+
.reconnect { margin-top: 16px; }
|
|
78
|
+
.reconnect button {
|
|
79
|
+
background: var(--accent); color: #fff; border: 0; border-radius: 8px;
|
|
80
|
+
padding: 8px 18px; font: inherit; font-weight: 600; cursor: pointer;
|
|
81
|
+
}
|
|
82
|
+
</style>
|
|
83
|
+
</head>
|
|
84
|
+
<body>
|
|
85
|
+
<div id="app">
|
|
86
|
+
<header>
|
|
87
|
+
<span class="brand"><span class="logo"></span>StackMemory Portal</span>
|
|
88
|
+
<span class="session">session <b>${escapeHtml(opts.session)}</b></span>
|
|
89
|
+
<span class="spacer"></span>
|
|
90
|
+
<button class="tool" id="btn-clear" title="Clear the local view">clear</button>
|
|
91
|
+
<button class="tool" id="btn-fit" title="Re-fit the terminal">fit</button>
|
|
92
|
+
<span class="dot" id="dot"></span>
|
|
93
|
+
<span class="status" id="status">connecting\u2026</span>
|
|
94
|
+
</header>
|
|
95
|
+
<div id="term-wrap"><div id="terminal"></div></div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<div id="overlay">
|
|
99
|
+
<div class="card">
|
|
100
|
+
<h2 id="ov-title">Disconnected</h2>
|
|
101
|
+
<p id="ov-msg">The connection to your agent was lost.</p>
|
|
102
|
+
<div class="reconnect"><button id="btn-reconnect">Reconnect</button></div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@${XTERM_VERSION}/lib/xterm.min.js"></script>
|
|
107
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@${FIT_VERSION}/lib/addon-fit.min.js"></script>
|
|
108
|
+
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@${SOCKET_IO_VERSION}/dist/socket.io.min.js"></script>
|
|
109
|
+
<script>
|
|
110
|
+
(function () {
|
|
111
|
+
var params = new URLSearchParams(location.search);
|
|
112
|
+
var token = params.get('token') || '';
|
|
113
|
+
|
|
114
|
+
var term = new Terminal({
|
|
115
|
+
cursorBlink: true,
|
|
116
|
+
fontSize: 14,
|
|
117
|
+
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
|
|
118
|
+
scrollback: 10000,
|
|
119
|
+
theme: {
|
|
120
|
+
background: '#0b0f17', foreground: '#c7d0dc', cursor: '#7c5cff',
|
|
121
|
+
selectionBackground: '#2a3550', black: '#0b0f17', brightBlack: '#3a4658'
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
var fit = new FitAddon.FitAddon();
|
|
125
|
+
term.loadAddon(fit);
|
|
126
|
+
term.open(document.getElementById('terminal'));
|
|
127
|
+
fit.fit();
|
|
128
|
+
|
|
129
|
+
var dot = document.getElementById('dot');
|
|
130
|
+
var status = document.getElementById('status');
|
|
131
|
+
var overlay = document.getElementById('overlay');
|
|
132
|
+
var ovTitle = document.getElementById('ov-title');
|
|
133
|
+
var ovMsg = document.getElementById('ov-msg');
|
|
134
|
+
|
|
135
|
+
function setStatus(text, state) {
|
|
136
|
+
status.textContent = text;
|
|
137
|
+
dot.className = 'dot' + (state ? ' ' + state : '');
|
|
138
|
+
}
|
|
139
|
+
function showOverlay(title, msg) {
|
|
140
|
+
ovTitle.textContent = title; ovMsg.textContent = msg; overlay.classList.add('show');
|
|
141
|
+
}
|
|
142
|
+
function hideOverlay() { overlay.classList.remove('show'); }
|
|
143
|
+
|
|
144
|
+
var socket = io({ auth: { token: token }, query: { token: token }, reconnectionAttempts: 8 });
|
|
145
|
+
|
|
146
|
+
socket.on('connect', function () {
|
|
147
|
+
setStatus('connected', 'ok'); hideOverlay(); term.focus(); sendResize();
|
|
148
|
+
});
|
|
149
|
+
socket.on('output', function (data) { term.write(data); });
|
|
150
|
+
socket.on('portal:error', function (msg) {
|
|
151
|
+
setStatus('error', 'err'); showOverlay('Cannot start session', String(msg));
|
|
152
|
+
});
|
|
153
|
+
socket.on('disconnect', function () {
|
|
154
|
+
setStatus('disconnected', 'err');
|
|
155
|
+
showOverlay('Disconnected', 'The portal connection dropped. Your tmux session keeps running in the background.');
|
|
156
|
+
});
|
|
157
|
+
socket.on('connect_error', function (err) {
|
|
158
|
+
setStatus('auth failed', 'err');
|
|
159
|
+
showOverlay('Connection refused', (err && err.message) || 'Check your access token and try again.');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
term.onData(function (data) { socket.emit('input', data); });
|
|
163
|
+
|
|
164
|
+
function sendResize() {
|
|
165
|
+
socket.emit('resize', { cols: term.cols, rows: term.rows });
|
|
166
|
+
}
|
|
167
|
+
function doFit() { try { fit.fit(); sendResize(); } catch (e) {} }
|
|
168
|
+
|
|
169
|
+
var rt;
|
|
170
|
+
window.addEventListener('resize', function () { clearTimeout(rt); rt = setTimeout(doFit, 120); });
|
|
171
|
+
document.getElementById('btn-fit').addEventListener('click', doFit);
|
|
172
|
+
document.getElementById('btn-clear').addEventListener('click', function () { term.clear(); term.focus(); });
|
|
173
|
+
document.getElementById('btn-reconnect').addEventListener('click', function () { hideOverlay(); socket.connect(); });
|
|
174
|
+
|
|
175
|
+
setStatus('connecting\u2026');
|
|
176
|
+
})();
|
|
177
|
+
</script>
|
|
178
|
+
</body>
|
|
179
|
+
</html>`;
|
|
180
|
+
}
|
|
181
|
+
function escapeHtml(s) {
|
|
182
|
+
return s.replace(
|
|
183
|
+
/[&<>"']/g,
|
|
184
|
+
(c) => ({
|
|
185
|
+
"&": "&",
|
|
186
|
+
"<": "<",
|
|
187
|
+
">": ">",
|
|
188
|
+
'"': """,
|
|
189
|
+
"'": "'"
|
|
190
|
+
})[c]
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
export {
|
|
194
|
+
renderPortalPage
|
|
195
|
+
};
|
|
@@ -30,7 +30,9 @@ class PtyWrapper {
|
|
|
30
30
|
claudeBin: config.claudeBin || this.findClaude(),
|
|
31
31
|
claudeArgs: config.claudeArgs || [],
|
|
32
32
|
stateFile: config.stateFile || getSweepPath("sweep-state.json"),
|
|
33
|
-
initialInput: config.initialInput || ""
|
|
33
|
+
initialInput: config.initialInput || "",
|
|
34
|
+
onExit: config.onExit || (() => void 0),
|
|
35
|
+
onSignal: config.onSignal || (() => void 0)
|
|
34
36
|
};
|
|
35
37
|
this.stateWatcher = new SweepStateWatcher(this.config.stateFile);
|
|
36
38
|
this.statusBar = new StatusBar();
|
|
@@ -115,8 +117,9 @@ class PtyWrapper {
|
|
|
115
117
|
this.ptyProcess?.resize(newCols, newRows - 1);
|
|
116
118
|
this.statusBar.resize(newRows, newCols);
|
|
117
119
|
});
|
|
118
|
-
this.ptyProcess.onExit(({ exitCode }) => {
|
|
120
|
+
this.ptyProcess.onExit(async ({ exitCode }) => {
|
|
119
121
|
this.cleanup();
|
|
122
|
+
await this.config.onExit(exitCode);
|
|
120
123
|
if (process.env["LINEAR_API_KEY"]) {
|
|
121
124
|
try {
|
|
122
125
|
execSync("stackmemory linear sync", {
|
|
@@ -128,12 +131,17 @@ class PtyWrapper {
|
|
|
128
131
|
}
|
|
129
132
|
process.exit(exitCode);
|
|
130
133
|
});
|
|
131
|
-
const onSignal = () => {
|
|
134
|
+
const onSignal = async (signal) => {
|
|
132
135
|
this.cleanup();
|
|
136
|
+
await this.config.onSignal(signal);
|
|
133
137
|
process.exit(0);
|
|
134
138
|
};
|
|
135
|
-
process.on("SIGINT",
|
|
136
|
-
|
|
139
|
+
process.on("SIGINT", () => {
|
|
140
|
+
void onSignal("SIGINT");
|
|
141
|
+
});
|
|
142
|
+
process.on("SIGTERM", () => {
|
|
143
|
+
void onSignal("SIGTERM");
|
|
144
|
+
});
|
|
137
145
|
}
|
|
138
146
|
acceptPrediction() {
|
|
139
147
|
if (!this.currentPrediction || !this.ptyProcess) return;
|
|
@@ -3,6 +3,7 @@ import { dirname as __pathDirname } from 'path';
|
|
|
3
3
|
const __filename = __fileURLToPath(import.meta.url);
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
5
|
import { logger } from "../../core/monitoring/logger.js";
|
|
6
|
+
import { estimateTokens } from "../../core/cache/token-estimator.js";
|
|
6
7
|
class TaskAwareContextManager {
|
|
7
8
|
db;
|
|
8
9
|
frameManager;
|
|
@@ -293,7 +294,7 @@ class TaskAwareContextManager {
|
|
|
293
294
|
const line = `- [${task.status.toUpperCase()}] ${task.name} (${task.priority})
|
|
294
295
|
`;
|
|
295
296
|
context += line;
|
|
296
|
-
totalTokens += line
|
|
297
|
+
totalTokens += estimateTokens(line);
|
|
297
298
|
relevanceScores[task.task_id] = 1;
|
|
298
299
|
});
|
|
299
300
|
context += "\n";
|
|
@@ -2,7 +2,6 @@ import { fileURLToPath as __fileURLToPath } from 'url';
|
|
|
2
2
|
import { dirname as __pathDirname } from 'path';
|
|
3
3
|
const __filename = __fileURLToPath(import.meta.url);
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
|
-
import { SwarmRegistry } from "../../integrations/ralph/monitoring/swarm-registry.js";
|
|
6
5
|
import { execSync } from "child_process";
|
|
7
6
|
class SimpleSwarmMonitor {
|
|
8
7
|
refreshInterval = null;
|
|
@@ -10,7 +9,6 @@ class SimpleSwarmMonitor {
|
|
|
10
9
|
* Start simple text-based monitoring
|
|
11
10
|
*/
|
|
12
11
|
start() {
|
|
13
|
-
console.log("\u{1F9BE} Ralph Swarm Monitor (Text Mode)");
|
|
14
12
|
console.log("=====================================");
|
|
15
13
|
console.log("");
|
|
16
14
|
console.log("Press Ctrl+C to quit");
|
|
@@ -42,7 +40,6 @@ class SimpleSwarmMonitor {
|
|
|
42
40
|
\u23F0 ${timestamp} - Swarm Status Update`);
|
|
43
41
|
console.log("\u2500".repeat(50));
|
|
44
42
|
try {
|
|
45
|
-
const registry = SwarmRegistry.getInstance();
|
|
46
43
|
const activeSwarms = registry.listActiveSwarms();
|
|
47
44
|
const stats = registry.getStatistics();
|
|
48
45
|
console.log(
|
|
@@ -59,26 +56,6 @@ class SimpleSwarmMonitor {
|
|
|
59
56
|
} else {
|
|
60
57
|
console.log("\u274C No active swarms in registry");
|
|
61
58
|
}
|
|
62
|
-
try {
|
|
63
|
-
const ralphProcesses = execSync(
|
|
64
|
-
'ps aux | grep "ralph" | grep -v grep',
|
|
65
|
-
{ encoding: "utf8" }
|
|
66
|
-
);
|
|
67
|
-
if (ralphProcesses.trim()) {
|
|
68
|
-
console.log("\n\u{1F50D} External Ralph Processes:");
|
|
69
|
-
const processLines = ralphProcesses.split("\n").filter((line) => line.trim());
|
|
70
|
-
processLines.slice(0, 3).forEach((line) => {
|
|
71
|
-
const parts = line.split(/\s+/);
|
|
72
|
-
console.log(
|
|
73
|
-
` PID ${parts[1]}: ${parts.slice(10).join(" ").slice(0, 50)}...`
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
if (processLines.length > 3) {
|
|
77
|
-
console.log(` ... and ${processLines.length - 3} more processes`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
} catch {
|
|
81
|
-
}
|
|
82
59
|
try {
|
|
83
60
|
const recentCommits = execSync(
|
|
84
61
|
'git log --oneline --since="1 hour ago" --pretty=format:"%h %an %s" | head -3',
|
|
@@ -4,8 +4,6 @@ const __filename = __fileURLToPath(import.meta.url);
|
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
5
|
import blessed from "blessed";
|
|
6
6
|
import { logger } from "../../core/monitoring/logger.js";
|
|
7
|
-
import { SwarmDashboard } from "../../integrations/ralph/monitoring/swarm-dashboard.js";
|
|
8
|
-
import { SwarmRegistry } from "../../integrations/ralph/monitoring/swarm-registry.js";
|
|
9
7
|
import { execSync } from "child_process";
|
|
10
8
|
class SwarmTUI {
|
|
11
9
|
screen;
|
|
@@ -14,8 +12,6 @@ class SwarmTUI {
|
|
|
14
12
|
agentsTable;
|
|
15
13
|
metricsBox;
|
|
16
14
|
logBox;
|
|
17
|
-
swarmCoordinator = null;
|
|
18
|
-
swarmDashboard = null;
|
|
19
15
|
refreshInterval = null;
|
|
20
16
|
commitMetrics = /* @__PURE__ */ new Map();
|
|
21
17
|
constructor() {
|
|
@@ -24,7 +20,6 @@ class SwarmTUI {
|
|
|
24
20
|
this.screen = blessed.screen({
|
|
25
21
|
smartCSR: !isGhostty,
|
|
26
22
|
// Disable smart CSR for ghostty
|
|
27
|
-
title: "Ralph Swarm Monitor",
|
|
28
23
|
terminal: isGhostty ? "xterm-256color" : void 0,
|
|
29
24
|
fullUnicode: !isBasicTerm,
|
|
30
25
|
dockBorders: false,
|
|
@@ -39,9 +34,7 @@ class SwarmTUI {
|
|
|
39
34
|
console.log(
|
|
40
35
|
"\u26A0\uFE0F TUI display error detected. Try setting TERM=xterm-256color"
|
|
41
36
|
);
|
|
42
|
-
console.log(
|
|
43
|
-
" Alternative: Use stackmemory ralph status for text-based monitoring"
|
|
44
|
-
);
|
|
37
|
+
console.log();
|
|
45
38
|
});
|
|
46
39
|
this.createUI();
|
|
47
40
|
this.setupKeyHandlers();
|
|
@@ -66,7 +59,6 @@ class SwarmTUI {
|
|
|
66
59
|
left: 0,
|
|
67
60
|
width: "100%",
|
|
68
61
|
height: 3,
|
|
69
|
-
content: "\u{1F9BE} Ralph Swarm Monitor - Real-time Swarm Operations",
|
|
70
62
|
style: safeColors ? {
|
|
71
63
|
fg: "white",
|
|
72
64
|
bold: false
|
|
@@ -229,10 +221,9 @@ class SwarmTUI {
|
|
|
229
221
|
/**
|
|
230
222
|
* Initialize swarm monitoring
|
|
231
223
|
*/
|
|
232
|
-
async initialize(
|
|
224
|
+
async initialize(swarmId) {
|
|
233
225
|
try {
|
|
234
226
|
if (swarmId) {
|
|
235
|
-
const registry = SwarmRegistry.getInstance();
|
|
236
227
|
const swarm = registry.getSwarm(swarmId);
|
|
237
228
|
if (swarm) {
|
|
238
229
|
this.swarmCoordinator = swarm.coordinator;
|
|
@@ -243,7 +234,6 @@ class SwarmTUI {
|
|
|
243
234
|
} else if (swarmCoordinator) {
|
|
244
235
|
this.swarmCoordinator = swarmCoordinator;
|
|
245
236
|
} else {
|
|
246
|
-
const registry = SwarmRegistry.getInstance();
|
|
247
237
|
const activeSwarms = registry.listActiveSwarms();
|
|
248
238
|
if (activeSwarms.length > 0) {
|
|
249
239
|
this.swarmCoordinator = activeSwarms[0].coordinator;
|
|
@@ -251,7 +241,6 @@ class SwarmTUI {
|
|
|
251
241
|
}
|
|
252
242
|
}
|
|
253
243
|
if (this.swarmCoordinator) {
|
|
254
|
-
this.swarmDashboard = new SwarmDashboard(this.swarmCoordinator);
|
|
255
244
|
this.swarmDashboard.startMonitoring(2e3);
|
|
256
245
|
this.swarmDashboard.on("metricsUpdated", (metrics) => {
|
|
257
246
|
this.updateUI(metrics);
|
|
@@ -271,7 +260,6 @@ class SwarmTUI {
|
|
|
271
260
|
*/
|
|
272
261
|
start() {
|
|
273
262
|
this.screen.render();
|
|
274
|
-
this.logBox.log("Ralph Swarm Monitor started");
|
|
275
263
|
this.logBox.log("Monitoring for active swarms...");
|
|
276
264
|
}
|
|
277
265
|
/**
|
|
@@ -450,7 +438,6 @@ Success Rate: ${status.performance.efficiency > 0 ? (status.performance.efficien
|
|
|
450
438
|
*/
|
|
451
439
|
async detectActiveSwarms() {
|
|
452
440
|
try {
|
|
453
|
-
const registry = SwarmRegistry.getInstance();
|
|
454
441
|
const activeSwarms = registry.listActiveSwarms();
|
|
455
442
|
const stats = registry.getStatistics();
|
|
456
443
|
if (activeSwarms.length > 0) {
|
|
@@ -469,22 +456,9 @@ Success Rate: ${status.performance.efficiency > 0 ? (status.performance.efficien
|
|
|
469
456
|
`Found ${activeSwarms.length} active swarms in registry`
|
|
470
457
|
);
|
|
471
458
|
} else {
|
|
472
|
-
|
|
473
|
-
'ps aux | grep "ralph" | grep -v grep',
|
|
474
|
-
{ encoding: "utf8" }
|
|
475
|
-
);
|
|
476
|
-
if (ralphProcesses.trim()) {
|
|
477
|
-
this.logBox.log("Detected Ralph processes running");
|
|
478
|
-
this.statusBox.setContent(
|
|
479
|
-
"External Ralph processes detected\n(Use swarm coordinator for full monitoring)"
|
|
480
|
-
);
|
|
481
|
-
} else {
|
|
482
|
-
this.statusBox.setContent(`No active swarms detected
|
|
459
|
+
this.statusBox.setContent(`No active swarms detected
|
|
483
460
|
Total swarms: ${stats.totalSwarms}
|
|
484
|
-
Completed: ${stats.completedSwarms}
|
|
485
|
-
|
|
486
|
-
Run: stackmemory ralph swarm <task>`);
|
|
487
|
-
}
|
|
461
|
+
Completed: ${stats.completedSwarms}`);
|
|
488
462
|
}
|
|
489
463
|
} catch {
|
|
490
464
|
}
|
|
@@ -518,9 +492,7 @@ Run: stackmemory ralph swarm <task>`);
|
|
|
518
492
|
*/
|
|
519
493
|
startSwarmInteractive() {
|
|
520
494
|
this.logBox.log("\u{1F680} Start Swarm Interactive Mode:");
|
|
521
|
-
this.logBox.log(
|
|
522
|
-
'Example: stackmemory ralph swarm "Implement feature" --agents developer,tester'
|
|
523
|
-
);
|
|
495
|
+
this.logBox.log();
|
|
524
496
|
this.logBox.log(
|
|
525
497
|
'Tip: Run the command in another terminal, then press "d" to detect it'
|
|
526
498
|
);
|
|
@@ -536,14 +508,12 @@ Run: stackmemory ralph swarm <task>`);
|
|
|
536
508
|
);
|
|
537
509
|
} else {
|
|
538
510
|
this.logBox.log("\u274C No active swarm coordinator to stop");
|
|
539
|
-
this.logBox.log("External Ralph processes must be stopped manually");
|
|
540
511
|
}
|
|
541
512
|
}
|
|
542
513
|
/**
|
|
543
514
|
* Show help dialog
|
|
544
515
|
*/
|
|
545
516
|
showHelp() {
|
|
546
|
-
this.logBox.log("\u{1F9BE} Ralph Swarm Monitor - Help");
|
|
547
517
|
this.logBox.log("");
|
|
548
518
|
this.logBox.log("Keyboard Shortcuts:");
|
|
549
519
|
this.logBox.log(" q, Esc, Ctrl+C - Quit TUI");
|
|
@@ -555,17 +525,11 @@ Run: stackmemory ralph swarm <task>`);
|
|
|
555
525
|
this.logBox.log(" d - Detect and list available swarms");
|
|
556
526
|
this.logBox.log("");
|
|
557
527
|
this.logBox.log("Usage:");
|
|
558
|
-
this.logBox.log(
|
|
559
|
-
|
|
560
|
-
);
|
|
561
|
-
this.logBox.log(
|
|
562
|
-
" stackmemory ralph tui --swarm-id <id> # Monitor specific swarm"
|
|
563
|
-
);
|
|
528
|
+
this.logBox.log();
|
|
529
|
+
this.logBox.log();
|
|
564
530
|
this.logBox.log("");
|
|
565
531
|
this.logBox.log("Starting Swarms:");
|
|
566
|
-
this.logBox.log(
|
|
567
|
-
' stackmemory ralph swarm "Task description" --agents developer,tester'
|
|
568
|
-
);
|
|
532
|
+
this.logBox.log();
|
|
569
533
|
this.logBox.log("");
|
|
570
534
|
}
|
|
571
535
|
/**
|
|
@@ -581,7 +545,6 @@ Run: stackmemory ralph swarm <task>`);
|
|
|
581
545
|
async showDetectedSwarms() {
|
|
582
546
|
this.logBox.log("\u{1F50D} Detecting active swarms...");
|
|
583
547
|
try {
|
|
584
|
-
const registry = SwarmRegistry.getInstance();
|
|
585
548
|
const activeSwarms = registry.listActiveSwarms();
|
|
586
549
|
const stats = registry.getStatistics();
|
|
587
550
|
this.logBox.log("");
|
|
@@ -601,27 +564,6 @@ Run: stackmemory ralph swarm <task>`);
|
|
|
601
564
|
} else {
|
|
602
565
|
this.logBox.log("");
|
|
603
566
|
this.logBox.log("\u274C No active swarms in registry");
|
|
604
|
-
try {
|
|
605
|
-
const ralphProcesses = execSync(
|
|
606
|
-
'ps aux | grep "ralph" | grep -v grep',
|
|
607
|
-
{ encoding: "utf8" }
|
|
608
|
-
);
|
|
609
|
-
if (ralphProcesses.trim()) {
|
|
610
|
-
this.logBox.log("\u{1F50D} External Ralph processes detected:");
|
|
611
|
-
ralphProcesses.split("\n").filter((line) => line.trim()).forEach((line) => {
|
|
612
|
-
const parts = line.split(/\s+/);
|
|
613
|
-
this.logBox.log(
|
|
614
|
-
` PID ${parts[1]}: ${parts.slice(10).join(" ").slice(0, 60)}`
|
|
615
|
-
);
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
} catch {
|
|
619
|
-
this.logBox.log("\u{1F50D} No external Ralph processes found");
|
|
620
|
-
}
|
|
621
|
-
this.logBox.log("");
|
|
622
|
-
this.logBox.log(
|
|
623
|
-
'\u{1F4A1} Start a swarm: stackmemory ralph swarm "Task" --agents developer'
|
|
624
|
-
);
|
|
625
567
|
}
|
|
626
568
|
} catch (error) {
|
|
627
569
|
this.logBox.log(`\u274C Detection failed: ${error.message}`);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
"use client";
|
|
6
|
+
import { useSocketContext } from "@/components/socket-provider";
|
|
7
|
+
function useSocket() {
|
|
8
|
+
return useSocketContext();
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
useSocket
|
|
12
|
+
};
|
|
@@ -2,7 +2,11 @@ import { fileURLToPath as __fileURLToPath } from 'url';
|
|
|
2
2
|
import { dirname as __pathDirname } from 'path';
|
|
3
3
|
const __filename = __fileURLToPath(import.meta.url);
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
import { clsx } from "clsx";
|
|
6
|
+
import { twMerge } from "tailwind-merge";
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
cn
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { create } from "zustand";
|
|
6
|
+
const useSessionStore = create((set) => ({
|
|
7
|
+
sessions: [],
|
|
8
|
+
setSessions: (sessions) => set({ sessions })
|
|
9
|
+
}));
|
|
10
|
+
export {
|
|
11
|
+
useSessionStore
|
|
12
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
function getTableReference() {
|
|
6
|
+
const fullTable = process.env["GCP_BILLING_BIGQUERY_TABLE"];
|
|
7
|
+
if (fullTable) return fullTable;
|
|
8
|
+
const project = process.env["GCP_BILLING_PROJECT_ID"];
|
|
9
|
+
const dataset = process.env["GCP_BILLING_DATASET"];
|
|
10
|
+
const table = process.env["GCP_BILLING_TABLE"];
|
|
11
|
+
if (project && dataset && table) {
|
|
12
|
+
return `${project}.${dataset}.${table}`;
|
|
13
|
+
}
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
async function getBigQuery() {
|
|
17
|
+
const { BigQuery } = await import("@google-cloud/bigquery");
|
|
18
|
+
return new BigQuery();
|
|
19
|
+
}
|
|
20
|
+
async function getGcpSpend(days = 30) {
|
|
21
|
+
const table = getTableReference();
|
|
22
|
+
if (!table) return void 0;
|
|
23
|
+
try {
|
|
24
|
+
const bq = await getBigQuery();
|
|
25
|
+
const [totalRows] = await bq.query({
|
|
26
|
+
query: `
|
|
27
|
+
SELECT
|
|
28
|
+
SUM(cost) + SUM(IFNULL((SELECT SUM(c.amount) FROM UNNEST(credits) c), 0)) AS total_cost,
|
|
29
|
+
currency
|
|
30
|
+
FROM \`${table}\`
|
|
31
|
+
WHERE usage_start_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL @days DAY)
|
|
32
|
+
LIMIT 1
|
|
33
|
+
`,
|
|
34
|
+
params: { days },
|
|
35
|
+
location: "US"
|
|
36
|
+
});
|
|
37
|
+
const [dailyRows] = await bq.query({
|
|
38
|
+
query: `
|
|
39
|
+
SELECT
|
|
40
|
+
DATE(usage_start_time) AS date,
|
|
41
|
+
SUM(cost) + SUM(IFNULL((SELECT SUM(c.amount) FROM UNNEST(credits) c), 0)) AS cost_usd
|
|
42
|
+
FROM \`${table}\`
|
|
43
|
+
WHERE usage_start_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL @days DAY)
|
|
44
|
+
GROUP BY date
|
|
45
|
+
ORDER BY date ASC
|
|
46
|
+
`,
|
|
47
|
+
params: { days },
|
|
48
|
+
location: "US"
|
|
49
|
+
});
|
|
50
|
+
const total = Number(totalRows?.[0]?.total_cost ?? 0);
|
|
51
|
+
const currency = String(totalRows?.[0]?.currency ?? "USD");
|
|
52
|
+
const daily = (dailyRows || []).map((r) => ({
|
|
53
|
+
date: String(r.date ?? ""),
|
|
54
|
+
costUsd: Number(r.cost_usd ?? 0)
|
|
55
|
+
})).filter((d) => d.date);
|
|
56
|
+
return {
|
|
57
|
+
totalCostUsd: total,
|
|
58
|
+
daily,
|
|
59
|
+
currency,
|
|
60
|
+
table
|
|
61
|
+
};
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.warn("Failed to fetch GCP spend from BigQuery:", error);
|
|
64
|
+
return void 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function getGcpSpendEnvFallback() {
|
|
68
|
+
const raw = process.env["GCP_AI_SPEND_USD"];
|
|
69
|
+
if (!raw) return void 0;
|
|
70
|
+
const value = Number.parseFloat(raw);
|
|
71
|
+
return Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
getGcpSpend,
|
|
75
|
+
getGcpSpendEnvFallback
|
|
76
|
+
};
|
|
@@ -12,6 +12,7 @@ import { FrameManager } from "../../../core/context/index.js";
|
|
|
12
12
|
import Database from "better-sqlite3";
|
|
13
13
|
import { existsSync } from "fs";
|
|
14
14
|
import { join } from "path";
|
|
15
|
+
import { getSpendSummary } from "./spend-calculator.js";
|
|
15
16
|
function _getEnv(key, defaultValue) {
|
|
16
17
|
const value = process.env[key];
|
|
17
18
|
if (value === void 0) {
|
|
@@ -149,6 +150,15 @@ app.get("/api/analytics", (req, res) => {
|
|
|
149
150
|
res.status(500).json({ error: "Failed to fetch analytics" });
|
|
150
151
|
}
|
|
151
152
|
});
|
|
153
|
+
app.get("/api/evals", async (req, res) => {
|
|
154
|
+
try {
|
|
155
|
+
const spend = await getSpendSummary();
|
|
156
|
+
res.json(spend);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.error("Error fetching evals spend:", error);
|
|
159
|
+
res.status(500).json({ error: "Failed to fetch AI spend estimate" });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
152
162
|
io.on("connection", (socket) => {
|
|
153
163
|
console.log("\u{1F464} Client connected:", socket.id);
|
|
154
164
|
socket.emit("initial-data", {
|