total-recall-brain 3.41.4 → 3.41.6

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.
Files changed (147) hide show
  1. package/bin/total-recall.mjs +10 -6
  2. package/docs/reference/cli-reference.md +18 -0
  3. package/frontend/dist/assets/index-4tGQfTa2.js +5605 -0
  4. package/frontend/dist/assets/index-DtwsDyFM.css +1 -0
  5. package/frontend/dist/index.html +2 -2
  6. package/frontend/package.json +11 -0
  7. package/frontend/src/App.tsx +47 -122
  8. package/frontend/src/components/plugins/plugin-frame.ts +1 -0
  9. package/frontend/src/index.css +152 -86
  10. package/frontend/src/pages/HelpPage.tsx +13 -10
  11. package/frontend/src/pages/MemoryPage.tsx +28 -30
  12. package/frontend/src/pages/NetworkPage.tsx +3 -3
  13. package/frontend/src/pages/PluginsPage.tsx +6 -5
  14. package/frontend/src/pages/RulesPage.tsx +16 -12
  15. package/package.json +4 -3
  16. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +3114 -36
  17. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +4 -1
  18. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +3114 -36
  19. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +4 -1
  20. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +3121 -33
  21. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +4 -1
  22. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +3122 -34
  23. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +4 -1
  24. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +3127 -49
  25. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +5 -2
  26. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +1 -1
  27. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/capability/index.mjs +136 -7
  28. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/cli.mjs +135 -26
  29. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package-lock.json +3121 -33
  30. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/package.json +5 -2
  31. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/plugin.json +228 -9
  32. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/skills/meta-harness/SKILL.md +24 -30
  33. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/alerts.mjs +29 -16
  34. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/cascade.mjs +73 -0
  35. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/coordinator.mjs +182 -365
  36. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/dispatch.mjs +33 -0
  37. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/documentation.mjs +40 -14
  38. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/executor.mjs +4 -4
  39. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/agy.mjs +187 -0
  40. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/claude.mjs +275 -0
  41. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cline.mjs +132 -0
  42. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/codex.mjs +187 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/cursor.mjs +158 -0
  44. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/devin.mjs +157 -0
  45. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/dsh.mjs +183 -0
  46. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/grok.mjs +132 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/hermes.mjs +173 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/index.mjs +87 -0
  49. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/openclaw.mjs +173 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate-adapters/pi.mjs +129 -0
  51. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/gate.mjs +12 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/json-reply.mjs +22 -0
  53. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/learner.mjs +72 -70
  54. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/model-catalog.mjs +195 -0
  55. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/paths.mjs +11 -0
  56. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/pool.mjs +36 -0
  57. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/review.mjs +169 -0
  58. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/router.mjs +136 -346
  59. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/runtime.mjs +1 -1
  60. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/ssss-store.mjs +20 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/telemetry.mjs +43 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/threads.mjs +156 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worker.mjs +117 -35
  64. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/src/worktree.mjs +51 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/meta-harness/ui/card.js +398 -57
  66. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +304 -46
  67. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +367 -41
  68. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +5 -2
  69. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +33 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/config.mjs +2 -2
  71. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/generate.mjs +159 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/graph.mjs +197 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/lint.mjs +153 -0
  74. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/mesh-vaults.mjs +261 -0
  75. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/refresh.mjs +151 -4
  76. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/search.mjs +114 -0
  77. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/token.mjs +96 -0
  78. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +899 -662
  79. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/openwiki.mjs +8987 -0
  80. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/mermaid-diagrams/SKILL.md +44 -0
  81. package/scaffold/.agent/skills/total-recall/plugins/openwiki/vendor/skills/write-connector/SKILL.md +56 -0
  82. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3097 -81
  83. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +4 -1
  84. package/src/cli/agent.mjs +12 -1
  85. package/src/cli/compact.mjs +108 -0
  86. package/src/cli/connect.mjs +56 -1
  87. package/src/cli/context.mjs +55 -23
  88. package/src/cli/doctor.mjs +5 -2
  89. package/src/cli/forget.mjs +2 -0
  90. package/src/cli/harness.mjs +62 -50
  91. package/src/cli/memory-entry.mjs +2 -2
  92. package/src/cli/preflight.mjs +152 -16
  93. package/src/cli/release.mjs +386 -0
  94. package/src/cli/remember.mjs +19 -1
  95. package/src/cli/rules.mjs +54 -12
  96. package/src/cli/startup.mjs +1 -1
  97. package/src/cli/status.mjs +12 -2
  98. package/src/core/claude-hooks.mjs +41 -3
  99. package/src/core/cli-commands.mjs +2 -0
  100. package/src/core/cli-semantic-index.mjs +3 -2
  101. package/src/core/conflict-detector.mjs +23 -12
  102. package/src/core/context-compactor.mjs +145 -0
  103. package/src/core/context-policy.mjs +20 -0
  104. package/src/core/duplicate-guard.mjs +350 -0
  105. package/src/core/meta-harness.mjs +31 -11
  106. package/src/core/preflight-gate.mjs +2 -2
  107. package/src/core/preflight-receipt.mjs +29 -1
  108. package/src/core/scaffold-plugins.mjs +7 -0
  109. package/src/core/stdio-flush.mjs +23 -0
  110. package/src/core/surface.mjs +33 -10
  111. package/src/server/invoke-durability.mjs +39 -0
  112. package/src/server/invoke-write-child.mjs +25 -0
  113. package/src/server/routes/invoke.mjs +7 -0
  114. package/src/server/routes/plugins.mjs +2 -2
  115. package/frontend/dist/assets/index-BIQmqDKP.css +0 -1
  116. package/frontend/dist/assets/index-CRzjQLu7.js +0 -5631
  117. package/frontend/src/components/DaemonLogsTab.tsx +0 -54
  118. package/frontend/src/components/ResearchAgendaTab.tsx +0 -712
  119. package/frontend/src/components/TaskQueueTab.tsx +0 -193
  120. package/frontend/src/components/VoiceInput.tsx +0 -112
  121. package/frontend/src/pages/ChatPage.tsx +0 -1059
  122. package/frontend/src/pages/CollabPage.tsx +0 -470
  123. package/frontend/src/pages/DesignDocsPage.tsx +0 -621
  124. package/frontend/src/pages/FilesPage.tsx +0 -393
  125. package/frontend/src/pages/HealthPage.tsx +0 -359
  126. package/frontend/src/pages/InboxPage.tsx +0 -83
  127. package/frontend/src/pages/InstructionsPage.tsx +0 -583
  128. package/frontend/src/pages/IntegrationsPage.tsx +0 -761
  129. package/frontend/src/pages/MeshPage.css +0 -58
  130. package/frontend/src/pages/MeshPage.tsx +0 -1219
  131. package/frontend/src/pages/OkfPage.tsx +0 -535
  132. package/frontend/src/pages/SandboxPage.tsx +0 -113
  133. package/frontend/src/pages/SkillsPage.tsx +0 -1994
  134. package/frontend/src/pages/TasksPage.tsx +0 -427
  135. package/frontend/src/pages/UsagePage.tsx +0 -702
  136. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +0 -65
  137. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +0 -76
  138. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +0 -19
  139. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +0 -266
  140. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +0 -446
  141. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +0 -152
  142. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +0 -71
  143. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +0 -36
  144. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +0 -35
  145. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +0 -175
  146. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +0 -32
  147. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +0 -575
