@columenlabs/lumen-ui 0.1.259 → 0.1.260

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.
@@ -1,4 +1,4 @@
1
- import type { AllowanceRequest, AssistantChat, AssistantChatRow, AssistantEvent, AssistantMessage, AssistantOwnWay, AssistantProvider, AssistantSettingsPatch, AssistantStatus, DraftItem, KeyState, StepItem, UsageHistory } from '@columenlabs/lumen-ui-types/assistant';
1
+ import type { AllowanceRequest, AssistantChat, AssistantChatRow, AssistantConnectorInput, AssistantEvent, AssistantMessage, AssistantOwnWay, AssistantProvider, AssistantSettingsPatch, AssistantStatus, DraftItem, KeyState, StepItem, UsageHistory } from '@columenlabs/lumen-ui-types/assistant';
2
2
  type Test = NonNullable<KeyState['test']>;
3
3
  export interface AssistantAdapter {
4
4
  /** Names the cache its reads keep (two assistants on one page need two keys). */
@@ -31,6 +31,13 @@ export interface AssistantAdapter {
31
31
  client_secret: string;
32
32
  } | null): Promise<AssistantStatus>;
33
33
  testScrapeql(): Promise<Test>;
34
+ /** Admin: adds a connector (an MCP server), kept only when its test lists its tools. */
35
+ addConnector?(input: AssistantConnectorInput): Promise<AssistantStatus>;
36
+ /** Admin: changes one; left out, `headers` keeps the ones saved. */
37
+ saveConnector?(id: string, input: AssistantConnectorInput): Promise<AssistantStatus>;
38
+ removeConnector?(id: string): Promise<AssistantStatus>;
39
+ /** Admin: lists its tools again. */
40
+ testConnector?(id: string): Promise<Test>;
34
41
  chats(): Promise<AssistantChatRow[]>;
35
42
  chat(id: string): Promise<AssistantChat>;
36
43
  removeChat(id: string): Promise<void>;
