@korso/shepherd-ui 0.4.0 → 0.5.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.
- package/LICENSE +661 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.js +14 -0
- package/dist/config/ConfigPanel.js +1 -1
- package/dist/config/ConnectAgent.d.ts +1 -2
- package/dist/config/ConnectAgent.js +18 -21
- package/dist/config/Members.js +1 -1
- package/dist/lib/{Dashboard-CTXdGRxM.js → Dashboard-qipRa5MO.js} +124 -109
- package/dist/lib/index.d.ts +15 -2
- package/dist/lib/index.js +238 -241
- package/dist/lib/selfhost.js +1 -1
- package/dist/lib/styles.css +4 -3
- package/dist/preview.js +15 -3
- package/dist/selfhost/assets/{index-CK7qrsNo.css → index-DfVGGaT0.css} +1 -1
- package/dist/selfhost/assets/{index-BSnsUIT-.js → index-xMSuivCM.js} +8 -8
- package/dist/selfhost/index.html +2 -2
- package/dist/test/mockClient.js +3 -0
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -177,6 +177,20 @@ export function createShepherdClient(config) {
|
|
|
177
177
|
revokeToken(workspaceId, tokenId) {
|
|
178
178
|
return request("DELETE", `/workspaces/${enc(workspaceId)}/tokens/${enc(tokenId)}`);
|
|
179
179
|
},
|
|
180
|
+
mintAccountToken(body) {
|
|
181
|
+
return request("POST", "/tokens", {
|
|
182
|
+
body,
|
|
183
|
+
schema: MintTokenResponse,
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
listAccountTokens() {
|
|
187
|
+
return request("GET", "/tokens", {
|
|
188
|
+
schema: ListTokensResponse,
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
revokeAccountToken(id) {
|
|
192
|
+
return request("DELETE", `/tokens/${enc(id)}`);
|
|
193
|
+
},
|
|
180
194
|
createInvite(workspaceId, body) {
|
|
181
195
|
return request("POST", `/workspaces/${enc(workspaceId)}/invites`, {
|
|
182
196
|
body,
|
|
@@ -12,5 +12,5 @@ const SECTIONS = [
|
|
|
12
12
|
export function ConfigPanel({ workspace, hubUrl, membersRefreshKey, onMembersChanged, onLeft, }) {
|
|
13
13
|
const [section, setSection] = useState("general");
|
|
14
14
|
const isAdmin = workspace.role === "admin";
|
|
15
|
-
return (_jsxs("div", { className: "config-layout", children: [_jsx("nav", { className: "config-nav", "aria-label": "Configuration sections", children: SECTIONS.map(({ id, label }) => (_jsx("button", { type: "button", className: "config-nav__item" + (section === id ? " config-nav__item--on" : ""), "aria-current": section === id ? "page" : undefined, onClick: () => setSection(id), children: label }, id))) }), _jsxs("div", { className: "config-panel", children: [section === "general" && (_jsx(GeneralSettings, { workspace: workspace, onLeft: onLeft })), section === "members" && (_jsxs(_Fragment, { children: [_jsx(Members, { workspaceId: workspace.id, refreshKey: membersRefreshKey, canRemove: isAdmin }), isAdmin && (_jsx(Invites, { workspaceId: workspace.id, onMembersChanged: onMembersChanged }))] })), section === "agent" && _jsx(ConnectAgent, {
|
|
15
|
+
return (_jsxs("div", { className: "config-layout", children: [_jsx("nav", { className: "config-nav", "aria-label": "Configuration sections", children: SECTIONS.map(({ id, label }) => (_jsx("button", { type: "button", className: "config-nav__item" + (section === id ? " config-nav__item--on" : ""), "aria-current": section === id ? "page" : undefined, onClick: () => setSection(id), children: label }, id))) }), _jsxs("div", { className: "config-panel", children: [section === "general" && (_jsx(GeneralSettings, { workspace: workspace, onLeft: onLeft })), section === "members" && (_jsxs(_Fragment, { children: [_jsx(Members, { workspaceId: workspace.id, refreshKey: membersRefreshKey, canRemove: isAdmin }), isAdmin && (_jsx(Invites, { workspaceId: workspace.id, onMembersChanged: onMembersChanged }))] })), section === "agent" && _jsx(ConnectAgent, { hubUrl: hubUrl })] })] }));
|
|
16
16
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export interface ConnectAgentProps {
|
|
2
|
-
workspaceId: string;
|
|
3
2
|
/**
|
|
4
3
|
* The DIRECT Hub URL the agent connects to (public Cloud Run URL when hosted).
|
|
5
4
|
* Defaults to the dashboard client's baseUrl, which is correct for self-host
|
|
@@ -7,4 +6,4 @@ export interface ConnectAgentProps {
|
|
|
7
6
|
*/
|
|
8
7
|
hubUrl?: string;
|
|
9
8
|
}
|
|
10
|
-
export declare function ConnectAgent({
|
|
9
|
+
export declare function ConnectAgent({ hubUrl }: ConnectAgentProps): import("react").JSX.Element;
|
|
@@ -12,12 +12,12 @@ const TOOLS = [
|
|
|
12
12
|
// The token placeholder shown before a real token is minted. Switching tools or
|
|
13
13
|
// reading the command pre-mint shows this, never a real secret.
|
|
14
14
|
const TOKEN_PLACEHOLDER = "shp_<paste-after-generating>";
|
|
15
|
-
// The CLI tools differ only in the
|
|
16
|
-
//
|
|
15
|
+
// The CLI tools differ only in the `mcp add` invocation; the rest of the
|
|
16
|
+
// command (the env flags and the npx tail) is shared.
|
|
17
17
|
const CLI_PREFIX = {
|
|
18
|
-
claude: "claude mcp add shepherd -s user
|
|
19
|
-
codex: "codex mcp add shepherd
|
|
20
|
-
pi: "pi mcp add shepherd
|
|
18
|
+
claude: "claude mcp add shepherd -s user",
|
|
19
|
+
codex: "codex mcp add shepherd",
|
|
20
|
+
pi: "pi mcp add shepherd",
|
|
21
21
|
};
|
|
22
22
|
// "created 3d ago · never used" / "created 3d ago · last used 2h ago" — helps
|
|
23
23
|
// an operator tell which tokens are still active before revoking one.
|
|
@@ -38,14 +38,17 @@ function installCommand(tool, hubUrl, token) {
|
|
|
38
38
|
},
|
|
39
39
|
}, null, 2);
|
|
40
40
|
}
|
|
41
|
+
// A single line with no shell-specific continuation character (`\` in
|
|
42
|
+
// bash/zsh, backtick in PowerShell, `^` in cmd) so the command pastes
|
|
43
|
+
// cleanly into any terminal.
|
|
41
44
|
return [
|
|
42
45
|
CLI_PREFIX[tool],
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
].join("
|
|
46
|
+
`-e HUB_URL=${hubUrl}`,
|
|
47
|
+
`-e SHEPHERD_TOKEN=${token}`,
|
|
48
|
+
"-- npx -y @korso/shepherd",
|
|
49
|
+
].join(" ");
|
|
47
50
|
}
|
|
48
|
-
export function ConnectAgent({
|
|
51
|
+
export function ConnectAgent({ hubUrl }) {
|
|
49
52
|
const client = useShepherdClient();
|
|
50
53
|
const directHubUrl = hubUrl ?? client.baseUrl;
|
|
51
54
|
const headingId = useId();
|
|
@@ -66,7 +69,7 @@ export function ConnectAgent({ workspaceId, hubUrl }) {
|
|
|
66
69
|
const [revokingId, setRevokingId] = useState(null);
|
|
67
70
|
async function loadTokens() {
|
|
68
71
|
try {
|
|
69
|
-
const res = await client.
|
|
72
|
+
const res = await client.listAccountTokens();
|
|
70
73
|
setTokens(res.tokens);
|
|
71
74
|
}
|
|
72
75
|
catch (err) {
|
|
@@ -80,18 +83,12 @@ export function ConnectAgent({ workspaceId, hubUrl }) {
|
|
|
80
83
|
setLoading(true);
|
|
81
84
|
void loadTokens();
|
|
82
85
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
83
|
-
}, [client
|
|
84
|
-
// The component is re-rendered (not remounted) across a workspace switch, so
|
|
85
|
-
// clear the once-shown raw token so it never leaks into another workspace's
|
|
86
|
-
// install command (mirrors Workspaces' setInvite(null) reset).
|
|
87
|
-
useEffect(() => {
|
|
88
|
-
setRawToken(null);
|
|
89
|
-
}, [workspaceId]);
|
|
86
|
+
}, [client]);
|
|
90
87
|
async function generate() {
|
|
91
88
|
setBusy(true);
|
|
92
89
|
setError(null);
|
|
93
90
|
try {
|
|
94
|
-
const res = await client.
|
|
91
|
+
const res = await client.mintAccountToken(name.trim() ? { name: name.trim() } : {});
|
|
95
92
|
setRawToken(res.token);
|
|
96
93
|
setName("");
|
|
97
94
|
await loadTokens();
|
|
@@ -110,7 +107,7 @@ export function ConnectAgent({ workspaceId, hubUrl }) {
|
|
|
110
107
|
setError(null);
|
|
111
108
|
setStatus(null);
|
|
112
109
|
try {
|
|
113
|
-
await client.
|
|
110
|
+
await client.revokeAccountToken(tokenId);
|
|
114
111
|
setTokens((prev) => prev.filter((t) => t.id !== tokenId));
|
|
115
112
|
setStatus("Token revoked");
|
|
116
113
|
}
|
|
@@ -127,7 +124,7 @@ export function ConnectAgent({ workspaceId, hubUrl }) {
|
|
|
127
124
|
setCopied(true);
|
|
128
125
|
setTimeout(() => setCopied(false), 2000);
|
|
129
126
|
}
|
|
130
|
-
return (_jsxs("section", { className: "shepherd-connect-agent", "aria-labelledby": headingId, children: [_jsxs("div", { className: "card-head", children: [_jsx("h3", { id: headingId, children: "Connect your agent" }), _jsx("p", { className: "card-sub", children: "Generate a token
|
|
127
|
+
return (_jsxs("section", { className: "shepherd-connect-agent", "aria-labelledby": headingId, children: [_jsxs("div", { className: "card-head", children: [_jsx("h3", { id: headingId, children: "Connect your agent" }), _jsx("p", { className: "card-sub", children: "Generate a token \u2014 it works across all your workspaces \u2014 then paste the command into your coding tool." }), _jsx("p", { className: "card-sub", children: "The first time the agent opens a repo, it'll ask which workspace to coordinate that repo with." })] }), _jsxs("div", { className: "card-body", children: [_jsxs("div", { className: "field", children: [_jsx("label", { htmlFor: "connect-tool", children: "Tool" }), _jsx("select", { id: "connect-tool", value: tool, onChange: (e) => setTool(e.target.value), children: TOOLS.map((t) => (_jsx("option", { value: t.id, children: t.label }, t.id))) })] }), _jsxs("div", { className: "field generate", children: [_jsx("label", { htmlFor: "token-name", children: "Token name (optional)" }), _jsxs("div", { className: "field__row", children: [_jsx("input", { id: "token-name", type: "text", value: name, onChange: (e) => setName(e.target.value), placeholder: "e.g. laptop" }), _jsx("button", { type: "button", onClick: () => void generate(), disabled: busy, children: "Generate token" })] })] }), error && _jsx("p", { role: "alert", children: error }), status && _jsx("p", { role: "status", children: status }), rawToken && (_jsx("p", { className: "token-once", role: "status", children: "Copy this token now \u2014 it won't be shown again." })), _jsxs("div", { className: "install-command", children: [_jsx("pre", { "data-testid": "install-command", children: command }), _jsx("button", { type: "button", className: "install-command__copy", "aria-label": copied ? "Copied" : "Copy command", title: copied ? "Copied" : "Copy", onClick: () => void copyCommand(), children: copied ? (_jsx("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) })) : (_jsxs("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), _jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] })) })] }), _jsx("h4", { children: "Existing tokens" }), loading ? (_jsx("p", { role: "status", children: "Loading\u2026" })) : tokens.length === 0 ? (_jsx("p", { children: "No tokens yet." })) : (_jsx("ul", { children: tokens.map((t) => {
|
|
131
128
|
const name = t.name ?? t.id;
|
|
132
129
|
return (_jsxs("li", { children: [_jsx("span", { children: name }), _jsx("span", { className: "token-meta", children: tokenMeta(t, Date.now()) }), t.revokedAt ? (_jsx("span", { className: "revoked", children: "revoked" })) : (_jsx("button", { type: "button", "aria-label": `Revoke token ${name}`, onClick: () => void revoke(t.id), disabled: revokingId === t.id, children: "Revoke" }))] }, t.id));
|
|
133
130
|
}) }))] })] }));
|
package/dist/config/Members.js
CHANGED
|
@@ -58,7 +58,7 @@ export function Members({ workspaceId, refreshKey = 0, canRemove = false }) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
return (_jsxs("section", { className: "shepherd-members", "aria-labelledby": headingId, children: [_jsx("div", { className: "card-head", children: _jsx("h3", { id: headingId, children: "Members" }) }), _jsxs("div", { className: "card-body", children: [error && _jsx("p", { role: "alert", children: error }), status && _jsx("p", { role: "status", children: status }), loading ? (_jsx("p", { role: "status", children: "Loading\u2026" })) : members.length === 0 ? (_jsx("p", { children: "No members." })) : (_jsx("ul", { children: members.map((m) => {
|
|
61
|
-
const display = m.displayName ?? m.githubLogin ?? m.accountId;
|
|
61
|
+
const display = m.displayName ?? m.githubLogin ?? m.email ?? m.accountId;
|
|
62
62
|
return (_jsxs("li", { children: [_jsx("span", { children: display }), _jsx("span", { className: "role", children: m.role }), canRemove && (_jsx("button", { type: "button", "aria-label": `Remove ${display}`, onClick: () => void remove(m.accountId, display), disabled: removingId === m.accountId, children: "Remove" }))] }, m.accountId));
|
|
63
63
|
}) }))] })] }));
|
|
64
64
|
}
|