stagent 0.9.5 → 0.10.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.
Files changed (277) hide show
  1. package/README.md +5 -42
  2. package/dist/cli.js +42 -18
  3. package/docs/.coverage-gaps.json +13 -55
  4. package/docs/.last-generated +1 -1
  5. package/docs/features/provider-runtimes.md +4 -0
  6. package/docs/features/schedules.md +32 -4
  7. package/docs/features/settings.md +28 -5
  8. package/docs/features/tables.md +9 -2
  9. package/docs/features/workflows.md +10 -4
  10. package/docs/journeys/developer.md +15 -1
  11. package/docs/journeys/personal-use.md +21 -4
  12. package/docs/superpowers/plans/2026-04-07-instance-bootstrap.md +1691 -0
  13. package/docs/superpowers/plans/2026-04-08-schedule-orchestration.md +2983 -0
  14. package/docs/superpowers/plans/2026-04-11-schedule-maxturns-api-control.md +551 -0
  15. package/docs/superpowers/plans/2026-04-11-task-create-profile-validation.md +864 -0
  16. package/docs/superpowers/plans/2026-04-11-task-runtime-stagent-mcp-injection.md +739 -0
  17. package/docs/superpowers/specs/2026-04-08-chat-sse-resilience-hotfix-design.md +201 -0
  18. package/docs/superpowers/specs/2026-04-08-schedule-orchestration-design.md +371 -0
  19. package/docs/superpowers/specs/2026-04-08-swarm-visibility-design.md +213 -0
  20. package/package.json +3 -2
  21. package/src/__tests__/instrumentation-smoke.test.ts +15 -0
  22. package/src/app/analytics/page.tsx +1 -21
  23. package/src/app/api/chat/conversations/[id]/messages/route.ts +22 -1
  24. package/src/app/api/diagnostics/chat-streams/route.ts +65 -0
  25. package/src/app/api/instance/config/route.ts +41 -0
  26. package/src/app/api/instance/init/route.ts +34 -0
  27. package/src/app/api/instance/upgrade/check/route.ts +26 -0
  28. package/src/app/api/instance/upgrade/route.ts +96 -0
  29. package/src/app/api/instance/upgrade/status/route.ts +35 -0
  30. package/src/app/api/memory/route.ts +0 -11
  31. package/src/app/api/notifications/route.ts +4 -2
  32. package/src/app/api/projects/[id]/route.ts +5 -155
  33. package/src/app/api/projects/__tests__/delete-project.test.ts +10 -19
  34. package/src/app/api/schedules/[id]/execute/route.ts +111 -0
  35. package/src/app/api/schedules/[id]/route.ts +9 -1
  36. package/src/app/api/schedules/__tests__/execute-route.test.ts +118 -0
  37. package/src/app/api/schedules/route.ts +3 -12
  38. package/src/app/api/settings/openai/login/route.ts +22 -0
  39. package/src/app/api/settings/openai/logout/route.ts +7 -0
  40. package/src/app/api/settings/openai/route.ts +21 -1
  41. package/src/app/api/settings/providers/route.ts +35 -8
  42. package/src/app/api/tables/[id]/enrich/__tests__/route.test.ts +153 -0
  43. package/src/app/api/tables/[id]/enrich/plan/route.ts +98 -0
  44. package/src/app/api/tables/[id]/enrich/route.ts +147 -0
  45. package/src/app/api/tables/[id]/enrich/runs/route.ts +25 -0
  46. package/src/app/api/tasks/[id]/execute/route.ts +0 -21
  47. package/src/app/api/workflows/[id]/resume/route.ts +59 -0
  48. package/src/app/api/workflows/[id]/status/route.ts +22 -8
  49. package/src/app/api/workspace/context/route.ts +2 -0
  50. package/src/app/api/workspace/fix-data-dir/route.ts +81 -0
  51. package/src/app/chat/page.tsx +11 -0
  52. package/src/app/inbox/page.tsx +12 -5
  53. package/src/app/layout.tsx +42 -21
  54. package/src/app/page.tsx +0 -2
  55. package/src/app/settings/page.tsx +6 -9
  56. package/src/components/chat/__tests__/chat-session-provider.test.tsx +408 -0
  57. package/src/components/chat/chat-command-popover.tsx +2 -2
  58. package/src/components/chat/chat-input.tsx +2 -3
  59. package/src/components/chat/chat-session-provider.tsx +720 -0
  60. package/src/components/chat/chat-shell.tsx +92 -401
  61. package/src/components/instance/__tests__/instance-section.test.tsx +125 -0
  62. package/src/components/instance/instance-section.tsx +382 -0
  63. package/src/components/instance/upgrade-badge.tsx +219 -0
  64. package/src/components/notifications/__tests__/batch-proposal-review.test.tsx +95 -0
  65. package/src/components/notifications/__tests__/notification-item.test.tsx +106 -0
  66. package/src/components/notifications/batch-proposal-review.tsx +20 -5
  67. package/src/components/notifications/inbox-list.tsx +11 -2
  68. package/src/components/notifications/notification-item.tsx +56 -2
  69. package/src/components/notifications/pending-approval-host.tsx +56 -37
  70. package/src/components/schedules/schedule-create-sheet.tsx +19 -1
  71. package/src/components/schedules/schedule-edit-sheet.tsx +20 -1
  72. package/src/components/schedules/schedule-form.tsx +31 -0
  73. package/src/components/settings/__tests__/providers-runtimes-section.test.tsx +149 -0
  74. package/src/components/settings/auth-method-selector.tsx +19 -4
  75. package/src/components/settings/auth-status-badge.tsx +28 -3
  76. package/src/components/settings/openai-chatgpt-auth-control.tsx +278 -0
  77. package/src/components/settings/openai-runtime-section.tsx +7 -1
  78. package/src/components/settings/providers-runtimes-section.tsx +138 -19
  79. package/src/components/shared/app-sidebar.tsx +4 -3
  80. package/src/components/shared/command-palette.tsx +4 -5
  81. package/src/components/shared/theme-toggle.tsx +5 -24
  82. package/src/components/shared/workspace-indicator.tsx +61 -2
  83. package/src/components/tables/__tests__/table-enrichment-sheet.test.tsx +130 -0
  84. package/src/components/tables/table-create-sheet.tsx +4 -0
  85. package/src/components/tables/table-enrichment-runs.tsx +103 -0
  86. package/src/components/tables/table-enrichment-sheet.tsx +538 -0
  87. package/src/components/tables/table-spreadsheet.tsx +29 -5
  88. package/src/components/tables/table-toolbar.tsx +10 -1
  89. package/src/components/tasks/kanban-board.tsx +1 -0
  90. package/src/components/tasks/kanban-column.tsx +53 -14
  91. package/src/components/tasks/task-bento-grid.tsx +19 -0
  92. package/src/components/tasks/task-card.tsx +26 -3
  93. package/src/components/tasks/task-chip-bar.tsx +24 -0
  94. package/src/components/tasks/task-result-renderer.tsx +1 -1
  95. package/src/components/workflows/delay-step-body.tsx +109 -0
  96. package/src/components/workflows/hooks/use-workflow-status.ts +50 -0
  97. package/src/components/workflows/loop-status-view.tsx +1 -1
  98. package/src/components/workflows/shared/step-result.tsx +78 -0
  99. package/src/components/workflows/shared/workflow-header.tsx +141 -0
  100. package/src/components/workflows/shared/workflow-loading-skeleton.tsx +36 -0
  101. package/src/components/workflows/swarm-dashboard.tsx +2 -15
  102. package/src/components/workflows/views/loop-pattern-view.tsx +137 -0
  103. package/src/components/workflows/views/sequence-pattern-view.tsx +511 -0
  104. package/src/components/workflows/workflow-form-view.tsx +133 -16
  105. package/src/components/workflows/workflow-status-view.tsx +30 -740
  106. package/src/instrumentation-node.ts +94 -0
  107. package/src/instrumentation.ts +4 -48
  108. package/src/lib/agents/__tests__/claude-agent.test.ts +199 -0
  109. package/src/lib/agents/__tests__/execution-manager.test.ts +1 -27
  110. package/src/lib/agents/__tests__/failure-reason.test.ts +68 -0
  111. package/src/lib/agents/__tests__/learned-context.test.ts +0 -11
  112. package/src/lib/agents/__tests__/learning-session.test.ts +158 -0
  113. package/src/lib/agents/__tests__/pattern-extractor.test.ts +48 -0
  114. package/src/lib/agents/claude-agent.ts +155 -18
  115. package/src/lib/agents/execution-manager.ts +0 -35
  116. package/src/lib/agents/learned-context.ts +0 -12
  117. package/src/lib/agents/learning-session.ts +18 -5
  118. package/src/lib/agents/profiles/__tests__/registry.test.ts +6 -4
  119. package/src/lib/agents/profiles/builtins/upgrade-assistant/SKILL.md +70 -0
  120. package/src/lib/agents/profiles/builtins/upgrade-assistant/profile.yaml +32 -0
  121. package/src/lib/agents/runtime/__tests__/openai-codex-auth.test.ts +118 -0
  122. package/src/lib/agents/runtime/codex-app-server-client.ts +11 -5
  123. package/src/lib/agents/runtime/openai-codex-auth.ts +389 -0
  124. package/src/lib/agents/runtime/openai-codex.ts +29 -60
  125. package/src/lib/agents/runtime/types.ts +8 -0
  126. package/src/lib/book/chapter-mapping.ts +11 -0
  127. package/src/lib/book/content.ts +10 -0
  128. package/src/lib/chat/__tests__/active-streams.test.ts +49 -0
  129. package/src/lib/chat/__tests__/finalize-safety-net.test.ts +139 -0
  130. package/src/lib/chat/__tests__/reconcile.test.ts +137 -0
  131. package/src/lib/chat/__tests__/stream-telemetry.test.ts +151 -0
  132. package/src/lib/chat/active-streams.ts +27 -0
  133. package/src/lib/chat/codex-engine.ts +16 -17
  134. package/src/lib/chat/context-builder.ts +5 -3
  135. package/src/lib/chat/engine.ts +50 -3
  136. package/src/lib/chat/reconcile.ts +117 -0
  137. package/src/lib/chat/stagent-tools.ts +1 -0
  138. package/src/lib/chat/stream-telemetry.ts +132 -0
  139. package/src/lib/chat/suggested-prompts.ts +28 -1
  140. package/src/lib/chat/system-prompt.ts +26 -1
  141. package/src/lib/chat/tool-catalog.ts +2 -1
  142. package/src/lib/chat/tools/__tests__/enrich-table-tool.test.ts +127 -0
  143. package/src/lib/chat/tools/__tests__/schedule-tools.test.ts +261 -0
  144. package/src/lib/chat/tools/__tests__/task-tools.test.ts +352 -0
  145. package/src/lib/chat/tools/__tests__/workflow-tools-dedup.test.ts +217 -0
  146. package/src/lib/chat/tools/document-tools.ts +29 -13
  147. package/src/lib/chat/tools/helpers.ts +39 -0
  148. package/src/lib/chat/tools/notification-tools.ts +9 -5
  149. package/src/lib/chat/tools/project-tools.ts +33 -0
  150. package/src/lib/chat/tools/schedule-tools.ts +44 -11
  151. package/src/lib/chat/tools/table-tools.ts +71 -0
  152. package/src/lib/chat/tools/task-tools.ts +84 -20
  153. package/src/lib/chat/tools/workflow-tools.ts +234 -32
  154. package/src/lib/constants/settings.ts +8 -18
  155. package/src/lib/data/__tests__/clear.test.ts +56 -2
  156. package/src/lib/data/clear.ts +20 -15
  157. package/src/lib/data/delete-project.ts +171 -0
  158. package/src/lib/db/__tests__/bootstrap.test.ts +1 -1
  159. package/src/lib/db/bootstrap.ts +45 -16
  160. package/src/lib/db/index.ts +5 -0
  161. package/src/lib/db/migrations/0009_add_app_instances.sql +25 -0
  162. package/src/lib/db/migrations/0024_add_workflow_resume_at.sql +10 -0
  163. package/src/lib/db/migrations/0025_drop_app_instances.sql +3 -0
  164. package/src/lib/db/migrations/0026_drop_license.sql +3 -0
  165. package/src/lib/db/migrations/meta/_journal.json +21 -0
  166. package/src/lib/db/schema.ts +68 -23
  167. package/src/lib/environment/workspace-context.ts +13 -1
  168. package/src/lib/import/dedup.ts +4 -54
  169. package/src/lib/instance/__tests__/bootstrap.test.ts +362 -0
  170. package/src/lib/instance/__tests__/detect.test.ts +115 -0
  171. package/src/lib/instance/__tests__/fingerprint.test.ts +48 -0
  172. package/src/lib/instance/__tests__/git-ops.test.ts +95 -0
  173. package/src/lib/instance/__tests__/settings.test.ts +83 -0
  174. package/src/lib/instance/__tests__/upgrade-poller.test.ts +131 -0
  175. package/src/lib/instance/bootstrap.ts +270 -0
  176. package/src/lib/instance/detect.ts +49 -0
  177. package/src/lib/instance/fingerprint.ts +78 -0
  178. package/src/lib/instance/git-ops.ts +95 -0
  179. package/src/lib/instance/settings.ts +61 -0
  180. package/src/lib/instance/types.ts +77 -0
  181. package/src/lib/instance/upgrade-poller.ts +153 -0
  182. package/src/lib/notifications/__tests__/visibility.test.ts +51 -0
  183. package/src/lib/notifications/visibility.ts +33 -0
  184. package/src/lib/schedules/__tests__/collision-check.test.ts +93 -0
  185. package/src/lib/schedules/__tests__/config.test.ts +62 -0
  186. package/src/lib/schedules/__tests__/firing-metrics.test.ts +99 -0
  187. package/src/lib/schedules/__tests__/integration.test.ts +82 -0
  188. package/src/lib/schedules/__tests__/slot-claim.test.ts +242 -0
  189. package/src/lib/schedules/__tests__/tick-scheduler.test.ts +102 -0
  190. package/src/lib/schedules/__tests__/turn-budget.test.ts +228 -0
  191. package/src/lib/schedules/collision-check.ts +105 -0
  192. package/src/lib/schedules/config.ts +53 -0
  193. package/src/lib/schedules/scheduler.ts +232 -13
  194. package/src/lib/schedules/slot-claim.ts +105 -0
  195. package/src/lib/settings/__tests__/openai-auth.test.ts +101 -0
  196. package/src/lib/settings/__tests__/openai-login-manager.test.ts +64 -0
  197. package/src/lib/settings/__tests__/runtime-setup.test.ts +33 -0
  198. package/src/lib/settings/openai-auth.ts +105 -10
  199. package/src/lib/settings/openai-login-manager.ts +260 -0
  200. package/src/lib/settings/runtime-setup.ts +14 -4
  201. package/src/lib/tables/__tests__/enrichment-planner.test.ts +124 -0
  202. package/src/lib/tables/__tests__/enrichment.test.ts +147 -0
  203. package/src/lib/tables/enrichment-planner.ts +454 -0
  204. package/src/lib/tables/enrichment.ts +328 -0
  205. package/src/lib/tables/query-builder.ts +5 -2
  206. package/src/lib/tables/trigger-evaluator.ts +3 -2
  207. package/src/lib/theme.ts +71 -0
  208. package/src/lib/usage/ledger.ts +2 -18
  209. package/src/lib/util/__tests__/similarity.test.ts +106 -0
  210. package/src/lib/util/similarity.ts +77 -0
  211. package/src/lib/utils/format-timestamp.ts +24 -0
  212. package/src/lib/utils/stagent-paths.ts +12 -0
  213. package/src/lib/validators/__tests__/blueprint.test.ts +172 -0
  214. package/src/lib/validators/__tests__/settings.test.ts +10 -0
  215. package/src/lib/validators/blueprint.ts +70 -9
  216. package/src/lib/validators/profile.ts +2 -2
  217. package/src/lib/validators/settings.ts +3 -1
  218. package/src/lib/workflows/__tests__/delay.test.ts +196 -0
  219. package/src/lib/workflows/__tests__/engine.test.ts +8 -0
  220. package/src/lib/workflows/__tests__/loop-executor.test.ts +54 -0
  221. package/src/lib/workflows/__tests__/post-action.test.ts +108 -0
  222. package/src/lib/workflows/blueprints/instantiator.ts +22 -1
  223. package/src/lib/workflows/blueprints/types.ts +10 -2
  224. package/src/lib/workflows/delay.ts +106 -0
  225. package/src/lib/workflows/engine.ts +207 -4
  226. package/src/lib/workflows/loop-executor.ts +349 -24
  227. package/src/lib/workflows/post-action.ts +91 -0
  228. package/src/lib/workflows/types.ts +166 -1
  229. package/src/app/api/license/checkout/route.ts +0 -28
  230. package/src/app/api/license/portal/route.ts +0 -26
  231. package/src/app/api/license/route.ts +0 -89
  232. package/src/app/api/license/usage/route.ts +0 -63
  233. package/src/app/api/marketplace/browse/route.ts +0 -15
  234. package/src/app/api/marketplace/import/route.ts +0 -28
  235. package/src/app/api/marketplace/publish/route.ts +0 -40
  236. package/src/app/api/onboarding/email/route.ts +0 -53
  237. package/src/app/api/settings/telemetry/route.ts +0 -14
  238. package/src/app/api/sync/export/route.ts +0 -54
  239. package/src/app/api/sync/restore/route.ts +0 -37
  240. package/src/app/api/sync/sessions/route.ts +0 -24
  241. package/src/app/auth/callback/route.ts +0 -73
  242. package/src/app/marketplace/page.tsx +0 -19
  243. package/src/components/analytics/analytics-gate-card.tsx +0 -101
  244. package/src/components/marketplace/blueprint-card.tsx +0 -61
  245. package/src/components/marketplace/marketplace-browser.tsx +0 -131
  246. package/src/components/onboarding/email-capture-card.tsx +0 -104
  247. package/src/components/settings/activation-form.tsx +0 -95
  248. package/src/components/settings/cloud-account-section.tsx +0 -147
  249. package/src/components/settings/cloud-sync-section.tsx +0 -155
  250. package/src/components/settings/subscription-section.tsx +0 -410
  251. package/src/components/settings/telemetry-section.tsx +0 -80
  252. package/src/components/shared/premium-gate-overlay.tsx +0 -50
  253. package/src/components/shared/schedule-gate-dialog.tsx +0 -64
  254. package/src/components/shared/upgrade-banner.tsx +0 -112
  255. package/src/hooks/use-supabase-auth.ts +0 -79
  256. package/src/lib/billing/email.ts +0 -54
  257. package/src/lib/billing/products.ts +0 -80
  258. package/src/lib/billing/stripe.ts +0 -101
  259. package/src/lib/cloud/supabase-browser.ts +0 -32
  260. package/src/lib/cloud/supabase-client.ts +0 -56
  261. package/src/lib/license/__tests__/features.test.ts +0 -56
  262. package/src/lib/license/__tests__/key-format.test.ts +0 -88
  263. package/src/lib/license/__tests__/manager.test.ts +0 -64
  264. package/src/lib/license/__tests__/tier-limits.test.ts +0 -79
  265. package/src/lib/license/cloud-validation.ts +0 -60
  266. package/src/lib/license/features.ts +0 -44
  267. package/src/lib/license/key-format.ts +0 -101
  268. package/src/lib/license/limit-check.ts +0 -111
  269. package/src/lib/license/limit-queries.ts +0 -51
  270. package/src/lib/license/manager.ts +0 -345
  271. package/src/lib/license/notifications.ts +0 -59
  272. package/src/lib/license/tier-limits.ts +0 -71
  273. package/src/lib/marketplace/marketplace-client.ts +0 -107
  274. package/src/lib/sync/cloud-sync.ts +0 -235
  275. package/src/lib/telemetry/conversion-events.ts +0 -71
  276. package/src/lib/telemetry/queue.ts +0 -122
  277. package/src/lib/validators/license.ts +0 -33
