total-recall-brain 3.40.1 → 3.40.2

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 (186) hide show
  1. package/README.md +152 -41
  2. package/bin/total-recall.mjs +16 -76
  3. package/docs/reference/cli-reference.md +10 -10
  4. package/extension/background.js +6 -2
  5. package/extension/config.js +8 -0
  6. package/extension/lib/brain-client.js +29 -4
  7. package/extension/options/options.html +1 -0
  8. package/extension/sidepanel/sidepanel.html +1 -0
  9. package/frontend/dist/assets/{index-CvQRdT2j.css → index-AGUes-VG.css} +1 -1
  10. package/frontend/dist/assets/index-Bt5iVPZj.js +5618 -0
  11. package/frontend/dist/index.html +2 -2
  12. package/frontend/src/api/keys.ts +9 -2
  13. package/frontend/src/api/plugins.ts +4 -0
  14. package/frontend/src/components/plugins/card/CardEngine.tsx +2 -1
  15. package/frontend/src/components/plugins/card/card.css +1 -1
  16. package/frontend/src/pages/IntegrationsPage.tsx +315 -8
  17. package/frontend/src/pages/PluginsPage.tsx +532 -87
  18. package/package.json +6 -5
  19. package/scaffold/.agent/skills/meta-harness/references/optimized/original.md +1 -1
  20. package/scaffold/.agent/skills/meta-harness/references/optimized/section-09.md +1 -1
  21. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +10 -10
  22. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -6
  23. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +0 -1
  24. package/scaffold/.agent/skills/total-recall/plugins/auth/src/browser-session.mjs +309 -4
  25. package/scaffold/.agent/skills/total-recall/plugins/auth/src/cli-secret.mjs +44 -34
  26. package/scaffold/.agent/skills/total-recall/plugins/auth/src/google-oauth.mjs +6 -4
  27. package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-catalog.mjs +1 -1
  28. package/scaffold/.agent/skills/total-recall/plugins/auth/src/provider-rotation-recipes.mjs +118 -4
  29. package/scaffold/.agent/skills/total-recall/plugins/auth/src/routes/auth.mjs +2 -2
  30. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-env-export.mjs +3 -3
  31. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-remote-deploy.mjs +1 -1
  32. package/scaffold/.agent/skills/total-recall/plugins/auth/src/secrets-rotate.mjs +24 -6
  33. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +164 -123
  34. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/README.md +65 -0
  35. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/cli.mjs +76 -0
  36. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/package.json +19 -0
  37. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/plugin.json +290 -0
  38. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/adapters/dsh.mjs +446 -0
  39. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/documentation.mjs +152 -0
  40. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/executor.mjs +71 -0
  41. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/runtime.mjs +36 -0
  42. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/setup.mjs +35 -0
  43. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/src/worker.mjs +175 -0
  44. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +18 -0
  45. package/scaffold/.agent/skills/total-recall/plugins/code-quality/LICENSE +21 -0
  46. package/scaffold/.agent/skills/total-recall/plugins/code-quality/README.md +84 -0
  47. package/scaffold/.agent/skills/total-recall/plugins/code-quality/cli.mjs +79 -0
  48. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/check.mjs +6 -0
  49. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/report.mjs +6 -0
  50. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/run-core.mjs +27 -0
  51. package/scaffold/.agent/skills/total-recall/plugins/code-quality/commands/snapshot.mjs +163 -0
  52. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/check.mjs +757 -0
  53. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/config.schema.json +86 -0
  54. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/detect.mjs +89 -0
  55. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report-data.mjs +145 -0
  56. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/report.mjs +210 -0
  57. package/scaffold/.agent/skills/total-recall/plugins/code-quality/core/state.mjs +20 -0
  58. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +2869 -0
  59. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +28 -0
  60. package/scaffold/.agent/skills/total-recall/plugins/code-quality/plugin.json +186 -0
  61. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +18 -0
  62. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +272 -0
  63. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/code-quality-report.mjs +129 -0
  64. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/LICENSE +21 -0
  65. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/README.md +73 -0
  66. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/cli.mjs +375 -0
  67. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/echo-tool.mjs +23 -0
  68. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/group.mjs +262 -0
  69. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/examples/commands/project-id.mjs +74 -0
  70. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +2869 -0
  71. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +31 -0
  72. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +221 -0
  73. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/schemas/cli_command.schema.json +107 -0
  74. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/skills/composable-cli/SKILL.md +63 -0
  75. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/command.mjs +313 -0
  76. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compile.mjs +19 -0
  77. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/compose.mjs +144 -0
  78. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/contract.mjs +110 -0
  79. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/library.mjs +23 -0
  80. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/registry.mjs +192 -0
  81. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/resolve.mjs +221 -0
  82. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/risk.mjs +126 -0
  83. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/src/ssss-documents.mjs +29 -0
  84. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +18 -0
  85. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +273 -0
  86. package/scaffold/.agent/skills/total-recall/plugins/embeddings/LICENSE +21 -0
  87. package/scaffold/.agent/skills/total-recall/plugins/embeddings/README.md +20 -0
  88. package/scaffold/.agent/skills/total-recall/plugins/embeddings/cli.mjs +310 -0
  89. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +2826 -0
  90. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package.json +11 -0
  91. package/scaffold/.agent/skills/total-recall/plugins/embeddings/plugin.json +62 -0
  92. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +297 -0
  93. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/LICENSE +21 -0
  94. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/README.md +43 -0
  95. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/cli.mjs +258 -0
  96. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4005 -0
  97. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package.json +20 -0
  98. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/plugin.json +125 -0
  99. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/DESIGN.md +18 -0
  100. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +278 -0
  101. package/scaffold/.agent/skills/total-recall/plugins/mesh/README.md +9 -0
  102. package/scaffold/.agent/skills/total-recall/plugins/mesh/cli.mjs +6 -0
  103. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +4721 -0
  104. package/scaffold/.agent/skills/total-recall/plugins/mesh/package.json +3 -21
  105. package/scaffold/.agent/skills/total-recall/plugins/mesh/plugin.json +14 -1
  106. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/cli/mesh.mjs +10 -0
  107. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/headscale-client.mjs +5 -1
  108. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/leader-election.mjs +4 -13
  109. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-access-verify.mjs +22 -0
  110. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh-files.mjs +18 -0
  111. package/scaffold/.agent/skills/total-recall/plugins/mesh/src/core/mesh.mjs +2 -0
  112. package/scaffold/.agent/skills/total-recall/plugins/mesh/test-support/memory-root.mjs +14 -0
  113. package/scaffold/.agent/skills/total-recall/plugins/openwiki/LICENSE +21 -0
  114. package/scaffold/.agent/skills/total-recall/plugins/openwiki/README.md +35 -0
  115. package/scaffold/.agent/skills/total-recall/plugins/openwiki/cli.mjs +124 -0
  116. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4097 -0
  117. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package.json +12 -0
  118. package/scaffold/.agent/skills/total-recall/plugins/openwiki/plugin.json +165 -0
  119. package/scaffold/.agent/skills/total-recall/plugins/openwiki/src/ingest.mjs +127 -0
  120. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +18 -0
  121. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +279 -0
  122. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/panel.js +539 -0
  123. package/scaffold/.agent/skills/total-recall/plugins/security/LICENSE +21 -0
  124. package/scaffold/.agent/skills/total-recall/plugins/security/README.md +67 -0
  125. package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +932 -0
  126. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +3256 -0
  127. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +25 -0
  128. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +297 -0
  129. package/scaffold/.agent/skills/total-recall/plugins/security/scripts/build-ui.mjs +4 -0
  130. package/scaffold/.agent/skills/total-recall/plugins/security/skills/security/SKILL.md +37 -0
  131. package/scaffold/.agent/skills/total-recall/plugins/security/src/android.mjs +21 -0
  132. package/scaffold/.agent/skills/total-recall/plugins/security/src/devices.mjs +94 -0
  133. package/scaffold/.agent/skills/total-recall/plugins/security/src/machines.mjs +164 -0
  134. package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +41 -0
  135. package/scaffold/.agent/skills/total-recall/plugins/security/src/windows.mjs +42 -0
  136. package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +18 -0
  137. package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +95 -0
  138. package/scaffold/.agent/skills/total-recall/plugins/security/ui/panel.js +98 -0
  139. package/scaffold/.agent/skills/total-recall/references/auth.md +1 -1
  140. package/src/cli/brief.mjs +33 -9
  141. package/src/cli/command.mjs +2 -7
  142. package/src/cli/config.mjs +1 -0
  143. package/src/cli/connect.mjs +5 -0
  144. package/src/cli/context.mjs +2 -1
  145. package/src/cli/daemon.mjs +36 -1
  146. package/src/cli/doctor.mjs +6 -0
  147. package/src/cli/harness.mjs +3 -0
  148. package/src/cli/help.mjs +26 -0
  149. package/src/cli/init.mjs +4 -4
  150. package/src/cli/memory-entry.mjs +1 -0
  151. package/src/cli/memory-init.mjs +2 -0
  152. package/src/cli/plugin/index.mjs +11 -0
  153. package/src/cli/plugin/install.mjs +2 -1
  154. package/src/cli/rebuild.mjs +3 -1
  155. package/src/cli/startup.mjs +2 -1
  156. package/src/cli/update.mjs +19 -0
  157. package/src/core/automatic-brain-updates.mjs +67 -0
  158. package/src/core/brain-updates.mjs +21 -2
  159. package/src/core/cli-commands.mjs +71 -0
  160. package/src/core/cli-maintenance.mjs +45 -0
  161. package/src/core/cli-quick-reference.mjs +20 -0
  162. package/src/core/cli-semantic-index.mjs +722 -0
  163. package/src/core/context-compiler.mjs +27 -1
  164. package/src/core/crons.mjs +2 -13
  165. package/src/core/daemon-control.mjs +6 -4
  166. package/src/core/daemon-loop.mjs +12 -8
  167. package/src/core/default-plugins.mjs +5 -0
  168. package/src/core/global-brain-updates.mjs +39 -0
  169. package/src/core/mesh-brain-updates.mjs +30 -0
  170. package/src/core/meta-harness.mjs +108 -0
  171. package/src/core/plugin-dependencies.mjs +2 -1
  172. package/src/core/plugin-loader.mjs +2 -1
  173. package/src/core/plugin-runner.mjs +12 -4
  174. package/src/core/plugin-setup.mjs +20 -0
  175. package/src/core/plugin-store.mjs +22 -4
  176. package/src/core/plugin-tasks.mjs +1 -1
  177. package/src/core/project-brain.mjs +2 -0
  178. package/src/core/project-status.mjs +64 -0
  179. package/src/core/required-plugins.mjs +46 -0
  180. package/src/core/scaffold-plugins.mjs +17 -3
  181. package/src/core/source-brain-updates.mjs +47 -0
  182. package/src/core/startup-health.mjs +5 -3
  183. package/src/core/surface.mjs +44 -10
  184. package/src/server/routes/extension.mjs +53 -8
  185. package/templates/onboarding-interview.md +1 -1
  186. package/frontend/dist/assets/index-SOVUlZqm.js +0 -5596
