@webx-ui/module-auth 0.12.0 → 0.14.0

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.
@@ -4,7 +4,11 @@ import { Admin } from './types';
4
4
  * Creating an administrator and editing one — the same form, because they differ in one field.
5
5
  *
6
6
  * The password is required the first time and optional afterwards, and a blank one on an edit
7
- * means "leave it alone" rather than "set it to nothing". That is the only conditional here.
7
+ * means "leave it alone" rather than "set it to nothing".
8
+ *
9
+ * What the reader may not change is not on the form (WEBX_UI_PERMISSIONS.md §11): the roles need
10
+ * `roles.assign`, «Super administrator» needs to be one. A form without them does not send them,
11
+ * so a save never touches what it did not show — the server would refuse it anyway.
8
12
  */
9
13
  type __VLS_Props = {
10
14
  /** Absent creates somebody; present edits them. */
@@ -1,6 +1,6 @@
1
1
  import { Component } from 'vue';
2
2
  import { AdminsView } from './module';
3
- import { Admin } from './types';
3
+ import { Admin, Role } from './types';
4
4
  /**
5
5
  * The administrators section: the list, the form over it, the trail of what their agents did,
6
6
  * and the agents themselves.
@@ -17,6 +17,11 @@ import { Admin } from './types';
17
17
  * behind; everybody's connections are behind `admins.manage`, the one that is about other
18
18
  * people's accounts. Keys are behind it too: a key speaks for the administrator it names.
19
19
  *
20
+ * The roles are a view of their own too (WEBX_UI_PERMISSIONS.md §11): read by whoever deals with
21
+ * people or roles at all, edited with `roles.manage`. Each view is shown only to whoever may
22
+ * open it; the section itself is in the menu for anybody holding one of its permissions, so an
23
+ * address of a view they may not open lands on the first one they may.
24
+ *
20
25
  * The heading and `Add` used to live inside the card, which made this the one section of the
21
26
  * panel with no line of its own at the top. They stand outside it now, where every other list
22
27
  * keeps them (§19).
@@ -35,9 +40,19 @@ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {
35
40
  resolveAvatar: (key: string) => Promise<string | null>;
36
41
  current: AdminsView;
37
42
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
38
- keys: import('vue').CreateComponentPublicInstanceWithMixins<Readonly<{}> & Readonly<{}>, {
43
+ roles: import('vue').CreateComponentPublicInstanceWithMixins<Readonly<{
44
+ removable?: boolean;
45
+ }> & Readonly<{
46
+ onOpen?: ((role: Role) => any) | undefined;
47
+ onRemove?: ((role: Role) => any) | undefined;
48
+ }>, {
39
49
  reload: () => Promise<void>;
40
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, import('vue').PublicProps, {}, true, {}, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, {
50
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
51
+ open: (role: Role) => any;
52
+ remove: (role: Role) => any;
53
+ }, import('vue').PublicProps, {
54
+ removable: boolean;
55
+ }, false, {}, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, {
41
56
  root: HTMLDivElement;
42
57
  }, HTMLDivElement, import('vue').ComponentProvideOptions, {
43
58
  P: {};
@@ -46,9 +61,38 @@ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {
46
61
  C: {};
47
62
  M: {};
48
63
  Defaults: {};
49
- }, Readonly<{}> & Readonly<{}>, {
64
+ }, Readonly<{
65
+ removable?: boolean;
66
+ }> & Readonly<{
67
+ onOpen?: ((role: Role) => any) | undefined;
68
+ onRemove?: ((role: Role) => any) | undefined;
69
+ }>, {
50
70
  reload: () => Promise<void>;
51
- }, {}, {}, {}, {}> | null;
71
+ }, {}, {}, {}, {
72
+ removable: boolean;
73
+ }> | null;
74
+ keys: import('vue').CreateComponentPublicInstanceWithMixins<Readonly<{
75
+ mine?: boolean;
76
+ }> & Readonly<{}>, {
77
+ reload: () => Promise<void>;
78
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, import('vue').PublicProps, {
79
+ mine: boolean;
80
+ }, false, {}, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, {
81
+ root: HTMLDivElement;
82
+ }, HTMLDivElement, import('vue').ComponentProvideOptions, {
83
+ P: {};
84
+ B: {};
85
+ D: {};
86
+ C: {};
87
+ M: {};
88
+ Defaults: {};
89
+ }, Readonly<{
90
+ mine?: boolean;
91
+ }> & Readonly<{}>, {
92
+ reload: () => Promise<void>;
93
+ }, {}, {}, {}, {
94
+ mine: boolean;
95
+ }> | null;
52
96
  list: import('vue').CreateComponentPublicInstanceWithMixins<Readonly<{
53
97
  picking?: boolean;
54
98
  multiple?: boolean;
@@ -0,0 +1,426 @@
1
+ import { defineComponent as L, computed as v, ref as $, onMounted as O, unref as e, openBlock as z, createElementBlock as D, createVNode as t, withCtx as c, createTextVNode as l, toDisplayString as o, createSlots as H, createBlock as V, createElementVNode as a, createCommentVNode as E, useTemplateRef as j } from "vue";
2
+ import { useAdmin as M, useTranslate as B, useErrorText as q, WxScreen as F, WxListScreen as G } from "@webx-ui/module-admin";
3
+ import { toast as I, WxHeading as w, WxCard as N, WxSkeleton as Q, WxText as g, WxButton as p, createModal as X, WxIcon as h, WxTabs as Y, WxTab as R, WxAlert as Z } from "@webx-ui/core";
4
+ import { u as A, _ as P, a as ee, b as ne, c as te, d as ce } from "./index-Bd6Dkuu8.js";
5
+ const oe = {
6
+ key: 0,
7
+ class: "wx-content-rules"
8
+ }, ae = { class: "wx-content-rules__foot" }, le = /* @__PURE__ */ L({
9
+ __name: "ContentRulesCard",
10
+ setup(J) {
11
+ const _ = M();
12
+ A();
13
+ const n = B("webx-auth"), T = B("webx-admin"), W = q(), b = v(() => {
14
+ const d = _.state.manifest?.modules.find((i) => i.id === "settings")?.meta?.content_screen;
15
+ return typeof d == "string" ? d : null;
16
+ }), f = _.can("settings.view") || _.can("settings.manage"), k = _.can("settings.manage"), s = $({}), m = $({}), x = $(!0), y = $(!1), S = `${_.apiPath}/settings/content`;
17
+ O(async () => {
18
+ if (!(!b.value || !f))
19
+ try {
20
+ const u = await _.http.get(S);
21
+ s.value = u.data.values;
22
+ } catch (u) {
23
+ I.danger(W(u));
24
+ } finally {
25
+ x.value = !1;
26
+ }
27
+ });
28
+ async function U() {
29
+ y.value = !0, m.value = {};
30
+ try {
31
+ const u = await _.http.put(S, {
32
+ values: s.value
33
+ });
34
+ s.value = u.data.values, I.success(T("editor.saved"));
35
+ } catch (u) {
36
+ const d = u.body;
37
+ d?.errors && (m.value = d.errors), I.danger(W(u));
38
+ } finally {
39
+ y.value = !1;
40
+ }
41
+ }
42
+ return (u, d) => b.value && e(f) ? (z(), D("section", oe, [
43
+ t(e(w), {
44
+ level: 2,
45
+ size: "sm"
46
+ }, {
47
+ default: c(() => [
48
+ l(o(e(n)("connect.rules")), 1)
49
+ ]),
50
+ _: 1
51
+ }),
52
+ t(e(N), null, H({
53
+ default: c(() => [
54
+ x.value ? (z(), V(e(Q), {
55
+ key: 0,
56
+ rows: 4
57
+ })) : (z(), V(e(F), {
58
+ key: 1,
59
+ modelValue: s.value,
60
+ "onUpdate:modelValue": d[0] || (d[0] = (i) => s.value = i),
61
+ name: b.value,
62
+ errors: m.value,
63
+ disabled: !e(k)
64
+ }, null, 8, ["modelValue", "name", "errors", "disabled"]))
65
+ ]),
66
+ _: 2
67
+ }, [
68
+ x.value ? void 0 : {
69
+ name: "footer",
70
+ fn: c(() => [
71
+ a("div", ae, [
72
+ t(e(g), {
73
+ size: "sm",
74
+ tone: "muted"
75
+ }, {
76
+ default: c(() => [
77
+ l(o(e(n)("connect.rules-hint")), 1)
78
+ ]),
79
+ _: 1
80
+ }),
81
+ e(k) ? (z(), V(e(p), {
82
+ key: 0,
83
+ type: "primary",
84
+ loading: y.value,
85
+ onClick: U
86
+ }, {
87
+ default: c(() => [
88
+ l(o(e(T)("editor.save")), 1)
89
+ ]),
90
+ _: 1
91
+ }, 8, ["loading"])) : E("", !0)
92
+ ])
93
+ ]),
94
+ key: "0"
95
+ }
96
+ ]), 1024)
97
+ ])) : E("", !0);
98
+ }
99
+ }), se = /* @__PURE__ */ P(le, [["__scopeId", "data-v-55be157e"]]), ie = { class: "wx-connect" }, ue = { class: "wx-connect__section" }, de = { class: "wx-connect__row" }, re = { class: "wx-connect__url" }, _e = { class: "wx-connect__section" }, me = { class: "wx-connect__steps" }, fe = { class: "wx-connect__steps" }, ve = { class: "wx-connect__snippet" }, pe = { class: "wx-connect__snippet" }, xe = { class: "wx-connect__section" }, ye = { class: "wx-connect__links" }, he = { class: "wx-connect__steps" }, we = { class: "wx-connect__section" }, ge = { class: "wx-connect__section" }, be = { class: "wx-connect__head" }, ke = /* @__PURE__ */ L({
100
+ __name: "ConnectPage",
101
+ setup(J) {
102
+ const _ = M();
103
+ A();
104
+ const n = B("webx-auth"), T = j("own-keys"), W = X(ee);
105
+ async function b() {
106
+ await W({ mine: !0 }) && T.value?.reload();
107
+ }
108
+ const f = $(!1);
109
+ let k;
110
+ const s = v(() => {
111
+ const i = _.state.manifest?.modules.find((r) => r.id === "connect")?.meta;
112
+ return typeof i?.url == "string" ? i.url : "";
113
+ }), m = v(() => {
114
+ try {
115
+ return new URL(s.value).hostname.split(".")[0] || "webx";
116
+ } catch {
117
+ return "webx";
118
+ }
119
+ }), x = v(() => `claude mcp add --transport http ${m.value} ${s.value}`), y = v(() => `[mcp_servers.${m.value}]
120
+ url = "${s.value}"`), S = v(() => {
121
+ const i = u(JSON.stringify({ url: s.value }));
122
+ return `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(m.value)}&config=${i}`;
123
+ }), U = v(() => {
124
+ const i = JSON.stringify({ name: m.value, type: "http", url: s.value });
125
+ return `vscode:mcp/install?${encodeURIComponent(i)}`;
126
+ });
127
+ function u(i) {
128
+ const r = new TextEncoder().encode(i);
129
+ let C = "";
130
+ for (const K of r) C += String.fromCharCode(K);
131
+ return encodeURIComponent(btoa(C));
132
+ }
133
+ async function d(i) {
134
+ if (!await ce(i)) {
135
+ I.danger(n("connect.copy-failed"));
136
+ return;
137
+ }
138
+ f.value = !0, clearTimeout(k), k = setTimeout(() => f.value = !1, 2e3);
139
+ }
140
+ return (i, r) => (z(), V(e(G), {
141
+ title: e(n)("connect.title"),
142
+ card: !1
143
+ }, {
144
+ default: c(() => [
145
+ a("div", ie, [
146
+ t(e(g), { class: "wx-connect__lead" }, {
147
+ default: c(() => [
148
+ l(o(e(n)("connect.lead")), 1)
149
+ ]),
150
+ _: 1
151
+ }),
152
+ a("section", ue, [
153
+ t(e(w), {
154
+ level: 2,
155
+ size: "sm"
156
+ }, {
157
+ default: c(() => [
158
+ l(o(e(n)("connect.address")), 1)
159
+ ]),
160
+ _: 1
161
+ }),
162
+ t(e(N), { bordered: "" }, {
163
+ default: c(() => [
164
+ a("div", de, [
165
+ a("code", re, o(s.value), 1),
166
+ t(e(p), {
167
+ type: "primary",
168
+ disabled: s.value === "",
169
+ onClick: r[0] || (r[0] = (C) => d(s.value))
170
+ }, {
171
+ icon: c(() => [
172
+ t(e(h), {
173
+ name: f.value ? "check" : "copy"
174
+ }, null, 8, ["name"])
175
+ ]),
176
+ default: c(() => [
177
+ l(" " + o(f.value ? e(n)("connect.copied") : e(n)("connect.copy")), 1)
178
+ ]),
179
+ _: 1
180
+ }, 8, ["disabled"])
181
+ ])
182
+ ]),
183
+ _: 1
184
+ })
185
+ ]),
186
+ a("section", _e, [
187
+ t(e(w), {
188
+ level: 2,
189
+ size: "sm"
190
+ }, {
191
+ default: c(() => [
192
+ l(o(e(n)("connect.how")), 1)
193
+ ]),
194
+ _: 1
195
+ }),
196
+ t(e(Y), {
197
+ variant: "line",
198
+ "aria-label": e(n)("connect.how")
199
+ }, {
200
+ default: c(() => [
201
+ t(e(R), {
202
+ value: "claude",
203
+ label: e(n)("connect.claude-title")
204
+ }, {
205
+ default: c(() => [
206
+ a("ol", me, [
207
+ a("li", null, o(e(n)("connect.claude-1")), 1),
208
+ a("li", null, o(e(n)("connect.claude-2")), 1),
209
+ a("li", null, o(e(n)("connect.claude-3")), 1)
210
+ ])
211
+ ]),
212
+ _: 1
213
+ }, 8, ["label"]),
214
+ t(e(R), {
215
+ value: "chatgpt",
216
+ label: e(n)("connect.chatgpt-title")
217
+ }, {
218
+ default: c(() => [
219
+ a("ol", fe, [
220
+ a("li", null, o(e(n)("connect.chatgpt-1")), 1),
221
+ a("li", null, o(e(n)("connect.chatgpt-2")), 1),
222
+ a("li", null, o(e(n)("connect.chatgpt-3")), 1)
223
+ ])
224
+ ]),
225
+ _: 1
226
+ }, 8, ["label"]),
227
+ t(e(R), {
228
+ value: "claude-code",
229
+ label: e(n)("connect.claude-code-title")
230
+ }, {
231
+ default: c(() => [
232
+ t(e(g), { size: "sm" }, {
233
+ default: c(() => [
234
+ l(o(e(n)("connect.claude-code-hint")), 1)
235
+ ]),
236
+ _: 1
237
+ }),
238
+ a("div", ve, [
239
+ a("pre", null, o(x.value), 1),
240
+ t(e(p), {
241
+ variant: "text",
242
+ size: "sm",
243
+ onClick: r[1] || (r[1] = (C) => d(x.value))
244
+ }, {
245
+ icon: c(() => [
246
+ t(e(h), { name: "copy" })
247
+ ]),
248
+ default: c(() => [
249
+ l(" " + o(e(n)("connect.copy-line")), 1)
250
+ ]),
251
+ _: 1
252
+ })
253
+ ])
254
+ ]),
255
+ _: 1
256
+ }, 8, ["label"]),
257
+ t(e(R), {
258
+ value: "codex",
259
+ label: e(n)("connect.codex-title")
260
+ }, {
261
+ default: c(() => [
262
+ t(e(g), { size: "sm" }, {
263
+ default: c(() => [
264
+ l(o(e(n)("connect.codex-hint")), 1)
265
+ ]),
266
+ _: 1
267
+ }),
268
+ a("div", pe, [
269
+ a("pre", null, o(y.value), 1),
270
+ t(e(p), {
271
+ variant: "text",
272
+ size: "sm",
273
+ onClick: r[2] || (r[2] = (C) => d(y.value))
274
+ }, {
275
+ icon: c(() => [
276
+ t(e(h), { name: "copy" })
277
+ ]),
278
+ default: c(() => [
279
+ l(" " + o(e(n)("connect.copy-line")), 1)
280
+ ]),
281
+ _: 1
282
+ })
283
+ ])
284
+ ]),
285
+ _: 1
286
+ }, 8, ["label"])
287
+ ]),
288
+ _: 1
289
+ }, 8, ["aria-label"])
290
+ ]),
291
+ a("section", xe, [
292
+ t(e(w), {
293
+ level: 2,
294
+ size: "sm"
295
+ }, {
296
+ default: c(() => [
297
+ l(o(e(n)("connect.one-click")), 1)
298
+ ]),
299
+ _: 1
300
+ }),
301
+ t(e(g), {
302
+ size: "sm",
303
+ tone: "muted"
304
+ }, {
305
+ default: c(() => [
306
+ l(o(e(n)("connect.one-click-hint")), 1)
307
+ ]),
308
+ _: 1
309
+ }),
310
+ a("div", ye, [
311
+ t(e(p), {
312
+ href: S.value,
313
+ variant: "outline",
314
+ disabled: s.value === "",
315
+ class: "wx-connect__link"
316
+ }, {
317
+ icon: c(() => [
318
+ t(e(h), { name: "external-link" })
319
+ ]),
320
+ default: c(() => [
321
+ l(" " + o(e(n)("connect.install-in", { client: e(n)("connect.cursor-title") })), 1)
322
+ ]),
323
+ _: 1
324
+ }, 8, ["href", "disabled"]),
325
+ t(e(p), {
326
+ href: U.value,
327
+ variant: "outline",
328
+ disabled: s.value === "",
329
+ class: "wx-connect__link"
330
+ }, {
331
+ icon: c(() => [
332
+ t(e(h), { name: "external-link" })
333
+ ]),
334
+ default: c(() => [
335
+ l(" " + o(e(n)("connect.install-in", { client: e(n)("connect.vscode-title") })), 1)
336
+ ]),
337
+ _: 1
338
+ }, 8, ["href", "disabled"])
339
+ ])
340
+ ]),
341
+ t(e(Z), {
342
+ type: "info",
343
+ title: e(n)("connect.next")
344
+ }, {
345
+ default: c(() => [
346
+ a("ol", he, [
347
+ a("li", null, o(e(n)("connect.next-1")), 1),
348
+ a("li", null, o(e(n)("connect.next-2")), 1),
349
+ a("li", null, o(e(n)("connect.next-3")), 1)
350
+ ])
351
+ ]),
352
+ _: 1
353
+ }, 8, ["title"]),
354
+ t(se),
355
+ a("section", we, [
356
+ t(e(w), {
357
+ level: 2,
358
+ size: "sm"
359
+ }, {
360
+ default: c(() => [
361
+ l(o(e(n)("connect.mine")), 1)
362
+ ]),
363
+ _: 1
364
+ }),
365
+ t(e(N), { padding: "none" }, {
366
+ default: c(() => [
367
+ t(ne, { scope: "mine" })
368
+ ]),
369
+ _: 1
370
+ })
371
+ ]),
372
+ a("section", ge, [
373
+ a("div", be, [
374
+ t(e(w), {
375
+ level: 2,
376
+ size: "sm"
377
+ }, {
378
+ default: c(() => [
379
+ l(o(e(n)("connect.keys")), 1)
380
+ ]),
381
+ _: 1
382
+ }),
383
+ t(e(p), {
384
+ size: "sm",
385
+ variant: "outline",
386
+ onClick: b
387
+ }, {
388
+ icon: c(() => [
389
+ t(e(h), { name: "plus" })
390
+ ]),
391
+ default: c(() => [
392
+ l(" " + o(e(n)("keys.issue")), 1)
393
+ ]),
394
+ _: 1
395
+ })
396
+ ]),
397
+ t(e(g), {
398
+ size: "sm",
399
+ tone: "muted",
400
+ class: "wx-connect__lead"
401
+ }, {
402
+ default: c(() => [
403
+ l(o(e(n)("connect.keys-hint")), 1)
404
+ ]),
405
+ _: 1
406
+ }),
407
+ t(e(N), { padding: "none" }, {
408
+ default: c(() => [
409
+ t(te, {
410
+ ref: "own-keys",
411
+ mine: ""
412
+ }, null, 512)
413
+ ]),
414
+ _: 1
415
+ })
416
+ ])
417
+ ])
418
+ ]),
419
+ _: 1
420
+ }, 8, ["title"]));
421
+ }
422
+ }), We = /* @__PURE__ */ P(ke, [["__scopeId", "data-v-73da42dd"]]);
423
+ export {
424
+ We as default
425
+ };
426
+ //# sourceMappingURL=ConnectPage-CMJlMsmn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConnectPage-CMJlMsmn.js","sources":["../src/ContentRulesCard.vue","../src/ConnectPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport { useAdmin, useErrorText, useTranslate, WxScreen } from '@webx-ui/module-admin'\nimport { toast, WxButton, WxCard, WxHeading, WxSkeleton, WxText } from '@webx-ui/core'\nimport { useAuthMessages } from './i18n'\n\n/** The values as the screen holds them, keyed by field name. */\ntype Values = Record<string, unknown>\n\n/**\n * The site's house rules for content — tone, what it never says, notes — beside the place\n * agents are connected, since agents are who reads them (`settings://content-rules`).\n *\n * They are `module-settings`' values on its own screen; this card shows only when that module is\n * installed and says which screen it is, and only to whoever may see the settings. Saving needs\n * the right to change them; without it the card is read-only. The card is this component's, not\n * the screen's — its root is a plain column — so «Save» sits in the card's own footer.\n */\nconst admin = useAdmin()\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\nconst panel = useTranslate('webx-admin')\nconst message = useErrorText()\n\nconst screen = computed<string | null>(() => {\n const meta = admin.state.manifest?.modules.find((module) => module.id === 'settings')?.meta\n const name = meta?.content_screen\n\n return typeof name === 'string' ? name : null\n})\n\nconst canSee = admin.can('settings.view') || admin.can('settings.manage')\nconst canManage = admin.can('settings.manage')\n\nconst values = ref<Values>({})\nconst errors = ref<Record<string, string[]>>({})\nconst loading = ref(true)\nconst saving = ref(false)\n\nconst endpoint = `${admin.apiPath}/settings/content`\n\nonMounted(async () => {\n if (!screen.value || !canSee) return\n\n try {\n const body = await admin.http.get<{ data: { values: Values } }>(endpoint)\n\n values.value = body.data.values\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n})\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n try {\n const body = await admin.http.put<{ data: { values: Values } }>(endpoint, {\n values: values.value,\n })\n\n values.value = body.data.values\n toast.success(panel('editor.saved'))\n } catch (error) {\n const answer = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (answer?.errors) errors.value = answer.errors\n\n toast.danger(message(error))\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <section v-if=\"screen && canSee\" class=\"wx-content-rules\">\n <wx-heading :level=\"2\" size=\"sm\">{{ t('connect.rules') }}</wx-heading>\n\n <wx-card>\n <wx-skeleton v-if=\"loading\" :rows=\"4\" />\n <wx-screen v-else v-model=\"values\" :name=\"screen\" :errors=\"errors\" :disabled=\"!canManage\" />\n\n <template v-if=\"!loading\" #footer>\n <div class=\"wx-content-rules__foot\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('connect.rules-hint') }}</wx-text>\n <wx-button v-if=\"canManage\" type=\"primary\" :loading=\"saving\" @click=\"save\">{{\n panel('editor.save')\n }}</wx-button>\n </div>\n </template>\n </wx-card>\n </section>\n</template>\n\n<style scoped>\n/* A section of the page like the others: its heading above, the card under it. */\n.wx-content-rules {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n min-width: 0;\n}\n\n.wx-content-rules__foot {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-12);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useTemplateRef } from 'vue'\nimport { useAdmin, useTranslate, WxListScreen } from '@webx-ui/module-admin'\nimport {\n createModal,\n toast,\n WxAlert,\n WxButton,\n WxCard,\n WxHeading,\n WxIcon,\n WxTab,\n WxTabs,\n WxText,\n} from '@webx-ui/core'\nimport { copyText } from './clipboard'\nimport ConnectionList from './ConnectionList.vue'\nimport ContentRulesCard from './ContentRulesCard.vue'\nimport KeyDialog from './KeyDialog.vue'\nimport KeyList from './KeyList.vue'\nimport { useAuthMessages } from './i18n'\nimport type { McpKey } from './types'\n\n/**\n * How a person connects their own agent, on one page.\n *\n * It exists because the alternative is explaining it — over a call, to somebody who has never\n * heard of MCP and has no reason to. So the page is written for that call: the address large\n * enough to read out, a button that copies it, and three steps for each client, in the order\n * they appear on that client's screen.\n *\n * The address carries no secret, which is the whole point of the OAuth path (§2.3 of the\n * spec): it can be printed in a letter, read aloud, or left on this page for anybody signed\n * in. What a person has to do themselves is sign in, and that happens on our own screen.\n *\n * Then the rules agents read before they write, when the site keeps them. Under them are their\n * own connections, because the first thing somebody does after connecting\n * is look for proof that it worked — and the second, weeks later, is come back to end it.\n *\n * Last, their own keys: the other way in, for a program with no browser to sign in with. They\n * are here rather than in the profile because this is the page about letting programs in, and\n * because an administrator without `admins.*` never sees the keys view of «Administrators»\n * (WEBX_UI_PERMISSIONS.md §7). A key acts as its owner, so issuing one for yourself needs no\n * permission and its scopes are only those your own permissions reach.\n */\nconst admin = useAdmin()\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n\nconst ownKeys = useTemplateRef<{ reload: () => void }>('own-keys')\nconst issueKey = createModal<McpKey, { mine: boolean }>(KeyDialog)\n\nasync function issue(): Promise<void> {\n if (await issueKey({ mine: true })) ownKeys.value?.reload()\n}\n\nconst copied = ref(false)\nlet copiedTimer: ReturnType<typeof setTimeout> | undefined\n\n/**\n * The address agents connect to, as the server printed it — absolute, because it is pasted\n * into a program on another machine.\n */\nconst url = computed<string>(() => {\n const meta = admin.state.manifest?.modules.find((module) => module.id === 'connect')?.meta\n\n return typeof meta?.url === 'string' ? meta.url : ''\n})\n\n/**\n * What the server is called in the client's own list of servers.\n *\n * The site's own name, taken off the address: somebody with three sites connected reads this\n * word and nothing else, and \"webx\" three times over would be three servers they cannot tell\n * apart.\n */\nconst name = computed<string>(() => {\n try {\n return new URL(url.value).hostname.split('.')[0] || 'webx'\n } catch {\n return 'webx'\n }\n})\n\nconst command = computed(() => `claude mcp add --transport http ${name.value} ${url.value}`)\n\nconst toml = computed(() => `[mcp_servers.${name.value}]\\nurl = \"${url.value}\"`)\n\n/**\n * The links Cursor and VS Code take a whole server from. Both are documented shapes, and both\n * carry only the address — there is nothing secret to leak into a link.\n */\nconst cursorLink = computed(() => {\n const config = encode(JSON.stringify({ url: url.value }))\n\n return `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(name.value)}&config=${config}`\n})\n\nconst vscodeLink = computed(() => {\n const config = JSON.stringify({ name: name.value, type: 'http', url: url.value })\n\n return `vscode:mcp/install?${encodeURIComponent(config)}`\n})\n\n/** Base64 of a UTF-8 string: `btoa` alone throws on anything above ASCII. */\nfunction encode(value: string): string {\n const bytes = new TextEncoder().encode(value)\n let binary = ''\n\n for (const byte of bytes) binary += String.fromCharCode(byte)\n\n return encodeURIComponent(btoa(binary))\n}\n\n/** Copying the address — the one thing the person came here for — and saying so either way. */\nasync function copy(value: string): Promise<void> {\n if (!(await copyText(value))) {\n toast.danger(t('connect.copy-failed'))\n\n return\n }\n\n copied.value = true\n clearTimeout(copiedTimer)\n copiedTimer = setTimeout(() => (copied.value = false), 2000)\n}\n</script>\n\n<template>\n <wx-list-screen :title=\"t('connect.title')\" :card=\"false\">\n <div class=\"wx-connect\">\n <wx-text class=\"wx-connect__lead\">{{ t('connect.lead') }}</wx-text>\n\n <!-- The address, large. Everything else on this page is about what to do with it. Its\n heading is the page's section heading, as every other part of the page has. -->\n <section class=\"wx-connect__section\">\n <wx-heading :level=\"2\" size=\"sm\">{{ t('connect.address') }}</wx-heading>\n\n <wx-card bordered>\n <div class=\"wx-connect__row\">\n <code class=\"wx-connect__url\">{{ url }}</code>\n\n <wx-button type=\"primary\" :disabled=\"url === ''\" @click=\"copy(url)\">\n <template #icon><wx-icon :name=\"copied ? 'check' : 'copy'\" /></template>\n {{ copied ? t('connect.copied') : t('connect.copy') }}\n </wx-button>\n </div>\n </wx-card>\n </section>\n\n <section class=\"wx-connect__section\">\n <wx-heading :level=\"2\" size=\"sm\">{{ t('connect.how') }}</wx-heading>\n\n <wx-tabs variant=\"line\" :aria-label=\"t('connect.how')\">\n <wx-tab value=\"claude\" :label=\"t('connect.claude-title')\">\n <ol class=\"wx-connect__steps\">\n <li>{{ t('connect.claude-1') }}</li>\n <li>{{ t('connect.claude-2') }}</li>\n <li>{{ t('connect.claude-3') }}</li>\n </ol>\n </wx-tab>\n\n <wx-tab value=\"chatgpt\" :label=\"t('connect.chatgpt-title')\">\n <ol class=\"wx-connect__steps\">\n <li>{{ t('connect.chatgpt-1') }}</li>\n <li>{{ t('connect.chatgpt-2') }}</li>\n <li>{{ t('connect.chatgpt-3') }}</li>\n </ol>\n </wx-tab>\n\n <wx-tab value=\"claude-code\" :label=\"t('connect.claude-code-title')\">\n <wx-text size=\"sm\">{{ t('connect.claude-code-hint') }}</wx-text>\n\n <div class=\"wx-connect__snippet\">\n <pre>{{ command }}</pre>\n <wx-button variant=\"text\" size=\"sm\" @click=\"copy(command)\">\n <template #icon><wx-icon name=\"copy\" /></template>\n {{ t('connect.copy-line') }}\n </wx-button>\n </div>\n </wx-tab>\n\n <wx-tab value=\"codex\" :label=\"t('connect.codex-title')\">\n <wx-text size=\"sm\">{{ t('connect.codex-hint') }}</wx-text>\n\n <div class=\"wx-connect__snippet\">\n <pre>{{ toml }}</pre>\n <wx-button variant=\"text\" size=\"sm\" @click=\"copy(toml)\">\n <template #icon><wx-icon name=\"copy\" /></template>\n {{ t('connect.copy-line') }}\n </wx-button>\n </div>\n </wx-tab>\n </wx-tabs>\n </section>\n\n <section class=\"wx-connect__section\">\n <wx-heading :level=\"2\" size=\"sm\">{{ t('connect.one-click') }}</wx-heading>\n <wx-text size=\"sm\" tone=\"muted\">{{ t('connect.one-click-hint') }}</wx-text>\n\n <div class=\"wx-connect__links\">\n <wx-button\n :href=\"cursorLink\"\n variant=\"outline\"\n :disabled=\"url === ''\"\n class=\"wx-connect__link\"\n >\n <template #icon><wx-icon name=\"external-link\" /></template>\n {{ t('connect.install-in', { client: t('connect.cursor-title') }) }}\n </wx-button>\n\n <wx-button\n :href=\"vscodeLink\"\n variant=\"outline\"\n :disabled=\"url === ''\"\n class=\"wx-connect__link\"\n >\n <template #icon><wx-icon name=\"external-link\" /></template>\n {{ t('connect.install-in', { client: t('connect.vscode-title') }) }}\n </wx-button>\n </div>\n </section>\n\n <wx-alert type=\"info\" :title=\"t('connect.next')\">\n <ol class=\"wx-connect__steps\">\n <li>{{ t('connect.next-1') }}</li>\n <li>{{ t('connect.next-2') }}</li>\n <li>{{ t('connect.next-3') }}</li>\n </ol>\n </wx-alert>\n\n <content-rules-card />\n\n <section class=\"wx-connect__section\">\n <wx-heading :level=\"2\" size=\"sm\">{{ t('connect.mine') }}</wx-heading>\n\n <wx-card padding=\"none\">\n <connection-list scope=\"mine\" />\n </wx-card>\n </section>\n\n <section class=\"wx-connect__section\">\n <div class=\"wx-connect__head\">\n <wx-heading :level=\"2\" size=\"sm\">{{ t('connect.keys') }}</wx-heading>\n <wx-button size=\"sm\" variant=\"outline\" @click=\"issue\">\n <template #icon><wx-icon name=\"plus\" /></template>\n {{ t('keys.issue') }}\n </wx-button>\n </div>\n <wx-text size=\"sm\" tone=\"muted\" class=\"wx-connect__lead\">{{\n t('connect.keys-hint')\n }}</wx-text>\n\n <wx-card padding=\"none\">\n <key-list ref=\"own-keys\" mine />\n </wx-card>\n </section>\n </div>\n </wx-list-screen>\n</template>\n\n<style scoped>\n.wx-connect {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-24);\n min-width: 0;\n}\n\n.wx-connect__lead {\n max-width: 68ch;\n}\n\n/* The address and its button on one line, and under each other as soon as they do not fit:\n the address is long, and shrinking it is what would make it unreadable. */\n.wx-connect__row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-12);\n}\n\n.wx-connect__url {\n flex: 1 1 260px;\n min-width: 0;\n padding: var(--wx-space-10) var(--wx-space-12);\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n color: var(--wx-text-default);\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-md);\n /* Broken anywhere rather than pushed off the card: it is read, not clicked. */\n word-break: break-all;\n}\n\n.wx-connect__section {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n min-width: 0;\n}\n\n.wx-connect__steps {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n margin: 0;\n padding-left: var(--wx-space-24);\n color: var(--wx-text-default);\n font-size: var(--wx-font-size-sm);\n line-height: var(--wx-font-line-height-relaxed);\n}\n\n.wx-connect__snippet {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--wx-space-8);\n padding: var(--wx-space-12);\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n}\n\n.wx-connect__snippet pre {\n flex: 1 1 240px;\n margin: 0;\n min-width: 0;\n color: var(--wx-text-default);\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-xs);\n line-height: var(--wx-font-line-height-relaxed);\n white-space: pre-wrap;\n word-break: break-all;\n}\n\n/* The heading and the button that adds to the list under it, apart; stacked when they meet. */\n.wx-connect__head {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: var(--wx-space-8);\n}\n\n.wx-connect__links {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-12);\n}\n</style>\n"],"names":["admin","useAdmin","useAuthMessages","t","useTranslate","panel","message","useErrorText","screen","computed","name","module","canSee","canManage","values","ref","errors","loading","saving","endpoint","onMounted","body","error","toast","save","answer","_unref","_openBlock","_createElementBlock","_hoisted_1","_createVNode","WxHeading","WxCard","_createSlots","_createBlock","WxSkeleton","WxScreen","$event","_createElementVNode","_hoisted_2","WxText","WxButton","ownKeys","useTemplateRef","issueKey","createModal","KeyDialog","issue","copied","copiedTimer","url","meta","command","toml","cursorLink","config","encode","vscodeLink","value","bytes","binary","byte","copy","copyText","WxListScreen","_hoisted_3","_hoisted_4","_toDisplayString","_cache","WxIcon","_createTextVNode","_hoisted_5","WxTabs","WxTab","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","WxAlert","_hoisted_12","ContentRulesCard","_hoisted_13","ConnectionList","_hoisted_14","_hoisted_15","KeyList"],"mappings":"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"}
@@ -1,2 +1,25 @@
1
- declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, HTMLDivElement>;
1
+ declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
2
+ 'own-keys': import('vue').CreateComponentPublicInstanceWithMixins<Readonly<{
3
+ mine?: boolean;
4
+ }> & Readonly<{}>, {
5
+ reload: () => Promise<void>;
6
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, import('vue').PublicProps, {
7
+ mine: boolean;
8
+ }, false, {}, {}, import('vue').GlobalComponents, import('vue').GlobalDirectives, string, {
9
+ root: HTMLDivElement;
10
+ }, HTMLDivElement, import('vue').ComponentProvideOptions, {
11
+ P: {};
12
+ B: {};
13
+ D: {};
14
+ C: {};
15
+ M: {};
16
+ Defaults: {};
17
+ }, Readonly<{
18
+ mine?: boolean;
19
+ }> & Readonly<{}>, {
20
+ reload: () => Promise<void>;
21
+ }, {}, {}, {}, {
22
+ mine: boolean;
23
+ }> | null;
24
+ }, HTMLDivElement>;
2
25
  export default _default;
@@ -1,2 +1,29 @@
1
- declare const _default: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
1
+ /**
2
+ * Issuing a key for a program, and then showing it — once.
3
+ *
4
+ * Two steps in one dialog rather than a form and a toast, because the second step is the one
5
+ * that matters: the secret exists on this screen and nowhere else, and a toast that faded
6
+ * before somebody copied it would mean issuing another. The dialog resolves with the key when
7
+ * it is closed after issuing, so the list behind it can show the new row.
8
+ *
9
+ * The scopes on offer are the owner's: a key acts as the administrator it names and can never
10
+ * do more than they may, so a module none of whose tools they may use is not offered — the
11
+ * server would refuse it anyway (WEBX_UI_PERMISSIONS.md §7). Asked again whenever the owner
12
+ * changes.
13
+ *
14
+ * The lifetime is a choice of a few, not a date picker. A key is for something that runs
15
+ * unattended, and the honest question is "how long until somebody has to come back to it" —
16
+ * a month, a quarter, a year, three, ten. There is no key without an end: a signature that
17
+ * never expires is one nobody can take back by waiting.
18
+ */
19
+ type __VLS_Props = {
20
+ /**
21
+ * A key for whoever is signed in, from the page that connects their agent: no owner to
22
+ * choose, and no list of people to fetch — which somebody without `admins.view` could not.
23
+ */
24
+ mine?: boolean;
25
+ };
26
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
27
+ mine: boolean;
28
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
2
29
  export default _default;
package/dist/KeyList.d.ts CHANGED
@@ -1,7 +1,13 @@
1
+ type __VLS_Props = {
2
+ /** Only the reader's own keys — the page that connects their agent — with no "who" column. */
3
+ mine?: boolean;
4
+ };
1
5
  declare function load(): Promise<void>;
2
- declare const _default: import('vue').DefineComponent<{}, {
6
+ declare const _default: import('vue').DefineComponent<__VLS_Props, {
3
7
  reload: typeof load;
4
- }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
8
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
9
+ mine: boolean;
10
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
5
11
  root: HTMLDivElement;
6
12
  }, HTMLDivElement>;
7
13
  export default _default;