@@ -1,147 +0,0 @@
1
- "use client";
2
-
3
- import { useState, useEffect } from "react";
4
- import { User, LogOut, Mail, CheckCircle2 } from "lucide-react";
5
- import { toast } from "sonner";
6
- import {
7
- Card,
8
- CardContent,
9
- CardDescription,
10
- CardHeader,
11
- CardTitle,
12
- } from "@/components/ui/card";
13
- import { Button } from "@/components/ui/button";
14
- import { Input } from "@/components/ui/input";
15
- import { Badge } from "@/components/ui/badge";
16
- import { useSupabaseAuth } from "@/hooks/use-supabase-auth";
17
-
18
- /**
19
- * Cloud Account section — sign-in with email for cloud features.
20
- * Shows sign-in form when not authenticated, account info when signed in.
21
- * Placed in Settings between Subscription and Cloud Sync.
22
- */
23
- export function CloudAccountSection() {
24
- const { isSignedIn, email, loading, signInWithEmail, signOut } = useSupabaseAuth();
25
- const [inputEmail, setInputEmail] = useState("");
26
- const [sending, setSending] = useState(false);
27
- const [sent, setSent] = useState(false);
28
-
29
- async function handleSignIn(e: React.FormEvent) {
30
- e.preventDefault();
31
- if (!inputEmail.includes("@")) return;
32
-
33
- setSending(true);
34
- const { error } = await signInWithEmail(inputEmail);
35
- setSending(false);
36
-
37
- if (error) {
38
- toast.error(error);
39
- } else {
40
- setSent(true);
41
- toast.success("Magic link sent — check your email");
42
- }
43
- }
44
-
45
- async function handleSignOut() {
46
- await signOut();
47
- toast.success("Signed out");
48
- }
49
-
50
- // Handle auth callback URL params — must be in useEffect, not during render
51
- useEffect(() => {
52
- const params = new URLSearchParams(window.location.search);
53
- const authParam = params.get("auth");
54
- if (!authParam || loading) return;
55
-
56
- if (authParam === "success") {
57
- toast.success("Signed in successfully");
58
- } else if (authParam === "error") {
59
- toast.error("Sign-in failed — try again");
60
- }
61
-
62
- // Clean up URL
63
- const url = new URL(window.location.href);
64
- url.searchParams.delete("auth");
65
- window.history.replaceState({}, "", url.toString());
66
- }, [loading]);
67
-
68
- if (loading) {
69
- return (
70
- <Card>
71
- <CardHeader>
72
- <CardTitle className="flex items-center gap-2">
73
- <User className="h-5 w-5" />
74
- Cloud Account
75
- </CardTitle>
76
- </CardHeader>
77
- <CardContent>
78
- <div className="animate-pulse h-8 bg-muted rounded w-1/3" />
79
- </CardContent>
80
- </Card>
81
- );
82
- }
83
-
84
- return (
85
- <Card>
86
- <CardHeader>
87
- <CardTitle className="flex items-center gap-2">
88
- <User className="h-5 w-5" />
89
- Cloud Account
90
- </CardTitle>
91
- <CardDescription>
92
- {isSignedIn
93
- ? "Connected to Stagent cloud for sync, marketplace, and billing"
94
- : "Sign in to enable cloud sync, marketplace access, and license activation"}
95
- </CardDescription>
96
- </CardHeader>
97
- <CardContent>
98
- {isSignedIn ? (
99
- <div className="flex items-center justify-between">
100
- <div className="flex items-center gap-3">
101
- <Badge variant="outline" className="gap-1.5">
102
- <CheckCircle2 className="h-3 w-3 text-status-completed" />
103
- Connected
104
- </Badge>
105
- <span className="text-sm text-muted-foreground">{email}</span>
106
- </div>
107
- <Button variant="ghost" size="sm" onClick={handleSignOut}>
108
- <LogOut className="h-3.5 w-3.5 mr-1.5" />
109
- Sign out
110
- </Button>
111
- </div>
112
- ) : sent ? (
113
- <div className="flex items-center gap-3">
114
- <Mail className="h-5 w-5 text-primary" />
115
- <div>
116
- <p className="text-sm font-medium">Check your email</p>
117
- <p className="text-xs text-muted-foreground">
118
- Click the magic link sent to <strong>{inputEmail}</strong> to sign in.
119
- After clicking, you&apos;ll be redirected back here.
120
- </p>
121
- </div>
122
- </div>
123
- ) : (
124
- <form onSubmit={handleSignIn} className="space-y-3">
125
- <p className="text-xs text-muted-foreground">
126
- We&apos;ll send a magic link to your email — no password needed.
127
- Use the same email you used to purchase your subscription.
128
- </p>
129
- <div className="flex gap-2 max-w-md">
130
- <Input
131
- type="email"
132
- placeholder="you@example.com"
133
- value={inputEmail}
134
- onChange={(e) => setInputEmail(e.target.value)}
135
- className="text-sm"
136
- required
137
- />
138
- <Button size="sm" type="submit" disabled={sending}>
139
- {sending ? "Sending..." : "Sign in"}
140
- </Button>
141
- </div>
142
- </form>
143
- )}
144
- </CardContent>
145
- </Card>
146
- );
147
- }
@@ -1,155 +0,0 @@
1
- "use client";
2
-
3
- import { useState, useEffect } from "react";
4
- import { Cloud, Upload, Download } from "lucide-react";
5
- import { getSupabaseBrowserClient } from "@/lib/cloud/supabase-browser";
6
- import { toast } from "sonner";
7
- import {
8
- Card,
9
- CardContent,
10
- CardDescription,
11
- CardHeader,
12
- CardTitle,
13
- } from "@/components/ui/card";
14
- import { Button } from "@/components/ui/button";
15
- import { Badge } from "@/components/ui/badge";
16
-
17
- interface SyncSession {
18
- id: string;
19
- device_name: string;
20
- sync_type: string;
21
- blob_size_bytes: number;
22
- created_at: string;
23
- }
24
-
25
- export function CloudSyncSection() {
26
- const [sessions, setSessions] = useState<SyncSession[]>([]);
27
- const [exporting, setExporting] = useState(false);
28
- const [restoring, setRestoring] = useState(false);
29
-
30
- useEffect(() => {
31
- fetch("/api/sync/sessions")
32
- .then((r) => (r.ok ? r.json() : { sessions: [] }))
33
- .then((d) => setSessions(d.sessions ?? []))
34
- .catch(() => {});
35
- }, []);
36
-
37
- async function handleExport() {
38
- setExporting(true);
39
- try {
40
- // Get the user's access token for authenticated Storage uploads
41
- const supabase = getSupabaseBrowserClient();
42
- const { data: { session } } = await supabase.auth.getSession();
43
- const accessToken = session?.access_token;
44
-
45
- const res = await fetch("/api/sync/export", {
46
- method: "POST",
47
- headers: { "Content-Type": "application/json" },
48
- body: JSON.stringify({ accessToken }),
49
- });
50
- const data = await res.json();
51
- if (res.ok) {
52
- toast.success(`Backup uploaded (${formatBytes(data.sizeBytes)})`);
53
- // Refresh sessions
54
- const sessRes = await fetch("/api/sync/sessions");
55
- if (sessRes.ok) setSessions((await sessRes.json()).sessions ?? []);
56
- } else {
57
- toast.error(data.error ?? "Export failed");
58
- }
59
- } catch {
60
- toast.error("Failed to export");
61
- } finally {
62
- setExporting(false);
63
- }
64
- }
65
-
66
- async function handleRestore() {
67
- if (!confirm("This will replace your local database with the latest cloud backup. A safety backup will be created first. Continue?")) {
68
- return;
69
- }
70
- setRestoring(true);
71
- try {
72
- const res = await fetch("/api/sync/restore", { method: "POST" });
73
- const data = await res.json();
74
- if (res.ok) {
75
- toast.success("Database restored. Restart the app to apply changes.");
76
- } else {
77
- toast.error(data.error ?? "Restore failed");
78
- }
79
- } catch {
80
- toast.error("Failed to restore");
81
- } finally {
82
- setRestoring(false);
83
- }
84
- }
85
-
86
- return (
87
- <Card>
88
- <CardHeader>
89
- <CardTitle className="flex items-center gap-2">
90
- <Cloud className="h-5 w-5" />
91
- Cloud Sync
92
- </CardTitle>
93
- <CardDescription>
94
- Encrypted database backup and restore via Supabase Storage
95
- </CardDescription>
96
- </CardHeader>
97
- <CardContent className="space-y-4">
98
- <div className="flex gap-3">
99
- <Button
100
- variant="outline"
101
- size="sm"
102
- onClick={handleExport}
103
- disabled={exporting || restoring}
104
- >
105
- <Upload className="h-3.5 w-3.5 mr-1.5" />
106
- {exporting ? "Exporting..." : "Backup Now"}
107
- </Button>
108
- <Button
109
- variant="outline"
110
- size="sm"
111
- onClick={handleRestore}
112
- disabled={exporting || restoring || sessions.length === 0}
113
- >
114
- <Download className="h-3.5 w-3.5 mr-1.5" />
115
- {restoring ? "Restoring..." : "Restore Latest"}
116
- </Button>
117
- </div>
118
-
119
- {sessions.length > 0 && (
120
- <div className="space-y-2">
121
- <h4 className="text-xs font-medium text-muted-foreground">Recent Syncs</h4>
122
- <div className="space-y-1">
123
- {sessions.slice(0, 5).map((s) => (
124
- <div key={s.id} className="flex items-center justify-between text-xs py-1">
125
- <div className="flex items-center gap-2">
126
- <Badge variant={s.sync_type === "backup" ? "secondary" : "outline"} className="text-[10px]">
127
- {s.sync_type}
128
- </Badge>
129
- <span className="text-muted-foreground">{s.device_name}</span>
130
- </div>
131
- <div className="flex items-center gap-2 text-muted-foreground">
132
- <span>{formatBytes(s.blob_size_bytes)}</span>
133
- <span>{new Date(s.created_at).toLocaleDateString()}</span>
134
- </div>
135
- </div>
136
- ))}
137
- </div>
138
- </div>
139
- )}
140
-
141
- {sessions.length === 0 && (
142
- <p className="text-xs text-muted-foreground">
143
- No backups yet. Click "Backup Now" to create your first encrypted cloud backup.
144
- </p>
145
- )}
146
- </CardContent>
147
- </Card>
148
- );
149
- }
150
-
151
- function formatBytes(bytes: number): string {
152
- if (bytes < 1024) return `${bytes} B`;
153
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
154
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
155
- }