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.
- package/README.md +5 -42
- package/dist/cli.js +42 -18
- package/docs/.coverage-gaps.json +13 -55
- package/docs/.last-generated +1 -1
- package/docs/features/provider-runtimes.md +4 -0
- package/docs/features/schedules.md +32 -4
- package/docs/features/settings.md +28 -5
- package/docs/features/tables.md +9 -2
- package/docs/features/workflows.md +10 -4
- package/docs/journeys/developer.md +15 -1
- package/docs/journeys/personal-use.md +21 -4
- package/docs/superpowers/plans/2026-04-07-instance-bootstrap.md +1691 -0
- package/docs/superpowers/plans/2026-04-08-schedule-orchestration.md +2983 -0
- package/docs/superpowers/plans/2026-04-11-schedule-maxturns-api-control.md +551 -0
- package/docs/superpowers/plans/2026-04-11-task-create-profile-validation.md +864 -0
- package/docs/superpowers/plans/2026-04-11-task-runtime-stagent-mcp-injection.md +739 -0
- package/docs/superpowers/specs/2026-04-08-chat-sse-resilience-hotfix-design.md +201 -0
- package/docs/superpowers/specs/2026-04-08-schedule-orchestration-design.md +371 -0
- package/docs/superpowers/specs/2026-04-08-swarm-visibility-design.md +213 -0
- package/package.json +3 -2
- package/src/__tests__/instrumentation-smoke.test.ts +15 -0
- package/src/app/analytics/page.tsx +1 -21
- package/src/app/api/chat/conversations/[id]/messages/route.ts +22 -1
- package/src/app/api/diagnostics/chat-streams/route.ts +65 -0
- package/src/app/api/instance/config/route.ts +41 -0
- package/src/app/api/instance/init/route.ts +34 -0
- package/src/app/api/instance/upgrade/check/route.ts +26 -0
- package/src/app/api/instance/upgrade/route.ts +96 -0
- package/src/app/api/instance/upgrade/status/route.ts +35 -0
- package/src/app/api/memory/route.ts +0 -11
- package/src/app/api/notifications/route.ts +4 -2
- package/src/app/api/projects/[id]/route.ts +5 -155
- package/src/app/api/projects/__tests__/delete-project.test.ts +10 -19
- package/src/app/api/schedules/[id]/execute/route.ts +111 -0
- package/src/app/api/schedules/[id]/route.ts +9 -1
- package/src/app/api/schedules/__tests__/execute-route.test.ts +118 -0
- package/src/app/api/schedules/route.ts +3 -12
- package/src/app/api/settings/openai/login/route.ts +22 -0
- package/src/app/api/settings/openai/logout/route.ts +7 -0
- package/src/app/api/settings/openai/route.ts +21 -1
- package/src/app/api/settings/providers/route.ts +35 -8
- package/src/app/api/tables/[id]/enrich/__tests__/route.test.ts +153 -0
- package/src/app/api/tables/[id]/enrich/plan/route.ts +98 -0
- package/src/app/api/tables/[id]/enrich/route.ts +147 -0
- package/src/app/api/tables/[id]/enrich/runs/route.ts +25 -0
- package/src/app/api/tasks/[id]/execute/route.ts +0 -21
- package/src/app/api/workflows/[id]/resume/route.ts +59 -0
- package/src/app/api/workflows/[id]/status/route.ts +22 -8
- package/src/app/api/workspace/context/route.ts +2 -0
- package/src/app/api/workspace/fix-data-dir/route.ts +81 -0
- package/src/app/chat/page.tsx +11 -0
- package/src/app/inbox/page.tsx +12 -5
- package/src/app/layout.tsx +42 -21
- package/src/app/page.tsx +0 -2
- package/src/app/settings/page.tsx +6 -9
- package/src/components/chat/__tests__/chat-session-provider.test.tsx +408 -0
- package/src/components/chat/chat-command-popover.tsx +2 -2
- package/src/components/chat/chat-input.tsx +2 -3
- package/src/components/chat/chat-session-provider.tsx +720 -0
- package/src/components/chat/chat-shell.tsx +92 -401
- package/src/components/instance/__tests__/instance-section.test.tsx +125 -0
- package/src/components/instance/instance-section.tsx +382 -0
- package/src/components/instance/upgrade-badge.tsx +219 -0
- package/src/components/notifications/__tests__/batch-proposal-review.test.tsx +95 -0
- package/src/components/notifications/__tests__/notification-item.test.tsx +106 -0
- package/src/components/notifications/batch-proposal-review.tsx +20 -5
- package/src/components/notifications/inbox-list.tsx +11 -2
- package/src/components/notifications/notification-item.tsx +56 -2
- package/src/components/notifications/pending-approval-host.tsx +56 -37
- package/src/components/schedules/schedule-create-sheet.tsx +19 -1
- package/src/components/schedules/schedule-edit-sheet.tsx +20 -1
- package/src/components/schedules/schedule-form.tsx +31 -0
- package/src/components/settings/__tests__/providers-runtimes-section.test.tsx +149 -0
- package/src/components/settings/auth-method-selector.tsx +19 -4
- package/src/components/settings/auth-status-badge.tsx +28 -3
- package/src/components/settings/openai-chatgpt-auth-control.tsx +278 -0
- package/src/components/settings/openai-runtime-section.tsx +7 -1
- package/src/components/settings/providers-runtimes-section.tsx +138 -19
- package/src/components/shared/app-sidebar.tsx +4 -3
- package/src/components/shared/command-palette.tsx +4 -5
- package/src/components/shared/theme-toggle.tsx +5 -24
- package/src/components/shared/workspace-indicator.tsx +61 -2
- package/src/components/tables/__tests__/table-enrichment-sheet.test.tsx +130 -0
- package/src/components/tables/table-create-sheet.tsx +4 -0
- package/src/components/tables/table-enrichment-runs.tsx +103 -0
- package/src/components/tables/table-enrichment-sheet.tsx +538 -0
- package/src/components/tables/table-spreadsheet.tsx +29 -5
- package/src/components/tables/table-toolbar.tsx +10 -1
- package/src/components/tasks/kanban-board.tsx +1 -0
- package/src/components/tasks/kanban-column.tsx +53 -14
- package/src/components/tasks/task-bento-grid.tsx +19 -0
- package/src/components/tasks/task-card.tsx +26 -3
- package/src/components/tasks/task-chip-bar.tsx +24 -0
- package/src/components/tasks/task-result-renderer.tsx +1 -1
- package/src/components/workflows/delay-step-body.tsx +109 -0
- package/src/components/workflows/hooks/use-workflow-status.ts +50 -0
- package/src/components/workflows/loop-status-view.tsx +1 -1
- package/src/components/workflows/shared/step-result.tsx +78 -0
- package/src/components/workflows/shared/workflow-header.tsx +141 -0
- package/src/components/workflows/shared/workflow-loading-skeleton.tsx +36 -0
- package/src/components/workflows/swarm-dashboard.tsx +2 -15
- package/src/components/workflows/views/loop-pattern-view.tsx +137 -0
- package/src/components/workflows/views/sequence-pattern-view.tsx +511 -0
- package/src/components/workflows/workflow-form-view.tsx +133 -16
- package/src/components/workflows/workflow-status-view.tsx +30 -740
- package/src/instrumentation-node.ts +94 -0
- package/src/instrumentation.ts +4 -48
- package/src/lib/agents/__tests__/claude-agent.test.ts +199 -0
- package/src/lib/agents/__tests__/execution-manager.test.ts +1 -27
- package/src/lib/agents/__tests__/failure-reason.test.ts +68 -0
- package/src/lib/agents/__tests__/learned-context.test.ts +0 -11
- package/src/lib/agents/__tests__/learning-session.test.ts +158 -0
- package/src/lib/agents/__tests__/pattern-extractor.test.ts +48 -0
- package/src/lib/agents/claude-agent.ts +155 -18
- package/src/lib/agents/execution-manager.ts +0 -35
- package/src/lib/agents/learned-context.ts +0 -12
- package/src/lib/agents/learning-session.ts +18 -5
- package/src/lib/agents/profiles/__tests__/registry.test.ts +6 -4
- package/src/lib/agents/profiles/builtins/upgrade-assistant/SKILL.md +70 -0
- package/src/lib/agents/profiles/builtins/upgrade-assistant/profile.yaml +32 -0
- package/src/lib/agents/runtime/__tests__/openai-codex-auth.test.ts +118 -0
- package/src/lib/agents/runtime/codex-app-server-client.ts +11 -5
- package/src/lib/agents/runtime/openai-codex-auth.ts +389 -0
- package/src/lib/agents/runtime/openai-codex.ts +29 -60
- package/src/lib/agents/runtime/types.ts +8 -0
- package/src/lib/book/chapter-mapping.ts +11 -0
- package/src/lib/book/content.ts +10 -0
- package/src/lib/chat/__tests__/active-streams.test.ts +49 -0
- package/src/lib/chat/__tests__/finalize-safety-net.test.ts +139 -0
- package/src/lib/chat/__tests__/reconcile.test.ts +137 -0
- package/src/lib/chat/__tests__/stream-telemetry.test.ts +151 -0
- package/src/lib/chat/active-streams.ts +27 -0
- package/src/lib/chat/codex-engine.ts +16 -17
- package/src/lib/chat/context-builder.ts +5 -3
- package/src/lib/chat/engine.ts +50 -3
- package/src/lib/chat/reconcile.ts +117 -0
- package/src/lib/chat/stagent-tools.ts +1 -0
- package/src/lib/chat/stream-telemetry.ts +132 -0
- package/src/lib/chat/suggested-prompts.ts +28 -1
- package/src/lib/chat/system-prompt.ts +26 -1
- package/src/lib/chat/tool-catalog.ts +2 -1
- package/src/lib/chat/tools/__tests__/enrich-table-tool.test.ts +127 -0
- package/src/lib/chat/tools/__tests__/schedule-tools.test.ts +261 -0
- package/src/lib/chat/tools/__tests__/task-tools.test.ts +352 -0
- package/src/lib/chat/tools/__tests__/workflow-tools-dedup.test.ts +217 -0
- package/src/lib/chat/tools/document-tools.ts +29 -13
- package/src/lib/chat/tools/helpers.ts +39 -0
- package/src/lib/chat/tools/notification-tools.ts +9 -5
- package/src/lib/chat/tools/project-tools.ts +33 -0
- package/src/lib/chat/tools/schedule-tools.ts +44 -11
- package/src/lib/chat/tools/table-tools.ts +71 -0
- package/src/lib/chat/tools/task-tools.ts +84 -20
- package/src/lib/chat/tools/workflow-tools.ts +234 -32
- package/src/lib/constants/settings.ts +8 -18
- package/src/lib/data/__tests__/clear.test.ts +56 -2
- package/src/lib/data/clear.ts +20 -15
- package/src/lib/data/delete-project.ts +171 -0
- package/src/lib/db/__tests__/bootstrap.test.ts +1 -1
- package/src/lib/db/bootstrap.ts +45 -16
- package/src/lib/db/index.ts +5 -0
- package/src/lib/db/migrations/0009_add_app_instances.sql +25 -0
- package/src/lib/db/migrations/0024_add_workflow_resume_at.sql +10 -0
- package/src/lib/db/migrations/0025_drop_app_instances.sql +3 -0
- package/src/lib/db/migrations/0026_drop_license.sql +3 -0
- package/src/lib/db/migrations/meta/_journal.json +21 -0
- package/src/lib/db/schema.ts +68 -23
- package/src/lib/environment/workspace-context.ts +13 -1
- package/src/lib/import/dedup.ts +4 -54
- package/src/lib/instance/__tests__/bootstrap.test.ts +362 -0
- package/src/lib/instance/__tests__/detect.test.ts +115 -0
- package/src/lib/instance/__tests__/fingerprint.test.ts +48 -0
- package/src/lib/instance/__tests__/git-ops.test.ts +95 -0
- package/src/lib/instance/__tests__/settings.test.ts +83 -0
- package/src/lib/instance/__tests__/upgrade-poller.test.ts +131 -0
- package/src/lib/instance/bootstrap.ts +270 -0
- package/src/lib/instance/detect.ts +49 -0
- package/src/lib/instance/fingerprint.ts +78 -0
- package/src/lib/instance/git-ops.ts +95 -0
- package/src/lib/instance/settings.ts +61 -0
- package/src/lib/instance/types.ts +77 -0
- package/src/lib/instance/upgrade-poller.ts +153 -0
- package/src/lib/notifications/__tests__/visibility.test.ts +51 -0
- package/src/lib/notifications/visibility.ts +33 -0
- package/src/lib/schedules/__tests__/collision-check.test.ts +93 -0
- package/src/lib/schedules/__tests__/config.test.ts +62 -0
- package/src/lib/schedules/__tests__/firing-metrics.test.ts +99 -0
- package/src/lib/schedules/__tests__/integration.test.ts +82 -0
- package/src/lib/schedules/__tests__/slot-claim.test.ts +242 -0
- package/src/lib/schedules/__tests__/tick-scheduler.test.ts +102 -0
- package/src/lib/schedules/__tests__/turn-budget.test.ts +228 -0
- package/src/lib/schedules/collision-check.ts +105 -0
- package/src/lib/schedules/config.ts +53 -0
- package/src/lib/schedules/scheduler.ts +232 -13
- package/src/lib/schedules/slot-claim.ts +105 -0
- package/src/lib/settings/__tests__/openai-auth.test.ts +101 -0
- package/src/lib/settings/__tests__/openai-login-manager.test.ts +64 -0
- package/src/lib/settings/__tests__/runtime-setup.test.ts +33 -0
- package/src/lib/settings/openai-auth.ts +105 -10
- package/src/lib/settings/openai-login-manager.ts +260 -0
- package/src/lib/settings/runtime-setup.ts +14 -4
- package/src/lib/tables/__tests__/enrichment-planner.test.ts +124 -0
- package/src/lib/tables/__tests__/enrichment.test.ts +147 -0
- package/src/lib/tables/enrichment-planner.ts +454 -0
- package/src/lib/tables/enrichment.ts +328 -0
- package/src/lib/tables/query-builder.ts +5 -2
- package/src/lib/tables/trigger-evaluator.ts +3 -2
- package/src/lib/theme.ts +71 -0
- package/src/lib/usage/ledger.ts +2 -18
- package/src/lib/util/__tests__/similarity.test.ts +106 -0
- package/src/lib/util/similarity.ts +77 -0
- package/src/lib/utils/format-timestamp.ts +24 -0
- package/src/lib/utils/stagent-paths.ts +12 -0
- package/src/lib/validators/__tests__/blueprint.test.ts +172 -0
- package/src/lib/validators/__tests__/settings.test.ts +10 -0
- package/src/lib/validators/blueprint.ts +70 -9
- package/src/lib/validators/profile.ts +2 -2
- package/src/lib/validators/settings.ts +3 -1
- package/src/lib/workflows/__tests__/delay.test.ts +196 -0
- package/src/lib/workflows/__tests__/engine.test.ts +8 -0
- package/src/lib/workflows/__tests__/loop-executor.test.ts +54 -0
- package/src/lib/workflows/__tests__/post-action.test.ts +108 -0
- package/src/lib/workflows/blueprints/instantiator.ts +22 -1
- package/src/lib/workflows/blueprints/types.ts +10 -2
- package/src/lib/workflows/delay.ts +106 -0
- package/src/lib/workflows/engine.ts +207 -4
- package/src/lib/workflows/loop-executor.ts +349 -24
- package/src/lib/workflows/post-action.ts +91 -0
- package/src/lib/workflows/types.ts +166 -1
- package/src/app/api/license/checkout/route.ts +0 -28
- package/src/app/api/license/portal/route.ts +0 -26
- package/src/app/api/license/route.ts +0 -89
- package/src/app/api/license/usage/route.ts +0 -63
- package/src/app/api/marketplace/browse/route.ts +0 -15
- package/src/app/api/marketplace/import/route.ts +0 -28
- package/src/app/api/marketplace/publish/route.ts +0 -40
- package/src/app/api/onboarding/email/route.ts +0 -53
- package/src/app/api/settings/telemetry/route.ts +0 -14
- package/src/app/api/sync/export/route.ts +0 -54
- package/src/app/api/sync/restore/route.ts +0 -37
- package/src/app/api/sync/sessions/route.ts +0 -24
- package/src/app/auth/callback/route.ts +0 -73
- package/src/app/marketplace/page.tsx +0 -19
- package/src/components/analytics/analytics-gate-card.tsx +0 -101
- package/src/components/marketplace/blueprint-card.tsx +0 -61
- package/src/components/marketplace/marketplace-browser.tsx +0 -131
- package/src/components/onboarding/email-capture-card.tsx +0 -104
- package/src/components/settings/activation-form.tsx +0 -95
- package/src/components/settings/cloud-account-section.tsx +0 -147
- package/src/components/settings/cloud-sync-section.tsx +0 -155
- package/src/components/settings/subscription-section.tsx +0 -410
- package/src/components/settings/telemetry-section.tsx +0 -80
- package/src/components/shared/premium-gate-overlay.tsx +0 -50
- package/src/components/shared/schedule-gate-dialog.tsx +0 -64
- package/src/components/shared/upgrade-banner.tsx +0 -112
- package/src/hooks/use-supabase-auth.ts +0 -79
- package/src/lib/billing/email.ts +0 -54
- package/src/lib/billing/products.ts +0 -80
- package/src/lib/billing/stripe.ts +0 -101
- package/src/lib/cloud/supabase-browser.ts +0 -32
- package/src/lib/cloud/supabase-client.ts +0 -56
- package/src/lib/license/__tests__/features.test.ts +0 -56
- package/src/lib/license/__tests__/key-format.test.ts +0 -88
- package/src/lib/license/__tests__/manager.test.ts +0 -64
- package/src/lib/license/__tests__/tier-limits.test.ts +0 -79
- package/src/lib/license/cloud-validation.ts +0 -60
- package/src/lib/license/features.ts +0 -44
- package/src/lib/license/key-format.ts +0 -101
- package/src/lib/license/limit-check.ts +0 -111
- package/src/lib/license/limit-queries.ts +0 -51
- package/src/lib/license/manager.ts +0 -345
- package/src/lib/license/notifications.ts +0 -59
- package/src/lib/license/tier-limits.ts +0 -71
- package/src/lib/marketplace/marketplace-client.ts +0 -107
- package/src/lib/sync/cloud-sync.ts +0 -235
- package/src/lib/telemetry/conversion-events.ts +0 -71
- package/src/lib/telemetry/queue.ts +0 -122
- 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'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'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
|
-
}
|