@@ -1,359 +0,0 @@
1
- import { useState, useEffect } from 'react'
2
- import { fetchHealth, checkUpdate, runUpdate, restartDaemon } from '../api'
3
- import type { HealthData, UpdateCheckResult } from '../types'
4
-
5
- function formatUptime(seconds: number): string {
6
- const d = Math.floor(seconds / 86400)
7
- const h = Math.floor((seconds % 86400) / 3600)
8
- const m = Math.floor((seconds % 3600) / 60)
9
- if (d > 0) return `${d}d ${h}h ${m}m`
10
- if (h > 0) return `${h}h ${m}m`
11
- return `${m}m ${seconds % 60}s`
12
- }
13
-
14
- export default function HealthPage() {
15
- const [health, setHealth] = useState<HealthData | null>(null)
16
- const [error, setError] = useState('')
17
- const [lastPoll, setLastPoll] = useState<Date | null>(null)
18
-
19
- // Auto-Update States
20
- const [updateInfo, setUpdateInfo] = useState<UpdateCheckResult | null>(null)
21
- const [isUpdating, setIsUpdating] = useState(false)
22
- const [showConfirmModal, setShowConfirmModal] = useState(false)
23
- const [updateError, setUpdateError] = useState('')
24
- const [isCheckingUpdate, setIsCheckingUpdate] = useState(false)
25
- const [checkMessage, setCheckMessage] = useState('')
26
- const [isRestartingDaemon, setIsRestartingDaemon] = useState(false)
27
- const [daemonMessage, setDaemonMessage] = useState('')
28
-
29
- const handleRestartDaemon = async () => {
30
- setIsRestartingDaemon(true)
31
- setDaemonMessage('')
32
- try {
33
- const res = await restartDaemon()
34
- setDaemonMessage(res.message || 'Daemon restarted successfully')
35
- setTimeout(() => setDaemonMessage(''), 4000)
36
- const data = await fetchHealth()
37
- setHealth(data)
38
- } catch (e) {
39
- setDaemonMessage(`Failed to restart daemon: ${(e as Error).message}`)
40
- } finally {
41
- setIsRestartingDaemon(false)
42
- }
43
- }
44
-
45
- const handleManualCheckUpdate = async () => {
46
- setIsCheckingUpdate(true)
47
- setCheckMessage('')
48
- setUpdateError('')
49
- try {
50
- const data = await checkUpdate()
51
- setUpdateInfo(data)
52
- if (!data.updateAvailable) {
53
- setCheckMessage(data.previewFeatures ? 'Preview features — running ahead of the public release.' : data.latestVersion ? 'Host package is up to date.' : 'Public release status unavailable.')
54
- setTimeout(() => setCheckMessage(''), 3000)
55
- }
56
- } catch (e) {
57
- setUpdateError(`Failed to check updates: ${(e as Error).message}`)
58
- } finally {
59
- setIsCheckingUpdate(false)
60
- }
61
- }
62
-
63
- useEffect(() => {
64
- let active = true
65
- const poll = async () => {
66
- try {
67
- const data = await fetchHealth()
68
- if (active) {
69
- setHealth(data)
70
- setError('')
71
- setLastPoll(new Date())
72
- }
73
- } catch (e) {
74
- if (active) setError((e as Error).message)
75
- }
76
- }
77
-
78
- poll()
79
- const interval = setInterval(poll, 5000)
80
-
81
- // Check for updates
82
- const checkVersion = async () => {
83
- try {
84
- const data = await checkUpdate()
85
- if (active) setUpdateInfo(data)
86
- } catch (e) {
87
- console.error('Failed to check updates', e)
88
- }
89
- }
90
- checkVersion()
91
- const updateInterval = setInterval(checkVersion, 3600000)
92
-
93
- return () => {
94
- active = false;
95
- clearInterval(interval);
96
- clearInterval(updateInterval);
97
- }
98
- }, [])
99
-
100
- const handleStartUpdate = async () => {
101
- setShowConfirmModal(false)
102
- setIsUpdating(true)
103
- setUpdateError('')
104
-
105
- try {
106
- const result = await runUpdate()
107
- if (!result.success) {
108
- setUpdateError(result.message || 'Package auto-update reported failures')
109
- setIsUpdating(false)
110
- return
111
- }
112
-
113
- // Multi-repo npm update does not reboot this host — show success, optional reload
114
- setIsUpdating(false)
115
- setCheckMessage(
116
- result.message ||
117
- 'Registered projects updated. Restart daemon if you need new code running.',
118
- )
119
-
120
- // Refresh version check immediately so banner / badge clears
121
- const refreshed = await checkUpdate().catch(() => null)
122
- if (refreshed) {
123
- setUpdateInfo(refreshed)
124
- }
125
- } catch (e) {
126
- setUpdateError((e as Error).message)
127
- setIsUpdating(false)
128
- }
129
- }
130
-
131
- return (
132
- <div className="page" style={{ position: 'relative' }}>
133
- {isUpdating && (
134
- <div style={{
135
- position: 'fixed',
136
- inset: 0,
137
- background: 'rgba(13, 17, 23, 0.9)',
138
- zIndex: 1000,
139
- display: 'flex',
140
- flexDirection: 'column',
141
- alignItems: 'center',
142
- justifyContent: 'center',
143
- backdropFilter: 'blur(8px)',
144
- animation: 'fadeIn 0.3s ease'
145
- }}>
146
- <div className="spinner" style={{
147
- width: 50,
148
- height: 50,
149
- border: '4px solid var(--border)',
150
- borderTopColor: 'var(--accent)',
151
- borderRadius: '50%',
152
- animation: 'spin 1s linear infinite',
153
- marginBottom: 20
154
- }} />
155
- <h2 style={{ color: 'var(--text-primary)', marginBottom: 8 }}>Updating Total Recall...</h2>
156
- <p style={{ color: 'var(--text-muted)', fontSize: 14 }}>Applying git changes, installing npm modules, and restarting daemon.</p>
157
- <p style={{ color: 'var(--accent)', fontSize: 13, marginTop: 10 }}>Please do not close this window.</p>
158
- </div>
159
- )}
160
-
161
- {showConfirmModal && (
162
- <div style={{
163
- position: 'fixed',
164
- inset: 0,
165
- background: 'rgba(0, 0, 0, 0.7)',
166
- zIndex: 900,
167
- display: 'flex',
168
- alignItems: 'center',
169
- justifyContent: 'center',
170
- backdropFilter: 'blur(4px)',
171
- padding: 20
172
- }}>
173
- <div className="card" style={{
174
- maxWidth: 450,
175
- width: '100%',
176
- backgroundColor: 'var(--bg-surface)',
177
- border: '1px solid var(--border)',
178
- borderRadius: 12,
179
- padding: 24,
180
- boxShadow: '0 20px 40px rgba(0, 0, 0, 0.5)'
181
- }}>
182
- <h3 style={{ marginBottom: 12, color: 'var(--text-primary)' }}>Confirm Core Self-Update</h3>
183
- <p style={{ color: 'var(--text-muted)', marginBottom: 20, fontSize: 14, lineHeight: 1.5 }}>
184
- Install Total Recall {updateInfo?.latestVersion} where an update is needed. Newer preview versions are retained.
185
- </p>
186
- <div style={{ display: 'flex', gap: 12, justifyContent: 'flex-end' }}>
187
- <button
188
- className="btn btn-secondary"
189
- onClick={() => setShowConfirmModal(false)}
190
- style={{ padding: '8px 16px', borderRadius: 8 }}
191
- >
192
- Cancel
193
- </button>
194
- <button
195
- className="btn btn-primary"
196
- onClick={handleStartUpdate}
197
- style={{ padding: '8px 16px', borderRadius: 8, background: 'var(--accent)' }}
198
- >
199
- Update Now
200
- </button>
201
- </div>
202
- </div>
203
- </div>
204
- )}
205
-
206
- <div className="page-header" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
207
- <div>
208
- <h1>System Health</h1>
209
- <p>Live monitoring of your memory brain {lastPoll && <span>· Last poll: {lastPoll.toLocaleTimeString()}</span>}</p>
210
- </div>
211
- {updateInfo?.updateAvailable ? (
212
- <div className="badge badge-success" style={{
213
- display: 'flex',
214
- alignItems: 'center',
215
- gap: 12,
216
- padding: '12px 18px',
217
- borderRadius: 12,
218
- background: 'rgba(63, 185, 80, 0.1)',
219
- border: '1px solid rgba(63, 185, 80, 0.3)',
220
- animation: 'pulse 2s infinite'
221
- }}>
222
- <span style={{ fontSize: 14, color: 'var(--text-primary)', fontWeight: 600 }}>
223
- New version available: {updateInfo.latestVersion}
224
- </span>
225
- <button
226
- className="btn btn-primary"
227
- onClick={() => setShowConfirmModal(true)}
228
- style={{
229
- fontSize: 12,
230
- padding: '6px 12px',
231
- borderRadius: 6,
232
- backgroundColor: '#3fb950',
233
- color: '#fff',
234
- border: 'none',
235
- cursor: 'pointer',
236
- fontWeight: 600
237
- }}
238
- >
239
- Update Now
240
- </button>
241
- </div>
242
- ) : (
243
- <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
244
- {checkMessage && (
245
- <span style={{ color: '#3fb950', fontSize: 14, fontWeight: 500, animation: 'fadeIn 0.3s ease' }}>
246
- {checkMessage}
247
- </span>
248
- )}
249
- <button
250
- className="btn btn-secondary"
251
- disabled={isCheckingUpdate}
252
- onClick={handleManualCheckUpdate}
253
- style={{
254
- fontSize: 13,
255
- padding: '8px 16px',
256
- borderRadius: 8,
257
- cursor: 'pointer',
258
- display: 'flex',
259
- alignItems: 'center',
260
- gap: 6
261
- }}
262
- >
263
- {isCheckingUpdate ? 'Checking...' : 'Check for Updates'}
264
- </button>
265
- </div>
266
- )}
267
- </div>
268
-
269
- {error && <div className="badge badge-error" style={{ marginBottom: 16 }}>⚠️ {error}</div>}
270
- {updateError && <div className="badge badge-error" style={{ marginBottom: 16 }}>⚠️ Update failed: {updateError}</div>}
271
-
272
- {health ? (
273
- <>
274
- <div className="health-grid">
275
- <div className="stat-card healthy">
276
- <div className="value" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
277
- <span className="pulse" /> {health.status}
278
- </div>
279
- <div className="label">Status</div>
280
- </div>
281
- <div className="stat-card">
282
- <div className="value" style={{ color: 'var(--accent)' }}>
283
- {health.version} {updateInfo?.previewFeatures
284
- ? <span style={{ fontSize: 12, color: 'var(--text-muted)' }}>(Preview features)</span>
285
- : updateInfo?.latestVersion && !updateInfo.updateAvailable && <span style={{ fontSize: 12, color: 'var(--text-muted)' }}>(Latest)</span>}
286
- </div>
287
- <div className="label">Version</div>
288
- </div>
289
- <div className="stat-card">
290
- <div className="value" style={{ color: 'var(--text-primary)' }}>{formatUptime(health.uptime_seconds)}</div>
291
- <div className="label">Uptime</div>
292
- </div>
293
- <div className="stat-card">
294
- <div className="value" style={{ color: 'var(--text-primary)', fontSize: 16 }}>{new Date(health.timestamp).toLocaleString()}</div>
295
- <div className="label">Server Time</div>
296
- </div>
297
- </div>
298
-
299
- <div className="health-log">
300
- <h3>System Info</h3>
301
- <table>
302
- <tbody>
303
- <tr><th>Status</th><td>{health.status}</td></tr>
304
- <tr><th>Version</th><td>{health.version}</td></tr>
305
- <tr><th>Uptime</th><td>{formatUptime(health.uptime_seconds)}</td></tr>
306
- <tr><th>Timestamp</th><td>{health.timestamp}</td></tr>
307
- <tr>
308
- <th>Daemon Status</th>
309
- <td>
310
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
311
- <span style={{ fontWeight: 600 }}>{health.daemon || 'unknown'}</span>
312
- <button
313
- className="btn btn-secondary"
314
- onClick={handleRestartDaemon}
315
- disabled={isRestartingDaemon}
316
- style={{
317
- fontSize: 12,
318
- padding: '4px 12px',
319
- borderRadius: 6,
320
- cursor: 'pointer'
321
- }}
322
- >
323
- {isRestartingDaemon ? 'Restarting…' : 'Restart Daemon'}
324
- </button>
325
- </div>
326
- {daemonMessage && (
327
- <div style={{ fontSize: 12, color: daemonMessage.includes('Failed') ? '#ef4444' : '#3fb950', marginTop: 4 }}>
328
- {daemonMessage}
329
- </div>
330
- )}
331
- </td>
332
- </tr>
333
- {health.cli_agents && (
334
- <tr><th>CLI Agents</th><td>{health.cli_agents.join(', ')}</td></tr>
335
- )}
336
- <tr><th>Polling</th><td>Every 5 seconds</td></tr>
337
- </tbody>
338
- </table>
339
- </div>
340
- </>
341
- ) : !error ? (
342
- <div className="health-grid">
343
- {[1, 2, 3, 4].map(i => <div key={i} className="skeleton" style={{ height: 100 }} />)}
344
- </div>
345
- ) : null}
346
-
347
- <style>{`
348
- @keyframes spin {
349
- 0% { transform: rotate(0deg); }
350
- 100% { transform: rotate(360deg); }
351
- }
352
- @keyframes fadeIn {
353
- from { opacity: 0; }
354
- to { opacity: 1; }
355
- }
356
- `}</style>
357
- </div>
358
- )
359
- }
@@ -1,83 +0,0 @@
1
- import { useState, useEffect, useCallback } from "react"
2
- import { fetchDocs, readDoc, updateDoc, postDecision, type VaultDocument } from "../api"
3
- import { DocumentTable } from "../components/DocumentTable"
4
-
5
- export default function InboxPage({ activeBrainId }: { activeBrainId?: string }) {
6
- const [items, setItems] = useState<VaultDocument[]>([])
7
- const [loading, setLoading] = useState(true)
8
- const [error, setError] = useState("")
9
-
10
- const loadData = useCallback(async () => {
11
- try {
12
- setLoading(true)
13
- const data = await fetchDocs(activeBrainId, { status: "pending_approval" })
14
- setItems(data.docs)
15
- } catch (err: unknown) {
16
- setError(err instanceof Error ? err.message : "Failed to load the approval inbox")
17
- } finally {
18
- setLoading(false)
19
- }
20
- }, [activeBrainId])
21
-
22
- useEffect(() => {
23
- const timer = window.setTimeout(() => { void loadData() }, 0)
24
- return () => window.clearTimeout(timer)
25
- }, [loadData])
26
-
27
- const handleDecision = async (path: string, action: string) => {
28
- setError("")
29
- try {
30
- const doc = await readDoc(path, activeBrainId)
31
- // Extract proposal ID from path (e.g. proposals/p2.md -> p2)
32
- const idMatch = path.match(/proposals\/(.+?)\.md$/)
33
- if (idMatch) {
34
- const id = idMatch[1]
35
- // This hits the proxy, which forwards to droplet and runs sync
36
- await postDecision(id, action)
37
- } else {
38
- // Fallback for non-proposal docs (just optimistic update)
39
- const newBody = doc.raw.replace(/status:.*$/m, `status: ${action}`)
40
- await updateDoc(path, newBody, activeBrainId)
41
- }
42
- loadData()
43
- } catch (err: unknown) {
44
- setError("Decision failed: " + (err instanceof Error ? err.message : "Unknown error"))
45
- }
46
- }
47
-
48
- return (
49
- <div style={{ padding: '20px', maxWidth: '1200px', margin: '0 auto', color: 'white' }}>
50
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
51
- <h1 style={{ margin: 0, fontSize: '24px' }}>Approval Inbox</h1>
52
- <button onClick={loadData} style={{ padding: '8px 16px', background: 'rgba(255,255,255,0.1)', border: 'none', color: 'white', cursor: 'pointer', borderRadius: '4px' }}>Refresh</button>
53
- </div>
54
-
55
- {loading && <p>Loading inbox items...</p>}
56
- {error && <p style={{ color: 'red' }}>Error: {error}</p>}
57
-
58
- {!loading && !error && (
59
- <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: '30px' }}>
60
- <div>
61
- <DocumentTable
62
- data={items}
63
- emptyMessage="No items pending approval."
64
- columns={[
65
- { key: 'type', header: 'Type', render: (d) => <span style={{ opacity: 0.6 }}>{d.type}</span> },
66
- { key: 'name', header: 'Name', render: (d) => d.name },
67
- { key: 'status', header: 'Status', render: (d) => <span style={{ fontSize: '0.8em', background: 'rgba(234,179,8,0.15)', color: '#eab308', padding: '2px 6px', borderRadius: '4px' }}>{d.status || 'pending'}</span> },
68
- { key: 'updated', header: 'Last Updated', render: (d) => d.updatedAt ? new Date(d.updatedAt).toLocaleDateString() : '-' },
69
- { key: 'actions', header: 'Actions', render: (d) => (
70
- <>
71
- <button onClick={() => handleDecision(d.path, 'approved')} style={{ marginRight: '10px', background: '#22c55e', border: 'none', color: 'white', padding: '4px 8px', borderRadius: '4px', cursor: 'pointer' }}>Approve</button>
72
- <button onClick={() => handleDecision(d.path, 'rejected')} style={{ background: '#e22b22', border: 'none', color: 'white', padding: '4px 8px', borderRadius: '4px', cursor: 'pointer' }}>Reject</button>
73
- </>
74
- )
75
- }
76
- ]}
77
- />
78
- </div>
79
- </div>
80
- )}
81
- </div>
82
- )
83
- }