@@ -17,8 +17,8 @@
17
17
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
18
18
  <link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,600;12..96,800&family=Cabinet+Grotesk:wght@700;800&family=Chivo+Mono:wght@500;700&family=Cinzel:wght@600;700;900&family=Cormorant+Garamond:ital,wght@0,600;0,700;1,600;1,700&family=DM+Mono:wght@500&family=DM+Sans:wght@500;700&family=Fraunces:opsz,wght@9..144,600;9..144,800&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&family=Lexend:wght@600;800&family=Newsreader:ital,opsz,wght@0,6..72,600;1,6..72,600&family=Orbitron:wght@700;900&family=Outfit:wght@500;700;800&family=Plus+Jakarta+Sans:wght@600;700;800&family=Space+Grotesk:wght@600;700&family=Syne:wght@700;800&display=swap" rel="stylesheet" />
19
19
  <title>Total Recall — Portable Memory</title>
20
- <script type="module" crossorigin src="/assets/index-SOVUlZqm.js"></script>
21
- <link rel="stylesheet" crossorigin href="/assets/index-CvQRdT2j.css">
20
+ <script type="module" crossorigin src="/assets/index-Bt5iVPZj.js"></script>
21
+ <link rel="stylesheet" crossorigin href="/assets/index-AGUes-VG.css">
22
22
  </head>
