opencode-supabase 0.0.1

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.
@@ -0,0 +1,142 @@
1
+ import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
2
+ import { createSignal } from "solid-js";
3
+
4
+ type SupabaseDialogProps = {
5
+ api: TuiPluginApi;
6
+ onClose: () => void;
7
+ };
8
+
9
+ type OAuthState =
10
+ | { type: "idle" }
11
+ | { type: "authorizing"; url: string }
12
+ | { type: "waiting_callback"; url: string }
13
+ | { type: "success" }
14
+ | { type: "error"; message: string };
15
+
16
+ // API response types
17
+ type ApiError = { message?: string; [key: string]: unknown };
18
+ type ApiResponse<T> = { data?: T; error?: ApiError };
19
+
20
+ type AuthData = {
21
+ url: string;
22
+ instructions: string;
23
+ method: string;
24
+ };
25
+
26
+ export function SupabaseDialog(props: SupabaseDialogProps) {
27
+ const [state, setState] = createSignal<OAuthState>({ type: "idle" });
28
+
29
+ const startOAuth = async () => {
30
+ try {
31
+ setState({ type: "authorizing", url: "" });
32
+
33
+ // Start OAuth authorization
34
+ const authResponse = (await props.api.client.provider.oauth.authorize({
35
+ providerID: "supabase",
36
+ method: 0,
37
+ })) as unknown as ApiResponse<AuthData>;
38
+
39
+ // Handle the response shape from the plugin API
40
+ if (authResponse.error) {
41
+ throw new Error(authResponse.error.message || "Failed to start OAuth authorization");
42
+ }
43
+
44
+ const authData = authResponse.data;
45
+
46
+ if (!authData?.url) {
47
+ throw new Error("Invalid OAuth authorization response");
48
+ }
49
+
50
+ const { url, method } = authData;
51
+ setState({ type: "authorizing", url });
52
+
53
+ // Attempt to open browser automatically
54
+ if (method === "auto") {
55
+ try {
56
+ const open = await import("open");
57
+ await open.default(url);
58
+ } catch {
59
+ // Browser auto-open failed, user can click the URL manually
60
+ }
61
+ }
62
+
63
+ setState({ type: "waiting_callback", url });
64
+
65
+ // Wait for callback
66
+ const callbackResponse = (await props.api.client.provider.oauth.callback({
67
+ providerID: "supabase",
68
+ method: 0,
69
+ })) as unknown as ApiResponse<boolean>;
70
+
71
+ if (callbackResponse.error) {
72
+ throw new Error(callbackResponse.error.message || "OAuth callback failed");
73
+ }
74
+
75
+ const callbackSucceeded = callbackResponse.data === true;
76
+
77
+ if (callbackSucceeded) {
78
+ setState({ type: "success" });
79
+ props.api.ui.toast({
80
+ variant: "success",
81
+ message: "Connected to Supabase. Management tools coming in next update.",
82
+ });
83
+ props.onClose();
84
+ } else {
85
+ throw new Error("OAuth authorization was denied");
86
+ }
87
+ } catch (error) {
88
+ const message = error instanceof Error ? error.message : "Authorization failed";
89
+ setState({ type: "error", message });
90
+ props.api.ui.toast({
91
+ variant: "error",
92
+ message: `Supabase authorization failed: ${message}`,
93
+ });
94
+ props.onClose();
95
+ }
96
+ };
97
+
98
+ const currentState = state();
99
+
100
+ if (currentState.type === "idle") {
101
+ return props.api.ui.DialogConfirm({
102
+ title: "Connect Supabase",
103
+ message:
104
+ "This will open a browser window to authorize OpenCode to access your Supabase account. Continue?",
105
+ onConfirm: startOAuth,
106
+ onCancel: props.onClose,
107
+ });
108
+ }
109
+
110
+ if (currentState.type === "authorizing") {
111
+ return props.api.ui.DialogAlert({
112
+ title: "Connect Supabase",
113
+ message: currentState.url
114
+ ? `Opening browser to authorize Supabase...\n\nIf the browser doesn't open automatically, visit:\n${currentState.url}`
115
+ : "Starting authorization...",
116
+ onConfirm: props.onClose,
117
+ });
118
+ }
119
+
120
+ if (currentState.type === "waiting_callback") {
121
+ return props.api.ui.DialogAlert({
122
+ title: "Connect Supabase",
123
+ message: `Waiting for authorization in your browser...\n\nIf you need to complete authorization manually, visit:\n${currentState.url}`,
124
+ onConfirm: props.onClose,
125
+ });
126
+ }
127
+
128
+ if (currentState.type === "error") {
129
+ return props.api.ui.DialogAlert({
130
+ title: "Authorization Failed",
131
+ message: currentState.message,
132
+ onConfirm: props.onClose,
133
+ });
134
+ }
135
+
136
+ // Success state (should close immediately via onClose)
137
+ return props.api.ui.DialogAlert({
138
+ title: "Connected",
139
+ message: "Successfully connected to Supabase.",
140
+ onConfirm: props.onClose,
141
+ });
142
+ }
@@ -0,0 +1,14 @@
1
+ import type { TuiPlugin } from "@opencode-ai/plugin/tui";
2
+
3
+ import { createSupabaseCommand } from "./commands";
4
+ import { SupabaseDialog } from "./dialog";
5
+
6
+ const tui: TuiPlugin = async (api) => {
7
+ api.command.register(() => [
8
+ createSupabaseCommand(() => {
9
+ api.ui.dialog.replace(() => SupabaseDialog({ api, onClose: () => api.ui.dialog.clear() }));
10
+ }),
11
+ ]);
12
+ };
13
+
14
+ export default { id: "supabase", tui };