@korso/shepherd-ui 0.11.0 → 0.11.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 -661
- package/README.md +50 -50
- package/dist/components/Chat.js +1 -1
- package/dist/components/Composer.js +1 -1
- package/dist/components/DoneList.js +1 -1
- package/dist/config/ConfirmDeleteWorkspace.js +1 -1
- package/dist/config/ConnectAgent.js +2 -2
- package/dist/lib/{Dashboard-YgxAVEUF.js → Dashboard-DNr0hdbW.js} +3 -3
- package/dist/lib/index.js +61 -60
- package/dist/lib/selfhost.js +1 -1
- package/dist/lib/styles.css +507 -507
- package/dist/selfhost/assets/{index-BhrpCYNE.js → index-DLumdd7Z.js} +1 -1
- package/dist/selfhost/index.html +12 -12
- package/package.json +67 -67
package/README.md
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
# @korso/shepherd-ui
|
|
2
|
-
|
|
3
|
-
The Shepherd dashboard: an **auth-agnostic** React app (Vite) that renders the
|
|
4
|
-
workspace landscape (crew, territory, active/done work, chat) and issues operator
|
|
5
|
-
actions against the hub. "Auth-agnostic" means the core holds no notion of who
|
|
6
|
-
the user is or how they authenticated — it renders coordination state and issues
|
|
7
|
-
operator actions, leaving authentication to the layer in front of it.
|
|
8
|
-
|
|
9
|
-
## Two build outputs
|
|
10
|
-
|
|
11
|
-
One source tree, two Vite builds (`npm run build` runs both):
|
|
12
|
-
|
|
13
|
-
- **`dist/lib`** (`build:lib`) — a component library for **Korso** to embed in
|
|
14
|
-
the hosted product. Exposes `<Dashboard/>`, `createShepherdClient`, the React
|
|
15
|
-
context (`ShepherdClientProvider` / `useShepherdClient`), and types via the `.`
|
|
16
|
-
export. It is **token-blind**: auth lives in the BFF in front of it. The
|
|
17
|
-
stylesheet is opt-in (`@korso/shepherd-ui/styles.css`), not auto-imported.
|
|
18
|
-
- **`dist/selfhost`** (`build:app`) — a self-contained SPA the **hub serves**
|
|
19
|
-
verbatim via `@fastify/static` (the hub resolves `../../ui/dist/selfhost/`).
|
|
20
|
-
It bundles its own CSS and mounts the token-gated root.
|
|
21
|
-
|
|
22
|
-
Both `dist/` outputs are gitignored; `npm run build` (root) and the hub Docker
|
|
23
|
-
image regenerate them.
|
|
24
|
-
|
|
25
|
-
## Two mounts
|
|
26
|
-
|
|
27
|
-
- **Self-host (the Hub).** The Hub serves `<SelfHostApp/>` (the `./selfhost`
|
|
28
|
-
export, mounted by `src/main.tsx`). This is the **one** place a team token is
|
|
29
|
-
handled — the named exception to the auth-agnostic core: it prompts for the
|
|
30
|
-
`TEAM_TOKEN`, persists it, builds a same-origin client that sends
|
|
31
|
-
`Authorization: Bearer <token>`, and re-gates on a 401. The token gate lives
|
|
32
|
-
**only** here, never in the `.` export or `<Dashboard/>`.
|
|
33
|
-
- **Hosted (Korso).** Korso imports `<Dashboard/>` + `createShepherdClient` from
|
|
34
|
-
the `.` export and supplies its own auth layer (the BFF). No token gate.
|
|
35
|
-
Hosted shells that embed `<ShepherdRoot/>` may pass `onLogout`; Shepherd renders
|
|
36
|
-
the Sign out action at the bottom of Config, while the host callback performs
|
|
37
|
-
the actual session cleanup and navigation (cookies, OIDC, BFF state, etc.).
|
|
38
|
-
|
|
39
|
-
## Development
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
npm -w @korso/shepherd-ui run dev # Vite dev server for the self-host SPA
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Other scripts: `build` (both outputs), `build:lib`, `build:app`, `type-check`
|
|
46
|
-
(`tsc --noEmit`), `test` (Vitest, jsdom).
|
|
47
|
-
|
|
48
|
-
## Phase note
|
|
49
|
-
|
|
50
|
-
Korso-side consumption of the `dist/lib` library is **finalized in Phase 5**.
|
|
1
|
+
# @korso/shepherd-ui
|
|
2
|
+
|
|
3
|
+
The Shepherd dashboard: an **auth-agnostic** React app (Vite) that renders the
|
|
4
|
+
workspace landscape (crew, territory, active/done work, chat) and issues operator
|
|
5
|
+
actions against the hub. "Auth-agnostic" means the core holds no notion of who
|
|
6
|
+
the user is or how they authenticated — it renders coordination state and issues
|
|
7
|
+
operator actions, leaving authentication to the layer in front of it.
|
|
8
|
+
|
|
9
|
+
## Two build outputs
|
|
10
|
+
|
|
11
|
+
One source tree, two Vite builds (`npm run build` runs both):
|
|
12
|
+
|
|
13
|
+
- **`dist/lib`** (`build:lib`) — a component library for **Korso** to embed in
|
|
14
|
+
the hosted product. Exposes `<Dashboard/>`, `createShepherdClient`, the React
|
|
15
|
+
context (`ShepherdClientProvider` / `useShepherdClient`), and types via the `.`
|
|
16
|
+
export. It is **token-blind**: auth lives in the BFF in front of it. The
|
|
17
|
+
stylesheet is opt-in (`@korso/shepherd-ui/styles.css`), not auto-imported.
|
|
18
|
+
- **`dist/selfhost`** (`build:app`) — a self-contained SPA the **hub serves**
|
|
19
|
+
verbatim via `@fastify/static` (the hub resolves `../../ui/dist/selfhost/`).
|
|
20
|
+
It bundles its own CSS and mounts the token-gated root.
|
|
21
|
+
|
|
22
|
+
Both `dist/` outputs are gitignored; `npm run build` (root) and the hub Docker
|
|
23
|
+
image regenerate them.
|
|
24
|
+
|
|
25
|
+
## Two mounts
|
|
26
|
+
|
|
27
|
+
- **Self-host (the Hub).** The Hub serves `<SelfHostApp/>` (the `./selfhost`
|
|
28
|
+
export, mounted by `src/main.tsx`). This is the **one** place a team token is
|
|
29
|
+
handled — the named exception to the auth-agnostic core: it prompts for the
|
|
30
|
+
`TEAM_TOKEN`, persists it, builds a same-origin client that sends
|
|
31
|
+
`Authorization: Bearer <token>`, and re-gates on a 401. The token gate lives
|
|
32
|
+
**only** here, never in the `.` export or `<Dashboard/>`.
|
|
33
|
+
- **Hosted (Korso).** Korso imports `<Dashboard/>` + `createShepherdClient` from
|
|
34
|
+
the `.` export and supplies its own auth layer (the BFF). No token gate.
|
|
35
|
+
Hosted shells that embed `<ShepherdRoot/>` may pass `onLogout`; Shepherd renders
|
|
36
|
+
the Sign out action at the bottom of Config, while the host callback performs
|
|
37
|
+
the actual session cleanup and navigation (cookies, OIDC, BFF state, etc.).
|
|
38
|
+
|
|
39
|
+
## Development
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
npm -w @korso/shepherd-ui run dev # Vite dev server for the self-host SPA
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Other scripts: `build` (both outputs), `build:lib`, `build:app`, `type-check`
|
|
46
|
+
(`tsc --noEmit`), `test` (Vitest, jsdom).
|
|
47
|
+
|
|
48
|
+
## Phase note
|
|
49
|
+
|
|
50
|
+
Korso-side consumption of the `dist/lib` library is **finalized in Phase 5**.
|
package/dist/components/Chat.js
CHANGED
|
@@ -48,7 +48,7 @@ export function Chat({ announcements, selectedRepo, nowMs }) {
|
|
|
48
48
|
chatRef.current.scrollTop = chatRef.current.scrollHeight;
|
|
49
49
|
}
|
|
50
50
|
}, [messages.length]);
|
|
51
|
-
return (_jsx("div", { id: "chat", className: "chat", ref: chatRef, children: messages.length === 0 ? (_jsx("div", { className: "empty", children: "No announcements yet
|
|
51
|
+
return (_jsx("div", { id: "chat", className: "chat", ref: chatRef, children: messages.length === 0 ? (_jsx("div", { className: "empty", children: "No announcements yet. Agents will post here as they coordinate." })) : (messages.map((a, i) => {
|
|
52
52
|
const targeted = a.targetAgentName !== null || a.toAdmin;
|
|
53
53
|
const className = "msg" +
|
|
54
54
|
(targeted ? " msg--targeted" : "") +
|
|
@@ -142,5 +142,5 @@ export function Composer({ agents, selectedRepo, workspaceId, onSent }) {
|
|
|
142
142
|
accept(name);
|
|
143
143
|
}, children: [_jsx("div", { className: "ma", style: { background: colorForName(name) }, children: initialsFor(name) }), _jsx("span", { children: name })] }, name))) }), _jsxs("form", { id: "chat-form", className: "chat-form", onSubmit: (e) => {
|
|
144
144
|
void onSubmit(e);
|
|
145
|
-
}, children: [_jsx("input", { id: "chat-input", className: "chat-input", type: "text", autoComplete: "off", "aria-label": "Message the team", placeholder: "Message the team\u2026 use @name to direct it", role: "combobox", "aria-expanded": popOpen, "aria-controls": "mention-pop", "aria-autocomplete": "list", "aria-activedescendant": popOpen ? `mention-opt-${active}` : undefined, ref: inputRef, value: value, onChange: (e) => onInput(e.target.value), onKeyDown: onKeyDown }), _jsx("button", { id: "chat-send", className: "chat-send", type: "submit", disabled: sending, children: "Send" })] }), failed ? (_jsx("div", { className: "chat__note", role: "status", children: "send failed
|
|
145
|
+
}, children: [_jsx("input", { id: "chat-input", className: "chat-input", type: "text", autoComplete: "off", "aria-label": "Message the team", placeholder: "Message the team\u2026 use @name to direct it", role: "combobox", "aria-expanded": popOpen, "aria-controls": "mention-pop", "aria-autocomplete": "list", "aria-activedescendant": popOpen ? `mention-opt-${active}` : undefined, ref: inputRef, value: value, onChange: (e) => onInput(e.target.value), onKeyDown: onKeyDown }), _jsx("button", { id: "chat-send", className: "chat-send", type: "submit", disabled: sending, children: "Send" })] }), failed ? (_jsx("div", { className: "chat__note", role: "status", children: "send failed, retry" })) : null] }));
|
|
146
146
|
}
|
|
@@ -36,7 +36,7 @@ export function DoneList({ tasks, nowMs, selectedRepo, doneShown, onLoadMore, })
|
|
|
36
36
|
lastDay = day;
|
|
37
37
|
const dropped = t.status === "dropped";
|
|
38
38
|
const meta = dropped
|
|
39
|
-
? `went offline ${formatRelative(t.endedAt ?? t.createdAt, nowMs)}
|
|
39
|
+
? `went offline ${formatRelative(t.endedAt ?? t.createdAt, nowMs)}, no done signal`
|
|
40
40
|
: `finished ${formatRelative(t.endedAt ?? t.createdAt, nowMs)}${formatActiveDuration(t.createdAt, t.endedAt)
|
|
41
41
|
? " · " + formatActiveDuration(t.createdAt, t.endedAt)
|
|
42
42
|
: ""}`;
|
|
@@ -24,5 +24,5 @@ export function ConfirmDeleteWorkspace({ workspaceName, busy, error, onConfirm,
|
|
|
24
24
|
onCancel();
|
|
25
25
|
}, children: _jsxs("div", { className: "shepherd-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": headingId, "aria-describedby": descId,
|
|
26
26
|
// Clicks inside the dialog must not bubble to the backdrop's cancel.
|
|
27
|
-
onClick: (e) => e.stopPropagation(), children: [_jsx("h3", { id: headingId, children: "Delete workspace" }), _jsxs("p", { id: descId, className: "shepherd-modal__body", children: ["This permanently deletes ", _jsx("b", { children: workspaceName }), " and all of its data
|
|
27
|
+
onClick: (e) => e.stopPropagation(), children: [_jsx("h3", { id: headingId, children: "Delete workspace" }), _jsxs("p", { id: descId, className: "shepherd-modal__body", children: ["This permanently deletes ", _jsx("b", { children: workspaceName }), " and all of its data: agents, sessions, tasks, announcements, change history, tokens, invites, and members. This cannot be undone."] }), _jsxs("label", { htmlFor: `${headingId}-input`, className: "shepherd-modal__label", children: ["Type ", _jsx("b", { children: workspaceName }), " to confirm"] }), _jsx("input", { id: `${headingId}-input`, ref: inputRef, type: "text", value: typed, autoComplete: "off", disabled: busy, onChange: (e) => setTyped(e.target.value) }), error && _jsx("p", { role: "alert", children: error }), _jsxs("div", { className: "shepherd-modal__actions", children: [_jsx("button", { type: "button", onClick: onCancel, disabled: busy, children: "Cancel" }), _jsx("button", { type: "button", className: "danger", onClick: onConfirm, disabled: !confirmed || busy, children: busy ? "Deleting…" : "Delete workspace" })] })] }) }));
|
|
28
28
|
}
|
|
@@ -206,12 +206,12 @@ export function ConnectAgent({ hubUrl }) {
|
|
|
206
206
|
setCopied(true);
|
|
207
207
|
setTimeout(() => setCopied(false), 2000);
|
|
208
208
|
}
|
|
209
|
-
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
|
|
209
|
+
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; it works across all your workspaces, so paste the command into your coding tool." }), _jsx("p", { className: "card-sub", children: "The first time the agent changes files in a repo, Shepherd asks which workspace to coordinate that repo with, once per repo, right in your tool." })] }), _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), onKeyDown: (e) => {
|
|
210
210
|
if (e.key === "Enter" && !busy) {
|
|
211
211
|
e.preventDefault();
|
|
212
212
|
void generate();
|
|
213
213
|
}
|
|
214
|
-
}, 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
|
|
214
|
+
}, 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; 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" })] })) })] }), hook && (_jsxs("div", { className: "hook-setup", children: [_jsxs("p", { className: "card-sub", children: ["Message delivery sets itself up: the first time the agent runs, Shepherd adds its inbox hook to ", _jsx("code", { children: hook.target }), " ", "automatically. It delivers teammate announcements and reminds the agent to link each repo before its first write. This happens at most once; set", " ", _jsx("code", { children: "SHEPHERD_NO_AUTO_HOOKS=1" }), " to opt out."] }), hook.snippet && (_jsxs("details", { className: "hook-reference", children: [_jsx("summary", { children: "What gets added (manual equivalent)" }), _jsx("div", { className: "install-command", children: _jsx("pre", { "data-testid": "hook-snippet", children: hook.snippet }) })] }))] })), _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) => {
|
|
215
215
|
const name = t.name ?? t.id;
|
|
216
216
|
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));
|
|
217
217
|
}) }))] })] }));
|
|
@@ -928,7 +928,7 @@ function Dt({
|
|
|
928
928
|
c.map((u) => {
|
|
929
929
|
const d = yt(u.endedAt ?? u.createdAt, n), g = d !== h ? d : null;
|
|
930
930
|
h = d;
|
|
931
|
-
const f = u.status === "dropped", k = f ? `went offline ${K(u.endedAt ?? u.createdAt, n)}
|
|
931
|
+
const f = u.status === "dropped", k = f ? `went offline ${K(u.endedAt ?? u.createdAt, n)}, no done signal` : `finished ${K(u.endedAt ?? u.createdAt, n)}${ke(u.createdAt, u.endedAt) ? " · " + ke(u.createdAt, u.endedAt) : ""}`;
|
|
932
932
|
return /* @__PURE__ */ m(Fe, { children: [
|
|
933
933
|
g !== null && /* @__PURE__ */ r("div", { className: "day", children: g }),
|
|
934
934
|
/* @__PURE__ */ m("div", { className: "task", children: [
|
|
@@ -971,7 +971,7 @@ function xt({ announcements: t, selectedRepo: n, nowMs: i }) {
|
|
|
971
971
|
const l = [...t].filter((c) => M(c, n)).reverse();
|
|
972
972
|
return Ue(() => {
|
|
973
973
|
a.current && o.current && (o.current.scrollTop = o.current.scrollHeight);
|
|
974
|
-
}, [l.length]), /* @__PURE__ */ r("div", { id: "chat", className: "chat", ref: o, children: l.length === 0 ? /* @__PURE__ */ r("div", { className: "empty", children: "No announcements yet
|
|
974
|
+
}, [l.length]), /* @__PURE__ */ r("div", { id: "chat", className: "chat", ref: o, children: l.length === 0 ? /* @__PURE__ */ r("div", { className: "empty", children: "No announcements yet. Agents will post here as they coordinate." }) : l.map((c, h) => {
|
|
975
975
|
const d = "msg" + (c.targetAgentName !== null || c.toAdmin ? " msg--targeted" : "") + (c.fromAdmin ? " msg--me" : "");
|
|
976
976
|
return (
|
|
977
977
|
// The feed has no stable id; index is acceptable because the list is
|
|
@@ -1130,7 +1130,7 @@ function Lt({ agents: t, selectedRepo: n, workspaceId: i, onSent: o }) {
|
|
|
1130
1130
|
]
|
|
1131
1131
|
}
|
|
1132
1132
|
),
|
|
1133
|
-
T ? /* @__PURE__ */ r("div", { className: "chat__note", role: "status", children: "send failed
|
|
1133
|
+
T ? /* @__PURE__ */ r("div", { className: "chat__note", role: "status", children: "send failed, retry" }) : null
|
|
1134
1134
|
] });
|
|
1135
1135
|
}
|
|
1136
1136
|
function Ae({
|
package/dist/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { u as L, d as x, S as M, f as W, D as V } from "./Dashboard-
|
|
2
|
-
import { E as ye, a as be, c as ke } from "./Dashboard-
|
|
1
|
+
import { u as L, d as x, S as M, f as W, D as V } from "./Dashboard-DNr0hdbW.js";
|
|
2
|
+
import { E as ye, a as be, c as ke } from "./Dashboard-DNr0hdbW.js";
|
|
3
3
|
import { jsxs as n, jsx as e, Fragment as $ } from "react/jsx-runtime";
|
|
4
|
-
import { useId as j, useRef as
|
|
4
|
+
import { useId as j, useRef as B, useState as s, useEffect as T, useCallback as G } from "react";
|
|
5
5
|
function F(t) {
|
|
6
6
|
return (t.trim()[0] ?? "?").toUpperCase();
|
|
7
7
|
}
|
|
@@ -9,10 +9,10 @@ function q({
|
|
|
9
9
|
workspaces: t,
|
|
10
10
|
selected: a,
|
|
11
11
|
onSelect: h,
|
|
12
|
-
onChanged:
|
|
12
|
+
onChanged: r,
|
|
13
13
|
onMembersChanged: i
|
|
14
14
|
}) {
|
|
15
|
-
const p = L(), u = j(), c =
|
|
15
|
+
const p = L(), u = j(), c = B(null), [d, m] = s(!1), [y, l] = s("menu"), [N, g] = s(""), [_, b] = s(""), [C, o] = s(null), [k, E] = s(!1);
|
|
16
16
|
function S() {
|
|
17
17
|
m(!1), l("menu"), g(""), b(""), o(null);
|
|
18
18
|
}
|
|
@@ -33,7 +33,7 @@ function q({
|
|
|
33
33
|
E(!0), o(null);
|
|
34
34
|
try {
|
|
35
35
|
const v = await p.createWorkspace({ name: N.trim() });
|
|
36
|
-
h(v.id),
|
|
36
|
+
h(v.id), r(), S();
|
|
37
37
|
} catch (v) {
|
|
38
38
|
o(x(v));
|
|
39
39
|
} finally {
|
|
@@ -46,7 +46,7 @@ function q({
|
|
|
46
46
|
E(!0), o(null);
|
|
47
47
|
try {
|
|
48
48
|
const v = await p.redeemInvite(_.trim());
|
|
49
|
-
h(v.workspace.id),
|
|
49
|
+
h(v.workspace.id), r(), i == null || i(), S();
|
|
50
50
|
} catch (v) {
|
|
51
51
|
o(x(v));
|
|
52
52
|
} finally {
|
|
@@ -184,10 +184,10 @@ function X({
|
|
|
184
184
|
workspaceName: t,
|
|
185
185
|
busy: a,
|
|
186
186
|
error: h,
|
|
187
|
-
onConfirm:
|
|
187
|
+
onConfirm: r,
|
|
188
188
|
onCancel: i
|
|
189
189
|
}) {
|
|
190
|
-
const p = j(), u = j(), [c, d] =
|
|
190
|
+
const p = j(), u = j(), [c, d] = s(""), m = B(null);
|
|
191
191
|
T(() => {
|
|
192
192
|
var l;
|
|
193
193
|
(l = m.current) == null || l.focus();
|
|
@@ -219,7 +219,7 @@ function X({
|
|
|
219
219
|
/* @__PURE__ */ n("p", { id: u, className: "shepherd-modal__body", children: [
|
|
220
220
|
"This permanently deletes ",
|
|
221
221
|
/* @__PURE__ */ e("b", { children: t }),
|
|
222
|
-
" and all of its data
|
|
222
|
+
" and all of its data: agents, sessions, tasks, announcements, change history, tokens, invites, and members. This cannot be undone."
|
|
223
223
|
] }),
|
|
224
224
|
/* @__PURE__ */ n("label", { htmlFor: `${p}-input`, className: "shepherd-modal__label", children: [
|
|
225
225
|
"Type ",
|
|
@@ -246,7 +246,7 @@ function X({
|
|
|
246
246
|
{
|
|
247
247
|
type: "button",
|
|
248
248
|
className: "danger",
|
|
249
|
-
onClick:
|
|
249
|
+
onClick: r,
|
|
250
250
|
disabled: !y || a,
|
|
251
251
|
children: a ? "Deleting…" : "Delete workspace"
|
|
252
252
|
}
|
|
@@ -259,12 +259,12 @@ function X({
|
|
|
259
259
|
);
|
|
260
260
|
}
|
|
261
261
|
function z({ workspace: t, onLeft: a, onDeleted: h }) {
|
|
262
|
-
const
|
|
262
|
+
const r = L(), i = j(), [p, u] = s(null), [c, d] = s(!1), [m, y] = s(!1), [l, N] = s(!1), [g, _] = s(!1), [b, C] = s(null), o = t.role === "admin";
|
|
263
263
|
async function k() {
|
|
264
264
|
if (!c) {
|
|
265
265
|
d(!0), u(null), y(!1);
|
|
266
266
|
try {
|
|
267
|
-
await
|
|
267
|
+
await r.leave(t.id), a == null || a();
|
|
268
268
|
} catch (S) {
|
|
269
269
|
u(x(S)), S instanceof M && S.status === 409 && y(!0);
|
|
270
270
|
} finally {
|
|
@@ -276,7 +276,7 @@ function z({ workspace: t, onLeft: a, onDeleted: h }) {
|
|
|
276
276
|
if (!g) {
|
|
277
277
|
_(!0), C(null);
|
|
278
278
|
try {
|
|
279
|
-
await
|
|
279
|
+
await r.deleteWorkspace(t.id), N(!1), h == null || h();
|
|
280
280
|
} catch (S) {
|
|
281
281
|
C(x(S));
|
|
282
282
|
} finally {
|
|
@@ -337,11 +337,11 @@ function z({ workspace: t, onLeft: a, onDeleted: h }) {
|
|
|
337
337
|
] });
|
|
338
338
|
}
|
|
339
339
|
function Q({ workspaceId: t, refreshKey: a = 0, canRemove: h = !1 }) {
|
|
340
|
-
const
|
|
340
|
+
const r = L(), i = j(), [p, u] = s([]), [c, d] = s(null), [m, y] = s(null), [l, N] = s(!0), [g, _] = s(null);
|
|
341
341
|
async function b({ keepError: o = !1 } = {}) {
|
|
342
342
|
o || d(null);
|
|
343
343
|
try {
|
|
344
|
-
const k = await
|
|
344
|
+
const k = await r.listMembers(t);
|
|
345
345
|
u(k.members);
|
|
346
346
|
} catch (k) {
|
|
347
347
|
d(x(k));
|
|
@@ -351,12 +351,12 @@ function Q({ workspaceId: t, refreshKey: a = 0, canRemove: h = !1 }) {
|
|
|
351
351
|
}
|
|
352
352
|
T(() => {
|
|
353
353
|
N(!0), b();
|
|
354
|
-
}, [
|
|
354
|
+
}, [r, t, a]);
|
|
355
355
|
async function C(o, k) {
|
|
356
356
|
if (!g) {
|
|
357
357
|
_(o), d(null), y(null), u((E) => E.filter((S) => S.accountId !== o));
|
|
358
358
|
try {
|
|
359
|
-
await
|
|
359
|
+
await r.removeMember(t, o), y(`Removed ${k}`);
|
|
360
360
|
} catch (E) {
|
|
361
361
|
d(x(E)), b({ keepError: !0 });
|
|
362
362
|
} finally {
|
|
@@ -390,7 +390,7 @@ function Q({ workspaceId: t, refreshKey: a = 0, canRemove: h = !1 }) {
|
|
|
390
390
|
] });
|
|
391
391
|
}
|
|
392
392
|
function Z({ workspaceId: t, onMembersChanged: a }) {
|
|
393
|
-
const h = L(),
|
|
393
|
+
const h = L(), r = j(), [i, p] = s(null), [u, c] = s(null), [d, m] = s(!1), [y, l] = s(""), [N, g] = s(null), [_, b] = s(!1), [C, o] = s(null);
|
|
394
394
|
T(() => {
|
|
395
395
|
p(null), g(null);
|
|
396
396
|
}, [t]);
|
|
@@ -436,9 +436,9 @@ function Z({ workspaceId: t, onMembersChanged: a }) {
|
|
|
436
436
|
async function A(w, O) {
|
|
437
437
|
await navigator.clipboard.writeText(O), o(w), setTimeout(() => o((v) => v === w ? null : v), 2e3);
|
|
438
438
|
}
|
|
439
|
-
return /* @__PURE__ */ n("section", { className: "shepherd-invites", "aria-labelledby":
|
|
439
|
+
return /* @__PURE__ */ n("section", { className: "shepherd-invites", "aria-labelledby": r, children: [
|
|
440
440
|
/* @__PURE__ */ n("div", { className: "card-head", children: [
|
|
441
|
-
/* @__PURE__ */ e("h3", { id:
|
|
441
|
+
/* @__PURE__ */ e("h3", { id: r, children: "Invite people" }),
|
|
442
442
|
/* @__PURE__ */ e("p", { className: "card-sub", children: "Share a code, or send a one-time join link by email." })
|
|
443
443
|
] }),
|
|
444
444
|
/* @__PURE__ */ n("div", { className: "card-body", children: [
|
|
@@ -522,16 +522,16 @@ const ee = [
|
|
|
522
522
|
}, ae = {
|
|
523
523
|
claude: "-e",
|
|
524
524
|
codex: "--env"
|
|
525
|
-
},
|
|
525
|
+
}, H = "npx -y --package=@korso/shepherd shepherd-inbox-hook";
|
|
526
526
|
function ie(t) {
|
|
527
527
|
return t === "claude" ? {
|
|
528
528
|
target: "~/.claude/settings.json",
|
|
529
529
|
snippet: JSON.stringify(
|
|
530
530
|
{
|
|
531
531
|
hooks: {
|
|
532
|
-
SessionStart: [{ hooks: [{ type: "command", command:
|
|
532
|
+
SessionStart: [{ hooks: [{ type: "command", command: H }] }],
|
|
533
533
|
PreToolUse: [
|
|
534
|
-
{ matcher: "*", hooks: [{ type: "command", command:
|
|
534
|
+
{ matcher: "*", hooks: [{ type: "command", command: H }] }
|
|
535
535
|
]
|
|
536
536
|
}
|
|
537
537
|
},
|
|
@@ -557,7 +557,7 @@ function ie(t) {
|
|
|
557
557
|
{
|
|
558
558
|
version: 1,
|
|
559
559
|
hooks: {
|
|
560
|
-
beforeSubmitPrompt: [{ command:
|
|
560
|
+
beforeSubmitPrompt: [{ command: H }]
|
|
561
561
|
}
|
|
562
562
|
},
|
|
563
563
|
null,
|
|
@@ -565,11 +565,11 @@ function ie(t) {
|
|
|
565
565
|
)
|
|
566
566
|
} : null;
|
|
567
567
|
}
|
|
568
|
-
function
|
|
569
|
-
const h = `created ${W(t.createdAt, a)}`,
|
|
570
|
-
return `${h} · ${
|
|
568
|
+
function se(t, a) {
|
|
569
|
+
const h = `created ${W(t.createdAt, a)}`, r = t.lastUsedAt ? `last used ${W(t.lastUsedAt, a)}` : "never used";
|
|
570
|
+
return `${h} · ${r}`;
|
|
571
571
|
}
|
|
572
|
-
function
|
|
572
|
+
function re(t, a, h) {
|
|
573
573
|
if (t === "generic" || t === "pi" || t === "cursor") {
|
|
574
574
|
const p = { HUB_URL: a, SHEPHERD_TOKEN: h };
|
|
575
575
|
return t !== "generic" && (p.PROGRAM = t), JSON.stringify(
|
|
@@ -586,15 +586,15 @@ function se(t, a, h) {
|
|
|
586
586
|
2
|
|
587
587
|
);
|
|
588
588
|
}
|
|
589
|
-
const
|
|
589
|
+
const r = ae[t], i = [
|
|
590
590
|
te[t],
|
|
591
|
-
`${
|
|
592
|
-
`${
|
|
591
|
+
`${r} HUB_URL=${a}`,
|
|
592
|
+
`${r} SHEPHERD_TOKEN=${h}`
|
|
593
593
|
];
|
|
594
|
-
return t === "codex" && i.push(`${
|
|
594
|
+
return t === "codex" && i.push(`${r} PROGRAM=codex`), i.push("-- npx -y --package=@korso/shepherd shepherd-mcp"), i.join(" ");
|
|
595
595
|
}
|
|
596
596
|
function le({ hubUrl: t }) {
|
|
597
|
-
const a = L(), h = t ?? a.baseUrl,
|
|
597
|
+
const a = L(), h = t ?? a.baseUrl, r = j(), [i, p] = s("claude"), [u, c] = s(""), [d, m] = s(null), [y, l] = s([]), [N, g] = s(null), [_, b] = s(null), [C, o] = s(!1), [k, E] = s(!1), [S, I] = s(!0), [A, w] = s(null);
|
|
598
598
|
async function O() {
|
|
599
599
|
try {
|
|
600
600
|
const f = await a.listAccountTokens();
|
|
@@ -633,15 +633,15 @@ function le({ hubUrl: t }) {
|
|
|
633
633
|
}
|
|
634
634
|
}
|
|
635
635
|
}
|
|
636
|
-
const D =
|
|
636
|
+
const D = re(i, h, d ?? ne), U = ie(i);
|
|
637
637
|
async function J() {
|
|
638
638
|
await navigator.clipboard.writeText(D), E(!0), setTimeout(() => E(!1), 2e3);
|
|
639
639
|
}
|
|
640
|
-
return /* @__PURE__ */ n("section", { className: "shepherd-connect-agent", "aria-labelledby":
|
|
640
|
+
return /* @__PURE__ */ n("section", { className: "shepherd-connect-agent", "aria-labelledby": r, children: [
|
|
641
641
|
/* @__PURE__ */ n("div", { className: "card-head", children: [
|
|
642
|
-
/* @__PURE__ */ e("h3", { id:
|
|
643
|
-
/* @__PURE__ */ e("p", { className: "card-sub", children: "Generate a token
|
|
644
|
-
/* @__PURE__ */ e("p", { className: "card-sub", children: "The first time the agent changes files in a repo, Shepherd asks which workspace to coordinate that repo with
|
|
642
|
+
/* @__PURE__ */ e("h3", { id: r, children: "Connect your agent" }),
|
|
643
|
+
/* @__PURE__ */ e("p", { className: "card-sub", children: "Generate a token; it works across all your workspaces, so paste the command into your coding tool." }),
|
|
644
|
+
/* @__PURE__ */ e("p", { className: "card-sub", children: "The first time the agent changes files in a repo, Shepherd asks which workspace to coordinate that repo with, once per repo, right in your tool." })
|
|
645
645
|
] }),
|
|
646
646
|
/* @__PURE__ */ n("div", { className: "card-body", children: [
|
|
647
647
|
/* @__PURE__ */ n("div", { className: "field", children: [
|
|
@@ -677,7 +677,7 @@ function le({ hubUrl: t }) {
|
|
|
677
677
|
] }),
|
|
678
678
|
N && /* @__PURE__ */ e("p", { role: "alert", children: N }),
|
|
679
679
|
_ && /* @__PURE__ */ e("p", { role: "status", children: _ }),
|
|
680
|
-
d && /* @__PURE__ */ e("p", { className: "token-once", role: "status", children: "Copy this token now
|
|
680
|
+
d && /* @__PURE__ */ e("p", { className: "token-once", role: "status", children: "Copy this token now; it won't be shown again." }),
|
|
681
681
|
/* @__PURE__ */ n("div", { className: "install-command", children: [
|
|
682
682
|
/* @__PURE__ */ e("pre", { "data-testid": "install-command", children: D }),
|
|
683
683
|
/* @__PURE__ */ e(
|
|
@@ -723,18 +723,19 @@ function le({ hubUrl: t }) {
|
|
|
723
723
|
}
|
|
724
724
|
)
|
|
725
725
|
] }),
|
|
726
|
-
|
|
726
|
+
U && /* @__PURE__ */ n("div", { className: "hook-setup", children: [
|
|
727
727
|
/* @__PURE__ */ n("p", { className: "card-sub", children: [
|
|
728
728
|
"Message delivery sets itself up: the first time the agent runs, Shepherd adds its inbox hook to ",
|
|
729
|
-
/* @__PURE__ */ e("code", { children:
|
|
729
|
+
/* @__PURE__ */ e("code", { children: U.target }),
|
|
730
|
+
" ",
|
|
731
|
+
"automatically. It delivers teammate announcements and reminds the agent to link each repo before its first write. This happens at most once; set",
|
|
730
732
|
" ",
|
|
731
|
-
"automatically — it delivers teammate announcements and reminds the agent to link each repo before its first write. Happens at most once; set ",
|
|
732
733
|
/* @__PURE__ */ e("code", { children: "SHEPHERD_NO_AUTO_HOOKS=1" }),
|
|
733
734
|
" to opt out."
|
|
734
735
|
] }),
|
|
735
|
-
|
|
736
|
+
U.snippet && /* @__PURE__ */ n("details", { className: "hook-reference", children: [
|
|
736
737
|
/* @__PURE__ */ e("summary", { children: "What gets added (manual equivalent)" }),
|
|
737
|
-
/* @__PURE__ */ e("div", { className: "install-command", children: /* @__PURE__ */ e("pre", { "data-testid": "hook-snippet", children:
|
|
738
|
+
/* @__PURE__ */ e("div", { className: "install-command", children: /* @__PURE__ */ e("pre", { "data-testid": "hook-snippet", children: U.snippet }) })
|
|
738
739
|
] })
|
|
739
740
|
] }),
|
|
740
741
|
/* @__PURE__ */ e("h4", { children: "Existing tokens" }),
|
|
@@ -742,7 +743,7 @@ function le({ hubUrl: t }) {
|
|
|
742
743
|
const P = f.name ?? f.id;
|
|
743
744
|
return /* @__PURE__ */ n("li", { children: [
|
|
744
745
|
/* @__PURE__ */ e("span", { children: P }),
|
|
745
|
-
/* @__PURE__ */ e("span", { className: "token-meta", children:
|
|
746
|
+
/* @__PURE__ */ e("span", { className: "token-meta", children: se(f, Date.now()) }),
|
|
746
747
|
f.revokedAt ? /* @__PURE__ */ e("span", { className: "revoked", children: "revoked" }) : /* @__PURE__ */ e(
|
|
747
748
|
"button",
|
|
748
749
|
{
|
|
@@ -777,12 +778,12 @@ function ce({
|
|
|
777
778
|
workspace: t,
|
|
778
779
|
hubUrl: a,
|
|
779
780
|
membersRefreshKey: h,
|
|
780
|
-
onMembersChanged:
|
|
781
|
+
onMembersChanged: r,
|
|
781
782
|
onLeft: i,
|
|
782
783
|
onDeleted: p,
|
|
783
784
|
onLogout: u
|
|
784
785
|
}) {
|
|
785
|
-
const [c, d] =
|
|
786
|
+
const [c, d] = s("general"), m = t.role === "admin";
|
|
786
787
|
return /* @__PURE__ */ n($, { children: [
|
|
787
788
|
/* @__PURE__ */ n("div", { className: "config-layout", children: [
|
|
788
789
|
/* @__PURE__ */ e("nav", { className: "config-nav", "aria-label": "Configuration sections", children: oe.map(({ id: y, label: l }) => /* @__PURE__ */ e(
|
|
@@ -807,7 +808,7 @@ function ce({
|
|
|
807
808
|
canRemove: m
|
|
808
809
|
}
|
|
809
810
|
),
|
|
810
|
-
m && /* @__PURE__ */ e(Z, { workspaceId: t.id, onMembersChanged:
|
|
811
|
+
m && /* @__PURE__ */ e(Z, { workspaceId: t.id, onMembersChanged: r })
|
|
811
812
|
] }),
|
|
812
813
|
c === "agent" && /* @__PURE__ */ e(le, { hubUrl: a })
|
|
813
814
|
] })
|
|
@@ -816,7 +817,7 @@ function ce({
|
|
|
816
817
|
] });
|
|
817
818
|
}
|
|
818
819
|
function me({ hubUrl: t, onLogout: a }) {
|
|
819
|
-
const h = L(), [
|
|
820
|
+
const h = L(), [r, i] = s({ status: "loading" }), [p, u] = s(null), [c, d] = s(0), m = G(async () => {
|
|
820
821
|
try {
|
|
821
822
|
const b = await h.listWorkspaces();
|
|
822
823
|
i({ status: "ready", workspaces: b.workspaces }), u(
|
|
@@ -832,11 +833,11 @@ function me({ hubUrl: t, onLogout: a }) {
|
|
|
832
833
|
T(() => {
|
|
833
834
|
m();
|
|
834
835
|
}, [m]);
|
|
835
|
-
const y =
|
|
836
|
-
if (
|
|
836
|
+
const y = r.status === "ready" ? r.workspaces : [], l = y.find((b) => b.id === p) ?? null;
|
|
837
|
+
if (r.status === "loading")
|
|
837
838
|
return /* @__PURE__ */ e("div", { className: "shepherd-root", children: /* @__PURE__ */ e("p", { role: "status", children: "Loading…" }) });
|
|
838
|
-
if (
|
|
839
|
-
return /* @__PURE__ */ e("div", { className: "shepherd-root", children: /* @__PURE__ */ e("p", { role: "alert", children:
|
|
839
|
+
if (r.status === "error")
|
|
840
|
+
return /* @__PURE__ */ e("div", { className: "shepherd-root", children: /* @__PURE__ */ e("p", { role: "alert", children: r.message }) });
|
|
840
841
|
const N = y.length > 0, g = /* @__PURE__ */ e(
|
|
841
842
|
q,
|
|
842
843
|
{
|
|
@@ -881,7 +882,7 @@ function me({ hubUrl: t, onLogout: a }) {
|
|
|
881
882
|
) });
|
|
882
883
|
}
|
|
883
884
|
function pe({ code: t, onJoined: a }) {
|
|
884
|
-
const h = L(),
|
|
885
|
+
const h = L(), r = j(), [i, p] = s({ status: "joining" }), u = B(a);
|
|
885
886
|
u.current = a;
|
|
886
887
|
const c = G(async () => {
|
|
887
888
|
p({ status: "joining" });
|
|
@@ -891,18 +892,18 @@ function pe({ code: t, onJoined: a }) {
|
|
|
891
892
|
} catch (m) {
|
|
892
893
|
p({ status: "error", message: x(m) });
|
|
893
894
|
}
|
|
894
|
-
}, [h, t]), d =
|
|
895
|
+
}, [h, t]), d = B(!1);
|
|
895
896
|
return T(() => {
|
|
896
897
|
d.current || (d.current = !0, c());
|
|
897
|
-
}, [c]), /* @__PURE__ */ e("section", { className: "shepherd-join", "aria-labelledby":
|
|
898
|
+
}, [c]), /* @__PURE__ */ e("section", { className: "shepherd-join", "aria-labelledby": r, children: /* @__PURE__ */ n("div", { className: "shepherd-join__card", children: [
|
|
898
899
|
/* @__PURE__ */ e("p", { className: "shepherd-join__brand", children: "Shepherd" }),
|
|
899
900
|
i.status === "joining" && /* @__PURE__ */ n($, { children: [
|
|
900
|
-
/* @__PURE__ */ e("h1", { id:
|
|
901
|
+
/* @__PURE__ */ e("h1", { id: r, children: "Joining workspace…" }),
|
|
901
902
|
/* @__PURE__ */ e("p", { className: "shepherd-join__copy", role: "status", children: "Hold on while we add you to the team." }),
|
|
902
903
|
/* @__PURE__ */ e("div", { className: "shepherd-join__spinner", "aria-hidden": "true" })
|
|
903
904
|
] }),
|
|
904
905
|
i.status === "joined" && /* @__PURE__ */ n($, { children: [
|
|
905
|
-
/* @__PURE__ */ e("h1", { id:
|
|
906
|
+
/* @__PURE__ */ e("h1", { id: r, children: "You’re in" }),
|
|
906
907
|
/* @__PURE__ */ n("p", { className: "shepherd-join__copy", role: "status", children: [
|
|
907
908
|
"Joined ",
|
|
908
909
|
/* @__PURE__ */ e("strong", { children: i.workspace.name }),
|
|
@@ -910,7 +911,7 @@ function pe({ code: t, onJoined: a }) {
|
|
|
910
911
|
] })
|
|
911
912
|
] }),
|
|
912
913
|
i.status === "error" && /* @__PURE__ */ n($, { children: [
|
|
913
|
-
/* @__PURE__ */ e("h1", { id:
|
|
914
|
+
/* @__PURE__ */ e("h1", { id: r, children: "Couldn’t join the workspace" }),
|
|
914
915
|
/* @__PURE__ */ e("p", { className: "shepherd-join__error", role: "alert", children: i.message }),
|
|
915
916
|
/* @__PURE__ */ e(
|
|
916
917
|
"button",
|
package/dist/lib/selfhost.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as e, jsxs as l } from "react/jsx-runtime";
|
|
2
2
|
import { useState as s, useCallback as h, useMemo as d } from "react";
|
|
3
|
-
import { c as m, a as u, D as p } from "./Dashboard-
|
|
3
|
+
import { c as m, a as u, D as p } from "./Dashboard-DNr0hdbW.js";
|
|
4
4
|
const n = "shepherd.token";
|
|
5
5
|
function f() {
|
|
6
6
|
try {
|