23
23
  <body>
24
24
  <div id="root"></div>
@@ -7,7 +7,8 @@ import { apiFetch, API_BASE } from './_base'
7
7
  export interface ApiKey {
8
8
  id: string
9
9
  name: string
10
- token_preview: string
10
+ token_preview?: string
11
+ token_prefix?: string
11
12
  scopes: string[]
12
13
  expires_at: string | null
13
14
  created_at: string
@@ -29,7 +30,13 @@ export async function listApiKeys(): Promise<ApiKeyListResponse> {
29
30
  const res = await apiFetch(`${API_BASE}/api/keys`)
30
31
  if (!res.ok) throw new Error(`Keys API error: ${res.status}`)
31
32
  const data = await res.json()
32
- return Array.isArray(data) ? { keys: data, available_scopes: ['*'] } : data
33
+ if (Array.isArray(data)) {
34
+ return { keys: data, available_scopes: ['*'] }
35
+ }
36
+ return {
37
+ keys: data.keys || [],
38
+ available_scopes: data.available_scopes || ['*']
39
+ }
33
40
  }
34
41
 
35
42
  export async function issueApiKey(name: string, scopes?: string[], expiresAt?: string | null): Promise<IssuedApiKey> {
@@ -226,6 +226,10 @@ export function setPluginLocked(id: string, locked: boolean): Promise<Result> {
226
226
  return post(`${API_BASE}/api/plugins/${encodeURIComponent(id)}/lock`, { locked })
227
227
  }
228
228
 
229
+ export function setPluginActivation(id: string, enabled: boolean): Promise<Result & { activation?: { enabled: boolean; state: string } }> {
230
+ return post(`${API_BASE}/api/plugins/${encodeURIComponent(id)}/activation`, { enabled }, "PUT")
231
+ }
232
+
229
233
  export function deployPluginToStore(id: string, autoLock = true): Promise<Result & { storePackage?: any }> {
230
234
  return post(`${API_BASE}/api/plugins/${encodeURIComponent(id)}/deploy-store`, { autoLock })
231
235
  }
@@ -102,7 +102,8 @@ export function CardEngine({ plugin }: { plugin: PluginInfo }) {
102
102
  const [drawer, setDrawer] = useState(false)
103
103
  return <CardRunner card={card}>
104
104
  <section className={`card card-${card.template}${owned ? ' card-owned' : ''}`} style={brandStyle(card.branding)} data-density={card.branding.density} aria-label={`${card.title} card`}>
105
- {owned ? <button type="button" className="card-link" onClick={() => setDrawer(true)}>{card.workspace ? 'Open workspace' : 'Settings & usage'}</button> : <><Header card={card} onOpenDrawer={() => setDrawer(true)} /><p className="card-muted card-description">{card.description}</p></>}
105
+ <Header card={card} onOpenDrawer={() => setDrawer(true)} />
106
+ {card.description && <p className="card-muted card-description">{card.description}</p>}
106
107
  <CardScenes plugin={plugin}><div className="card-regions">
107
108
  {card.regions.filter(region => !['actions', 'form', 'keypad'].includes(region.widget)).map((region, i) => <section key={i} className={`card-region card-region-${region.widget}`} style={{ gridColumn: `span ${Math.min(12, Math.max(1, region.span || 12))}` }}>
108
109
  {region.title && <h3>{region.title}</h3>}<Region region={region} plugin={plugin} />
@@ -31,5 +31,5 @@
31
31
  @media (prefers-reduced-motion:no-preference){.card-drawer{animation:card-slide .18s ease-out}@keyframes card-slide{from{transform:translateX(24px);opacity:0}}}
32
32
  .card-scene-track{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:thin;gap:var(--card-gap);overscroll-behavior-x:contain}.card-scene{flex:0 0 100%;min-width:0;scroll-snap-align:start;padding:4px}.card-scene-controls{margin-bottom:var(--card-gap)}.card-scenes[data-state=inactive] .card-scene-track{opacity:.65}.card-scenes[aria-busy=true] .card-pill{animation:card-running 1.1s ease-in-out infinite}@keyframes card-running{50%{box-shadow:0 0 0 3px color-mix(in srgb,var(--card-color) 28%,transparent)}}@media(prefers-reduced-motion:reduce){.card-scenes[aria-busy=true] .card-pill{animation:none}.card-scene-track{scroll-behavior:auto}}
33
33
  .card-scene-track{position:relative}.card-context-menu{display:grid;gap:8px;padding:12px;border:1px solid var(--card-line);border-radius:var(--card-radius);background:var(--card-surface);box-shadow:0 8px 24px #0002}.card-context-menu button{text-align:left}
34
- .card.card-owned{border:0;background:transparent;padding:0;box-shadow:none}.card-owned .card-link{color:var(--text-primary,#111)}.card-permissions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.card-permissions label{display:flex;align-items:center;gap:8px}.card-execution{margin-bottom:var(--card-gap)}
34
+ .card.card-owned{border:1px solid var(--card-line);background:var(--card-surface);border-radius:var(--card-radius);padding:var(--card-gap);box-shadow:0 1px 3px rgba(0,0,0,0.06)}.card-owned .card-link{color:var(--text-primary,#111)}.card-permissions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.card-permissions label{display:flex;align-items:center;gap:8px}.card-execution{margin-bottom:var(--card-gap)}
35
35
  .card-scenes,.card-scene-track{min-width:0;max-width:100%}.card-scene{box-sizing:border-box}.card-execution fieldset{min-inline-size:0;box-sizing:border-box;width:100%}.card-execution select,.card-execution input:not([type=checkbox]){min-width:0;width:100%;box-sizing:border-box}.card-permissions label{white-space:normal}
@@ -1,5 +1,15 @@
1
1
  import { useMemo, useState, useEffect } from 'react'
2
- import { getApiBase, connectClient, fetchActiveIntegrations, fetchExtensionStatus } from '../api'
2
+ import {
3
+ getApiBase,
4
+ connectClient,
5
+ fetchActiveIntegrations,
6
+ fetchExtensionStatus,
7
+ listApiKeys,
8
+ issueApiKey,
9
+ revokeApiKey,
10
+ type ApiKey,
11
+ type IssuedApiKey,
12
+ } from '../api'
3
13
 
4
14
  type Preset = {
5
15
  id: string
@@ -88,6 +98,13 @@ export default function IntegrationsPage({ activeBrainId }: { activeBrainId?: st
88
98
  const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null)
89
99
  const [activeIdes, setActiveIdes] = useState<string[]>([])
90
100
  const [extStatus, setExtStatus] = useState<{ available: boolean; connected: boolean } | null>(null)
101
+ const [keys, setKeys] = useState<ApiKey[]>([])
102
+ const [keysLoading, setKeysLoading] = useState(false)
103
+ const [createdToken, setCreatedToken] = useState<IssuedApiKey | null>(null)
104
+ const [tokenName, setTokenName] = useState('')
105
+ const [tokenScope, setTokenScope] = useState('*')
106
+ const [isIssuingToken, setIsIssuingToken] = useState(false)
107
+ const [revokingId, setRevokingId] = useState<string | null>(null)
91
108
 
92
109
  const baseUrl = useMemo(() => {
93
110
  const configured = getApiBase()
@@ -95,9 +112,25 @@ export default function IntegrationsPage({ activeBrainId }: { activeBrainId?: st
95
112
  return window.location.origin
96
113
  }, [])
97
114
 
115
+ const loadKeys = () => {
116
+ setKeysLoading(true)
117
+ listApiKeys()
118
+ .then(res => {
119
+ setKeys(res.keys || [])
120
+ })
121
+ .catch(e => {
122
+ console.error('Failed to load keys', e)
123
+ })
124
+ .finally(() => {
125
+ setKeysLoading(false)
126
+ })
127
+ }
128
+
98
129
  useEffect(() => {
99
130
  let active = true
100
131
 
132
+ loadKeys()
133
+
101
134
  fetchActiveIntegrations()
102
135
  .then(res => {
103
136
  if (active && res.success && Array.isArray(res.active)) {
@@ -119,6 +152,34 @@ export default function IntegrationsPage({ activeBrainId }: { activeBrainId?: st
119
152
  return () => { active = false }
120
153
  }, [activeBrainId])
121
154
 
155
+ const handleCreateToken = async (name: string, scopes: string[]) => {
156
+ setIsIssuingToken(true)
157
+ setMessage(null)
158
+ try {
159
+ const issued = await issueApiKey(name || 'Personal Access Token', scopes)
160
+ setCreatedToken(issued)
161
+ setMessage({ type: 'success', text: `Generated new token "${issued.name}". Copy it below.` })
162
+ loadKeys()
163
+ } catch (e) {
164
+ setMessage({ type: 'error', text: (e as Error).message || 'Failed to issue token.' })
165
+ } finally {
166
+ setIsIssuingToken(false)
167
+ }
168
+ }
169
+
170
+ const handleRevokeToken = async (id: string) => {
171
+ setRevokingId(id)
172
+ try {
173
+ await revokeApiKey(id)
174
+ setMessage({ type: 'success', text: 'Token revoked successfully.' })
175
+ loadKeys()
176
+ } catch (e) {
177
+ setMessage({ type: 'error', text: (e as Error).message || 'Failed to revoke token.' })
178
+ } finally {
179
+ setRevokingId(null)
180
+ }
181
+ }
182
+
122
183
  const filteredPresets = useMemo(() => {
123
184
  if (activeIdes.length === 0) return PRESETS
124
185
  return PRESETS.filter(preset => activeIdes.includes(preset.id))
@@ -281,7 +342,7 @@ export default function IntegrationsPage({ activeBrainId }: { activeBrainId?: st
281
342
  color: 'var(--text-secondary)',
282
343
  lineHeight: '1.4'
283
344
  }}>
284
- Install the Total Recall extension to passively capture context, store quick notes, and access the active brain search overlay inside any Chrome tab.
345
+ Download the extension package with your active brain address and access token automatically baked inside. Zero manual token entry required.
285
346
  </div>
286
347
 
287
348
  <div style={{
@@ -297,32 +358,48 @@ export default function IntegrationsPage({ activeBrainId }: { activeBrainId?: st
297
358
  1. Download zip & unzip locally.{"\n"}
298
359
  2. Go to chrome://extensions in browser.{"\n"}
299
360
  3. Enable "Developer mode" (top right).{"\n"}
300
- 4. Click "Load unpacked" & select unzipped folder.
361
+ 4. Click "Load unpacked" & select unzipped folder.{"\n"}
362
+ ✓ Token and brain address are pre-configured automatically.
301
363
  </div>
302
364
 
303
- <div style={{ display: 'flex', gap: 10, marginTop: 'auto', paddingTop: 8 }}>
365
+ <div style={{ display: 'flex', gap: 10, marginTop: 'auto', paddingTop: 8, flexWrap: 'wrap' }}>
304
366
  <a
305
367
  href={`${baseUrl}/api/extension/download`}
306
368
  download
307
369
  className="btn btn-sm btn-primary"
308
370
  style={{
309
- background: 'linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)',
371
+ background: 'linear-gradient(135deg, #10b981 0%, #047857 100%)',
310
372
  border: 'none',
311
- boxShadow: '0 2px 4px rgba(59, 130, 246, 0.3)',
373
+ boxShadow: '0 2px 4px rgba(16, 185, 129, 0.3)',
312
374
  fontWeight: 600,
313
375
  cursor: 'pointer',
314
376
  textDecoration: 'none',
315
377
  display: 'inline-flex',
316
378
  alignItems: 'center',
317
379
  justifyContent: 'center',
318
- padding: '6px 12px',
380
+ padding: '8px 14px',
319
381
  borderRadius: '6px',
320
382
  color: '#fff',
321
383
  fontSize: '12px'
322
384
  }}
323
385
  >
324
- ⬇ Download Extension Zip
386
+ ⬇ Download Pre-Configured Extension Zip
325
387
  </a>
388
+ <button
389
+ className="btn btn-sm"
390
+ onClick={() => handleCreateToken('Chrome Extension', ['*'])}
391
+ disabled={isIssuingToken}
392
+ style={{
393
+ background: 'rgba(255, 255, 255, 0.05)',
394
+ border: '1px solid var(--border)',
395
+ fontWeight: 500,
396
+ cursor: isIssuingToken ? 'default' : 'pointer',
397
+ color: 'var(--text-secondary)',
398
+ fontSize: '12px'
399
+ }}
400
+ >
401
+ {isIssuingToken ? 'Generating...' : '🔑 Generate Extension Token'}
402
+ </button>
326
403
  </div>
327
404
  </div>
328
405
 
@@ -449,6 +526,236 @@ export default function IntegrationsPage({ activeBrainId }: { activeBrainId?: st
449
526
  )
450
527
  })}
451
528
  </div>
529
+
530
+ {createdToken && (
531
+ <div style={{
532
+ marginTop: 24,
533
+ marginBottom: 16,
534
+ padding: 16,
535
+ borderRadius: 10,
536
+ background: 'rgba(16, 185, 129, 0.08)',
537
+ border: '1px solid rgba(16, 185, 129, 0.4)',
538
+ boxShadow: '0 4px 16px rgba(16, 185, 129, 0.1)',
539
+ }}>
540
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
541
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
542
+ <span style={{ fontSize: 18 }}>🔑</span>
543
+ <strong style={{ color: '#34d399', fontSize: 15 }}>Token Issued: {createdToken.name}</strong>
544
+ </div>
545
+ <button
546
+ onClick={() => setCreatedToken(null)}
547
+ style={{
548
+ background: 'none',
549
+ border: 'none',
550
+ color: 'var(--text-tertiary)',
551
+ cursor: 'pointer',
552
+ fontSize: 16,
553
+ }}
554
+ >
555
+ ✕
556
+ </button>
557
+ </div>
558
+ <p style={{ margin: '0 0 10px', fontSize: 13, color: 'var(--text-secondary)' }}>
559
+ Make sure to copy this token now. It will not be shown again. Paste this token into the Chrome extension settings or your client authorization headers.
560
+ </p>
561
+ <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
562
+ <input
563
+ readOnly
564
+ value={createdToken.token}
565
+ style={{
566
+ flex: 1,
567
+ minWidth: 260,
568
+ background: 'var(--bg-primary)',
569
+ border: '1px solid rgba(16, 185, 129, 0.4)',
570
+ borderRadius: 6,
571
+ padding: '8px 12px',
572
+ fontFamily: 'monospace',
573
+ fontSize: 13,
574
+ color: '#34d399',
575
+ }}
576
+ onClick={(e) => (e.target as HTMLInputElement).select()}
577
+ />
578
+ <button
579
+ className="btn btn-sm btn-primary"
580
+ onClick={() => copy('new-token', createdToken.token)}
581
+ style={{
582
+ background: '#10b981',
583
+ border: 'none',
584
+ fontWeight: 600,
585
+ cursor: 'pointer',
586
+ padding: '8px 16px',
587
+ }}
588
+ >
589
+ {copied === 'new-token' ? 'Copied!' : 'Copy Token'}
590
+ </button>
591
+ </div>
592
+ </div>
593
+ )}
594
+
595
+ <div style={{ marginTop: 32, borderTop: '1px solid var(--border)', paddingTop: 24 }}>
596
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16, flexWrap: 'wrap', gap: 12 }}>
597
+ <div>
598
+ <h2 style={{ margin: '0 0 4px', fontSize: 20 }}>Personal Access Tokens &amp; API Keys</h2>
599
+ <p style={{ margin: 0, color: 'var(--text-tertiary)', fontSize: 13 }}>
600
+ Authenticate external applications, browser extensions, and IDE daemons with your Total Recall brain.
601
+ </p>
602
+ </div>
603
+ </div>
604
+
605
+ <div style={{
606
+ background: 'var(--bg-secondary)',
607
+ border: '1px solid var(--border)',
608
+ borderRadius: 10,
609
+ padding: 16,
610
+ marginBottom: 20,
611
+ display: 'flex',
612
+ flexDirection: 'column',
613
+ gap: 12
614
+ }}>
615
+ <h4 style={{ margin: 0, fontSize: 14 }}>Create New Access Token</h4>
616
+ <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
617
+ <input
618
+ type="text"
619
+ placeholder="Token name (e.g. Chrome Extension, Laptop, CI)"
620
+ value={tokenName}
621
+ onChange={(e) => setTokenName(e.target.value)}
622
+ style={{
623
+ flex: '2 1 200px',
624
+ background: 'var(--bg-primary)',
625
+ border: '1px solid var(--border)',
626
+ borderRadius: 6,
627
+ padding: '8px 12px',
628
+ color: 'var(--text-primary)',
629
+ fontSize: 13,
630
+ }}
631
+ />
632
+ <select
633
+ value={tokenScope}
634
+ onChange={(e) => setTokenScope(e.target.value)}
635
+ style={{
636
+ flex: '1 1 140px',
637
+ background: 'var(--bg-primary)',
638
+ border: '1px solid var(--border)',
639
+ borderRadius: 6,
640
+ padding: '8px 12px',
641
+ color: 'var(--text-primary)',
642
+ fontSize: 13,
643
+ }}
644
+ >
645
+ <option value="*">Full Access (*)</option>
646
+ <option value="memory:read,memory:write">Memory Read/Write</option>
647
+ <option value="memory:read">Memory Read Only</option>
648
+ </select>
649
+ <button
650
+ className="btn btn-sm btn-primary"
651
+ onClick={() => {
652
+ handleCreateToken(tokenName || 'Personal Access Token', tokenScope.split(','))
653
+ setTokenName('')
654
+ }}
655
+ disabled={isIssuingToken}
656
+ style={{
657
+ background: 'linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%)',
658
+ border: 'none',
659
+ fontWeight: 600,
660
+ cursor: isIssuingToken ? 'default' : 'pointer',
661
+ padding: '8px 16px',
662
+ color: '#fff',
663
+ }}
664
+ >
665
+ {isIssuingToken ? 'Generating...' : 'Generate Token'}
666
+ </button>
667
+ </div>
668
+ </div>
669
+
670
+ <div style={{
671
+ background: 'var(--bg-secondary)',
672
+ border: '1px solid var(--border)',
673
+ borderRadius: 10,
674
+ overflow: 'hidden'
675
+ }}>
676
+ <table style={{ width: '100%', borderCollapse: 'collapse', textAlign: 'left', fontSize: 13 }}>
677
+ <thead>
678
+ <tr style={{ borderBottom: '1px solid var(--border)', background: 'rgba(255, 255, 255, 0.02)' }}>
679
+ <th style={{ padding: '10px 14px', color: 'var(--text-tertiary)', fontWeight: 500 }}>Name</th>
680
+ <th style={{ padding: '10px 14px', color: 'var(--text-tertiary)', fontWeight: 500 }}>Token Prefix</th>
681
+ <th style={{ padding: '10px 14px', color: 'var(--text-tertiary)', fontWeight: 500 }}>Scopes</th>
682
+ <th style={{ padding: '10px 14px', color: 'var(--text-tertiary)', fontWeight: 500 }}>Created</th>
683
+ <th style={{ padding: '10px 14px', color: 'var(--text-tertiary)', fontWeight: 500 }}>Status</th>
684
+ <th style={{ padding: '10px 14px', color: 'var(--text-tertiary)', fontWeight: 500, textAlign: 'right' }}>Actions</th>
685
+ </tr>
686
+ </thead>
687
+ <tbody>
688
+ {keysLoading && (
689
+ <tr>
690
+ <td colSpan={6} style={{ padding: 20, textAlign: 'center', color: 'var(--text-tertiary)' }}>
691
+ Loading tokens...
692
+ </td>
693
+ </tr>
694
+ )}
695
+ {!keysLoading && keys.length === 0 && (
696
+ <tr>
697
+ <td colSpan={6} style={{ padding: 20, textAlign: 'center', color: 'var(--text-tertiary)' }}>
698
+ No access tokens generated yet.
699
+ </td>
700
+ </tr>
701
+ )}
702
+ {!keysLoading && keys.map(k => {
703
+ const prefix = k.token_prefix || k.token_preview || 'tr_...'
704
+ const isRevoked = k.revoked
705
+ return (
706
+ <tr key={k.id} style={{ borderBottom: '1px solid var(--border)', opacity: isRevoked ? 0.5 : 1 }}>
707
+ <td style={{ padding: '10px 14px', fontWeight: 500 }}>
708
+ {k.name}
709
+ </td>
710
+ <td style={{ padding: '10px 14px', fontFamily: 'monospace', color: 'var(--text-secondary)' }}>
711
+ {prefix}...
712
+ </td>
713
+ <td style={{ padding: '10px 14px', color: 'var(--text-tertiary)', fontSize: 12 }}>
714
+ {Array.isArray(k.scopes) ? k.scopes.join(', ') : '*'}
715
+ </td>
716
+ <td style={{ padding: '10px 14px', color: 'var(--text-tertiary)', fontSize: 12 }}>
717
+ {k.created_at ? new Date(k.created_at).toLocaleDateString() : '—'}
718
+ </td>
719
+ <td style={{ padding: '10px 14px' }}>
720
+ <span style={{
721
+ display: 'inline-block',
722
+ padding: '2px 8px',
723
+ borderRadius: 4,
724
+ fontSize: 11,
725
+ fontWeight: 600,
726
+ background: isRevoked ? 'rgba(239, 68, 68, 0.1)' : 'rgba(16, 185, 129, 0.1)',
727
+ color: isRevoked ? '#f87171' : '#34d399',
728
+ border: isRevoked ? '1px solid rgba(239, 68, 68, 0.2)' : '1px solid rgba(16, 185, 129, 0.2)'
729
+ }}>
730
+ {isRevoked ? 'Revoked' : 'Active'}
731
+ </span>
732
+ </td>
733
+ <td style={{ padding: '10px 14px', textAlign: 'right' }}>
734
+ {!isRevoked && (
735
+ <button
736
+ className="btn btn-sm"
737
+ onClick={() => handleRevokeToken(k.id)}
738
+ disabled={revokingId === k.id}
739
+ style={{
740
+ background: 'rgba(239, 68, 68, 0.1)',
741
+ border: '1px solid rgba(239, 68, 68, 0.2)',
742
+ color: '#f87171',
743
+ cursor: 'pointer',
744
+ fontSize: 11,
745
+ padding: '4px 8px'
746
+ }}
747
+ >
748
+ {revokingId === k.id ? 'Revoking...' : 'Revoke'}
749
+ </button>
750
+ )}
751
+ </td>
752
+ </tr>
753
+ )
754
+ })}
755
+ </tbody>
756
+ </table>
757
+ </div>
758
+ </div>
452
759
  </div>
453
760
  )
454
761
  }