@burdenoff/microfe-vibecontrols 2026.709.2 → 2026.710.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/dist/components/agent-manager/AgentManagerBookmarksDrawer.js +87 -85
- package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js.map +1 -1
- package/dist/components/ai/InstructionFilesManager.js +138 -136
- package/dist/components/ai/InstructionFilesManager.js.map +1 -1
- package/dist/components/ai/PluginHarnessPicker.js +70 -64
- package/dist/components/ai/PluginHarnessPicker.js.map +1 -1
- package/dist/components/docs/DocsEditPanel.js +1 -1
- package/dist/components/docs/DocsEditPanel.js.map +1 -1
- package/dist/components/docs/DocsPageTree.js +1 -1
- package/dist/components/docs/DocsPageTree.js.map +1 -1
- package/dist/components/docs/DocsTabPanel.js +108 -106
- package/dist/components/docs/DocsTabPanel.js.map +1 -1
- package/dist/components/notes/NotesTabPanel.js +99 -99
- package/dist/components/notes/NotesTabPanel.js.map +1 -1
- package/dist/components/scorecard/ScorecardPanel.js +309 -308
- package/dist/components/scorecard/ScorecardPanel.js.map +1 -1
- package/dist/components/sessions/SessionPublicLinkPanel.js +104 -102
- package/dist/components/sessions/SessionPublicLinkPanel.js.map +1 -1
- package/dist/components/settings/AdvancedSection.js +92 -90
- package/dist/components/settings/AdvancedSection.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js +104 -102
- package/dist/components/vibedeck/VibeDeckPublicLinkPanel.js.map +1 -1
- package/dist/constants/permissions.js +7 -0
- package/dist/constants/permissions.js.map +1 -1
- package/dist/pages/ActionsPage.js +303 -303
- package/dist/pages/ActionsPage.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +520 -516
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/AgentsPage.js +287 -283
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +554 -549
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/VibeAuditPage.js +2 -2
- package/dist/pages/VibeAuditPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +257 -257
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibeDetailsPage.js +688 -693
- package/dist/pages/VibeDetailsPage.js.map +1 -1
- package/dist/pages/VibesPage.js +49 -41
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/WebhooksPage.js +162 -138
- package/dist/pages/WebhooksPage.js.map +1 -1
- package/dist/pages/tunnels/DomainsTab.js +54 -47
- package/dist/pages/tunnels/DomainsTab.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,13 +3,15 @@ import { LoadingState as r } from "../shared/LoadingState.js";
|
|
|
3
3
|
import { ErrorState as i } from "../shared/ErrorState.js";
|
|
4
4
|
import { VibeDeckSharePermission as a } from "../../generated/wspace-types.js";
|
|
5
5
|
import "../shared/index.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
6
|
+
import { VIBE_PERMISSIONS as o } from "../../constants/permissions.js";
|
|
7
|
+
import { notify as s } from "../../lib/toast.js";
|
|
8
|
+
import { useState as c } from "react";
|
|
9
|
+
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
10
|
+
import { Check as u, Copy as d, Eye as f, Link2 as p, Lock as m, Play as h, Plus as g, Trash2 as _, X as v } from "lucide-react";
|
|
11
|
+
import { usePermissions as y } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
12
|
+
import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
11
13
|
//#region src/components/vibedeck/VibeDeckPublicLinkPanel.tsx
|
|
12
|
-
var
|
|
14
|
+
var C = [
|
|
13
15
|
{
|
|
14
16
|
label: "4 hours",
|
|
15
17
|
hours: 4
|
|
@@ -26,195 +28,195 @@ var x = [
|
|
|
26
28
|
label: "No expiry",
|
|
27
29
|
hours: 0
|
|
28
30
|
}
|
|
29
|
-
],
|
|
30
|
-
let { t:
|
|
31
|
-
let n =
|
|
31
|
+
], w = ({ vibeDeckId: w, vibeDeckName: E, onClose: D }) => {
|
|
32
|
+
let { t: O } = l(), k = (e, t) => {
|
|
33
|
+
let n = O(e);
|
|
32
34
|
return n === e ? t : n;
|
|
33
|
-
}, { data:
|
|
34
|
-
variables: { vibeDeckId:
|
|
35
|
+
}, { hasPermission: A } = y(), j = A(o.VIBEDECK_UPDATE), { data: M, loading: N, error: P, refetch: F } = t({
|
|
36
|
+
variables: { vibeDeckId: w },
|
|
35
37
|
fetchPolicy: "cache-and-network"
|
|
36
|
-
}), [
|
|
38
|
+
}), [I, { loading: L }] = e(), [R] = n(), [z, B] = c(!0), [V, H] = c(4), [U, W] = c(""), [G, K] = c(""), [q, J] = c(null), Y = M?.vibeDeckShareLinks ?? [], X = Y.filter((e) => e.status === "ACTIVE"), Z = async (e, t) => {
|
|
37
39
|
try {
|
|
38
|
-
await navigator.clipboard.writeText(e),
|
|
40
|
+
await navigator.clipboard.writeText(e), J(t), s.success(k("deckLink.copied", "Link copied")), window.setTimeout(() => J(null), 1500);
|
|
39
41
|
} catch {
|
|
40
|
-
|
|
42
|
+
s.error(k("deckLink.copyFailed", "Could not copy — copy it manually"), e);
|
|
41
43
|
}
|
|
42
|
-
},
|
|
44
|
+
}, Q = async (e) => {
|
|
43
45
|
e.preventDefault();
|
|
44
|
-
let t =
|
|
46
|
+
let t = G.trim() ? Number.parseInt(G.trim(), 10) : null;
|
|
45
47
|
if (t !== null && (!Number.isFinite(t) || t < 1)) {
|
|
46
|
-
|
|
48
|
+
s.error(k("deckLink.badMax", "Max uses must be a positive number"));
|
|
47
49
|
return;
|
|
48
50
|
}
|
|
49
51
|
try {
|
|
50
|
-
let e = await
|
|
51
|
-
vibeDeckId:
|
|
52
|
-
permission:
|
|
53
|
-
expiresInHours:
|
|
54
|
-
password:
|
|
52
|
+
let e = await I({ variables: { input: {
|
|
53
|
+
vibeDeckId: w,
|
|
54
|
+
permission: z ? a.Execute : a.View,
|
|
55
|
+
expiresInHours: V > 0 ? V : null,
|
|
56
|
+
password: U.trim() ? U.trim() : null,
|
|
55
57
|
maxUses: t
|
|
56
58
|
} } });
|
|
57
|
-
await
|
|
59
|
+
await F();
|
|
58
60
|
let n = e.data?.createVibeDeckShareLink.shareUrl;
|
|
59
|
-
n && await
|
|
61
|
+
n && await Z(n, e.data?.createVibeDeckShareLink.id ?? "new"), s.success(k("deckLink.created", "Public link created"), z ? k("deckLink.createdExec", "Anyone with the link can run the deck tiles") : k("deckLink.createdView", "Anyone with the link can view the deck")), W(""), K("");
|
|
60
62
|
} catch (e) {
|
|
61
|
-
|
|
63
|
+
s.error(k("deckLink.createFailed", "Could not create link"), e.message);
|
|
62
64
|
}
|
|
63
|
-
},
|
|
65
|
+
}, $ = async (e) => {
|
|
64
66
|
try {
|
|
65
|
-
await
|
|
67
|
+
await R({ variables: { id: e } }), await F(), s.success(k("deckLink.revoked", "Link revoked — it no longer works"));
|
|
66
68
|
} catch (e) {
|
|
67
|
-
|
|
69
|
+
s.error(k("deckLink.revokeFailed", "Could not revoke"), e.message);
|
|
68
70
|
}
|
|
69
71
|
};
|
|
70
|
-
return /* @__PURE__ */
|
|
72
|
+
return /* @__PURE__ */ x("div", {
|
|
71
73
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay p-4",
|
|
72
|
-
onClick:
|
|
73
|
-
children: /* @__PURE__ */
|
|
74
|
+
onClick: D,
|
|
75
|
+
children: /* @__PURE__ */ S("div", {
|
|
74
76
|
className: "w-full max-w-lg rounded-card border border-border-subtle bg-bg-elevated p-6 shadow-[var(--shadow-elevation-4)]",
|
|
75
77
|
onClick: (e) => e.stopPropagation(),
|
|
76
78
|
children: [
|
|
77
|
-
/* @__PURE__ */
|
|
79
|
+
/* @__PURE__ */ S("div", {
|
|
78
80
|
className: "mb-4 flex items-start justify-between",
|
|
79
|
-
children: [/* @__PURE__ */
|
|
81
|
+
children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ S("h3", {
|
|
80
82
|
className: "flex items-center gap-2 text-lg font-semibold text-text-primary",
|
|
81
|
-
children: [/* @__PURE__ */
|
|
82
|
-
}), /* @__PURE__ */
|
|
83
|
+
children: [/* @__PURE__ */ x(p, { className: "size-5" }), k("deckLink.title", "Public links")]
|
|
84
|
+
}), /* @__PURE__ */ x("p", {
|
|
83
85
|
className: "mt-0.5 text-sm text-text-secondary",
|
|
84
|
-
children:
|
|
85
|
-
})] }), /* @__PURE__ */
|
|
86
|
+
children: E
|
|
87
|
+
})] }), /* @__PURE__ */ x("button", {
|
|
86
88
|
type: "button",
|
|
87
|
-
onClick:
|
|
89
|
+
onClick: D,
|
|
88
90
|
className: "p-1 text-text-tertiary hover:text-text-primary",
|
|
89
|
-
"aria-label":
|
|
90
|
-
children: /* @__PURE__ */
|
|
91
|
+
"aria-label": k("common.close", "Close"),
|
|
92
|
+
children: /* @__PURE__ */ x(v, { className: "size-5" })
|
|
91
93
|
})]
|
|
92
94
|
}),
|
|
93
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ x("div", {
|
|
94
96
|
className: "mb-4 space-y-2",
|
|
95
|
-
children:
|
|
96
|
-
message:
|
|
97
|
-
onRetry: () => void
|
|
98
|
-
}) :
|
|
97
|
+
children: N && Y.length === 0 ? /* @__PURE__ */ x(r, {}) : P ? /* @__PURE__ */ x(i, {
|
|
98
|
+
message: P.message,
|
|
99
|
+
onRetry: () => void F()
|
|
100
|
+
}) : X.length === 0 ? /* @__PURE__ */ x("p", {
|
|
99
101
|
className: "text-sm text-text-secondary",
|
|
100
|
-
children:
|
|
101
|
-
}) : /* @__PURE__ */
|
|
102
|
+
children: k("deckLink.none", "No public links yet. Create one below.")
|
|
103
|
+
}) : /* @__PURE__ */ x("ul", {
|
|
102
104
|
className: "space-y-2",
|
|
103
|
-
children:
|
|
105
|
+
children: X.map((e) => /* @__PURE__ */ S("li", {
|
|
104
106
|
className: "flex items-center gap-2 rounded-md border border-border-default px-3 py-2",
|
|
105
107
|
children: [
|
|
106
|
-
/* @__PURE__ */
|
|
107
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ x(T, { children: e.permission === a.Execute ? /* @__PURE__ */ S(b, { children: [
|
|
109
|
+
/* @__PURE__ */ x(h, { className: "size-3" }),
|
|
108
110
|
" ",
|
|
109
|
-
|
|
110
|
-
] }) : /* @__PURE__ */ b
|
|
111
|
-
/* @__PURE__ */
|
|
111
|
+
k("deckLink.execute", "Execute")
|
|
112
|
+
] }) : /* @__PURE__ */ S(b, { children: [
|
|
113
|
+
/* @__PURE__ */ x(f, { className: "size-3" }),
|
|
112
114
|
" ",
|
|
113
|
-
|
|
115
|
+
k("deckLink.view", "View")
|
|
114
116
|
] }) }),
|
|
115
|
-
e.hasPassword && /* @__PURE__ */
|
|
117
|
+
e.hasPassword && /* @__PURE__ */ x(m, {
|
|
116
118
|
className: "size-3.5 text-text-tertiary",
|
|
117
119
|
"aria-label": "password"
|
|
118
120
|
}),
|
|
119
|
-
/* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ x("span", {
|
|
120
122
|
className: "flex-1 truncate font-mono text-xs text-text-secondary",
|
|
121
123
|
children: e.shareUrl
|
|
122
124
|
}),
|
|
123
|
-
/* @__PURE__ */
|
|
125
|
+
/* @__PURE__ */ S("span", {
|
|
124
126
|
className: "shrink-0 text-xs text-text-tertiary",
|
|
125
127
|
children: [
|
|
126
128
|
e.useCount,
|
|
127
129
|
e.maxUses ? `/${e.maxUses}` : "",
|
|
128
130
|
" ",
|
|
129
|
-
|
|
131
|
+
k("deckLink.uses", "uses")
|
|
130
132
|
]
|
|
131
133
|
}),
|
|
132
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ x("button", {
|
|
133
135
|
type: "button",
|
|
134
|
-
onClick: () => void
|
|
136
|
+
onClick: () => void Z(e.shareUrl, e.id),
|
|
135
137
|
className: "p-1 text-text-secondary hover:text-text-primary",
|
|
136
|
-
"aria-label":
|
|
137
|
-
children:
|
|
138
|
+
"aria-label": k("common.copy", "Copy"),
|
|
139
|
+
children: q === e.id ? /* @__PURE__ */ x(u, { className: "size-4 text-status-success-text" }) : /* @__PURE__ */ x(d, { className: "size-4" })
|
|
138
140
|
}),
|
|
139
|
-
/* @__PURE__ */
|
|
141
|
+
j && /* @__PURE__ */ x("button", {
|
|
140
142
|
type: "button",
|
|
141
|
-
onClick: () => void
|
|
143
|
+
onClick: () => void $(e.id),
|
|
142
144
|
className: "p-1 text-text-secondary hover:text-action-danger-text",
|
|
143
|
-
"aria-label":
|
|
144
|
-
children: /* @__PURE__ */
|
|
145
|
+
"aria-label": k("common.revoke", "Revoke"),
|
|
146
|
+
children: /* @__PURE__ */ x(_, { className: "size-4" })
|
|
145
147
|
})
|
|
146
148
|
]
|
|
147
149
|
}, e.id))
|
|
148
150
|
})
|
|
149
151
|
}),
|
|
150
|
-
/* @__PURE__ */
|
|
151
|
-
onSubmit: (e) => void
|
|
152
|
+
j && /* @__PURE__ */ S("form", {
|
|
153
|
+
onSubmit: (e) => void Q(e),
|
|
152
154
|
className: "space-y-3 border-t border-border-default pt-4",
|
|
153
155
|
children: [
|
|
154
|
-
/* @__PURE__ */
|
|
156
|
+
/* @__PURE__ */ S("label", {
|
|
155
157
|
className: "flex cursor-pointer items-center gap-2 text-sm text-text-primary",
|
|
156
158
|
children: [
|
|
157
|
-
/* @__PURE__ */
|
|
159
|
+
/* @__PURE__ */ x("input", {
|
|
158
160
|
type: "checkbox",
|
|
159
|
-
checked:
|
|
160
|
-
onChange: (e) =>
|
|
161
|
+
checked: z,
|
|
162
|
+
onChange: (e) => B(e.target.checked),
|
|
161
163
|
className: "rounded border-border-default"
|
|
162
164
|
}),
|
|
163
|
-
/* @__PURE__ */
|
|
164
|
-
|
|
165
|
+
/* @__PURE__ */ x(h, { className: "size-3.5" }),
|
|
166
|
+
k("deckLink.allowExecution", "Allow execution (anyone with the link can run tiles)")
|
|
165
167
|
]
|
|
166
168
|
}),
|
|
167
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ S("div", {
|
|
168
170
|
className: "flex flex-col gap-3 sm:flex-row",
|
|
169
|
-
children: [/* @__PURE__ */
|
|
171
|
+
children: [/* @__PURE__ */ S("label", {
|
|
170
172
|
className: "flex-1",
|
|
171
|
-
children: [/* @__PURE__ */
|
|
173
|
+
children: [/* @__PURE__ */ x("span", {
|
|
172
174
|
className: "text-sm text-text-secondary",
|
|
173
|
-
children:
|
|
174
|
-
}), /* @__PURE__ */
|
|
175
|
-
value:
|
|
176
|
-
onChange: (e) =>
|
|
175
|
+
children: k("deckLink.expiry", "Expires")
|
|
176
|
+
}), /* @__PURE__ */ x("select", {
|
|
177
|
+
value: V,
|
|
178
|
+
onChange: (e) => H(Number(e.target.value)),
|
|
177
179
|
className: "mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary",
|
|
178
|
-
children:
|
|
180
|
+
children: C.map((e) => /* @__PURE__ */ x("option", {
|
|
179
181
|
value: e.hours,
|
|
180
182
|
children: e.label
|
|
181
183
|
}, e.label))
|
|
182
184
|
})]
|
|
183
|
-
}), /* @__PURE__ */
|
|
185
|
+
}), /* @__PURE__ */ S("label", {
|
|
184
186
|
className: "flex-1",
|
|
185
|
-
children: [/* @__PURE__ */
|
|
187
|
+
children: [/* @__PURE__ */ x("span", {
|
|
186
188
|
className: "text-sm text-text-secondary",
|
|
187
|
-
children:
|
|
188
|
-
}), /* @__PURE__ */
|
|
189
|
-
value:
|
|
190
|
-
onChange: (e) =>
|
|
189
|
+
children: k("deckLink.maxUses", "Max uses (optional)")
|
|
190
|
+
}), /* @__PURE__ */ x("input", {
|
|
191
|
+
value: G,
|
|
192
|
+
onChange: (e) => K(e.target.value),
|
|
191
193
|
inputMode: "numeric",
|
|
192
|
-
placeholder:
|
|
194
|
+
placeholder: k("deckLink.unlimited", "unlimited"),
|
|
193
195
|
className: "mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
194
196
|
})]
|
|
195
197
|
})]
|
|
196
198
|
}),
|
|
197
|
-
/* @__PURE__ */
|
|
199
|
+
/* @__PURE__ */ S("label", {
|
|
198
200
|
className: "block",
|
|
199
|
-
children: [/* @__PURE__ */
|
|
201
|
+
children: [/* @__PURE__ */ x("span", {
|
|
200
202
|
className: "text-sm text-text-secondary",
|
|
201
|
-
children:
|
|
202
|
-
}), /* @__PURE__ */
|
|
203
|
-
value:
|
|
204
|
-
onChange: (e) =>
|
|
203
|
+
children: k("deckLink.password", "Password (optional)")
|
|
204
|
+
}), /* @__PURE__ */ x("input", {
|
|
205
|
+
value: U,
|
|
206
|
+
onChange: (e) => W(e.target.value),
|
|
205
207
|
type: "password",
|
|
206
208
|
autoComplete: "new-password",
|
|
207
|
-
placeholder:
|
|
209
|
+
placeholder: k("deckLink.noPassword", "no password"),
|
|
208
210
|
className: "mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
209
211
|
})]
|
|
210
212
|
}),
|
|
211
|
-
/* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ x("div", {
|
|
212
214
|
className: "flex justify-end",
|
|
213
|
-
children: /* @__PURE__ */
|
|
215
|
+
children: /* @__PURE__ */ S("button", {
|
|
214
216
|
type: "submit",
|
|
215
|
-
disabled:
|
|
217
|
+
disabled: L,
|
|
216
218
|
className: "flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:opacity-90 disabled:opacity-50",
|
|
217
|
-
children: [/* @__PURE__ */
|
|
219
|
+
children: [/* @__PURE__ */ x(g, { className: "size-4" }), k("deckLink.create", "Create public link")]
|
|
218
220
|
})
|
|
219
221
|
})
|
|
220
222
|
]
|
|
@@ -222,11 +224,11 @@ var x = [
|
|
|
222
224
|
]
|
|
223
225
|
})
|
|
224
226
|
});
|
|
225
|
-
},
|
|
227
|
+
}, T = ({ children: e }) => /* @__PURE__ */ x("span", {
|
|
226
228
|
className: "inline-flex shrink-0 items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-primary",
|
|
227
229
|
children: e
|
|
228
230
|
});
|
|
229
231
|
//#endregion
|
|
230
|
-
export {
|
|
232
|
+
export { w as VibeDeckPublicLinkPanel };
|
|
231
233
|
|
|
232
234
|
//# sourceMappingURL=VibeDeckPublicLinkPanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeDeckPublicLinkPanel.js","names":[],"sources":["../../../src/components/vibedeck/VibeDeckPublicLinkPanel.tsx"],"sourcesContent":["import type { FC, FormEvent, ReactNode } from 'react';\nimport { useState } from 'react';\nimport { Link2, Play, Eye, Lock, Copy, Trash2, Plus, X, Check } from 'lucide-react';\n\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { notify } from '@/lib/toast';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useListVibeDeckShareLinksQuery,\n useCreateVibeDeckShareLinkMutation,\n useRevokeVibeDeckShareLinkMutation,\n type VibeDeckShareLinkFieldsFragment,\n} from '@/generated/wspace-operations';\nimport { VibeDeckSharePermission } from '@/generated/wspace-types';\n\ninterface VibeDeckPublicLinkPanelProps {\n vibeDeckId: string;\n vibeDeckName: string;\n onClose: () => void;\n}\n\nconst EXPIRY_OPTIONS = [\n { label: '4 hours', hours: 4 },\n { label: '24 hours', hours: 24 },\n { label: '7 days', hours: 168 },\n { label: 'No expiry', hours: 0 },\n];\n\n/**\n * VibeDeckPublicLinkPanel — create and manage public links to a VibeDeck with explicit\n * **execution** control. Unlike the generic member-share dialog, this exposes the deck story's\n * promised controls: \"Public access + Allow execution + optional password + expiry + usage limit\".\n * Links carry a `VibeDeckSharePermission` (VIEW or EXECUTE); the URL points at the live\n * `deck/:token` page (not an auth'd accept page). Backed by the existing `createVibeDeckShareLink`\n * mutation — no backend change.\n */\nexport const VibeDeckPublicLinkPanel: FC<VibeDeckPublicLinkPanelProps> = ({\n vibeDeckId,\n vibeDeckName,\n onClose,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const { data, loading, error, refetch } = useListVibeDeckShareLinksQuery({\n variables: { vibeDeckId },\n fetchPolicy: 'cache-and-network',\n });\n const [createLink, { loading: creating }] = useCreateVibeDeckShareLinkMutation();\n const [revokeLink] = useRevokeVibeDeckShareLinkMutation();\n\n const [allowExecution, setAllowExecution] = useState(true);\n const [expiryHours, setExpiryHours] = useState(4);\n const [password, setPassword] = useState('');\n const [maxUses, setMaxUses] = useState('');\n const [copiedId, setCopiedId] = useState<string | null>(null);\n\n const links: VibeDeckShareLinkFieldsFragment[] = data?.vibeDeckShareLinks ?? [];\n const activeLinks = links.filter((l) => l.status === 'ACTIVE');\n\n const copy = async (url: string, id: string): Promise<void> => {\n try {\n await navigator.clipboard.writeText(url);\n setCopiedId(id);\n notify.success(tr('deckLink.copied', 'Link copied'));\n window.setTimeout(() => setCopiedId(null), 1500);\n } catch {\n notify.error(tr('deckLink.copyFailed', 'Could not copy — copy it manually'), url);\n }\n };\n\n const handleCreate = async (e: FormEvent): Promise<void> => {\n e.preventDefault();\n const parsedMax = maxUses.trim() ? Number.parseInt(maxUses.trim(), 10) : null;\n if (parsedMax !== null && (!Number.isFinite(parsedMax) || parsedMax < 1)) {\n notify.error(tr('deckLink.badMax', 'Max uses must be a positive number'));\n return;\n }\n try {\n const res = await createLink({\n variables: {\n input: {\n vibeDeckId,\n permission: allowExecution\n ? VibeDeckSharePermission.Execute\n : VibeDeckSharePermission.View,\n expiresInHours: expiryHours > 0 ? expiryHours : null,\n password: password.trim() ? password.trim() : null,\n maxUses: parsedMax,\n },\n },\n });\n await refetch();\n const url = res.data?.createVibeDeckShareLink.shareUrl;\n if (url) await copy(url, res.data?.createVibeDeckShareLink.id ?? 'new');\n notify.success(\n tr('deckLink.created', 'Public link created'),\n allowExecution\n ? tr('deckLink.createdExec', 'Anyone with the link can run the deck tiles')\n : tr('deckLink.createdView', 'Anyone with the link can view the deck')\n );\n setPassword('');\n setMaxUses('');\n } catch (err) {\n notify.error(tr('deckLink.createFailed', 'Could not create link'), (err as Error).message);\n }\n };\n\n const handleRevoke = async (id: string): Promise<void> => {\n try {\n await revokeLink({ variables: { id } });\n await refetch();\n notify.success(tr('deckLink.revoked', 'Link revoked — it no longer works'));\n } catch (err) {\n notify.error(tr('deckLink.revokeFailed', 'Could not revoke'), (err as Error).message);\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay p-4\"\n onClick={onClose}\n >\n <div\n className=\"w-full max-w-lg rounded-card border border-border-subtle bg-bg-elevated p-6 shadow-[var(--shadow-elevation-4)]\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"mb-4 flex items-start justify-between\">\n <div>\n <h3 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Link2 className=\"size-5\" />\n {tr('deckLink.title', 'Public links')}\n </h3>\n <p className=\"mt-0.5 text-sm text-text-secondary\">{vibeDeckName}</p>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-tertiary hover:text-text-primary\"\n aria-label={tr('common.close', 'Close')}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Existing links */}\n <div className=\"mb-4 space-y-2\">\n {loading && links.length === 0 ? (\n <LoadingState />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => void refetch()} />\n ) : activeLinks.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('deckLink.none', 'No public links yet. Create one below.')}\n </p>\n ) : (\n <ul className=\"space-y-2\">\n {activeLinks.map((l) => (\n <li\n key={l.id}\n className=\"flex items-center gap-2 rounded-md border border-border-default px-3 py-2\"\n >\n <Badge>\n {l.permission === VibeDeckSharePermission.Execute ? (\n <>\n <Play className=\"size-3\" /> {tr('deckLink.execute', 'Execute')}\n </>\n ) : (\n <>\n <Eye className=\"size-3\" /> {tr('deckLink.view', 'View')}\n </>\n )}\n </Badge>\n {l.hasPassword && (\n <Lock className=\"size-3.5 text-text-tertiary\" aria-label=\"password\" />\n )}\n <span className=\"flex-1 truncate font-mono text-xs text-text-secondary\">\n {l.shareUrl}\n </span>\n <span className=\"shrink-0 text-xs text-text-tertiary\">\n {l.useCount}\n {l.maxUses ? `/${l.maxUses}` : ''} {tr('deckLink.uses', 'uses')}\n </span>\n <button\n type=\"button\"\n onClick={() => void copy(l.shareUrl, l.id)}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n aria-label={tr('common.copy', 'Copy')}\n >\n {copiedId === l.id ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : (\n <Copy className=\"size-4\" />\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleRevoke(l.id)}\n className=\"p-1 text-text-secondary hover:text-action-danger-text\"\n aria-label={tr('common.revoke', 'Revoke')}\n >\n <Trash2 className=\"size-4\" />\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n\n {/* Create form */}\n <form\n onSubmit={(e) => void handleCreate(e)}\n className=\"space-y-3 border-t border-border-default pt-4\"\n >\n <label className=\"flex cursor-pointer items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allowExecution}\n onChange={(e) => setAllowExecution(e.target.checked)}\n className=\"rounded border-border-default\"\n />\n <Play className=\"size-3.5\" />\n {tr('deckLink.allowExecution', 'Allow execution (anyone with the link can run tiles)')}\n </label>\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <label className=\"flex-1\">\n <span className=\"text-sm text-text-secondary\">\n {tr('deckLink.expiry', 'Expires')}\n </span>\n <select\n value={expiryHours}\n onChange={(e) => setExpiryHours(Number(e.target.value))}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {EXPIRY_OPTIONS.map((o) => (\n <option key={o.label} value={o.hours}>\n {o.label}\n </option>\n ))}\n </select>\n </label>\n <label className=\"flex-1\">\n <span className=\"text-sm text-text-secondary\">\n {tr('deckLink.maxUses', 'Max uses (optional)')}\n </span>\n <input\n value={maxUses}\n onChange={(e) => setMaxUses(e.target.value)}\n inputMode=\"numeric\"\n placeholder={tr('deckLink.unlimited', 'unlimited')}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </label>\n </div>\n <label className=\"block\">\n <span className=\"text-sm text-text-secondary\">\n {tr('deckLink.password', 'Password (optional)')}\n </span>\n <input\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={tr('deckLink.noPassword', 'no password')}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </label>\n <div className=\"flex justify-end\">\n <button\n type=\"submit\"\n disabled={creating}\n className=\"flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n <Plus className=\"size-4\" />\n {tr('deckLink.create', 'Create public link')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\nconst Badge: FC<{ children: ReactNode }> = ({ children }) => (\n <span className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-primary\">\n {children}\n </span>\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VibeDeckPublicLinkPanel.js","names":[],"sources":["../../../src/components/vibedeck/VibeDeckPublicLinkPanel.tsx"],"sourcesContent":["import type { FC, FormEvent, ReactNode } from 'react';\nimport { useState } from 'react';\nimport { Link2, Play, Eye, Lock, Copy, Trash2, Plus, X, Check } from 'lucide-react';\n\nimport { LoadingState, ErrorState } from '@/components/shared';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { notify } from '@/lib/toast';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n useListVibeDeckShareLinksQuery,\n useCreateVibeDeckShareLinkMutation,\n useRevokeVibeDeckShareLinkMutation,\n type VibeDeckShareLinkFieldsFragment,\n} from '@/generated/wspace-operations';\nimport { VibeDeckSharePermission } from '@/generated/wspace-types';\n\ninterface VibeDeckPublicLinkPanelProps {\n vibeDeckId: string;\n vibeDeckName: string;\n onClose: () => void;\n}\n\nconst EXPIRY_OPTIONS = [\n { label: '4 hours', hours: 4 },\n { label: '24 hours', hours: 24 },\n { label: '7 days', hours: 168 },\n { label: 'No expiry', hours: 0 },\n];\n\n/**\n * VibeDeckPublicLinkPanel — create and manage public links to a VibeDeck with explicit\n * **execution** control. Unlike the generic member-share dialog, this exposes the deck story's\n * promised controls: \"Public access + Allow execution + optional password + expiry + usage limit\".\n * Links carry a `VibeDeckSharePermission` (VIEW or EXECUTE); the URL points at the live\n * `deck/:token` page (not an auth'd accept page). Backed by the existing `createVibeDeckShareLink`\n * mutation — no backend change.\n */\nexport const VibeDeckPublicLinkPanel: FC<VibeDeckPublicLinkPanelProps> = ({\n vibeDeckId,\n vibeDeckName,\n onClose,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const { hasPermission } = usePermissions();\n const canManageLinks = hasPermission(VIBE_PERMISSIONS.VIBEDECK_UPDATE);\n\n const { data, loading, error, refetch } = useListVibeDeckShareLinksQuery({\n variables: { vibeDeckId },\n fetchPolicy: 'cache-and-network',\n });\n const [createLink, { loading: creating }] = useCreateVibeDeckShareLinkMutation();\n const [revokeLink] = useRevokeVibeDeckShareLinkMutation();\n\n const [allowExecution, setAllowExecution] = useState(true);\n const [expiryHours, setExpiryHours] = useState(4);\n const [password, setPassword] = useState('');\n const [maxUses, setMaxUses] = useState('');\n const [copiedId, setCopiedId] = useState<string | null>(null);\n\n const links: VibeDeckShareLinkFieldsFragment[] = data?.vibeDeckShareLinks ?? [];\n const activeLinks = links.filter((l) => l.status === 'ACTIVE');\n\n const copy = async (url: string, id: string): Promise<void> => {\n try {\n await navigator.clipboard.writeText(url);\n setCopiedId(id);\n notify.success(tr('deckLink.copied', 'Link copied'));\n window.setTimeout(() => setCopiedId(null), 1500);\n } catch {\n notify.error(tr('deckLink.copyFailed', 'Could not copy — copy it manually'), url);\n }\n };\n\n const handleCreate = async (e: FormEvent): Promise<void> => {\n e.preventDefault();\n const parsedMax = maxUses.trim() ? Number.parseInt(maxUses.trim(), 10) : null;\n if (parsedMax !== null && (!Number.isFinite(parsedMax) || parsedMax < 1)) {\n notify.error(tr('deckLink.badMax', 'Max uses must be a positive number'));\n return;\n }\n try {\n const res = await createLink({\n variables: {\n input: {\n vibeDeckId,\n permission: allowExecution\n ? VibeDeckSharePermission.Execute\n : VibeDeckSharePermission.View,\n expiresInHours: expiryHours > 0 ? expiryHours : null,\n password: password.trim() ? password.trim() : null,\n maxUses: parsedMax,\n },\n },\n });\n await refetch();\n const url = res.data?.createVibeDeckShareLink.shareUrl;\n if (url) await copy(url, res.data?.createVibeDeckShareLink.id ?? 'new');\n notify.success(\n tr('deckLink.created', 'Public link created'),\n allowExecution\n ? tr('deckLink.createdExec', 'Anyone with the link can run the deck tiles')\n : tr('deckLink.createdView', 'Anyone with the link can view the deck')\n );\n setPassword('');\n setMaxUses('');\n } catch (err) {\n notify.error(tr('deckLink.createFailed', 'Could not create link'), (err as Error).message);\n }\n };\n\n const handleRevoke = async (id: string): Promise<void> => {\n try {\n await revokeLink({ variables: { id } });\n await refetch();\n notify.success(tr('deckLink.revoked', 'Link revoked — it no longer works'));\n } catch (err) {\n notify.error(tr('deckLink.revokeFailed', 'Could not revoke'), (err as Error).message);\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay p-4\"\n onClick={onClose}\n >\n <div\n className=\"w-full max-w-lg rounded-card border border-border-subtle bg-bg-elevated p-6 shadow-[var(--shadow-elevation-4)]\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"mb-4 flex items-start justify-between\">\n <div>\n <h3 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Link2 className=\"size-5\" />\n {tr('deckLink.title', 'Public links')}\n </h3>\n <p className=\"mt-0.5 text-sm text-text-secondary\">{vibeDeckName}</p>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1 text-text-tertiary hover:text-text-primary\"\n aria-label={tr('common.close', 'Close')}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Existing links */}\n <div className=\"mb-4 space-y-2\">\n {loading && links.length === 0 ? (\n <LoadingState />\n ) : error ? (\n <ErrorState message={error.message} onRetry={() => void refetch()} />\n ) : activeLinks.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('deckLink.none', 'No public links yet. Create one below.')}\n </p>\n ) : (\n <ul className=\"space-y-2\">\n {activeLinks.map((l) => (\n <li\n key={l.id}\n className=\"flex items-center gap-2 rounded-md border border-border-default px-3 py-2\"\n >\n <Badge>\n {l.permission === VibeDeckSharePermission.Execute ? (\n <>\n <Play className=\"size-3\" /> {tr('deckLink.execute', 'Execute')}\n </>\n ) : (\n <>\n <Eye className=\"size-3\" /> {tr('deckLink.view', 'View')}\n </>\n )}\n </Badge>\n {l.hasPassword && (\n <Lock className=\"size-3.5 text-text-tertiary\" aria-label=\"password\" />\n )}\n <span className=\"flex-1 truncate font-mono text-xs text-text-secondary\">\n {l.shareUrl}\n </span>\n <span className=\"shrink-0 text-xs text-text-tertiary\">\n {l.useCount}\n {l.maxUses ? `/${l.maxUses}` : ''} {tr('deckLink.uses', 'uses')}\n </span>\n <button\n type=\"button\"\n onClick={() => void copy(l.shareUrl, l.id)}\n className=\"p-1 text-text-secondary hover:text-text-primary\"\n aria-label={tr('common.copy', 'Copy')}\n >\n {copiedId === l.id ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : (\n <Copy className=\"size-4\" />\n )}\n </button>\n {canManageLinks && (\n <button\n type=\"button\"\n onClick={() => void handleRevoke(l.id)}\n className=\"p-1 text-text-secondary hover:text-action-danger-text\"\n aria-label={tr('common.revoke', 'Revoke')}\n >\n <Trash2 className=\"size-4\" />\n </button>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n\n {/* Create form */}\n {canManageLinks && (\n <form\n onSubmit={(e) => void handleCreate(e)}\n className=\"space-y-3 border-t border-border-default pt-4\"\n >\n <label className=\"flex cursor-pointer items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allowExecution}\n onChange={(e) => setAllowExecution(e.target.checked)}\n className=\"rounded border-border-default\"\n />\n <Play className=\"size-3.5\" />\n {tr(\n 'deckLink.allowExecution',\n 'Allow execution (anyone with the link can run tiles)'\n )}\n </label>\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <label className=\"flex-1\">\n <span className=\"text-sm text-text-secondary\">\n {tr('deckLink.expiry', 'Expires')}\n </span>\n <select\n value={expiryHours}\n onChange={(e) => setExpiryHours(Number(e.target.value))}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n {EXPIRY_OPTIONS.map((o) => (\n <option key={o.label} value={o.hours}>\n {o.label}\n </option>\n ))}\n </select>\n </label>\n <label className=\"flex-1\">\n <span className=\"text-sm text-text-secondary\">\n {tr('deckLink.maxUses', 'Max uses (optional)')}\n </span>\n <input\n value={maxUses}\n onChange={(e) => setMaxUses(e.target.value)}\n inputMode=\"numeric\"\n placeholder={tr('deckLink.unlimited', 'unlimited')}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </label>\n </div>\n <label className=\"block\">\n <span className=\"text-sm text-text-secondary\">\n {tr('deckLink.password', 'Password (optional)')}\n </span>\n <input\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n type=\"password\"\n autoComplete=\"new-password\"\n placeholder={tr('deckLink.noPassword', 'no password')}\n className=\"mt-1 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </label>\n <div className=\"flex justify-end\">\n <button\n type=\"submit\"\n disabled={creating}\n className=\"flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n <Plus className=\"size-4\" />\n {tr('deckLink.create', 'Create public link')}\n </button>\n </div>\n </form>\n )}\n </div>\n </div>\n );\n};\n\nconst Badge: FC<{ children: ReactNode }> = ({ children }) => (\n <span className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-primary\">\n {children}\n </span>\n);\n"],"mappings":";;;;;;;;;;;;;AAuBA,IAAM,IAAiB;CACrB;EAAE,OAAO;EAAW,OAAO;EAAG;CAC9B;EAAE,OAAO;EAAY,OAAO;EAAI;CAChC;EAAE,OAAO;EAAU,OAAO;EAAK;CAC/B;EAAE,OAAO;EAAa,OAAO;EAAG;CACjC,EAUY,KAA6D,EACxE,eACA,iBACA,iBACI;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAI,EAAE,EAAI;AAChB,SAAO,MAAM,IAAM,IAAW;IAE1B,EAAE,qBAAkB,GAAgB,EACpC,IAAiB,EAAc,EAAiB,gBAAgB,EAEhE,EAAE,SAAM,YAAS,UAAO,eAAY,EAA+B;EACvE,WAAW,EAAE,eAAY;EACzB,aAAa;EACd,CAAC,EACI,CAAC,GAAY,EAAE,SAAS,OAAc,GAAoC,EAC1E,CAAC,KAAc,GAAoC,EAEnD,CAAC,GAAgB,KAAqB,EAAS,GAAK,EACpD,CAAC,GAAa,KAAkB,EAAS,EAAE,EAC3C,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAU,KAAe,EAAwB,KAAK,EAEvD,IAA2C,GAAM,sBAAsB,EAAE,EACzE,IAAc,EAAM,QAAQ,MAAM,EAAE,WAAW,SAAS,EAExD,IAAO,OAAO,GAAa,MAA8B;AAC7D,MAAI;AAIF,GAHA,MAAM,UAAU,UAAU,UAAU,EAAI,EACxC,EAAY,EAAG,EACf,EAAO,QAAQ,EAAG,mBAAmB,cAAc,CAAC,EACpD,OAAO,iBAAiB,EAAY,KAAK,EAAE,KAAK;UAC1C;AACN,KAAO,MAAM,EAAG,uBAAuB,oCAAoC,EAAE,EAAI;;IAI/E,IAAe,OAAO,MAAgC;AAC1D,IAAE,gBAAgB;EAClB,IAAM,IAAY,EAAQ,MAAM,GAAG,OAAO,SAAS,EAAQ,MAAM,EAAE,GAAG,GAAG;AACzE,MAAI,MAAc,SAAS,CAAC,OAAO,SAAS,EAAU,IAAI,IAAY,IAAI;AACxE,KAAO,MAAM,EAAG,mBAAmB,qCAAqC,CAAC;AACzE;;AAEF,MAAI;GACF,IAAM,IAAM,MAAM,EAAW,EAC3B,WAAW,EACT,OAAO;IACL;IACA,YAAY,IACR,EAAwB,UACxB,EAAwB;IAC5B,gBAAgB,IAAc,IAAI,IAAc;IAChD,UAAU,EAAS,MAAM,GAAG,EAAS,MAAM,GAAG;IAC9C,SAAS;IACV,EACF,EACF,CAAC;AACF,SAAM,GAAS;GACf,IAAM,IAAM,EAAI,MAAM,wBAAwB;AAS9C,GARI,KAAK,MAAM,EAAK,GAAK,EAAI,MAAM,wBAAwB,MAAM,MAAM,EACvE,EAAO,QACL,EAAG,oBAAoB,sBAAsB,EAC7C,IACI,EAAG,wBAAwB,8CAA8C,GACzE,EAAG,wBAAwB,yCAAyC,CACzE,EACD,EAAY,GAAG,EACf,EAAW,GAAG;WACP,GAAK;AACZ,KAAO,MAAM,EAAG,yBAAyB,wBAAwB,EAAG,EAAc,QAAQ;;IAIxF,IAAe,OAAO,MAA8B;AACxD,MAAI;AAGF,GAFA,MAAM,EAAW,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC,EACvC,MAAM,GAAS,EACf,EAAO,QAAQ,EAAG,oBAAoB,oCAAoC,CAAC;WACpE,GAAK;AACZ,KAAO,MAAM,EAAG,yBAAyB,mBAAmB,EAAG,EAAc,QAAQ;;;AAIzF,QACE,kBAAC,OAAD;EACE,WAAU;EACV,SAAS;YAET,kBAAC,OAAD;GACE,WAAU;GACV,UAAU,MAAM,EAAE,iBAAiB;aAFrC;IAIE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAd,CACE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAC3B,EAAG,kBAAkB,eAAe,CAClC;SACL,kBAAC,KAAD;MAAG,WAAU;gBAAsC;MAAiB,CAAA,CAChE,EAAA,CAAA,EACN,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;MACV,cAAY,EAAG,gBAAgB,QAAQ;gBAEvC,kBAAC,GAAD,EAAG,WAAU,UAAW,CAAA;MACjB,CAAA,CACL;;IAGN,kBAAC,OAAD;KAAK,WAAU;eACZ,KAAW,EAAM,WAAW,IAC3B,kBAAC,GAAD,EAAgB,CAAA,GACd,IACF,kBAAC,GAAD;MAAY,SAAS,EAAM;MAAS,eAAe,KAAK,GAAS;MAAI,CAAA,GACnE,EAAY,WAAW,IACzB,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,iBAAiB,yCAAyC;MAC5D,CAAA,GAEJ,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAY,KAAK,MAChB,kBAAC,MAAD;OAEE,WAAU;iBAFZ;QAIE,kBAAC,GAAD,EAAA,UACG,EAAE,eAAe,EAAwB,UACxC,kBAAA,GAAA,EAAA,UAAA;SACE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;;SAAE,EAAG,oBAAoB,UAAU;SAC7D,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA;SACE,kBAAC,GAAD,EAAK,WAAU,UAAW,CAAA;;SAAE,EAAG,iBAAiB,OAAO;SACtD,EAAA,CAAA,EAEC,CAAA;QACP,EAAE,eACD,kBAAC,GAAD;SAAM,WAAU;SAA8B,cAAW;SAAa,CAAA;QAExE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAE;SACE,CAAA;QACP,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACG,EAAE;UACF,EAAE,UAAU,IAAI,EAAE,YAAY;UAAG;UAAE,EAAG,iBAAiB,OAAO;UAC1D;;QACP,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,KAAK,EAAK,EAAE,UAAU,EAAE,GAAG;SAC1C,WAAU;SACV,cAAY,EAAG,eAAe,OAAO;mBAEpC,MAAa,EAAE,KACd,kBAAC,GAAD,EAAO,WAAU,mCAAoC,CAAA,GAErD,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA;SAEtB,CAAA;QACR,KACC,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,KAAK,EAAa,EAAE,GAAG;SACtC,WAAU;SACV,cAAY,EAAG,iBAAiB,SAAS;mBAEzC,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;SACtB,CAAA;QAER;SA9CE,EAAE,GA8CJ,CACL;MACC,CAAA;KAEH,CAAA;IAGL,KACC,kBAAC,QAAD;KACE,WAAW,MAAM,KAAK,EAAa,EAAE;KACrC,WAAU;eAFZ;MAIE,kBAAC,SAAD;OAAO,WAAU;iBAAjB;QACE,kBAAC,SAAD;SACE,MAAK;SACL,SAAS;SACT,WAAW,MAAM,EAAkB,EAAE,OAAO,QAAQ;SACpD,WAAU;SACV,CAAA;QACF,kBAAC,GAAD,EAAM,WAAU,YAAa,CAAA;QAC5B,EACC,2BACA,uDACD;QACK;;MACR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,mBAAmB,UAAU;SAC5B,CAAA,EACP,kBAAC,UAAD;SACE,OAAO;SACP,WAAW,MAAM,EAAe,OAAO,EAAE,OAAO,MAAM,CAAC;SACvD,WAAU;mBAET,EAAe,KAAK,MACnB,kBAAC,UAAD;UAAsB,OAAO,EAAE;oBAC5B,EAAE;UACI,EAFI,EAAE,MAEN,CACT;SACK,CAAA,CACH;WACR,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,oBAAoB,sBAAsB;SACzC,CAAA,EACP,kBAAC,SAAD;SACE,OAAO;SACP,WAAW,MAAM,EAAW,EAAE,OAAO,MAAM;SAC3C,WAAU;SACV,aAAa,EAAG,sBAAsB,YAAY;SAClD,WAAU;SACV,CAAA,CACI;UACJ;;MACN,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAG,qBAAqB,sBAAsB;QAC1C,CAAA,EACP,kBAAC,SAAD;QACE,OAAO;QACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM;QAC5C,MAAK;QACL,cAAa;QACb,aAAa,EAAG,uBAAuB,cAAc;QACrD,WAAU;QACV,CAAA,CACI;;MACR,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,UAAD;QACE,MAAK;QACL,UAAU;QACV,WAAU;kBAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EAAG,mBAAmB,qBAAqB,CACrC;;OACL,CAAA;MACD;;IAEL;;EACF,CAAA;GAIJ,KAAsC,EAAE,kBAC5C,kBAAC,QAAD;CAAM,WAAU;CACb;CACI,CAAA"}
|
|
@@ -37,6 +37,7 @@ var e = {
|
|
|
37
37
|
WEBHOOK_CREATE: "webhook:create",
|
|
38
38
|
WEBHOOK_UPDATE: "webhook:update",
|
|
39
39
|
WEBHOOK_DELETE: "webhook:delete",
|
|
40
|
+
WEBHOOK_EXECUTE: "webhook:execute",
|
|
40
41
|
VIBEDECK_LIST: "vibedeck:list",
|
|
41
42
|
VIBEDECK_READ: "vibedeck:read",
|
|
42
43
|
VIBEDECK_CREATE: "vibedeck:create",
|
|
@@ -44,6 +45,12 @@ var e = {
|
|
|
44
45
|
VIBEDECK_DELETE: "vibedeck:delete",
|
|
45
46
|
VIBEDECK_EXECUTE: "vibedeck:execute",
|
|
46
47
|
VIBEDECK_SHARE: "vibedeck:share",
|
|
48
|
+
VIBEDECK_BUTTON_LIST: "vibedeck-button:list",
|
|
49
|
+
VIBEDECK_BUTTON_READ: "vibedeck-button:read",
|
|
50
|
+
VIBEDECK_BUTTON_CREATE: "vibedeck-button:create",
|
|
51
|
+
VIBEDECK_BUTTON_UPDATE: "vibedeck-button:update",
|
|
52
|
+
VIBEDECK_BUTTON_DELETE: "vibedeck-button:delete",
|
|
53
|
+
VIBEDECK_BUTTON_EXECUTE: "vibedeck-button:execute",
|
|
47
54
|
CALENDAR_AGENT_TASK_LIST: "calendar-agent-task:list",
|
|
48
55
|
CALENDAR_AGENT_TASK_READ: "calendar-agent-task:read",
|
|
49
56
|
CALENDAR_AGENT_TASK_CREATE: "calendar-agent-task:create",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","names":[],"sources":["../../src/constants/permissions.ts"],"sourcesContent":["/**\n * VibeControls Permission Constants\n *\n * All permission strings for VibeControls resources.\n * Use with fe-libs usePermissions hook: hasPermission(VIBE_PERMISSIONS.VIBE_CREATE)\n *\n * IMPORTANT — only strings present in the wspace-rbac-svc seed\n * (`prisma/seed-unified-rbac.ts` ACTION_DEFINITIONS) are ever returned by\n * `myPermissions`/`hasPermission`. Gating on a string the seed does NOT\n * define makes `hasPermission` return false for EVERYONE → a full lockout.\n *\n * Read/CRUD for catalog, component, template, ai-context, ai-prompt,\n * instruction-file and docs_site were seeded in the BOFF-2034 follow-up, so\n * their pages now gate on their own resource (no more `vibe:list` proxy).\n * The new resourceTypes added in that pass (scorecard, aitoolevent,\n * docs_page, docs_revision, docs_build_image) only reach ALREADY-EXISTING\n * workspaces after their cloned roles are re-synced — do NOT hard-gate a\n * page on those until the backfill has run, or pre-existing workspaces lock\n * out.\n *\n * Resource types:\n * - vibe: list, read, create, update, delete, scan, manage\n * - agent: list, read, create, update, delete, admin, mutate, invoke\n * - session: list, read, create, update, delete, execute\n * - note: list, read, create, update, delete\n * - webhook: list, read, create, update, delete\n * - action: list, read, create, update, delete, execute\n * - calendar-agent-task: list, read, create, update, delete, execute\n * - configuration: list, read, create, update, delete\n * - docs_site: list, read, create, update, delete, execute\n * - docs_page: list, read, create, update, delete\n * - target: list, read, create, update, delete\n * - tunnel: list, read, create, delete\n * - analytics: list, read\n * - log: list, read\n */\n\nexport const VIBE_PERMISSIONS = {\n // Vibe resources\n VIBE_LIST: 'vibe:list',\n VIBE_READ: 'vibe:read',\n VIBE_CREATE: 'vibe:create',\n VIBE_UPDATE: 'vibe:update',\n VIBE_DELETE: 'vibe:delete',\n VIBE_SCAN: 'vibe:scan',\n VIBE_MANAGE: 'vibe:manage',\n\n // Agent resources\n AGENT_LIST: 'agent:list',\n AGENT_READ: 'agent:read',\n AGENT_CREATE: 'agent:create',\n AGENT_UPDATE: 'agent:update',\n AGENT_DELETE: 'agent:delete',\n // Elevated agent control-plane actions (gateway enforces these on\n // remote-exec / credential / code-server control mutations).\n AGENT_ADMIN: 'agent:admin', // set/rotate API key, nuke, dry-run\n AGENT_MUTATE: 'agent:mutate', // code-server start/stop, plugin install\n AGENT_INVOKE: 'agent:invoke', // execute scheduled prompt, mint agent capabilities\n\n // Agent message bookmark resources\n AGENT_MESSAGE_BOOKMARK_READ: 'agent-message-bookmark:read',\n AGENT_MESSAGE_BOOKMARK_CREATE: 'agent-message-bookmark:create',\n AGENT_MESSAGE_BOOKMARK_UPDATE: 'agent-message-bookmark:update',\n AGENT_MESSAGE_BOOKMARK_DELETE: 'agent-message-bookmark:delete',\n\n // Integration connection resources\n INTEGRATION_CONNECTION_READ: 'integration-connection:read',\n\n // Session resources\n SESSION_LIST: 'session:list',\n SESSION_READ: 'session:read',\n SESSION_CREATE: 'session:create',\n SESSION_UPDATE: 'session:update',\n SESSION_DELETE: 'session:delete',\n SESSION_EXECUTE: 'session:execute',\n SESSION_SHARE: 'session:share',\n\n // Note resources\n NOTE_LIST: 'note:list',\n NOTE_READ: 'note:read',\n NOTE_CREATE: 'note:create',\n NOTE_UPDATE: 'note:update',\n NOTE_DELETE: 'note:delete',\n\n // Webhook resources\n WEBHOOK_LIST: 'webhook:list',\n WEBHOOK_READ: 'webhook:read',\n WEBHOOK_CREATE: 'webhook:create',\n WEBHOOK_UPDATE: 'webhook:update',\n WEBHOOK_DELETE: 'webhook:delete',\n\n // VibeDeck resources\n VIBEDECK_LIST: 'vibedeck:list',\n VIBEDECK_READ: 'vibedeck:read',\n VIBEDECK_CREATE: 'vibedeck:create',\n VIBEDECK_UPDATE: 'vibedeck:update',\n VIBEDECK_DELETE: 'vibedeck:delete',\n VIBEDECK_EXECUTE: 'vibedeck:execute',\n VIBEDECK_SHARE: 'vibedeck:share',\n\n // Calendar Agent Task resources\n CALENDAR_AGENT_TASK_LIST: 'calendar-agent-task:list',\n CALENDAR_AGENT_TASK_READ: 'calendar-agent-task:read',\n CALENDAR_AGENT_TASK_CREATE: 'calendar-agent-task:create',\n CALENDAR_AGENT_TASK_UPDATE: 'calendar-agent-task:update',\n CALENDAR_AGENT_TASK_DELETE: 'calendar-agent-task:delete',\n CALENDAR_AGENT_TASK_EXECUTE: 'calendar-agent-task:execute',\n\n // Schedule resources (wspace-scheduler-svc — the unified-source\n // ScheduledJobs surfaced on the Calendar agenda live here).\n // `scheduledJob`/`scheduledJobs` enforce `schedule:read`;\n // `executeJobNow` enforces `schedule:execute`. Distinct from\n // `calendar-agent-task:*` so RBAC denials don't cross-bleed\n // between the two services.\n SCHEDULE_LIST: 'schedule:list',\n SCHEDULE_READ: 'schedule:read',\n SCHEDULE_EXECUTE: 'schedule:execute',\n\n // Configuration resources\n CONFIG_LIST: 'configuration:list',\n CONFIG_READ: 'configuration:read',\n CONFIG_CREATE: 'configuration:create',\n CONFIG_UPDATE: 'configuration:update',\n CONFIG_DELETE: 'configuration:delete',\n\n // Docs Site resources\n DOCS_SITE_LIST: 'docs_site:list',\n DOCS_SITE_READ: 'docs_site:read',\n DOCS_SITE_CREATE: 'docs_site:create',\n DOCS_SITE_UPDATE: 'docs_site:update',\n DOCS_SITE_DELETE: 'docs_site:delete',\n DOCS_COMPILE: 'docs_site:execute',\n\n // Docs Page resources\n DOCS_PAGE_LIST: 'docs_page:list',\n DOCS_PAGE_READ: 'docs_page:read',\n DOCS_PAGE_CREATE: 'docs_page:create',\n DOCS_PAGE_UPDATE: 'docs_page:update',\n DOCS_PAGE_DELETE: 'docs_page:delete',\n // Docs Site generic-sharing surface (backend `shareDocs`, @rbac resource `docs_site`)\n DOCS_SITE_SHARE: 'docs_site:share',\n\n // Action resources\n ACTION_LIST: 'action:list',\n ACTION_READ: 'action:read',\n ACTION_CREATE: 'action:create',\n ACTION_UPDATE: 'action:update',\n ACTION_DELETE: 'action:delete',\n ACTION_EXECUTE: 'action:execute',\n ACTION_SHARE: 'action:share',\n\n // AI Prompt resources (read/CRUD seeded in BOFF-2034 follow-up)\n AIPROMPT_READ: 'ai-prompt:read',\n AIPROMPT_CREATE: 'ai-prompt:create',\n AIPROMPT_UPDATE: 'ai-prompt:update',\n AIPROMPT_DELETE: 'ai-prompt:delete',\n AIPROMPT_SHARE: 'ai-prompt:share',\n\n // AI Context resources (read/CRUD seeded in BOFF-2034 follow-up)\n AICONTEXT_READ: 'ai-context:read',\n AICONTEXT_CREATE: 'ai-context:create',\n AICONTEXT_UPDATE: 'ai-context:update',\n AICONTEXT_DELETE: 'ai-context:delete',\n AICONTEXT_SHARE: 'ai-context:share',\n\n // Agent Instruction File resources (read/CRUD seeded in BOFF-2034 follow-up)\n INSTRUCTION_FILE_READ: 'instruction-file:read',\n INSTRUCTION_FILE_CREATE: 'instruction-file:create',\n INSTRUCTION_FILE_UPDATE: 'instruction-file:update',\n INSTRUCTION_FILE_DELETE: 'instruction-file:delete',\n INSTRUCTION_FILE_SHARE: 'instruction-file:share',\n\n // Audit resources\n AUDIT_LIST: 'audit:list',\n AUDIT_READ: 'audit:read',\n AUDIT_DELETE: 'audit:delete',\n AUDIT_EXPORT: 'audit:export',\n\n // Target resources — guards wired in BOFF-2034 (Remaining Pages Audit)\n TARGET_LIST: 'target:list',\n TARGET_READ: 'target:read',\n TARGET_CREATE: 'target:create',\n TARGET_UPDATE: 'target:update',\n TARGET_DELETE: 'target:delete',\n // Target generic-sharing surface (backend `shareTarget`, @rbac resource `target`)\n TARGET_SHARE: 'target:share',\n\n // Catalog resources (read/CRUD seeded in BOFF-2034 follow-up)\n CATALOG_READ: 'catalog:read',\n CATALOG_CREATE: 'catalog:create',\n CATALOG_UPDATE: 'catalog:update',\n CATALOG_DELETE: 'catalog:delete',\n CATALOG_SHARE: 'catalog:share',\n\n // Component resources (read/CRUD seeded in BOFF-2034 follow-up)\n COMPONENT_READ: 'component:read',\n COMPONENT_CREATE: 'component:create',\n COMPONENT_UPDATE: 'component:update',\n COMPONENT_DELETE: 'component:delete',\n COMPONENT_SHARE: 'component:share',\n\n // Template resources (read/CRUD seeded in BOFF-2034 follow-up)\n TEMPLATE_READ: 'template:read',\n TEMPLATE_CREATE: 'template:create',\n TEMPLATE_UPDATE: 'template:update',\n TEMPLATE_DELETE: 'template:delete',\n TEMPLATE_SHARE: 'template:share',\n\n // Scorecard resources. NOTE: `scorecard` is a NEW resourceType — pre-existing\n // workspaces only grant these after their cloned roles are re-synced, so do\n // not hard-gate a page on scorecard:* until that backfill has run.\n SCORECARD_READ: 'scorecard:read',\n SCORECARD_CREATE: 'scorecard:create',\n SCORECARD_UPDATE: 'scorecard:update',\n SCORECARD_DELETE: 'scorecard:delete',\n SCORECARD_SHARE: 'scorecard:share',\n\n // Tunnel resources. The svc enforces `tunnel:read` on list/detail queries\n // (there is no seeded `tunnel:list`), `tunnel:execute` on start/stop/session\n // control, and `tunnel:update` on edits.\n TUNNEL_READ: 'tunnel:read',\n TUNNEL_CREATE: 'tunnel:create',\n TUNNEL_UPDATE: 'tunnel:update',\n TUNNEL_DELETE: 'tunnel:delete',\n TUNNEL_EXECUTE: 'tunnel:execute',\n TUNNEL_SHARE: 'tunnel:share',\n\n // Analytics resources\n ANALYTICS_LIST: 'analytics:list',\n ANALYTICS_READ: 'analytics:read',\n\n // Log resources\n LOG_LIST: 'log:list',\n LOG_READ: 'log:read',\n} as const;\n\nexport type VibePermission = (typeof VIBE_PERMISSIONS)[keyof typeof VIBE_PERMISSIONS];\n"],"mappings":";AAqCA,IAAa,IAAmB;CAE9B,WAAW;CACX,WAAW;CACX,aAAa;CACb,aAAa;CACb,aAAa;CACb,WAAW;CACX,aAAa;CAGb,YAAY;CACZ,YAAY;CACZ,cAAc;CACd,cAAc;CACd,cAAc;CAGd,aAAa;CACb,cAAc;CACd,cAAc;CAGd,6BAA6B;CAC7B,+BAA+B;CAC/B,+BAA+B;CAC/B,+BAA+B;CAG/B,6BAA6B;CAG7B,cAAc;CACd,cAAc;CACd,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,iBAAiB;CACjB,eAAe;CAGf,WAAW;CACX,WAAW;CACX,aAAa;CACb,aAAa;CACb,aAAa;CAGb,cAAc;CACd,cAAc;CACd,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAGhB,eAAe;CACf,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,kBAAkB;CAClB,gBAAgB;CAGhB,0BAA0B;CAC1B,0BAA0B;CAC1B,4BAA4B;CAC5B,4BAA4B;CAC5B,4BAA4B;CAC5B,6BAA6B;CAQ7B,eAAe;CACf,eAAe;CACf,kBAAkB;CAGlB,aAAa;CACb,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CAGf,gBAAgB;CAChB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,cAAc;CAGd,gBAAgB;CAChB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAElB,iBAAiB;CAGjB,aAAa;CACb,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CACf,gBAAgB;CAChB,cAAc;CAGd,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAGhB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CAGjB,uBAAuB;CACvB,yBAAyB;CACzB,yBAAyB;CACzB,yBAAyB;CACzB,wBAAwB;CAGxB,YAAY;CACZ,YAAY;CACZ,cAAc;CACd,cAAc;CAGd,aAAa;CACb,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CAEf,cAAc;CAGd,cAAc;CACd,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,eAAe;CAGf,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CAGjB,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAKhB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CAKjB,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CACf,gBAAgB;CAChB,cAAc;CAGd,gBAAgB;CAChB,gBAAgB;CAGhB,UAAU;CACV,UAAU;CACX"}
|
|
1
|
+
{"version":3,"file":"permissions.js","names":[],"sources":["../../src/constants/permissions.ts"],"sourcesContent":["/**\n * VibeControls Permission Constants\n *\n * All permission strings for VibeControls resources.\n * Use with fe-libs usePermissions hook: hasPermission(VIBE_PERMISSIONS.VIBE_CREATE)\n *\n * IMPORTANT — only strings present in the wspace-rbac-svc seed\n * (`prisma/seed-unified-rbac.ts` ACTION_DEFINITIONS) are ever returned by\n * `myPermissions`/`hasPermission`. Gating on a string the seed does NOT\n * define makes `hasPermission` return false for EVERYONE → a full lockout.\n *\n * Read/CRUD for catalog, component, template, ai-context, ai-prompt,\n * instruction-file and docs_site were seeded in the BOFF-2034 follow-up, so\n * their pages now gate on their own resource (no more `vibe:list` proxy).\n * The new resourceTypes added in that pass (scorecard, aitoolevent,\n * docs_page, docs_revision, docs_build_image) only reach ALREADY-EXISTING\n * workspaces after their cloned roles are re-synced — do NOT hard-gate a\n * page on those until the backfill has run, or pre-existing workspaces lock\n * out.\n *\n * Resource types:\n * - vibe: list, read, create, update, delete, scan, manage\n * - agent: list, read, create, update, delete, admin, mutate, invoke\n * - session: list, read, create, update, delete, execute\n * - note: list, read, create, update, delete\n * - webhook: list, read, create, update, delete\n * - action: list, read, create, update, delete, execute\n * - calendar-agent-task: list, read, create, update, delete, execute\n * - configuration: list, read, create, update, delete\n * - docs_site: list, read, create, update, delete, execute\n * - docs_page: list, read, create, update, delete\n * - target: list, read, create, update, delete\n * - tunnel: list, read, create, delete\n * - analytics: list, read\n * - log: list, read\n */\n\nexport const VIBE_PERMISSIONS = {\n // Vibe resources\n VIBE_LIST: 'vibe:list',\n VIBE_READ: 'vibe:read',\n VIBE_CREATE: 'vibe:create',\n VIBE_UPDATE: 'vibe:update',\n VIBE_DELETE: 'vibe:delete',\n VIBE_SCAN: 'vibe:scan',\n VIBE_MANAGE: 'vibe:manage',\n\n // Agent resources\n AGENT_LIST: 'agent:list',\n AGENT_READ: 'agent:read',\n AGENT_CREATE: 'agent:create',\n AGENT_UPDATE: 'agent:update',\n AGENT_DELETE: 'agent:delete',\n // Elevated agent control-plane actions (gateway enforces these on\n // remote-exec / credential / code-server control mutations).\n AGENT_ADMIN: 'agent:admin', // set/rotate API key, nuke, dry-run\n AGENT_MUTATE: 'agent:mutate', // code-server start/stop, plugin install\n AGENT_INVOKE: 'agent:invoke', // execute scheduled prompt, mint agent capabilities\n\n // Agent message bookmark resources\n AGENT_MESSAGE_BOOKMARK_READ: 'agent-message-bookmark:read',\n AGENT_MESSAGE_BOOKMARK_CREATE: 'agent-message-bookmark:create',\n AGENT_MESSAGE_BOOKMARK_UPDATE: 'agent-message-bookmark:update',\n AGENT_MESSAGE_BOOKMARK_DELETE: 'agent-message-bookmark:delete',\n\n // Integration connection resources\n INTEGRATION_CONNECTION_READ: 'integration-connection:read',\n\n // Session resources\n SESSION_LIST: 'session:list',\n SESSION_READ: 'session:read',\n SESSION_CREATE: 'session:create',\n SESSION_UPDATE: 'session:update',\n SESSION_DELETE: 'session:delete',\n SESSION_EXECUTE: 'session:execute',\n SESSION_SHARE: 'session:share',\n\n // Note resources\n NOTE_LIST: 'note:list',\n NOTE_READ: 'note:read',\n NOTE_CREATE: 'note:create',\n NOTE_UPDATE: 'note:update',\n NOTE_DELETE: 'note:delete',\n\n // Webhook resources\n WEBHOOK_LIST: 'webhook:list',\n WEBHOOK_READ: 'webhook:read',\n WEBHOOK_CREATE: 'webhook:create',\n WEBHOOK_UPDATE: 'webhook:update',\n WEBHOOK_DELETE: 'webhook:delete',\n // `testWebhook` / `triggerWebhook` enforce `webhook:execute` (seeded).\n WEBHOOK_EXECUTE: 'webhook:execute',\n\n // VibeDeck resources\n VIBEDECK_LIST: 'vibedeck:list',\n VIBEDECK_READ: 'vibedeck:read',\n VIBEDECK_CREATE: 'vibedeck:create',\n VIBEDECK_UPDATE: 'vibedeck:update',\n VIBEDECK_DELETE: 'vibedeck:delete',\n VIBEDECK_EXECUTE: 'vibedeck:execute',\n VIBEDECK_SHARE: 'vibedeck:share',\n\n // VibeDeck button resources — sub-resource of vibedeck. The svc enforces\n // `vibedeck-button:*` on the button CRUD/execute mutations in vibedeck.gql\n // (seeded in BOFF-2659, defaults mirror vibedeck). Since it is a newer\n // resourceType, gate with the fail-open \"registered\" guard (fall back to the\n // parent `vibedeck:*` grant when the button perms aren't in the actor's set)\n // so pre-backfill workspaces don't lock out.\n VIBEDECK_BUTTON_LIST: 'vibedeck-button:list',\n VIBEDECK_BUTTON_READ: 'vibedeck-button:read',\n VIBEDECK_BUTTON_CREATE: 'vibedeck-button:create',\n VIBEDECK_BUTTON_UPDATE: 'vibedeck-button:update',\n VIBEDECK_BUTTON_DELETE: 'vibedeck-button:delete',\n VIBEDECK_BUTTON_EXECUTE: 'vibedeck-button:execute',\n\n // Calendar Agent Task resources\n CALENDAR_AGENT_TASK_LIST: 'calendar-agent-task:list',\n CALENDAR_AGENT_TASK_READ: 'calendar-agent-task:read',\n CALENDAR_AGENT_TASK_CREATE: 'calendar-agent-task:create',\n CALENDAR_AGENT_TASK_UPDATE: 'calendar-agent-task:update',\n CALENDAR_AGENT_TASK_DELETE: 'calendar-agent-task:delete',\n CALENDAR_AGENT_TASK_EXECUTE: 'calendar-agent-task:execute',\n\n // Schedule resources (wspace-scheduler-svc — the unified-source\n // ScheduledJobs surfaced on the Calendar agenda live here).\n // `scheduledJob`/`scheduledJobs` enforce `schedule:read`;\n // `executeJobNow` enforces `schedule:execute`. Distinct from\n // `calendar-agent-task:*` so RBAC denials don't cross-bleed\n // between the two services.\n SCHEDULE_LIST: 'schedule:list',\n SCHEDULE_READ: 'schedule:read',\n SCHEDULE_EXECUTE: 'schedule:execute',\n\n // Configuration resources\n CONFIG_LIST: 'configuration:list',\n CONFIG_READ: 'configuration:read',\n CONFIG_CREATE: 'configuration:create',\n CONFIG_UPDATE: 'configuration:update',\n CONFIG_DELETE: 'configuration:delete',\n\n // Docs Site resources\n DOCS_SITE_LIST: 'docs_site:list',\n DOCS_SITE_READ: 'docs_site:read',\n DOCS_SITE_CREATE: 'docs_site:create',\n DOCS_SITE_UPDATE: 'docs_site:update',\n DOCS_SITE_DELETE: 'docs_site:delete',\n DOCS_COMPILE: 'docs_site:execute',\n\n // Docs Page resources\n DOCS_PAGE_LIST: 'docs_page:list',\n DOCS_PAGE_READ: 'docs_page:read',\n DOCS_PAGE_CREATE: 'docs_page:create',\n DOCS_PAGE_UPDATE: 'docs_page:update',\n DOCS_PAGE_DELETE: 'docs_page:delete',\n // Docs Site generic-sharing surface (backend `shareDocs`, @rbac resource `docs_site`)\n DOCS_SITE_SHARE: 'docs_site:share',\n\n // Action resources\n ACTION_LIST: 'action:list',\n ACTION_READ: 'action:read',\n ACTION_CREATE: 'action:create',\n ACTION_UPDATE: 'action:update',\n ACTION_DELETE: 'action:delete',\n ACTION_EXECUTE: 'action:execute',\n ACTION_SHARE: 'action:share',\n\n // AI Prompt resources (read/CRUD seeded in BOFF-2034 follow-up)\n AIPROMPT_READ: 'ai-prompt:read',\n AIPROMPT_CREATE: 'ai-prompt:create',\n AIPROMPT_UPDATE: 'ai-prompt:update',\n AIPROMPT_DELETE: 'ai-prompt:delete',\n AIPROMPT_SHARE: 'ai-prompt:share',\n\n // AI Context resources (read/CRUD seeded in BOFF-2034 follow-up)\n AICONTEXT_READ: 'ai-context:read',\n AICONTEXT_CREATE: 'ai-context:create',\n AICONTEXT_UPDATE: 'ai-context:update',\n AICONTEXT_DELETE: 'ai-context:delete',\n AICONTEXT_SHARE: 'ai-context:share',\n\n // Agent Instruction File resources (read/CRUD seeded in BOFF-2034 follow-up)\n INSTRUCTION_FILE_READ: 'instruction-file:read',\n INSTRUCTION_FILE_CREATE: 'instruction-file:create',\n INSTRUCTION_FILE_UPDATE: 'instruction-file:update',\n INSTRUCTION_FILE_DELETE: 'instruction-file:delete',\n INSTRUCTION_FILE_SHARE: 'instruction-file:share',\n\n // Audit resources\n AUDIT_LIST: 'audit:list',\n AUDIT_READ: 'audit:read',\n AUDIT_DELETE: 'audit:delete',\n AUDIT_EXPORT: 'audit:export',\n\n // Target resources — guards wired in BOFF-2034 (Remaining Pages Audit)\n TARGET_LIST: 'target:list',\n TARGET_READ: 'target:read',\n TARGET_CREATE: 'target:create',\n TARGET_UPDATE: 'target:update',\n TARGET_DELETE: 'target:delete',\n // Target generic-sharing surface (backend `shareTarget`, @rbac resource `target`)\n TARGET_SHARE: 'target:share',\n\n // Catalog resources (read/CRUD seeded in BOFF-2034 follow-up)\n CATALOG_READ: 'catalog:read',\n CATALOG_CREATE: 'catalog:create',\n CATALOG_UPDATE: 'catalog:update',\n CATALOG_DELETE: 'catalog:delete',\n CATALOG_SHARE: 'catalog:share',\n\n // Component resources (read/CRUD seeded in BOFF-2034 follow-up)\n COMPONENT_READ: 'component:read',\n COMPONENT_CREATE: 'component:create',\n COMPONENT_UPDATE: 'component:update',\n COMPONENT_DELETE: 'component:delete',\n COMPONENT_SHARE: 'component:share',\n\n // Template resources (read/CRUD seeded in BOFF-2034 follow-up)\n TEMPLATE_READ: 'template:read',\n TEMPLATE_CREATE: 'template:create',\n TEMPLATE_UPDATE: 'template:update',\n TEMPLATE_DELETE: 'template:delete',\n TEMPLATE_SHARE: 'template:share',\n\n // Scorecard resources. NOTE: `scorecard` is a NEW resourceType — pre-existing\n // workspaces only grant these after their cloned roles are re-synced, so do\n // not hard-gate a page on scorecard:* until that backfill has run.\n SCORECARD_READ: 'scorecard:read',\n SCORECARD_CREATE: 'scorecard:create',\n SCORECARD_UPDATE: 'scorecard:update',\n SCORECARD_DELETE: 'scorecard:delete',\n SCORECARD_SHARE: 'scorecard:share',\n\n // Tunnel resources. The svc enforces `tunnel:read` on list/detail queries\n // (there is no seeded `tunnel:list`), `tunnel:execute` on start/stop/session\n // control, and `tunnel:update` on edits.\n TUNNEL_READ: 'tunnel:read',\n TUNNEL_CREATE: 'tunnel:create',\n TUNNEL_UPDATE: 'tunnel:update',\n TUNNEL_DELETE: 'tunnel:delete',\n TUNNEL_EXECUTE: 'tunnel:execute',\n TUNNEL_SHARE: 'tunnel:share',\n\n // Analytics resources\n ANALYTICS_LIST: 'analytics:list',\n ANALYTICS_READ: 'analytics:read',\n\n // Log resources\n LOG_LIST: 'log:list',\n LOG_READ: 'log:read',\n} as const;\n\nexport type VibePermission = (typeof VIBE_PERMISSIONS)[keyof typeof VIBE_PERMISSIONS];\n"],"mappings":";AAqCA,IAAa,IAAmB;CAE9B,WAAW;CACX,WAAW;CACX,aAAa;CACb,aAAa;CACb,aAAa;CACb,WAAW;CACX,aAAa;CAGb,YAAY;CACZ,YAAY;CACZ,cAAc;CACd,cAAc;CACd,cAAc;CAGd,aAAa;CACb,cAAc;CACd,cAAc;CAGd,6BAA6B;CAC7B,+BAA+B;CAC/B,+BAA+B;CAC/B,+BAA+B;CAG/B,6BAA6B;CAG7B,cAAc;CACd,cAAc;CACd,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,iBAAiB;CACjB,eAAe;CAGf,WAAW;CACX,WAAW;CACX,aAAa;CACb,aAAa;CACb,aAAa;CAGb,cAAc;CACd,cAAc;CACd,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAEhB,iBAAiB;CAGjB,eAAe;CACf,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,kBAAkB;CAClB,gBAAgB;CAQhB,sBAAsB;CACtB,sBAAsB;CACtB,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;CACxB,yBAAyB;CAGzB,0BAA0B;CAC1B,0BAA0B;CAC1B,4BAA4B;CAC5B,4BAA4B;CAC5B,4BAA4B;CAC5B,6BAA6B;CAQ7B,eAAe;CACf,eAAe;CACf,kBAAkB;CAGlB,aAAa;CACb,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CAGf,gBAAgB;CAChB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,cAAc;CAGd,gBAAgB;CAChB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAElB,iBAAiB;CAGjB,aAAa;CACb,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CACf,gBAAgB;CAChB,cAAc;CAGd,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAGhB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CAGjB,uBAAuB;CACvB,yBAAyB;CACzB,yBAAyB;CACzB,yBAAyB;CACzB,wBAAwB;CAGxB,YAAY;CACZ,YAAY;CACZ,cAAc;CACd,cAAc;CAGd,aAAa;CACb,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CAEf,cAAc;CAGd,cAAc;CACd,gBAAgB;CAChB,gBAAgB;CAChB,gBAAgB;CAChB,eAAe;CAGf,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CAGjB,eAAe;CACf,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAKhB,gBAAgB;CAChB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,iBAAiB;CAKjB,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CACf,gBAAgB;CAChB,cAAc;CAGd,gBAAgB;CAChB,gBAAgB;CAGhB,UAAU;CACV,UAAU;CACX"}
|