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,73 +0,0 @@
1
- import { NextRequest, NextResponse } from "next/server";
2
- import { createClient } from "@supabase/supabase-js";
3
- import { licenseManager } from "@/lib/license/manager";
4
- import { validateLicenseWithCloud } from "@/lib/license/cloud-validation";
5
- import { sendUpgradeConfirmation } from "@/lib/billing/email";
6
- import { getSupabaseUrl, getSupabaseAnonKey } from "@/lib/cloud/supabase-client";
7
-
8
- /**
9
- * GET /auth/callback
10
- *
11
- * Handles the Supabase magic link redirect. Exchanges the auth code
12
- * for a session, validates the user's license, and redirects to settings.
13
- *
14
- * Flow:
15
- * 1. User clicks magic link in email
16
- * 2. Supabase redirects here with ?code=...
17
- * 3. We exchange the code for a session (gets user email)
18
- * 4. Validate license against cloud (check if they have a paid subscription)
19
- * 5. Activate the license locally if found
20
- * 6. Redirect to /settings with success indicator
21
- */
22
- export async function GET(req: NextRequest) {
23
- const { searchParams } = req.nextUrl;
24
- const code = searchParams.get("code");
25
-
26
- if (!code) {
27
- return NextResponse.redirect(new URL("/settings?auth=error", req.url));
28
- }
29
-
30
- const supabase = createClient(getSupabaseUrl(), getSupabaseAnonKey(), {
31
- auth: { persistSession: false },
32
- });
33
-
34
- // Exchange the code for a session
35
- const { data, error } = await supabase.auth.exchangeCodeForSession(code);
36
-
37
- if (error || !data.session) {
38
- console.error("[auth/callback] Code exchange failed:", error?.message);
39
- return NextResponse.redirect(new URL("/settings?auth=error", req.url));
40
- }
41
-
42
- const email = data.session.user.email;
43
- if (!email) {
44
- return NextResponse.redirect(new URL("/settings?auth=error", req.url));
45
- }
46
-
47
- // Validate license against cloud — check if this email has a paid subscription
48
- const validation = await validateLicenseWithCloud(email);
49
-
50
- if (validation.valid && validation.tier !== "community") {
51
- // User has a paid subscription — activate locally
52
- licenseManager.activate({
53
- tier: validation.tier,
54
- email,
55
- expiresAt: validation.expiresAt,
56
- });
57
- // Send upgrade confirmation email (fire-and-forget)
58
- sendUpgradeConfirmation(email, validation.tier).catch(() => {});
59
- } else {
60
- // No paid subscription — still link the email for future activation
61
- // This sets the email so cloud sync and marketplace work when they upgrade
62
- const currentTier = licenseManager.getTierFromDb();
63
- if (currentTier === "community") {
64
- // Just store the email association without changing tier
65
- licenseManager.activate({
66
- tier: "community",
67
- email,
68
- });
69
- }
70
- }
71
-
72
- return NextResponse.redirect(new URL("/settings?auth=success", req.url));
73
- }
@@ -1,19 +0,0 @@
1
- import { PageShell } from "@/components/shared/page-shell";
2
- import { MarketplaceBrowser } from "@/components/marketplace/marketplace-browser";
3
- import { licenseManager } from "@/lib/license/manager";
4
- import { canAccessFeature } from "@/lib/license/features";
5
-
6
- export const dynamic = "force-dynamic";
7
-
8
- export default function MarketplacePage() {
9
- // Use getTierFromDb() for Server Components (Turbopack module instance separation)
10
- const tier = licenseManager.getTierFromDb();
11
- const canImport = canAccessFeature(tier, "marketplace-import");
12
- const canPublish = canAccessFeature(tier, "marketplace-publish");
13
-
14
- return (
15
- <PageShell title="Marketplace" description="Browse and import workflow blueprints">
16
- <MarketplaceBrowser canImport={canImport} canPublish={canPublish} />
17
- </PageShell>
18
- );
19
- }
@@ -1,101 +0,0 @@
1
- "use client";
2
-
3
- import Link from "next/link";
4
- import { TrendingUp, Trophy, Coins, Sparkles } from "lucide-react";
5
- import { Button } from "@/components/ui/button";
6
- import { Badge } from "@/components/ui/badge";
7
- import { TIER_PRICING } from "@/lib/license/tier-limits";
8
-
9
- function ValueProp({
10
- icon: Icon,
11
- title,
12
- description,
13
- }: {
14
- icon: typeof TrendingUp;
15
- title: string;
16
- description: string;
17
- }) {
18
- return (
19
- <div className="space-y-1.5">
20
- <div className="flex items-center gap-2">
21
- <div className="flex items-center justify-center w-7 h-7 rounded-lg border bg-background">
22
- <Icon className="h-3.5 w-3.5 text-primary" />
23
- </div>
24
- <span className="text-xs font-semibold tracking-tight">{title}</span>
25
- </div>
26
- <p className="text-[11px] leading-relaxed text-muted-foreground pl-9">
27
- {description}
28
- </p>
29
- </div>
30
- );
31
- }
32
-
33
- /**
34
- * Analytics-specific upgrade CTA card.
35
- * Replaces the generic PremiumGateOverlay lock card with
36
- * benefit-oriented messaging and value props.
37
- */
38
- export function AnalyticsGateCard() {
39
- const price = TIER_PRICING.operator.monthly;
40
-
41
- return (
42
- <div className="w-full max-w-lg mx-auto">
43
- <div className="surface-card rounded-xl border shadow-lg p-8 space-y-6">
44
- {/* Header */}
45
- <div className="space-y-2 text-center">
46
- <div className="flex items-center justify-center gap-2 mb-3">
47
- <Sparkles className="h-4 w-4 text-primary" />
48
- <Badge variant="secondary" className="text-[10px] font-medium tracking-wide uppercase">
49
- Operator Feature
50
- </Badge>
51
- </div>
52
- <h2 className="text-xl font-bold tracking-tight">
53
- See what your AI agents are worth
54
- </h2>
55
- <p className="text-sm text-muted-foreground max-w-xs mx-auto">
56
- Turn raw execution data into actionable ROI insights
57
- </p>
58
- </div>
59
-
60
- {/* Value Props */}
61
- <div className="space-y-4 py-2">
62
- <ValueProp
63
- icon={TrendingUp}
64
- title="ROI Tracking"
65
- description="Know exactly how much time and money your agents save with automated value calculations"
66
- />
67
- <ValueProp
68
- icon={Trophy}
69
- title="Profile Leaderboard"
70
- description="See which agent profiles deliver the best results and optimize your team"
71
- />
72
- <ValueProp
73
- icon={Coins}
74
- title="Cost Efficiency"
75
- description="Track cost-per-outcome trends to find the sweet spot between quality and spend"
76
- />
77
- </div>
78
-
79
- {/* Pricing + CTA */}
80
- <div className="space-y-3 pt-2">
81
- <div className="flex items-baseline justify-center gap-1.5">
82
- <span className="text-2xl font-bold">${price}</span>
83
- <span className="text-xs text-muted-foreground">/mo</span>
84
- <span className="text-xs text-muted-foreground mx-1">·</span>
85
- <span className="text-xs text-muted-foreground">Operator tier</span>
86
- </div>
87
- <Button className="w-full" size="lg" asChild>
88
- <Link href="/settings?highlight=operator">
89
- Unlock Analytics
90
- </Link>
91
- </Button>
92
- </div>
93
-
94
- {/* Social proof / objection handler */}
95
- <p className="text-[11px] text-center text-muted-foreground">
96
- Derived from data you already have — zero setup required
97
- </p>
98
- </div>
99
- </div>
100
- );
101
- }
@@ -1,61 +0,0 @@
1
- "use client";
2
-
3
- import { Download, Star } from "lucide-react";
4
- import { Badge } from "@/components/ui/badge";
5
- import { Button } from "@/components/ui/button";
6
- import type { MarketplaceBlueprint } from "@/lib/marketplace/marketplace-client";
7
-
8
- interface BlueprintCardProps {
9
- blueprint: MarketplaceBlueprint;
10
- canImport: boolean;
11
- onImport?: (id: string) => void;
12
- }
13
-
14
- export function BlueprintCard({ blueprint, canImport, onImport }: BlueprintCardProps) {
15
- return (
16
- <div className="surface-card-muted rounded-lg border p-4 space-y-3">
17
- <div className="flex items-start justify-between gap-2">
18
- <div className="space-y-1">
19
- <h3 className="text-sm font-medium">{blueprint.title}</h3>
20
- {blueprint.description && (
21
- <p className="text-xs text-muted-foreground line-clamp-2">
22
- {blueprint.description}
23
- </p>
24
- )}
25
- </div>
26
- <Badge variant="secondary" className="text-[10px] shrink-0">
27
- {blueprint.category}
28
- </Badge>
29
- </div>
30
-
31
- <div className="flex items-center justify-between">
32
- <div className="flex items-center gap-3 text-xs text-muted-foreground">
33
- <span className="flex items-center gap-1">
34
- <Download className="h-3 w-3" />
35
- {blueprint.install_count}
36
- </span>
37
- {blueprint.success_rate > 0 && (
38
- <span className="flex items-center gap-1">
39
- <Star className="h-3 w-3" />
40
- {Math.round(blueprint.success_rate * 100)}%
41
- </span>
42
- )}
43
- {blueprint.tags?.length > 0 && (
44
- <span>{blueprint.tags.slice(0, 2).join(", ")}</span>
45
- )}
46
- </div>
47
-
48
- {canImport && onImport && (
49
- <Button
50
- size="sm"
51
- variant="outline"
52
- onClick={() => onImport(blueprint.id)}
53
- >
54
- <Download className="h-3 w-3 mr-1" />
55
- Import
56
- </Button>
57
- )}
58
- </div>
59
- </div>
60
- );
61
- }
@@ -1,131 +0,0 @@
1
- "use client";
2
-
3
- import { useEffect, useState } from "react";
4
- import { Store } from "lucide-react";
5
- import { toast } from "sonner";
6
- import { Button } from "@/components/ui/button";
7
- import { Badge } from "@/components/ui/badge";
8
- import { EmptyState } from "@/components/shared/empty-state";
9
- import { BlueprintCard } from "./blueprint-card";
10
- import type { MarketplaceBlueprint } from "@/lib/marketplace/marketplace-client";
11
-
12
- interface MarketplaceBrowserProps {
13
- canImport: boolean;
14
- canPublish: boolean;
15
- }
16
-
17
- const CATEGORIES = ["all", "general", "research", "content", "data", "automation"];
18
-
19
- export function MarketplaceBrowser({ canImport }: MarketplaceBrowserProps) {
20
- const [blueprints, setBlueprints] = useState<MarketplaceBlueprint[]>([]);
21
- const [category, setCategory] = useState("all");
22
- const [loading, setLoading] = useState(true);
23
- const [page, setPage] = useState(1);
24
- const [total, setTotal] = useState(0);
25
-
26
- useEffect(() => {
27
- setLoading(true);
28
- const params = new URLSearchParams({ page: String(page) });
29
- if (category !== "all") params.set("category", category);
30
-
31
- fetch(`/api/marketplace/browse?${params}`)
32
- .then((r) => (r.ok ? r.json() : { blueprints: [], total: 0 }))
33
- .then((d) => {
34
- setBlueprints(d.blueprints ?? []);
35
- setTotal(d.total ?? 0);
36
- })
37
- .catch(() => setBlueprints([]))
38
- .finally(() => setLoading(false));
39
- }, [page, category]);
40
-
41
- async function handleImport(blueprintId: string) {
42
- try {
43
- const res = await fetch("/api/marketplace/import", {
44
- method: "POST",
45
- headers: { "Content-Type": "application/json" },
46
- body: JSON.stringify({ blueprintId }),
47
- });
48
- if (res.ok) {
49
- toast.success("Blueprint imported! Check your workflows.");
50
- } else {
51
- const data = await res.json();
52
- toast.error(data.error ?? "Import failed");
53
- }
54
- } catch {
55
- toast.error("Failed to import blueprint");
56
- }
57
- }
58
-
59
- return (
60
- <div className="space-y-4">
61
- {/* Category filter */}
62
- <div className="flex items-center gap-2 flex-wrap">
63
- {CATEGORIES.map((cat) => (
64
- <Badge
65
- key={cat}
66
- variant={category === cat ? "default" : "outline"}
67
- className="cursor-pointer capitalize"
68
- onClick={() => { setCategory(cat); setPage(1); }}
69
- >
70
- {cat}
71
- </Badge>
72
- ))}
73
- </div>
74
-
75
- {/* Results */}
76
- {loading ? (
77
- <div className="grid grid-cols-2 gap-3">
78
- {[1, 2, 3, 4].map((i) => (
79
- <div key={i} className="h-32 bg-muted rounded-lg animate-pulse" />
80
- ))}
81
- </div>
82
- ) : blueprints.length === 0 ? (
83
- <EmptyState
84
- icon={Store}
85
- heading="No blueprints found"
86
- description={
87
- category !== "all"
88
- ? `No blueprints in the "${category}" category yet.`
89
- : "The marketplace is empty. Be the first to publish a blueprint!"
90
- }
91
- />
92
- ) : (
93
- <div className="grid grid-cols-2 gap-3">
94
- {blueprints.map((bp) => (
95
- <BlueprintCard
96
- key={bp.id}
97
- blueprint={bp}
98
- canImport={canImport}
99
- onImport={handleImport}
100
- />
101
- ))}
102
- </div>
103
- )}
104
-
105
- {/* Pagination */}
106
- {total > 20 && (
107
- <div className="flex justify-center gap-2">
108
- <Button
109
- size="sm"
110
- variant="outline"
111
- disabled={page <= 1}
112
- onClick={() => setPage((p) => p - 1)}
113
- >
114
- Previous
115
- </Button>
116
- <span className="text-xs text-muted-foreground self-center">
117
- Page {page} of {Math.ceil(total / 20)}
118
- </span>
119
- <Button
120
- size="sm"
121
- variant="outline"
122
- disabled={page * 20 >= total}
123
- onClick={() => setPage((p) => p + 1)}
124
- >
125
- Next
126
- </Button>
127
- </div>
128
- )}
129
- </div>
130
- );
131
- }
@@ -1,104 +0,0 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { Mail, X } from "lucide-react";
5
- import { Button } from "@/components/ui/button";
6
- import { Input } from "@/components/ui/input";
7
- import { Card, CardContent } from "@/components/ui/card";
8
-
9
- const DISMISSED_KEY = "onboarding-email-dismissed";
10
-
11
- /**
12
- * Non-blocking email capture card for first-run users.
13
- * Appears once, never again after submit or dismiss.
14
- */
15
- export function EmailCaptureCard() {
16
- const [dismissed, setDismissed] = useState(() => {
17
- if (typeof window === "undefined") return true;
18
- return localStorage.getItem(DISMISSED_KEY) === "true";
19
- });
20
- const [email, setEmail] = useState("");
21
- const [status, setStatus] = useState<"idle" | "loading" | "success" | "error">("idle");
22
-
23
- function dismiss() {
24
- localStorage.setItem(DISMISSED_KEY, "true");
25
- setDismissed(true);
26
- }
27
-
28
- async function handleSubmit(e: React.FormEvent) {
29
- e.preventDefault();
30
- if (!email.includes("@")) return;
31
-
32
- setStatus("loading");
33
- try {
34
- const res = await fetch("/api/onboarding/email", {
35
- method: "POST",
36
- headers: { "Content-Type": "application/json" },
37
- body: JSON.stringify({ email }),
38
- });
39
- if (res.ok) {
40
- setStatus("success");
41
- localStorage.setItem(DISMISSED_KEY, "true");
42
- } else {
43
- setStatus("error");
44
- }
45
- } catch {
46
- setStatus("error");
47
- }
48
- }
49
-
50
- if (dismissed) return null;
51
-
52
- if (status === "success") {
53
- return (
54
- <Card className="border-primary/20">
55
- <CardContent className="py-4 text-center">
56
- <p className="text-sm text-muted-foreground">
57
- Check your email for a magic link to connect your account.
58
- </p>
59
- </CardContent>
60
- </Card>
61
- );
62
- }
63
-
64
- return (
65
- <Card className="border-primary/20">
66
- <CardContent className="py-4">
67
- <div className="flex items-start justify-between gap-4">
68
- <div className="flex items-start gap-3 flex-1">
69
- <Mail className="h-5 w-5 text-primary mt-0.5 shrink-0" />
70
- <div className="space-y-2 flex-1">
71
- <p className="text-sm font-medium">Connect your email for cloud features</p>
72
- <p className="text-xs text-muted-foreground">
73
- Enable cloud sync, billing, and marketplace access. No account required to use Stagent.
74
- </p>
75
- <form onSubmit={handleSubmit} className="flex gap-2 max-w-sm">
76
- <Input
77
- type="email"
78
- placeholder="you@example.com"
79
- value={email}
80
- onChange={(e) => setEmail(e.target.value)}
81
- className="text-sm"
82
- required
83
- />
84
- <Button size="sm" type="submit" disabled={status === "loading"}>
85
- {status === "loading" ? "..." : "Connect"}
86
- </Button>
87
- </form>
88
- {status === "error" && (
89
- <p className="text-xs text-destructive">Something went wrong. Try again later.</p>
90
- )}
91
- </div>
92
- </div>
93
- <button
94
- onClick={dismiss}
95
- className="text-muted-foreground hover:text-foreground transition-colors"
96
- aria-label="Dismiss"
97
- >
98
- <X className="h-4 w-4" />
99
- </button>
100
- </div>
101
- </CardContent>
102
- </Card>
103
- );
104
- }
@@ -1,95 +0,0 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { KeyRound } from "lucide-react";
5
- import { toast } from "sonner";
6
- import { Button } from "@/components/ui/button";
7
- import { Input } from "@/components/ui/input";
8
- import { validateLicenseKey, formatKeyInput } from "@/lib/license/key-format";
9
-
10
- interface ActivationFormProps {
11
- onActivated?: () => void;
12
- }
13
-
14
- export function ActivationForm({ onActivated }: ActivationFormProps) {
15
- const [keyInput, setKeyInput] = useState("");
16
- const [error, setError] = useState<string | null>(null);
17
- const [activating, setActivating] = useState(false);
18
-
19
- function handleKeyChange(value: string) {
20
- // Strip the STAG- prefix for formatting, then re-add
21
- const raw = value.replace(/^STAG-?/, "").replace(/-/g, "");
22
- setKeyInput(formatKeyInput(raw));
23
- setError(null);
24
- }
25
-
26
- async function handleActivate() {
27
- const validation = validateLicenseKey(keyInput);
28
- if (!validation.valid) {
29
- setError(validation.error ?? "Invalid key");
30
- return;
31
- }
32
-
33
- setActivating(true);
34
- setError(null);
35
-
36
- try {
37
- const res = await fetch("/api/license", {
38
- method: "POST",
39
- headers: { "Content-Type": "application/json" },
40
- body: JSON.stringify({ key: keyInput }),
41
- });
42
-
43
- const data = await res.json();
44
-
45
- if (!res.ok) {
46
- const errorMsg =
47
- res.status === 404
48
- ? "License key not found"
49
- : res.status === 409
50
- ? "This key has already been used"
51
- : res.status === 410
52
- ? "This key has expired"
53
- : data.error ?? "Activation failed";
54
- setError(errorMsg);
55
- return;
56
- }
57
-
58
- toast.success(`Activated ${data.tier} tier!`);
59
- setKeyInput("");
60
- onActivated?.();
61
- } catch {
62
- setError("Network error — please try again");
63
- } finally {
64
- setActivating(false);
65
- }
66
- }
67
-
68
- return (
69
- <div className="space-y-3">
70
- <div className="flex items-center gap-2">
71
- <KeyRound className="h-3.5 w-3.5 text-muted-foreground" />
72
- <span className="text-xs font-medium">Activate with License Key</span>
73
- </div>
74
- <div className="flex gap-2">
75
- <Input
76
- value={keyInput}
77
- onChange={(e) => handleKeyChange(e.target.value)}
78
- placeholder="STAG-XXXX-XXXX-XXXX-XXXX"
79
- className="font-mono text-sm"
80
- maxLength={24}
81
- />
82
- <Button
83
- size="sm"
84
- onClick={handleActivate}
85
- disabled={activating || keyInput.length < 24}
86
- >
87
- {activating ? "Activating..." : "Activate"}
88
- </Button>
89
- </div>
90
- {error && (
91
- <p className="text-xs text-destructive">{error}</p>
92
- )}
93
- </div>
94
- );
95
- }