@@ -55,6 +55,10 @@ export function assistantClient(base) {
55
55
  signOut: (p) => send('DELETE', `/my-signin?provider=${p}`),
56
56
  scrapeql: (token) => token ? send('PUT', '/web/scrapeql', token) : send('DELETE', '/web/scrapeql'),
57
57
  testScrapeql: () => send('POST', '/web/test'),
58
+ addConnector: (input) => send('POST', '/connectors', input),
59
+ saveConnector: (id, input) => send('PUT', `/connectors/${encodeURIComponent(id)}`, input),
60
+ removeConnector: (id) => send('DELETE', `/connectors/${encodeURIComponent(id)}`),
61
+ testConnector: (id) => send('POST', `/connectors/${encodeURIComponent(id)}/test`),
58
62
  chats: () => get('/chats'),
59
63
  chat: (id) => get(`/chats/${encodeURIComponent(id)}`),
60
64
  removeChat: async (id) => {
@@ -4,10 +4,12 @@ import type { AssistantAdapter } from './client.js';
4
4
  * The admin's section: the app's shared keys and each person's monthly allowance on them. Put it
5
5
  * where the app keeps its admin settings, shown only to admins.
6
6
  */
7
- export declare function assistantAdminSections({ client, allowance, providers, }: {
7
+ export declare function assistantAdminSections({ client, allowance, providers, connectors, }: {
8
8
  client: AssistantAdapter;
9
9
  /** Add Allowance requests (the app set `allowance` on the server). */
10
10
  allowance?: boolean;
11
+ /** Add Connectors: outside MCP servers the assistant may use (the app set `connectors` on the server). */
12
+ connectors?: boolean;
11
13
  /** Give each provider's shared key a section of its own (listed under Allowance) instead of one
12
14
  * Shared model keys section: the app's providers, in order. */
13
15
  providers?: {
@@ -1,6 +1,6 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useQuery, useQueryClient } from '@tanstack/react-query';
3
- import { ChartColumnIcon, CoinsIcon, GlobeIcon, InboxIcon, KeyRoundIcon, LayersIcon, Link2Icon, LogInIcon, ReceiptIcon, SendIcon, SparklesIcon, WrenchIcon, } from 'lucide-react';
3
+ import { ChartColumnIcon, CoinsIcon, GlobeIcon, InboxIcon, KeyRoundIcon, LayersIcon, Link2Icon, LogInIcon, PlugIcon, ReceiptIcon, SendIcon, SparklesIcon, WrenchIcon, } from 'lucide-react';
4
4
  import { useId, useState } from 'react';
5
5
  import { ConnectionLogo, LumenConnection, } from '../connection.js';
6
6
  import { LumenExpandList, LumenExpandRow } from '../expand-list.js';
@@ -312,6 +312,104 @@ function Web({ client }) {
312
312
  const { apply } = useApply(client);
313
313
  return (_jsx(WithStatus, { client: client, children: (s) => (_jsxs("div", { className: "flex flex-col", children: [_jsx(SettingField, { label: "Read the web", info: "A plain fetch of public pages and APIs from the server, never the app's own pages.", children: _jsx(Switch, { "aria-label": "Read the web", checked: s.web.fetch, onCheckedChange: (fetch) => void apply('web', () => client.settings({ web: { fetch } })) }) }), _jsx(ScrapeqlBlock, { client: client, s: s })] })) }));
314
314
  }
315
+ // ---------------------------------------------------------------- connectors
316
+ const CONNECTOR_ACCESS_LABELS = [
317
+ { value: 'off', label: 'Off' },
318
+ { value: 'read', label: 'Read' },
319
+ { value: 'draft', label: 'Read + draft' },
320
+ ];
321
+ /** Name, address and up to two sign-in headers; a pair left empty is not sent. */
322
+ const headerFields = (prefix = '') => [
323
+ { id: `${prefix}h1`, label: 'Header', placeholder: 'CF-Access-Client-Id' },
324
+ { id: `${prefix}v1`, label: 'Value', secret: true },
325
+ { id: `${prefix}h2`, label: 'Header', placeholder: 'CF-Access-Client-Secret' },
326
+ { id: `${prefix}v2`, label: 'Value', secret: true },
327
+ ];
328
+ const headersOf = (v) => {
329
+ const out = {};
330
+ for (const i of [1, 2]) {
331
+ const k = v[`h${i}`]?.trim();
332
+ const val = v[`v${i}`]?.trim();
333
+ if (k && val)
334
+ out[k] = val;
335
+ }
336
+ return out;
337
+ };
338
+ function ConnectorRow({ client, c }) {
339
+ const { apply, refresh } = useApply(client);
340
+ const reads = c.tools.filter((t) => t.read).length;
341
+ const access = (v) => apply('connector', () => client.saveConnector(c.id, {
342
+ name: c.name,
343
+ url: c.url,
344
+ access: v,
345
+ }));
346
+ return (_jsx(LumenConnection, { id: c.id, name: c.name, logo: _jsx(ConnectionLogo, { icon: PlugIcon }), meta: `${c.tools.length} tool${c.tools.length === 1 ? '' : 's'}${c.access === 'off' ? ' · off' : ''}`, ways: [
347
+ {
348
+ id: 'headers',
349
+ label: 'Sign-in headers',
350
+ icon: KeyRoundIcon,
351
+ info: 'Sent with every call to this server, such as a Cloudflare Access service token. Kept sealed on the server and never shown again.',
352
+ set: true,
353
+ ident: c.headers.length ? c.headers.join(', ') : 'None',
354
+ test: c.test
355
+ ? {
356
+ at: c.test.at,
357
+ ok: c.test.ok,
358
+ detail: c.test.detail,
359
+ error: c.test.ok ? null : c.test.detail,
360
+ }
361
+ : null,
362
+ fields: [{ id: 'url', label: 'Address', placeholder: c.url }, ...headerFields()],
363
+ addLabel: 'Replace',
364
+ onSave: (v) => apply('connector', () => client.saveConnector(c.id, {
365
+ name: c.name,
366
+ url: v.url?.trim() || c.url,
367
+ headers: headersOf(v),
368
+ }), 'Connector saved'),
369
+ ...(client.testConnector
370
+ ? {
371
+ onTest: () => client.testConnector(c.id).finally(refresh),
372
+ }
373
+ : {}),
374
+ onRemove: () => apply('connector', () => client.removeConnector(c.id), 'Connector removed'),
375
+ removeTitle: `Remove ${c.name}?`,
376
+ removeText: 'The assistant stops using its tools.',
377
+ },
378
+ ], lines: [
379
+ { label: 'Address', value: c.url, mono: true },
380
+ {
381
+ label: 'Tools',
382
+ info: 'The assistant finds these only when it needs them, so they cost nothing until used.',
383
+ value: c.tools.length
384
+ ? `${c.tools.map((t) => t.title ?? t.name).join(', ')} (${reads} read-only)`
385
+ : 'None listed',
386
+ },
387
+ ], children: _jsx(SettingField, { label: "Use", info: "Read uses the tools the server marks read-only. Read + draft also lets the others make a card the person saves or dismisses.", children: _jsx(ToggleGroup, { type: "single", variant: "outline", size: "sm", spacing: 0, value: c.access, "aria-label": `Use ${c.name}`, onValueChange: (v) => v && void access(v), children: CONNECTOR_ACCESS_LABELS.map((a) => (_jsx(ToggleGroupItem, { value: a.value, children: a.label }, a.value))) }) }) }));
388
+ }
389
+ /** Admin: the app's connectors, each an MCP server, and a row to add one. */
390
+ function Connectors({ client }) {
391
+ const { apply } = useApply(client);
392
+ return (_jsx(WithStatus, { client: client, children: (s) => (_jsxs(LumenExpandList, { children: [(s.connectors ?? []).map((c) => (_jsx(ConnectorRow, { client: client, c: c }, c.id))), _jsx(LumenConnection, { id: "connector-new", name: "Add connector", logo: _jsx(ConnectionLogo, { icon: PlugIcon }), meta: "Any MCP server over HTTP", ways: [
393
+ {
394
+ id: 'new',
395
+ label: 'MCP server',
396
+ icon: PlugIcon,
397
+ info: 'Its address (Streamable HTTP) and the headers it signs in with. It is tested before it is kept.',
398
+ set: false,
399
+ fields: [
400
+ { id: 'name', label: 'Name', placeholder: 'Web search' },
401
+ { id: 'url', label: 'Address', placeholder: 'https://example.com/mcp' },
402
+ ...headerFields(),
403
+ ],
404
+ addLabel: 'Add connector',
405
+ onSave: (v) => apply('connector', () => client.addConnector({
406
+ name: v.name?.trim() ?? '',
407
+ url: v.url?.trim() ?? '',
408
+ headers: headersOf(v),
409
+ }), 'Connector added'),
410
+ },
411
+ ] })] })) }));
412
+ }
315
413
  // ---------------------------------------------------------------- spend
316
414
  function Spend({ client }) {
317
415
  return (_jsx(WithStatus, { client: client, children: (s) => {
@@ -414,7 +512,7 @@ function AllowanceRequests({ client }) {
414
512
  * The admin's section: the app's shared keys and each person's monthly allowance on them. Put it
415
513
  * where the app keeps its admin settings, shown only to admins.
416
514
  */
417
- export function assistantAdminSections({ client, allowance = false, providers, }) {
515
+ export function assistantAdminSections({ client, allowance = false, providers, connectors = false, }) {
418
516
  const keys = providers
419
517
  ? [
420
518
  {
@@ -455,6 +553,17 @@ export function assistantAdminSections({ client, allowance = false, providers, }
455
553
  },
456
554
  ]
457
555
  : []),
556
+ ...(connectors
557
+ ? [
558
+ {
559
+ id: 'assistant-connectors',
560
+ title: 'Connectors',
561
+ icon: PlugIcon,
562
+ description: 'Outside services the assistant may use',
563
+ content: _jsx(Connectors, { client: client }),
564
+ },
565
+ ]
566
+ : []),
458
567
  ];
459
568
  }
460
569
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@columenlabs/lumen-ui",
3
- "version": "0.1.259",
3
+ "version": "0.1.260",
4
4
  "description": "Lumen: shared React components (frame, views, widgets, charts, drawers).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -49,7 +49,7 @@
49
49
  "@codemirror/lang-javascript": "6.2.5",
50
50
  "@codemirror/lang-json": "6.0.2",
51
51
  "@codemirror/lang-sql": "6.10.0",
52
- "@columenlabs/lumen-ui-types": "0.1.259",
52
+ "@columenlabs/lumen-ui-types": "0.1.260",
53
53
  "@dnd-kit/core": "6.3.1",
54
54
  "@dnd-kit/sortable": "10.0.0",
55
55
  "@dnd-kit/utilities": "3.2.2",