@stigmer/react 3.1.12 → 3.1.13
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/agent/AgentDetailView.d.ts +11 -1
- package/agent/AgentDetailView.d.ts.map +1 -1
- package/agent/AgentDetailView.js +17 -4
- package/agent/AgentDetailView.js.map +1 -1
- package/index.d.ts +2 -2
- package/index.d.ts.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +4 -4
- package/sharing/ShareAgentDialog.d.ts +11 -1
- package/sharing/ShareAgentDialog.d.ts.map +1 -1
- package/sharing/ShareAgentDialog.js +20 -11
- package/sharing/ShareAgentDialog.js.map +1 -1
- package/sharing/index.d.ts +2 -0
- package/sharing/index.d.ts.map +1 -1
- package/sharing/index.js +1 -0
- package/sharing/index.js.map +1 -1
- package/sharing/useAgentShare.d.ts +8 -2
- package/sharing/useAgentShare.d.ts.map +1 -1
- package/sharing/useAgentShare.js +23 -13
- package/sharing/useAgentShare.js.map +1 -1
- package/sharing/useCreateExternalShareLink.d.ts +90 -0
- package/sharing/useCreateExternalShareLink.d.ts.map +1 -0
- package/sharing/useCreateExternalShareLink.js +65 -0
- package/sharing/useCreateExternalShareLink.js.map +1 -0
- package/sharing/useSaveAgentShare.d.ts +7 -1
- package/sharing/useSaveAgentShare.d.ts.map +1 -1
- package/sharing/useSaveAgentShare.js +12 -5
- package/sharing/useSaveAgentShare.js.map +1 -1
- package/src/agent/AgentDetailView.tsx +28 -2
- package/src/index.ts +3 -0
- package/src/sharing/ShareAgentDialog.tsx +43 -11
- package/src/sharing/__tests__/ShareAgentDialog.test.tsx +48 -0
- package/src/sharing/__tests__/useAgentShare.test.tsx +61 -0
- package/src/sharing/__tests__/useCreateExternalShareLink.test.tsx +194 -0
- package/src/sharing/__tests__/useSaveAgentShare.test.tsx +48 -0
- package/src/sharing/index.ts +5 -0
- package/src/sharing/useAgentShare.ts +26 -12
- package/src/sharing/useCreateExternalShareLink.tsx +141 -0
- package/src/sharing/useSaveAgentShare.ts +12 -4
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState, type ReactNode } from "react";
|
|
4
|
+
import type { Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
5
|
+
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
6
|
+
import { useCheckPermission } from "../iam-policy/useCheckPermission.js";
|
|
7
|
+
import type { DetailAction } from "../resource-detail/types.js";
|
|
8
|
+
import { ShareAgentDialog } from "./ShareAgentDialog.js";
|
|
9
|
+
|
|
10
|
+
/** Arguments for {@link useCreateExternalShareLink}. */
|
|
11
|
+
export interface UseCreateExternalShareLinkArgs {
|
|
12
|
+
/**
|
|
13
|
+
* The agent to share, or `null` while it is still loading. When
|
|
14
|
+
* `null`, {@link UseCreateExternalShareLinkReturn.action} is `null`
|
|
15
|
+
* and the dialog renders nothing — safe to call before the resource
|
|
16
|
+
* is ready.
|
|
17
|
+
*/
|
|
18
|
+
readonly agent: Agent | null;
|
|
19
|
+
/**
|
|
20
|
+
* The viewer's own organization — the org that would own the external
|
|
21
|
+
* share, pay for its traffic, and host its URL. When empty or equal to
|
|
22
|
+
* the agent's org, the action is `null` (the same-org Share entry from
|
|
23
|
+
* {@link useShareAgent} covers that case).
|
|
24
|
+
*/
|
|
25
|
+
readonly viewerOrg: string;
|
|
26
|
+
/**
|
|
27
|
+
* Builds the absolute public chat URL for the shared agent. Same
|
|
28
|
+
* contract as {@link useShareAgent}: the host application owns URL
|
|
29
|
+
* construction. When omitted, the dialog falls back to the relative
|
|
30
|
+
* `/chat/<org>/<slug>`.
|
|
31
|
+
*/
|
|
32
|
+
readonly buildShareUrl?: (org: string, slug: string) => string;
|
|
33
|
+
/** Called after any sharing change is persisted. */
|
|
34
|
+
readonly onSharingChanged?: () => void;
|
|
35
|
+
/** Menu-item label. @default "Create share link" */
|
|
36
|
+
readonly label?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Return value of {@link useCreateExternalShareLink}. */
|
|
40
|
+
export interface UseCreateExternalShareLinkReturn {
|
|
41
|
+
/**
|
|
42
|
+
* A ready-to-spread {@link DetailAction} for a kebab/overflow menu, or
|
|
43
|
+
* `null` when the entry does not apply: agent still loading, same-org
|
|
44
|
+
* (or no) viewer org, agent not marketplace-public, or the viewer
|
|
45
|
+
* lacks `can_create_agent_share` in their own org. Lives in the
|
|
46
|
+
* `"sharing"` group, like its same-org sibling.
|
|
47
|
+
*/
|
|
48
|
+
readonly action: DetailAction | null;
|
|
49
|
+
/** The {@link ShareAgentDialog} node — render it once in the host tree. */
|
|
50
|
+
readonly dialog: ReactNode;
|
|
51
|
+
/** Imperatively open the dialog. */
|
|
52
|
+
readonly open: () => void;
|
|
53
|
+
/** Whether the dialog is currently open. */
|
|
54
|
+
readonly isOpen: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The marketplace entry for **cross-org shares** (decision 013): a
|
|
59
|
+
* "Create share link" action on another org's marketplace-public agent
|
|
60
|
+
* that opens the same {@link ShareAgentDialog} with the share org set to
|
|
61
|
+
* the viewer's own org. The resulting share is the viewer org's channel —
|
|
62
|
+
* its URL (`/chat/<viewer-org>/<slug>`), its billing, its credential
|
|
63
|
+
* bindings — while the agent blueprint stays live in its own org.
|
|
64
|
+
*
|
|
65
|
+
* The gate mirrors the server's create bar so the action never appears
|
|
66
|
+
* to a user whose create would be refused:
|
|
67
|
+
*
|
|
68
|
+
* - the agent lives in another org and is `visibility_public` — the
|
|
69
|
+
* origin org's implicit consent (D1); the agent-side `can_execute`
|
|
70
|
+
* half of the bar is granted to every authenticated account on public
|
|
71
|
+
* agents by the `allow_public` wildcard, so visibility is its exact
|
|
72
|
+
* client-side proxy;
|
|
73
|
+
* - the viewer holds `can_create_agent_share` in their own org (D2's
|
|
74
|
+
* org-side half — admin-level, since a public share spends that org's
|
|
75
|
+
* credits on the open internet). The org check uses the org slug as
|
|
76
|
+
* the resource id: an Organization's id equals its slug
|
|
77
|
+
* (ApiResourceMetadata.id).
|
|
78
|
+
*
|
|
79
|
+
* The same-org Share entry ({@link useShareAgent}, gated on agent
|
|
80
|
+
* `can_edit`) and this one are mutually exclusive by construction — at
|
|
81
|
+
* most one renders for any viewer/agent pair — so hosts can inject both
|
|
82
|
+
* unconditionally.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* const externalShare = useCreateExternalShareLink({
|
|
87
|
+
* agent,
|
|
88
|
+
* viewerOrg: activeOrgSlug,
|
|
89
|
+
* buildShareUrl,
|
|
90
|
+
* onSharingChanged: refetch,
|
|
91
|
+
* });
|
|
92
|
+
* // ...
|
|
93
|
+
* <ResourceDetailShell actions={[...actions, externalShare.action].filter(Boolean)} ... />
|
|
94
|
+
* {externalShare.dialog}
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export function useCreateExternalShareLink({
|
|
98
|
+
agent,
|
|
99
|
+
viewerOrg,
|
|
100
|
+
buildShareUrl,
|
|
101
|
+
onSharingChanged,
|
|
102
|
+
label = "Create share link",
|
|
103
|
+
}: UseCreateExternalShareLinkArgs): UseCreateExternalShareLinkReturn {
|
|
104
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
105
|
+
|
|
106
|
+
const agentOrg = agent?.metadata?.org ?? "";
|
|
107
|
+
const isCrossOrg = !!agent && viewerOrg !== "" && viewerOrg !== agentOrg;
|
|
108
|
+
const isPublic =
|
|
109
|
+
agent?.metadata?.visibility === ApiResourceVisibility.visibility_public;
|
|
110
|
+
|
|
111
|
+
// The org-side bar is only worth asking about when the entry could
|
|
112
|
+
// apply at all — passing null skips the RPC entirely.
|
|
113
|
+
const { allowed: canCreateShare } = useCheckPermission(
|
|
114
|
+
isCrossOrg && isPublic
|
|
115
|
+
? { kind: "organization", id: viewerOrg }
|
|
116
|
+
: null,
|
|
117
|
+
"can_create_agent_share",
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const open = useCallback(() => setIsOpen(true), []);
|
|
121
|
+
|
|
122
|
+
const applicable = isCrossOrg && isPublic && canCreateShare;
|
|
123
|
+
|
|
124
|
+
const action: DetailAction | null = applicable
|
|
125
|
+
? { id: "create-share-link", label, group: "sharing", onAction: open }
|
|
126
|
+
: null;
|
|
127
|
+
|
|
128
|
+
const dialog =
|
|
129
|
+
agent && applicable ? (
|
|
130
|
+
<ShareAgentDialog
|
|
131
|
+
open={isOpen}
|
|
132
|
+
onOpenChange={setIsOpen}
|
|
133
|
+
agent={agent}
|
|
134
|
+
shareOrg={viewerOrg}
|
|
135
|
+
buildShareUrl={buildShareUrl}
|
|
136
|
+
onSharingChanged={onSharingChanged}
|
|
137
|
+
/>
|
|
138
|
+
) : null;
|
|
139
|
+
|
|
140
|
+
return { action, dialog, open, isOpen };
|
|
141
|
+
}
|
|
@@ -112,6 +112,12 @@ export interface UseSaveAgentShareReturn {
|
|
|
112
112
|
* (decision 011 D1). Deleting the share is a separate, destructive
|
|
113
113
|
* operation this hook does not perform.
|
|
114
114
|
*
|
|
115
|
+
* `shareOrg` selects which org's channel a first save creates and
|
|
116
|
+
* defaults to the agent's own org. Pass the viewer's org to create a
|
|
117
|
+
* **cross-org share** (decision 013) — the server then authorizes on the
|
|
118
|
+
* public agent's `can_execute` plus `can_create_agent_share` in the
|
|
119
|
+
* sharing org, and bills that org.
|
|
120
|
+
*
|
|
115
121
|
* Pass `null` for `agent` to produce a stable no-op (useful while the
|
|
116
122
|
* agent is still loading).
|
|
117
123
|
*
|
|
@@ -126,6 +132,7 @@ export interface UseSaveAgentShareReturn {
|
|
|
126
132
|
*/
|
|
127
133
|
export function useSaveAgentShare(
|
|
128
134
|
agent: Agent | null,
|
|
135
|
+
shareOrg?: string,
|
|
129
136
|
): UseSaveAgentShareReturn {
|
|
130
137
|
const stigmer = useStigmer();
|
|
131
138
|
const [isPending, setIsPending] = useState(false);
|
|
@@ -134,6 +141,7 @@ export function useSaveAgentShare(
|
|
|
134
141
|
const agentOrg = agent?.metadata?.org ?? "";
|
|
135
142
|
const agentSlug = agent?.metadata?.slug ?? "";
|
|
136
143
|
const agentName = agent?.metadata?.name ?? "";
|
|
144
|
+
const resolvedShareOrg = shareOrg || agentOrg;
|
|
137
145
|
|
|
138
146
|
const save = useCallback(
|
|
139
147
|
async (
|
|
@@ -150,9 +158,9 @@ export function useSaveAgentShare(
|
|
|
150
158
|
// Identity: the existing share's org/slug when editing (a
|
|
151
159
|
// manifest-created share may carry a non-default slug — apply
|
|
152
160
|
// with the agent's slug would create a SECOND share), the
|
|
153
|
-
// agent's when creating (the server's
|
|
154
|
-
// explicit).
|
|
155
|
-
org: current?.metadata?.org ||
|
|
161
|
+
// sharing org + the agent's slug when creating (the server's
|
|
162
|
+
// own D2 default, made explicit).
|
|
163
|
+
org: current?.metadata?.org || resolvedShareOrg,
|
|
156
164
|
slug: current?.metadata?.slug || agentSlug,
|
|
157
165
|
name: current?.metadata?.name || agentName || agentSlug,
|
|
158
166
|
agentRef: { org: agentOrg, slug: agentSlug },
|
|
@@ -179,7 +187,7 @@ export function useSaveAgentShare(
|
|
|
179
187
|
setIsPending(false);
|
|
180
188
|
}
|
|
181
189
|
},
|
|
182
|
-
[agentOrg, agentSlug, agentName, stigmer],
|
|
190
|
+
[agentOrg, agentSlug, agentName, resolvedShareOrg, stigmer],
|
|
183
191
|
);
|
|
184
192
|
|
|
185
193
|
return { save, isPending, error };
|