@stigmer/react 0.0.84 → 0.0.86
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/demo/fixtures.d.ts +4 -0
- package/demo/fixtures.d.ts.map +1 -1
- package/demo/fixtures.js +4 -0
- package/demo/fixtures.js.map +1 -1
- package/demo/samples.d.ts +1 -0
- package/demo/samples.d.ts.map +1 -1
- package/demo/samples.js +1 -0
- package/demo/samples.js.map +1 -1
- package/execution/ArtifactPreviewModal.d.ts +78 -18
- package/execution/ArtifactPreviewModal.d.ts.map +1 -1
- package/execution/ArtifactPreviewModal.js +82 -60
- package/execution/ArtifactPreviewModal.js.map +1 -1
- package/execution/index.d.ts +2 -2
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +1 -1
- package/execution/index.js.map +1 -1
- package/index.d.ts +6 -4
- package/index.d.ts.map +1 -1
- package/index.js +4 -2
- package/index.js.map +1 -1
- package/library/ResourceListView.js +1 -1
- package/library/ResourceListView.js.map +1 -1
- package/mcp-server/McpServerConnectDialog.d.ts +51 -0
- package/mcp-server/McpServerConnectDialog.d.ts.map +1 -0
- package/mcp-server/McpServerConnectDialog.js +164 -0
- package/mcp-server/McpServerConnectDialog.js.map +1 -0
- package/mcp-server/McpServerDetailView.js +2 -2
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/mcp-server/McpServerPicker.d.ts.map +1 -1
- package/mcp-server/McpServerPicker.js +7 -1
- package/mcp-server/McpServerPicker.js.map +1 -1
- package/mcp-server/index.d.ts +2 -0
- package/mcp-server/index.d.ts.map +1 -1
- package/mcp-server/index.js +1 -0
- package/mcp-server/index.js.map +1 -1
- package/oauth-app/CreateOAuthAppForm.d.ts +41 -0
- package/oauth-app/CreateOAuthAppForm.d.ts.map +1 -0
- package/oauth-app/CreateOAuthAppForm.js +140 -0
- package/oauth-app/CreateOAuthAppForm.js.map +1 -0
- package/oauth-app/OAuthAppDetailPanel.d.ts +43 -0
- package/oauth-app/OAuthAppDetailPanel.d.ts.map +1 -0
- package/oauth-app/OAuthAppDetailPanel.js +202 -0
- package/oauth-app/OAuthAppDetailPanel.js.map +1 -0
- package/oauth-app/OAuthAppListPanel.d.ts +43 -0
- package/oauth-app/OAuthAppListPanel.d.ts.map +1 -0
- package/oauth-app/OAuthAppListPanel.js +79 -0
- package/oauth-app/OAuthAppListPanel.js.map +1 -0
- package/oauth-app/index.d.ts +15 -0
- package/oauth-app/index.d.ts.map +1 -0
- package/oauth-app/index.js +8 -0
- package/oauth-app/index.js.map +1 -0
- package/oauth-app/useCreateOAuthApp.d.ts +39 -0
- package/oauth-app/useCreateOAuthApp.d.ts.map +1 -0
- package/oauth-app/useCreateOAuthApp.js +50 -0
- package/oauth-app/useCreateOAuthApp.js.map +1 -0
- package/oauth-app/useDeleteOAuthApp.d.ts +31 -0
- package/oauth-app/useDeleteOAuthApp.d.ts.map +1 -0
- package/oauth-app/useDeleteOAuthApp.js +43 -0
- package/oauth-app/useDeleteOAuthApp.js.map +1 -0
- package/oauth-app/useOAuthAppList.d.ts +32 -0
- package/oauth-app/useOAuthAppList.d.ts.map +1 -0
- package/oauth-app/useOAuthAppList.js +61 -0
- package/oauth-app/useOAuthAppList.js.map +1 -0
- package/oauth-app/useUpdateOAuthApp.d.ts +38 -0
- package/oauth-app/useUpdateOAuthApp.d.ts.map +1 -0
- package/oauth-app/useUpdateOAuthApp.js +49 -0
- package/oauth-app/useUpdateOAuthApp.js.map +1 -0
- package/package.json +4 -4
- package/src/demo/fixtures.ts +8 -0
- package/src/demo/samples.ts +2 -0
- package/src/execution/ArtifactPreviewModal.tsx +206 -128
- package/src/execution/index.ts +2 -2
- package/src/index.ts +24 -0
- package/src/library/ResourceListView.tsx +8 -8
- package/src/mcp-server/McpServerConnectDialog.tsx +527 -0
- package/src/mcp-server/McpServerDetailView.tsx +2 -1
- package/src/mcp-server/McpServerPicker.tsx +8 -1
- package/src/mcp-server/index.ts +3 -0
- package/src/oauth-app/CreateOAuthAppForm.tsx +449 -0
- package/src/oauth-app/OAuthAppDetailPanel.tsx +671 -0
- package/src/oauth-app/OAuthAppListPanel.tsx +237 -0
- package/src/oauth-app/index.ts +14 -0
- package/src/oauth-app/useCreateOAuthApp.ts +70 -0
- package/src/oauth-app/useDeleteOAuthApp.ts +62 -0
- package/src/oauth-app/useOAuthAppList.ts +84 -0
- package/src/oauth-app/useUpdateOAuthApp.ts +69 -0
- package/styles.css +1 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@stigmer/theme";
|
|
4
|
+
import { getUserMessage } from "@stigmer/sdk";
|
|
5
|
+
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
|
6
|
+
import type { OAuthApp } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/api_pb";
|
|
7
|
+
import { useOAuthAppList } from "./useOAuthAppList";
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Public API
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
/** Props for {@link OAuthAppListPanel}. */
|
|
14
|
+
export interface OAuthAppListPanelProps {
|
|
15
|
+
/** Organization slug to list OAuth apps for. */
|
|
16
|
+
readonly org: string;
|
|
17
|
+
/**
|
|
18
|
+
* Fired when the user wants to view/edit an OAuth app.
|
|
19
|
+
* When provided, rows become interactive with a pencil icon button.
|
|
20
|
+
* When absent, rows remain static (backward compatible).
|
|
21
|
+
*/
|
|
22
|
+
readonly onEdit?: (app: OAuthApp) => void;
|
|
23
|
+
/** Re-expose refetch so parents can trigger a list refresh. */
|
|
24
|
+
readonly onRefetchRef?: (refetch: () => void) => void;
|
|
25
|
+
/** Additional CSS class names for the root container. */
|
|
26
|
+
readonly className?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Displays a list of {@link OAuthApp} resources owned by an
|
|
31
|
+
* organization.
|
|
32
|
+
*
|
|
33
|
+
* Each row shows the provider name, client ID (non-secret), and
|
|
34
|
+
* creation date. When `onEdit` is provided, rows include a pencil
|
|
35
|
+
* icon that fires the callback with the selected app — enabling
|
|
36
|
+
* navigation to a detail/edit view.
|
|
37
|
+
*
|
|
38
|
+
* All visual properties flow through `--stgm-*` design tokens.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* <OAuthAppListPanel org="acme" />
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* <OAuthAppListPanel
|
|
48
|
+
* org="acme"
|
|
49
|
+
* onEdit={(app) => setFlow({ phase: "editing", oauthApp: app })}
|
|
50
|
+
* onRefetchRef={(refetch) => { listRefetchRef.current = refetch; }}
|
|
51
|
+
* />
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export function OAuthAppListPanel({
|
|
55
|
+
org,
|
|
56
|
+
onEdit,
|
|
57
|
+
onRefetchRef,
|
|
58
|
+
className,
|
|
59
|
+
}: OAuthAppListPanelProps) {
|
|
60
|
+
const { oauthApps, isLoading, error, refetch } = useOAuthAppList(org);
|
|
61
|
+
|
|
62
|
+
if (onRefetchRef) {
|
|
63
|
+
onRefetchRef(refetch);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (isLoading) {
|
|
67
|
+
return (
|
|
68
|
+
<div
|
|
69
|
+
className={cn("space-y-2", className)}
|
|
70
|
+
aria-busy="true"
|
|
71
|
+
aria-label="Loading OAuth apps"
|
|
72
|
+
>
|
|
73
|
+
{Array.from({ length: 2 }, (_, i) => (
|
|
74
|
+
<div
|
|
75
|
+
key={i}
|
|
76
|
+
className="bg-muted/40 h-14 animate-pulse rounded-lg"
|
|
77
|
+
/>
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (error) {
|
|
84
|
+
return (
|
|
85
|
+
<p className={cn("text-destructive text-xs", className)} role="alert">
|
|
86
|
+
{getUserMessage(error)}
|
|
87
|
+
</p>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (oauthApps.length === 0) {
|
|
92
|
+
return (
|
|
93
|
+
<p
|
|
94
|
+
className={cn(
|
|
95
|
+
"text-muted-foreground py-4 text-center text-xs",
|
|
96
|
+
className,
|
|
97
|
+
)}
|
|
98
|
+
>
|
|
99
|
+
No OAuth apps configured yet.
|
|
100
|
+
</p>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<div
|
|
106
|
+
className={cn("space-y-2", className)}
|
|
107
|
+
role="list"
|
|
108
|
+
aria-label="OAuth apps"
|
|
109
|
+
>
|
|
110
|
+
{oauthApps.map((app) => (
|
|
111
|
+
<OAuthAppRow
|
|
112
|
+
key={app.metadata?.id ?? ""}
|
|
113
|
+
oauthApp={app}
|
|
114
|
+
onEdit={onEdit ? () => onEdit(app) : undefined}
|
|
115
|
+
/>
|
|
116
|
+
))}
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// OAuthAppRow (internal)
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
function OAuthAppRow({
|
|
126
|
+
oauthApp,
|
|
127
|
+
onEdit,
|
|
128
|
+
}: {
|
|
129
|
+
oauthApp: OAuthApp;
|
|
130
|
+
onEdit?: () => void;
|
|
131
|
+
}) {
|
|
132
|
+
const provider = oauthApp.spec?.provider || "OAuth App";
|
|
133
|
+
const clientId = oauthApp.spec?.clientId;
|
|
134
|
+
const createdAt = oauthApp.status?.audit?.specAudit?.createdAt;
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<div
|
|
138
|
+
role="listitem"
|
|
139
|
+
className="flex items-center gap-3 rounded-lg border border-border/60 px-3 py-2.5 transition-colors hover:border-border"
|
|
140
|
+
>
|
|
141
|
+
<OAuthIcon />
|
|
142
|
+
|
|
143
|
+
<div className="min-w-0 flex-1">
|
|
144
|
+
<span className="block truncate text-sm font-medium text-foreground">
|
|
145
|
+
{provider}
|
|
146
|
+
</span>
|
|
147
|
+
{clientId && (
|
|
148
|
+
<span className="block truncate text-xs font-mono text-muted-foreground">
|
|
149
|
+
{clientId}
|
|
150
|
+
</span>
|
|
151
|
+
)}
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div className="hidden shrink-0 items-center gap-4 text-xs text-muted-foreground sm:flex">
|
|
155
|
+
{createdAt && (
|
|
156
|
+
<span title={`Created ${timestampDate(createdAt).toISOString()}`}>
|
|
157
|
+
{formatShortDate(timestampDate(createdAt))}
|
|
158
|
+
</span>
|
|
159
|
+
)}
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
{onEdit && (
|
|
163
|
+
<button
|
|
164
|
+
type="button"
|
|
165
|
+
onClick={onEdit}
|
|
166
|
+
aria-label={`Edit ${provider}`}
|
|
167
|
+
className={cn(
|
|
168
|
+
"shrink-0 rounded p-1",
|
|
169
|
+
"text-muted-foreground hover:text-foreground hover:bg-accent/50",
|
|
170
|
+
"transition-colors",
|
|
171
|
+
)}
|
|
172
|
+
>
|
|
173
|
+
<PencilIcon />
|
|
174
|
+
</button>
|
|
175
|
+
)}
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Icons (edit action)
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
function PencilIcon() {
|
|
185
|
+
return (
|
|
186
|
+
<svg
|
|
187
|
+
width="14"
|
|
188
|
+
height="14"
|
|
189
|
+
viewBox="0 0 16 16"
|
|
190
|
+
fill="none"
|
|
191
|
+
stroke="currentColor"
|
|
192
|
+
strokeWidth="1.5"
|
|
193
|
+
strokeLinecap="round"
|
|
194
|
+
strokeLinejoin="round"
|
|
195
|
+
aria-hidden="true"
|
|
196
|
+
>
|
|
197
|
+
<path d="M11 2.5l2.5 2.5L5 13.5H2.5V11L11 2.5z" />
|
|
198
|
+
</svg>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Formatting helpers
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
function formatShortDate(date: Date): string {
|
|
207
|
+
return date.toLocaleDateString(undefined, {
|
|
208
|
+
month: "short",
|
|
209
|
+
day: "numeric",
|
|
210
|
+
year: "numeric",
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// Icons
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
function OAuthIcon() {
|
|
219
|
+
return (
|
|
220
|
+
<svg
|
|
221
|
+
width="14"
|
|
222
|
+
height="14"
|
|
223
|
+
viewBox="0 0 16 16"
|
|
224
|
+
fill="none"
|
|
225
|
+
stroke="currentColor"
|
|
226
|
+
strokeWidth="1.5"
|
|
227
|
+
strokeLinecap="round"
|
|
228
|
+
strokeLinejoin="round"
|
|
229
|
+
aria-hidden="true"
|
|
230
|
+
className="shrink-0 text-muted-foreground"
|
|
231
|
+
>
|
|
232
|
+
<rect x="2" y="3" width="12" height="10" rx="2" />
|
|
233
|
+
<path d="M8 7v2" />
|
|
234
|
+
<circle cx="8" cy="9.5" r="0.5" fill="currentColor" stroke="none" />
|
|
235
|
+
</svg>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { useOAuthAppList } from "./useOAuthAppList";
|
|
2
|
+
export type { UseOAuthAppListReturn } from "./useOAuthAppList";
|
|
3
|
+
export { useCreateOAuthApp } from "./useCreateOAuthApp";
|
|
4
|
+
export type { UseCreateOAuthAppReturn } from "./useCreateOAuthApp";
|
|
5
|
+
export { useUpdateOAuthApp } from "./useUpdateOAuthApp";
|
|
6
|
+
export type { UseUpdateOAuthAppReturn } from "./useUpdateOAuthApp";
|
|
7
|
+
export { useDeleteOAuthApp } from "./useDeleteOAuthApp";
|
|
8
|
+
export type { UseDeleteOAuthAppReturn } from "./useDeleteOAuthApp";
|
|
9
|
+
export { OAuthAppListPanel } from "./OAuthAppListPanel";
|
|
10
|
+
export type { OAuthAppListPanelProps } from "./OAuthAppListPanel";
|
|
11
|
+
export { CreateOAuthAppForm } from "./CreateOAuthAppForm";
|
|
12
|
+
export type { CreateOAuthAppFormProps } from "./CreateOAuthAppForm";
|
|
13
|
+
export { OAuthAppDetailPanel } from "./OAuthAppDetailPanel";
|
|
14
|
+
export type { OAuthAppDetailPanelProps } from "./OAuthAppDetailPanel";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import type { OAuthAppInput } from "@stigmer/sdk";
|
|
5
|
+
import type { OAuthApp } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/api_pb";
|
|
6
|
+
import { useStigmer } from "../hooks";
|
|
7
|
+
import { toError } from "../internal/toError";
|
|
8
|
+
|
|
9
|
+
/** Return value of {@link useCreateOAuthApp}. */
|
|
10
|
+
export interface UseCreateOAuthAppReturn {
|
|
11
|
+
/** Submit an {@link OAuthAppInput} to create a new OAuth app. Resolves with the server-created resource. */
|
|
12
|
+
readonly create: (input: OAuthAppInput) => Promise<OAuthApp>;
|
|
13
|
+
/** `true` while the create request is in flight. */
|
|
14
|
+
readonly isCreating: boolean;
|
|
15
|
+
/** Error from the last failed create, or `null` when healthy. */
|
|
16
|
+
readonly error: Error | null;
|
|
17
|
+
/** Reset `error` to `null`. */
|
|
18
|
+
readonly clearError: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Mutation hook that wraps `oauthapp.create()` with loading and error
|
|
23
|
+
* state.
|
|
24
|
+
*
|
|
25
|
+
* Creates an OAuth app resource within an organization. The caller
|
|
26
|
+
* provides an {@link OAuthAppInput} with the required metadata (name,
|
|
27
|
+
* org) and spec fields (provider, client ID, client secret, OAuth
|
|
28
|
+
* endpoint URLs).
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* const { create, isCreating, error } = useCreateOAuthApp();
|
|
33
|
+
*
|
|
34
|
+
* const app = await create({
|
|
35
|
+
* name: "My Slack App",
|
|
36
|
+
* org: "acme",
|
|
37
|
+
* provider: "Slack",
|
|
38
|
+
* clientId: "123456.789012",
|
|
39
|
+
* clientSecret: "secret",
|
|
40
|
+
* authorizationUrl: "https://slack.com/oauth/v2/authorize",
|
|
41
|
+
* tokenUrl: "https://slack.com/api/oauth.v2.access",
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function useCreateOAuthApp(): UseCreateOAuthAppReturn {
|
|
46
|
+
const stigmer = useStigmer();
|
|
47
|
+
const [isCreating, setIsCreating] = useState(false);
|
|
48
|
+
const [error, setError] = useState<Error | null>(null);
|
|
49
|
+
|
|
50
|
+
const clearError = useCallback(() => setError(null), []);
|
|
51
|
+
|
|
52
|
+
const create = useCallback(
|
|
53
|
+
async (input: OAuthAppInput): Promise<OAuthApp> => {
|
|
54
|
+
setIsCreating(true);
|
|
55
|
+
setError(null);
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
return await stigmer.oauthapp.create(input);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
setError(toError(err));
|
|
61
|
+
throw err;
|
|
62
|
+
} finally {
|
|
63
|
+
setIsCreating(false);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
[stigmer],
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return { create, isCreating, error, clearError };
|
|
70
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import type { OAuthApp } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/api_pb";
|
|
5
|
+
import { useStigmer } from "../hooks";
|
|
6
|
+
import { toError } from "../internal/toError";
|
|
7
|
+
|
|
8
|
+
/** Return value of {@link useDeleteOAuthApp}. */
|
|
9
|
+
export interface UseDeleteOAuthAppReturn {
|
|
10
|
+
/** Delete an OAuth app by its resource ID. Resolves with the deleted resource for confirmation display. */
|
|
11
|
+
readonly deleteApp: (id: string) => Promise<OAuthApp>;
|
|
12
|
+
/** `true` while the delete request is in flight. */
|
|
13
|
+
readonly isDeleting: boolean;
|
|
14
|
+
/** Error from the last failed delete, or `null` when healthy. */
|
|
15
|
+
readonly error: Error | null;
|
|
16
|
+
/** Reset `error` to `null`. */
|
|
17
|
+
readonly clearError: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Mutation hook that wraps `oauthapp.delete()` with loading and error
|
|
22
|
+
* state.
|
|
23
|
+
*
|
|
24
|
+
* Deletes an OAuth app by its resource ID. Returns the deleted
|
|
25
|
+
* {@link OAuthApp} on success so callers can confirm which app was
|
|
26
|
+
* removed. The deletion is permanent — any MCP server overrides
|
|
27
|
+
* referencing this app will lose their binding.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* const { deleteApp, isDeleting, error } = useDeleteOAuthApp();
|
|
32
|
+
*
|
|
33
|
+
* await deleteApp("oauth-app-id-abc123");
|
|
34
|
+
* refetch(); // refresh the list after deletion
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function useDeleteOAuthApp(): UseDeleteOAuthAppReturn {
|
|
38
|
+
const stigmer = useStigmer();
|
|
39
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
40
|
+
const [error, setError] = useState<Error | null>(null);
|
|
41
|
+
|
|
42
|
+
const clearError = useCallback(() => setError(null), []);
|
|
43
|
+
|
|
44
|
+
const deleteApp = useCallback(
|
|
45
|
+
async (id: string): Promise<OAuthApp> => {
|
|
46
|
+
setIsDeleting(true);
|
|
47
|
+
setError(null);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return await stigmer.oauthapp.delete({ resourceId: id });
|
|
51
|
+
} catch (err) {
|
|
52
|
+
setError(toError(err));
|
|
53
|
+
throw err;
|
|
54
|
+
} finally {
|
|
55
|
+
setIsDeleting(false);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
[stigmer],
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return { deleteApp, isDeleting, error, clearError };
|
|
62
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useState } from "react";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import { ListOAuthAppsByOrgInputSchema } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/io_pb";
|
|
6
|
+
import type { OAuthApp } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/api_pb";
|
|
7
|
+
import { useStigmer } from "../hooks";
|
|
8
|
+
import { toError } from "../internal/toError";
|
|
9
|
+
|
|
10
|
+
/** Return value of {@link useOAuthAppList}. */
|
|
11
|
+
export interface UseOAuthAppListReturn {
|
|
12
|
+
/** OAuth apps owned by the organization. Empty while loading or on error. */
|
|
13
|
+
readonly oauthApps: readonly OAuthApp[];
|
|
14
|
+
/** `true` while the initial fetch or a refetch is in flight. */
|
|
15
|
+
readonly isLoading: boolean;
|
|
16
|
+
/** Error from the last failed request, or `null` when healthy. */
|
|
17
|
+
readonly error: Error | null;
|
|
18
|
+
/** Discard cached data and re-fetch the list from the server. */
|
|
19
|
+
readonly refetch: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Data hook that fetches all {@link OAuthApp} entries for an organization.
|
|
24
|
+
*
|
|
25
|
+
* Returns every OAuthApp whose `metadata.org` matches the input. In
|
|
26
|
+
* practice these are the BYOA OAuth apps created through the "Bring
|
|
27
|
+
* your own app" flow on MCP server detail pages.
|
|
28
|
+
*
|
|
29
|
+
* Pass `null` for `org` to skip fetching (stable no-op). Useful when
|
|
30
|
+
* the active organization has not been resolved yet.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* const { oauthApps, isLoading, error } = useOAuthAppList(org);
|
|
35
|
+
*
|
|
36
|
+
* if (isLoading) return <Spinner />;
|
|
37
|
+
* oauthApps.map((app) => app.spec?.provider);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function useOAuthAppList(
|
|
41
|
+
org: string | null,
|
|
42
|
+
): UseOAuthAppListReturn {
|
|
43
|
+
const stigmer = useStigmer();
|
|
44
|
+
const [oauthApps, setOauthApps] = useState<OAuthApp[]>([]);
|
|
45
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
46
|
+
const [error, setError] = useState<Error | null>(null);
|
|
47
|
+
const [fetchKey, setFetchKey] = useState(0);
|
|
48
|
+
|
|
49
|
+
const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!org) {
|
|
53
|
+
setOauthApps([]);
|
|
54
|
+
setIsLoading(false);
|
|
55
|
+
setError(null);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const cancelled = { current: false };
|
|
60
|
+
setIsLoading(true);
|
|
61
|
+
setError(null);
|
|
62
|
+
|
|
63
|
+
stigmer.oauthapp
|
|
64
|
+
.listByOrg(create(ListOAuthAppsByOrgInputSchema, { org }))
|
|
65
|
+
.then(
|
|
66
|
+
(result) => {
|
|
67
|
+
if (cancelled.current) return;
|
|
68
|
+
setOauthApps([...result.entries]);
|
|
69
|
+
setIsLoading(false);
|
|
70
|
+
},
|
|
71
|
+
(err) => {
|
|
72
|
+
if (cancelled.current) return;
|
|
73
|
+
setError(toError(err));
|
|
74
|
+
setIsLoading(false);
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return () => {
|
|
79
|
+
cancelled.current = true;
|
|
80
|
+
};
|
|
81
|
+
}, [org, stigmer, fetchKey]);
|
|
82
|
+
|
|
83
|
+
return { oauthApps, isLoading, error, refetch };
|
|
84
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import type { OAuthAppInput } from "@stigmer/sdk";
|
|
5
|
+
import type { OAuthApp } from "@stigmer/protos/ai/stigmer/iam/oauthapp/v1/api_pb";
|
|
6
|
+
import { useStigmer } from "../hooks";
|
|
7
|
+
import { toError } from "../internal/toError";
|
|
8
|
+
|
|
9
|
+
/** Return value of {@link useUpdateOAuthApp}. */
|
|
10
|
+
export interface UseUpdateOAuthAppReturn {
|
|
11
|
+
/** Submit an {@link OAuthAppInput} to update an existing OAuth app. Resolves with the updated resource. */
|
|
12
|
+
readonly update: (input: OAuthAppInput) => Promise<OAuthApp>;
|
|
13
|
+
/** `true` while the update request is in flight. */
|
|
14
|
+
readonly isUpdating: boolean;
|
|
15
|
+
/** Error from the last failed update, or `null` when healthy. */
|
|
16
|
+
readonly error: Error | null;
|
|
17
|
+
/** Reset `error` to `null`. */
|
|
18
|
+
readonly clearError: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Mutation hook that wraps `oauthapp.update()` with loading and error
|
|
23
|
+
* state.
|
|
24
|
+
*
|
|
25
|
+
* Updates an existing OAuth app. The input must include the `name` and
|
|
26
|
+
* `org` fields to identify the target resource, along with the updated
|
|
27
|
+
* spec fields. Omit `clientSecret` to leave the existing secret
|
|
28
|
+
* unchanged.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* const { update, isUpdating, error } = useUpdateOAuthApp();
|
|
33
|
+
*
|
|
34
|
+
* await update({
|
|
35
|
+
* name: "My Slack App",
|
|
36
|
+
* org: "acme",
|
|
37
|
+
* provider: "Slack",
|
|
38
|
+
* clientId: "123456.789012",
|
|
39
|
+
* authorizationUrl: "https://slack.com/oauth/v2/authorize",
|
|
40
|
+
* tokenUrl: "https://slack.com/api/oauth.v2.access",
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function useUpdateOAuthApp(): UseUpdateOAuthAppReturn {
|
|
45
|
+
const stigmer = useStigmer();
|
|
46
|
+
const [isUpdating, setIsUpdating] = useState(false);
|
|
47
|
+
const [error, setError] = useState<Error | null>(null);
|
|
48
|
+
|
|
49
|
+
const clearError = useCallback(() => setError(null), []);
|
|
50
|
+
|
|
51
|
+
const update = useCallback(
|
|
52
|
+
async (input: OAuthAppInput): Promise<OAuthApp> => {
|
|
53
|
+
setIsUpdating(true);
|
|
54
|
+
setError(null);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
return await stigmer.oauthapp.update(input);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
setError(toError(err));
|
|
60
|
+
throw err;
|
|
61
|
+
} finally {
|
|
62
|
+
setIsUpdating(false);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
[stigmer],
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
return { update, isUpdating, error, clearError };
|
|
69
|
+
}
|