@datalayer/core 1.2.19 → 1.2.21
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/lib/api/evals/client.d.ts +6 -6
- package/lib/api/evals/client.js +5 -5
- package/lib/api/evals/status.d.ts +3 -3
- package/lib/api/evals/status.js +3 -3
- package/lib/api/evals/types.d.ts +9 -9
- package/lib/api/iam/secrets.js +2 -2
- package/lib/api/iam/servicePrincipals.js +1 -1
- package/lib/api/iam/trials.js +1 -1
- package/lib/api/orchestration/generated.d.ts +12 -12
- package/lib/api/orchestration/generated.js +1 -1
- package/lib/api/orchestration/index.d.ts +2 -2
- package/lib/api/orchestration/index.js +2 -2
- package/lib/api/orchestration/lifecycle.d.ts +1 -1
- package/lib/api/orchestration/lifecycle.js +1 -1
- package/lib/api/scheduler/client.d.ts +1 -1
- package/lib/api/scheduler/client.js +1 -1
- package/lib/api/scheduler/types.d.ts +1 -1
- package/lib/api/spacer/comments.d.ts +1 -1
- package/lib/api/spacer/comments.js +1 -1
- package/lib/api/spacer/spaces.d.ts +1 -1
- package/lib/api/spacer/spaces.js +1 -1
- package/lib/components/avatars/UserAvatar.d.ts +10 -11
- package/lib/components/avatars/UserAvatar.js +23 -28
- package/lib/components/avatars/UserProfileAvatar.js +1 -1
- package/lib/components/avatars/avatarChoice.d.ts +42 -0
- package/lib/components/avatars/avatarChoice.js +65 -0
- package/lib/components/avatars/index.d.ts +1 -0
- package/lib/components/avatars/index.js +1 -0
- package/lib/components/collaboration/LiveEditorCollaborators.d.ts +3 -4
- package/lib/components/collaboration/LiveEditorCollaborators.js +7 -5
- package/lib/components/emoji/faceEmojis.js +48 -0
- package/lib/components/emoji/fluent/loaders.js +46 -0
- package/lib/components/emoji/fluent/svg/1f30a.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f30a.js +5 -0
- package/lib/components/emoji/fluent/svg/1f310.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f310.js +5 -0
- package/lib/components/emoji/fluent/svg/1f324.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f324.js +5 -0
- package/lib/components/emoji/fluent/svg/1f326.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f326.js +5 -0
- package/lib/components/emoji/fluent/svg/1f327.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f327.js +5 -0
- package/lib/components/emoji/fluent/svg/1f33e.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f33e.js +5 -0
- package/lib/components/emoji/fluent/svg/1f372.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f372.js +5 -0
- package/lib/components/emoji/fluent/svg/1f373.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f373.js +5 -0
- package/lib/components/emoji/fluent/svg/1f39b.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f39b.js +5 -0
- package/lib/components/emoji/fluent/svg/1f3ab.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f3ab.js +5 -0
- package/lib/components/emoji/fluent/svg/1f41b.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f41b.js +5 -0
- package/lib/components/emoji/fluent/svg/1f441.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f441.js +5 -0
- package/lib/components/emoji/fluent/svg/1f465.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f465.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4b9.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4b9.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4be.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4be.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4c1.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4c1.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4c2.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4c2.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4c4.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4c4.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4d3.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4d3.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4d6.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4d6.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4dc.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4dc.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4e1.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4e1.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4e2.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4e2.js +5 -0
- package/lib/components/emoji/fluent/svg/1f4e7.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f4e7.js +5 -0
- package/lib/components/emoji/fluent/svg/1f500.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f500.js +5 -0
- package/lib/components/emoji/fluent/svg/1f501.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f501.js +5 -0
- package/lib/components/emoji/fluent/svg/1f504.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f504.js +5 -0
- package/lib/components/emoji/fluent/svg/1f52d.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f52d.js +5 -0
- package/lib/components/emoji/fluent/svg/1f552.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f552.js +5 -0
- package/lib/components/emoji/fluent/svg/1f58b.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f58b.js +5 -0
- package/lib/components/emoji/fluent/svg/1f5b2.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f5b2.js +5 -0
- package/lib/components/emoji/fluent/svg/1f5c3.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f5c3.js +5 -0
- package/lib/components/emoji/fluent/svg/1f5d1.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f5d1.js +5 -0
- package/lib/components/emoji/fluent/svg/1f5dc.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f5dc.js +5 -0
- package/lib/components/emoji/fluent/svg/1f604.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f604.js +5 -0
- package/lib/components/emoji/fluent/svg/1f917.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f917.js +5 -0
- package/lib/components/emoji/fluent/svg/1f9d1-200d-2696.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1f9d1-200d-2696.js +5 -0
- package/lib/components/emoji/fluent/svg/1fa79.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1fa79.js +5 -0
- package/lib/components/emoji/fluent/svg/1fa9d.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1fa9d.js +5 -0
- package/lib/components/emoji/fluent/svg/1fa9f.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/1fa9f.js +5 -0
- package/lib/components/emoji/fluent/svg/23f3.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/23f3.js +5 -0
- package/lib/components/emoji/fluent/svg/2611.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/2611.js +5 -0
- package/lib/components/emoji/fluent/svg/2702.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/2702.js +5 -0
- package/lib/components/emoji/fluent/svg/270f.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/270f.js +5 -0
- package/lib/components/emoji/fluent/svg/2728.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/2728.js +5 -0
- package/lib/components/emoji/fluent/svg/2b1c.d.ts +2 -0
- package/lib/components/emoji/fluent/svg/2b1c.js +5 -0
- package/lib/components/principal/Principal.js +1 -1
- package/lib/components/principal/PrincipalAppearance.d.ts +24 -1
- package/lib/components/principal/PrincipalAppearance.js +105 -34
- package/lib/components/principal/PrincipalAvatar.d.ts +5 -7
- package/lib/components/principal/PrincipalAvatar.js +12 -18
- package/lib/components/principal/PrincipalDetailsOverlay.d.ts +4 -7
- package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
- package/lib/components/principal/PrincipalSwitcherMenu.js +1 -1
- package/lib/hooks/unlinkIAMProvider.d.ts +22 -0
- package/lib/hooks/unlinkIAMProvider.js +24 -0
- package/lib/hooks/useCache.d.ts +8 -1
- package/lib/hooks/useCache.js +37 -6
- package/lib/hooks/useNavigate.js +8 -2
- package/lib/models/IAMProviderLinked.d.ts +9 -0
- package/lib/models/IAMProviderLinked.js +34 -5
- package/lib/models/McpTask.d.ts +18 -1
- package/lib/models/McpTask.js +15 -0
- package/lib/models/Profile.d.ts +2 -2
- package/lib/models/Secret.d.ts +1 -1
- package/lib/models/Secret.js +1 -1
- package/lib/models/StartedBy.d.ts +1 -1
- package/lib/models/StartedBy.js +1 -1
- package/lib/models/User.js +5 -12
- package/lib/models/UserOnboarding.d.ts +9 -0
- package/lib/models/UserOnboarding.js +33 -0
- package/lib/portable/index.d.ts +3 -2
- package/lib/portable/index.js +4 -2
- package/lib/portable/states.d.ts +39 -0
- package/lib/portable/states.js +71 -0
- package/lib/state/substates/NavigationState.d.ts +5 -3
- package/lib/state/substates/NavigationState.js +12 -3
- package/lib/views/mcp/AlertDestinations.js +2 -2
- package/lib/views/mcp/ApprovalQueue.js +2 -2
- package/lib/views/mcp/EnterpriseConsole.js +2 -2
- package/lib/views/mcp/McpDashboard.js +2 -2
- package/lib/views/mcp/McpHome.js +4 -4
- package/lib/views/mcp/McpObservability.js +5 -5
- package/lib/views/mcp/NotebookRuns.js +2 -2
- package/lib/views/mcp/OrganizationUsage.js +2 -2
- package/lib/views/mcp/Policies.js +2 -2
- package/lib/views/mcp/RunDetail.d.ts +1 -1
- package/lib/views/mcp/RunDetail.js +4 -1
- package/lib/views/mcp/ScimProvisioning.js +2 -2
- package/lib/views/mcp/ToolAccess.js +2 -2
- package/lib/views/secrets/SecretNew.js +1 -1
- package/package.json +1 -1
- package/scripts/generate-orchestration-types.py +12 -12
|
@@ -2,12 +2,41 @@
|
|
|
2
2
|
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* The account a provider returned, as IAM keeps it in `linked_account_s`.
|
|
7
|
+
* GitHub, Google and LinkedIn keep a JSON object; Bluesky and X keep a plain
|
|
8
|
+
* handle, which is no object to read, so it reads as `{}`.
|
|
9
|
+
*/
|
|
10
|
+
function linkedAccountOf(raw) {
|
|
11
|
+
if (typeof raw !== 'string' || raw.trim() === '') {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(raw);
|
|
16
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
17
|
+
? parsed
|
|
18
|
+
: {};
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* One `iam_providers` child of the user IAM returns (whoami, login): its
|
|
26
|
+
* Solr fields `iam_provider_name_s`, `linked_account_s`,
|
|
27
|
+
* `linked_account_url_s`, `linked_account_id_s`.
|
|
28
|
+
*/
|
|
5
29
|
export function asIAMProviderLinked(iamProvider) {
|
|
6
|
-
const info = iamProvider.linkedAccount.replaceAll("'", '"');
|
|
7
30
|
return {
|
|
8
|
-
iamProviderName: iamProvider.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
31
|
+
iamProviderName: String(iamProvider.iam_provider_name_s ?? '')
|
|
32
|
+
.trim()
|
|
33
|
+
.toLowerCase(),
|
|
34
|
+
linkedAccount: linkedAccountOf(iamProvider.linked_account_s),
|
|
35
|
+
linkedAccountUrl: iamProvider.linked_account_url_s ?? '',
|
|
36
|
+
linkedAccountId: iamProvider.linked_account_id_s ?? '',
|
|
12
37
|
};
|
|
13
38
|
}
|
|
39
|
+
/** Whether the user has linked this provider in IAM. */
|
|
40
|
+
export function hasLinkedIAMProvider(user, name) {
|
|
41
|
+
return (user?.iamProviders ?? []).some(p => p.iamProviderName === name);
|
|
42
|
+
}
|
package/lib/models/McpTask.d.ts
CHANGED
|
@@ -58,7 +58,12 @@ export interface McpTask {
|
|
|
58
58
|
pollIntervalMs?: number | null;
|
|
59
59
|
/** The `CallToolResult` for small results; a resource URI otherwise. */
|
|
60
60
|
result?: McpCallToolResult | string | null;
|
|
61
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Why it failed, as the server keeps it: an object — empty while nothing
|
|
63
|
+
* failed, else its `reason`, and where one applies the `code`, the `step`
|
|
64
|
+
* and what the resource was doing.
|
|
65
|
+
*/
|
|
66
|
+
error?: McpTaskError | null;
|
|
62
67
|
traceId?: string | null;
|
|
63
68
|
workflowEngine?: McpWorkflowEngine | null;
|
|
64
69
|
/** Always the task uid, so a retried start cannot create a second execution. */
|
|
@@ -92,3 +97,15 @@ export interface McpTaskEvent {
|
|
|
92
97
|
/** The statuses from which nothing more happens. */
|
|
93
98
|
export declare const MCP_TERMINAL_TASK_STATUSES: ReadonlySet<McpTaskStatus>;
|
|
94
99
|
export declare const isMcpTaskTerminal: (status: McpTaskStatus) => boolean;
|
|
100
|
+
/** A task's failure, as the server keeps it (camel-cased like the rest). */
|
|
101
|
+
export type McpTaskError = {
|
|
102
|
+
reason?: string;
|
|
103
|
+
code?: string;
|
|
104
|
+
step?: string;
|
|
105
|
+
[key: string]: unknown;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Why a task failed, in a line: its reason, after its code where it has one;
|
|
109
|
+
* `null` when the server's object says nothing — an empty one is no failure.
|
|
110
|
+
*/
|
|
111
|
+
export declare const taskFailureOf: (error: McpTaskError | null | undefined) => string | null;
|
package/lib/models/McpTask.js
CHANGED
|
@@ -5,3 +5,18 @@
|
|
|
5
5
|
/** The statuses from which nothing more happens. */
|
|
6
6
|
export const MCP_TERMINAL_TASK_STATUSES = new Set(['completed', 'failed', 'cancelled']);
|
|
7
7
|
export const isMcpTaskTerminal = (status) => MCP_TERMINAL_TASK_STATUSES.has(status);
|
|
8
|
+
/**
|
|
9
|
+
* Why a task failed, in a line: its reason, after its code where it has one;
|
|
10
|
+
* `null` when the server's object says nothing — an empty one is no failure.
|
|
11
|
+
*/
|
|
12
|
+
export const taskFailureOf = (error) => {
|
|
13
|
+
if (!error || Object.keys(error).length === 0) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const reason = typeof error.reason === 'string' ? error.reason : null;
|
|
17
|
+
const code = typeof error.code === 'string' ? error.code : null;
|
|
18
|
+
if (reason) {
|
|
19
|
+
return code ? `${code}: ${reason}` : reason;
|
|
20
|
+
}
|
|
21
|
+
return code ?? JSON.stringify(error);
|
|
22
|
+
};
|
package/lib/models/Profile.d.ts
CHANGED
|
@@ -34,8 +34,8 @@ export interface Profile {
|
|
|
34
34
|
avatar_url_s?: string;
|
|
35
35
|
avatar_icon_s?: string;
|
|
36
36
|
banner_s?: string;
|
|
37
|
-
/** Onboarding state
|
|
38
|
-
|
|
37
|
+
/** Onboarding state: the child IAM keeps on the user (read by `userOnboardingOf`). */
|
|
38
|
+
onboarding?: Record<string, unknown>;
|
|
39
39
|
/** New password request timestamp */
|
|
40
40
|
new_password_request_ts_dt?: string | null;
|
|
41
41
|
/** New password confirmation timestamp */
|
package/lib/models/Secret.d.ts
CHANGED
|
@@ -144,7 +144,7 @@ export declare class SecretDTO {
|
|
|
144
144
|
get variant(): ISecretVariant;
|
|
145
145
|
get name(): string;
|
|
146
146
|
get description(): string;
|
|
147
|
-
/** The value as its owner wrote it (
|
|
147
|
+
/** The value as its owner wrote it (E3-05). */
|
|
148
148
|
get value(): string;
|
|
149
149
|
/**
|
|
150
150
|
* Update this secret.
|
package/lib/models/Secret.js
CHANGED
|
@@ -74,7 +74,7 @@ export class SecretDTO {
|
|
|
74
74
|
this._checkDeleted();
|
|
75
75
|
return this._data.description_t;
|
|
76
76
|
}
|
|
77
|
-
/** The value as its owner wrote it (
|
|
77
|
+
/** The value as its owner wrote it (E3-05). */
|
|
78
78
|
get value() {
|
|
79
79
|
this._checkDeleted();
|
|
80
80
|
return this._data.value_s;
|
|
@@ -50,7 +50,7 @@ export declare const startedBy: (metadata: UsageMetadata) => StartedBy;
|
|
|
50
50
|
* The words a search box matches on for this row.
|
|
51
51
|
*
|
|
52
52
|
* "agent" matches every agent-started runtime, the client id matches one
|
|
53
|
-
* client, and "you" matches your own — so the filter
|
|
53
|
+
* client, and "you" matches your own — so the filter asked for is the
|
|
54
54
|
* search box that is already there rather than a second control beside it.
|
|
55
55
|
*/
|
|
56
56
|
export declare const startedBySearchTerms: (metadata: UsageMetadata) => string;
|
package/lib/models/StartedBy.js
CHANGED
|
@@ -65,7 +65,7 @@ export const startedBy = (metadata) => {
|
|
|
65
65
|
* The words a search box matches on for this row.
|
|
66
66
|
*
|
|
67
67
|
* "agent" matches every agent-started runtime, the client id matches one
|
|
68
|
-
* client, and "you" matches your own — so the filter
|
|
68
|
+
* client, and "you" matches your own — so the filter asked for is the
|
|
69
69
|
* search box that is already there rather than a second control beside it.
|
|
70
70
|
*/
|
|
71
71
|
export const startedBySearchTerms = (metadata) => {
|
package/lib/models/User.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { asDisplayName, namesAsInitials, toFriendlyName } from '../utils/index.js';
|
|
6
6
|
import { asIAMProviderLinked } from './IAMProviderLinked.js';
|
|
7
|
-
import {
|
|
7
|
+
import { userOnboardingOf } from './UserOnboarding.js';
|
|
8
8
|
import { UserSettings } from './UserSettings.js';
|
|
9
9
|
import { asUserEvent } from './UserEvent.js';
|
|
10
10
|
export const ANONYMOUS_USER = undefined;
|
|
@@ -76,20 +76,13 @@ export class User {
|
|
|
76
76
|
this.credits = u.credits_i ? Number(u.credits_i) : 0;
|
|
77
77
|
this.creditsCustomerId = u.stripe_customer_id_s;
|
|
78
78
|
this.roles = u.roles_ss ?? [];
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
catch (e) {
|
|
84
|
-
// no-op for backwards compatibility.
|
|
85
|
-
}
|
|
86
|
-
this.iamProviders = iamProviders;
|
|
79
|
+
// IAM sends one linked provider as an object, several as a list.
|
|
80
|
+
const rawIAMProviders = u.iam_providers ?? [];
|
|
81
|
+
this.iamProviders = (Array.isArray(rawIAMProviders) ? rawIAMProviders : [rawIAMProviders]).map(asIAMProviderLinked);
|
|
87
82
|
this.settings = new UserSettings(u.settings ?? {});
|
|
88
83
|
this.unsubscribedFromOutbounds = u.unsubscribed_from_outbounds_b ?? false;
|
|
89
84
|
this.mfaUrl = u.mfa_url_s;
|
|
90
|
-
this.onboarding = u.
|
|
91
|
-
? JSON.parse(u.onboarding_s)
|
|
92
|
-
: BOOTSTRAP_USER_ONBOARDING;
|
|
85
|
+
this.onboarding = userOnboardingOf(u.onboarding);
|
|
93
86
|
this.linkedContactId = u.linked_contact_uid;
|
|
94
87
|
this.subscription = u.subscription;
|
|
95
88
|
let events = u.events ?? [];
|
|
@@ -12,3 +12,12 @@ export type IUserOnboarding = {
|
|
|
12
12
|
position: IOnboardingPosition;
|
|
13
13
|
tours: IOnboardingTours;
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* The user's onboarding, as IAM keeps it: a child of the user whose fields
|
|
17
|
+
* say the guide's position (`position_s`), each client's progress
|
|
18
|
+
* (`client_<client>_i`) and each tour's status (`tour_<tour>_status_s`), over
|
|
19
|
+
* what nothing has said yet. Read from the old `onboarding_s` string, a
|
|
20
|
+
* status saved since was never seen: the welcome tour started again on every
|
|
21
|
+
* load and was saved again (13 October 2026).
|
|
22
|
+
*/
|
|
23
|
+
export declare const userOnboardingOf: (child: unknown) => IUserOnboarding;
|
|
@@ -17,3 +17,36 @@ export const BOOTSTRAP_USER_ONBOARDING = {
|
|
|
17
17
|
},
|
|
18
18
|
},
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* The user's onboarding, as IAM keeps it: a child of the user whose fields
|
|
22
|
+
* say the guide's position (`position_s`), each client's progress
|
|
23
|
+
* (`client_<client>_i`) and each tour's status (`tour_<tour>_status_s`), over
|
|
24
|
+
* what nothing has said yet. Read from the old `onboarding_s` string, a
|
|
25
|
+
* status saved since was never seen: the welcome tour started again on every
|
|
26
|
+
* load and was saved again (13 October 2026).
|
|
27
|
+
*/
|
|
28
|
+
export const userOnboardingOf = (child) => {
|
|
29
|
+
const stored = (Array.isArray(child) ? child[0] : child);
|
|
30
|
+
const onboarding = {
|
|
31
|
+
clients: { ...BOOTSTRAP_USER_ONBOARDING.clients },
|
|
32
|
+
position: BOOTSTRAP_USER_ONBOARDING.position,
|
|
33
|
+
tours: { ...BOOTSTRAP_USER_ONBOARDING.tours },
|
|
34
|
+
};
|
|
35
|
+
if (!stored || typeof stored !== 'object') {
|
|
36
|
+
return onboarding;
|
|
37
|
+
}
|
|
38
|
+
if (typeof stored.position_s === 'string') {
|
|
39
|
+
onboarding.position = stored.position_s;
|
|
40
|
+
}
|
|
41
|
+
for (const [key, value] of Object.entries(stored)) {
|
|
42
|
+
const client = /^client_(.+)_i$/.exec(key)?.[1];
|
|
43
|
+
if (client && typeof value === 'number') {
|
|
44
|
+
onboarding.clients[client] = value;
|
|
45
|
+
}
|
|
46
|
+
const tour = /^tour_(.+)_status_s$/.exec(key)?.[1];
|
|
47
|
+
if (tour && typeof value === 'string') {
|
|
48
|
+
onboarding.tours[tour] = { status: value };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return onboarding;
|
|
52
|
+
};
|
package/lib/portable/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What any JavaScript runtime can import from Core: no React, DOM, Jupyter or
|
|
3
|
-
* Node built-in.
|
|
4
|
-
*
|
|
3
|
+
* Node built-in. The mobile app reaches Core through this entry alone, and
|
|
4
|
+
* its modules import nothing but each other and Core's pure models.
|
|
5
5
|
*
|
|
6
6
|
* @module portable
|
|
7
7
|
*/
|
|
8
8
|
export { personOf, type Person } from '../models/Person';
|
|
9
|
+
export { detailOf, isProblem, kindOfStatus, Problem, PROBLEM_WORDS, problemOf, retries, STATE_WORDS, type ProblemKind, } from './states';
|
package/lib/portable/index.js
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
6
|
* What any JavaScript runtime can import from Core: no React, DOM, Jupyter or
|
|
7
|
-
* Node built-in.
|
|
8
|
-
*
|
|
7
|
+
* Node built-in. The mobile app reaches Core through this entry alone, and
|
|
8
|
+
* its modules import nothing but each other and Core's pure models.
|
|
9
9
|
*
|
|
10
10
|
* @module portable
|
|
11
11
|
*/
|
|
12
12
|
export { personOf } from '../models/Person.js';
|
|
13
|
+
// Loading, empty and problem states, in a person's words (MOBILE M-013b).
|
|
14
|
+
export { detailOf, isProblem, kindOfStatus, Problem, PROBLEM_WORDS, problemOf, retries, STATE_WORDS, } from './states.js';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loading, empty and problem states as a person is told them (MOBILE M-013b):
|
|
3
|
+
* what went wrong in their words, the diagnostic detail kept behind
|
|
4
|
+
* *Details* (never a token, a prompt or tool arguments, never a query value),
|
|
5
|
+
* whether trying again can help, and the words around them. A renderer on
|
|
6
|
+
* any platform draws these; it does not word them itself.
|
|
7
|
+
*
|
|
8
|
+
* @module portable/states
|
|
9
|
+
*/
|
|
10
|
+
/** What went wrong, as a person is told it. */
|
|
11
|
+
export type ProblemKind = 'unauthenticated' | 'forbidden' | 'not-found' | 'conflict' | 'rate-limited' | 'unavailable' | 'timeout' | 'offline' | 'unexpected';
|
|
12
|
+
export declare const PROBLEM_WORDS: Record<ProblemKind, string>;
|
|
13
|
+
/** The words around a state: loading, nothing yet, trying again, the detail. */
|
|
14
|
+
export declare const STATE_WORDS: {
|
|
15
|
+
readonly loading: "Loading";
|
|
16
|
+
readonly empty: "Nothing yet.";
|
|
17
|
+
readonly retry: "Try again";
|
|
18
|
+
readonly details: "Details";
|
|
19
|
+
readonly hideDetails: "Hide details";
|
|
20
|
+
readonly showDetailsLabel: "Show diagnostic details";
|
|
21
|
+
readonly hideDetailsLabel: "Hide diagnostic details";
|
|
22
|
+
};
|
|
23
|
+
/** A failed request: the person's words, and the detail behind *Details*. */
|
|
24
|
+
export declare class Problem extends Error {
|
|
25
|
+
readonly kind: ProblemKind;
|
|
26
|
+
readonly status?: number;
|
|
27
|
+
/** For *Details* only; never a token, a prompt or tool arguments. */
|
|
28
|
+
readonly detail: string;
|
|
29
|
+
constructor(kind: ProblemKind, detail: string, status?: number);
|
|
30
|
+
}
|
|
31
|
+
export declare const isProblem: (error: unknown) => error is Problem;
|
|
32
|
+
/** A thrown value as a Problem: anything unknown is *unexpected*. */
|
|
33
|
+
export declare const problemOf: (error: unknown) => Problem;
|
|
34
|
+
/** The kind of an HTTP status that failed. */
|
|
35
|
+
export declare function kindOfStatus(status: number): ProblemKind;
|
|
36
|
+
/** Whether trying again can help: not for what is gone or not theirs. */
|
|
37
|
+
export declare const retries: (problem: Problem) => boolean;
|
|
38
|
+
/** The detail a server gave, without query values that could carry a token. */
|
|
39
|
+
export declare function detailOf(method: string, url: string, status: number, body: unknown): string;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
export const PROBLEM_WORDS = {
|
|
6
|
+
unauthenticated: 'Your session has ended. Sign in again.',
|
|
7
|
+
forbidden: 'You do not have access to this.',
|
|
8
|
+
'not-found': 'This item is no longer available.',
|
|
9
|
+
conflict: 'This changed somewhere else. It has been refreshed.',
|
|
10
|
+
'rate-limited': 'Too many requests. Try again in a moment.',
|
|
11
|
+
unavailable: 'Datalayer is not answering right now. Try again.',
|
|
12
|
+
timeout: 'This is taking too long. Try again.',
|
|
13
|
+
offline: 'You are offline. Connect to continue.',
|
|
14
|
+
unexpected: 'Something went wrong.',
|
|
15
|
+
};
|
|
16
|
+
/** The words around a state: loading, nothing yet, trying again, the detail. */
|
|
17
|
+
export const STATE_WORDS = {
|
|
18
|
+
loading: 'Loading',
|
|
19
|
+
empty: 'Nothing yet.',
|
|
20
|
+
retry: 'Try again',
|
|
21
|
+
details: 'Details',
|
|
22
|
+
hideDetails: 'Hide details',
|
|
23
|
+
showDetailsLabel: 'Show diagnostic details',
|
|
24
|
+
hideDetailsLabel: 'Hide diagnostic details',
|
|
25
|
+
};
|
|
26
|
+
/** A failed request: the person's words, and the detail behind *Details*. */
|
|
27
|
+
export class Problem extends Error {
|
|
28
|
+
kind;
|
|
29
|
+
status;
|
|
30
|
+
/** For *Details* only; never a token, a prompt or tool arguments. */
|
|
31
|
+
detail;
|
|
32
|
+
constructor(kind, detail, status) {
|
|
33
|
+
super(PROBLEM_WORDS[kind]);
|
|
34
|
+
this.name = 'Problem';
|
|
35
|
+
this.kind = kind;
|
|
36
|
+
this.detail = detail;
|
|
37
|
+
this.status = status;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export const isProblem = (error) => error instanceof Problem;
|
|
41
|
+
/** A thrown value as a Problem: anything unknown is *unexpected*. */
|
|
42
|
+
export const problemOf = (error) => isProblem(error)
|
|
43
|
+
? error
|
|
44
|
+
: new Problem('unexpected', error instanceof Error ? error.message : String(error));
|
|
45
|
+
/** The kind of an HTTP status that failed. */
|
|
46
|
+
export function kindOfStatus(status) {
|
|
47
|
+
if (status === 401)
|
|
48
|
+
return 'unauthenticated';
|
|
49
|
+
if (status === 403)
|
|
50
|
+
return 'forbidden';
|
|
51
|
+
if (status === 404)
|
|
52
|
+
return 'not-found';
|
|
53
|
+
if (status === 409)
|
|
54
|
+
return 'conflict';
|
|
55
|
+
if (status === 429)
|
|
56
|
+
return 'rate-limited';
|
|
57
|
+
if (status === 502 || status === 503 || status === 504)
|
|
58
|
+
return 'unavailable';
|
|
59
|
+
return 'unexpected';
|
|
60
|
+
}
|
|
61
|
+
/** Whether trying again can help: not for what is gone or not theirs. */
|
|
62
|
+
export const retries = (problem) => problem.kind !== 'not-found' && problem.kind !== 'forbidden';
|
|
63
|
+
/** The detail a server gave, without query values that could carry a token. */
|
|
64
|
+
export function detailOf(method, url, status, body) {
|
|
65
|
+
const path = url.split('?')[0];
|
|
66
|
+
const said = body && typeof body === 'object'
|
|
67
|
+
? (body.detail ??
|
|
68
|
+
body.message)
|
|
69
|
+
: undefined;
|
|
70
|
+
return `${method} ${path} → ${status}${typeof said === 'string' ? `: ${said}` : ''}`;
|
|
71
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/** The signed-in shell: the platform, the business cases, or the admin. */
|
|
2
1
|
/**
|
|
3
|
-
* The
|
|
2
|
+
* The six shells of the signed-in application.
|
|
4
3
|
*
|
|
5
4
|
* `studio` is where an agentic UI application is composed, validated and
|
|
6
5
|
* shipped — the default for somebody who has not chosen, and what the root
|
|
@@ -12,8 +11,11 @@
|
|
|
12
11
|
* a sidebar of their own, every address under `/apps`, and the workspace at
|
|
13
12
|
* `/apps` itself. It was `loop`, at `/loop`: a cookie still saying `loop`
|
|
14
13
|
* names no shell and is ignored, the reader opening on the Studio.
|
|
14
|
+
* `research` is the research areas, each one a space of the
|
|
15
|
+
* `datalayer-research` organization, at `/research` (the public papers, read
|
|
16
|
+
* signed in, at `/research/papers/…` beside them).
|
|
15
17
|
*/
|
|
16
|
-
export type NavigationView = 'studio' | 'home' | 'agentify' | 'admin' | 'apps';
|
|
18
|
+
export type NavigationView = 'studio' | 'home' | 'agentify' | 'research' | 'admin' | 'apps';
|
|
17
19
|
/** The shells, and where each one opens when it has not been visited. */
|
|
18
20
|
export declare const NAVIGATION_VIEW_HOMES: Record<NavigationView, string>;
|
|
19
21
|
/** Well-known view identifiers for {@link NavigationState.tabsByView}. */
|
|
@@ -34,10 +34,13 @@ import { registerSessionState } from '../sessionEnd.js';
|
|
|
34
34
|
/** The shells, and where each one opens when it has not been visited. */
|
|
35
35
|
export const NAVIGATION_VIEW_HOMES = {
|
|
36
36
|
studio: '/studio',
|
|
37
|
-
// Not `/`: the root is the Studio's, so the
|
|
38
|
-
// its own to be sent to.
|
|
39
|
-
|
|
37
|
+
// Not `/`: the root is the Studio's, so the Platform shell has an address
|
|
38
|
+
// of its own to be sent to. It was `/power` until 2026-10-09, when the
|
|
39
|
+
// shell took the name it is called by everywhere else.
|
|
40
|
+
home: '/platform',
|
|
40
41
|
agentify: '/agentify',
|
|
42
|
+
// The research areas and their spaces.
|
|
43
|
+
research: '/research',
|
|
41
44
|
admin: '/admin',
|
|
42
45
|
// The applications: what a builder opens the Apps shell for.
|
|
43
46
|
apps: '/apps/apps',
|
|
@@ -58,6 +61,7 @@ const COOKIE_MAX_AGE_DAYS = 365;
|
|
|
58
61
|
const isView = (value) => value === 'studio' ||
|
|
59
62
|
value === 'home' ||
|
|
60
63
|
value === 'agentify' ||
|
|
64
|
+
value === 'research' ||
|
|
61
65
|
value === 'admin' ||
|
|
62
66
|
value === 'apps';
|
|
63
67
|
function readCookie(name) {
|
|
@@ -244,6 +248,11 @@ export function viewForRoute(route, current) {
|
|
|
244
248
|
if (path === '/agentify' || path.startsWith('/agentify/')) {
|
|
245
249
|
return 'agentify';
|
|
246
250
|
}
|
|
251
|
+
// The research areas — and the papers, read signed in under
|
|
252
|
+
// `/research/papers/…`, with the areas' sidebar beside them.
|
|
253
|
+
if (path === '/research' || path.startsWith('/research/')) {
|
|
254
|
+
return 'research';
|
|
255
|
+
}
|
|
247
256
|
if (path === '/admin' || path.startsWith('/admin/')) {
|
|
248
257
|
return 'admin';
|
|
249
258
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { Button, Flash, FormControl, Heading, Spinner, Text, TextInput, } from '@primer/react';
|
|
4
|
-
import { Box } from '@datalayer/primer-addons';
|
|
4
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
5
5
|
import { McpErrorBlankslate } from '../../components/mcp/index.js';
|
|
6
6
|
import { useAuditSettings, useSetAuditSettings } from '../../hooks/useMcp.js';
|
|
7
7
|
import { useToast } from '../../hooks/index.js';
|
|
@@ -62,6 +62,6 @@ export const AlertDestinations = ({ errorState, orgUid, readOnly = false, }) =>
|
|
|
62
62
|
if (settings.isPending && settings.data === undefined) {
|
|
63
63
|
return (_jsx(Box, { display: "flex", justifyContent: "center", py: 4, children: _jsx(Spinner, {}) }));
|
|
64
64
|
}
|
|
65
|
-
return (_jsxs(
|
|
65
|
+
return (_jsxs(Card, { border: true, shadow: "none", display: "grid", gap: 3, maxWidth: "42rem", p: 3, children: [_jsxs(Box, { children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2, mb: 1 }, children: "Where alerts go" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "Set once for the organization, not per rule \u2014 a URL repeated on twenty rules is nineteen places to forget when it rotates. Every firing also appears in the app, and that copy is sent first so it is not lost when one of these is down." })] }), refusal && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: refusal }) })), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Email" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertEmails, onChange: event => set('alertEmails', event.target.value), placeholder: "ops@example.co, oncall@example.co" }), _jsx(FormControl.Caption, { children: "Comma-separated, up to twenty. An alert list is the people who want to know, not a mailing list \u2014 a firing that emails two hundred people is a firing that gets the rule switched off." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Slack" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertSlackWebhookUrl, onChange: event => set('alertSlackWebhookUrl', event.target.value), placeholder: "https://hooks.slack.com/services/\u2026" }), _jsx(FormControl.Caption, { children: "A Slack incoming webhook, and only Slack\u2019s \u2014 anything else belongs in the field below. Through this one Slack gets a coloured attachment with the reading, the severity and the scope as separate fields; through the generic webhook it shows one grey line." })] }), _jsxs(FormControl, { children: [_jsx(FormControl.Label, { children: "Webhook" }), _jsx(TextInput, { block: true, disabled: readOnly, value: draft.alertWebhookUrl, onChange: event => set('alertWebhookUrl', event.target.value), placeholder: "https://events.pagerduty.com/\u2026" }), _jsxs(FormControl.Caption, { children: ["Anything that takes a JSON POST: PagerDuty, a chat relay, your own endpoint. ", _jsx("strong", { children: "https only" }), " \u2014 an alert names your organization and what its agents did. It carries no credential."] })] }), !readOnly && (_jsxs(Box, { display: "flex", gap: 2, children: [_jsx(Button, { variant: "primary", onClick: apply, disabled: !changed || save.isPending, children: "Save destinations" }), changed && (_jsx(Button, { variant: "invisible", onClick: () => setDraft(stored), children: "Discard changes" }))] })), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.subtle' }, children: ["Each copy is attempted on its own: if the webhook is down you still get the email. A copy that fails is recorded and", ' ', _jsx("strong", { children: "not retried" }), " \u2014 retrying a notice against an endpoint that is down turns its outage into a loop against it, and the alert is already in the app."] })] }));
|
|
66
66
|
};
|
|
67
67
|
export default AlertDestinations;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Button, Heading, Label, RelativeTime, SegmentedControl, Spinner, Text, Textarea, } from '@primer/react';
|
|
4
|
-
import { Box } from '@datalayer/primer-addons';
|
|
4
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
5
5
|
import { McpErrorBlankslate } from '../../components/mcp/index.js';
|
|
6
6
|
import { useDecideMcpApproval, useMcpApprovals } from '../../hooks/useContents.js';
|
|
7
7
|
/** How each state of an approval is drawn. */
|
|
@@ -59,7 +59,7 @@ export const ApprovalQueue = ({ errorState, sourceUid, }) => {
|
|
|
59
59
|
: `No ${APPROVAL_LOOK[status].label.toLowerCase()} approvals.` })) : (items.map(approval => {
|
|
60
60
|
const look = approvalLook(approval);
|
|
61
61
|
const decidable = isDecidable(approval);
|
|
62
|
-
return (_jsxs(
|
|
62
|
+
return (_jsxs(Card, { border: true, shadow: "none", borderColor: decidable ? 'attention.emphasis' : 'border.default', p: 3, display: "grid", gap: 2, minWidth: 0, children: [_jsxs(Box, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap", children: [_jsx(Label, { size: "small", variant: look.variant, children: look.label }), _jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: approval.tool }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', fontFamily: 'mono' }, children: approval.actorUid }), _jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["asked ", _jsx(RelativeTime, { datetime: approval.createdAt })] }), decidable && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["expires ", _jsx(RelativeTime, { datetime: approval.expiresAt })] }))] }), _jsx(Box, { as: "pre", fontFamily: "mono", fontSize: 0, whiteSpace: "pre-wrap", overflowX: "auto", bg: "canvas.subtle", borderRadius: 2, p: 2, m: 0, children: JSON.stringify(approval.argumentsRedacted, null, 2) }), approval.destinationUri && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Writes to", ' ', _jsx(Text, { as: "span", sx: { fontFamily: 'mono' }, children: approval.destinationUri })] })), decidable ? (_jsxs(_Fragment, { children: [_jsx(Textarea, { "aria-label": `Why, for ${approval.tool}`, rows: 2, placeholder: "Why (optional; kept with the decision)", value: noteFor[approval.uid] ?? '', onChange: event => setNoteFor(current => ({
|
|
63
63
|
...current,
|
|
64
64
|
[approval.uid]: event.target.value,
|
|
65
65
|
})) }), _jsxs(Box, { display: "flex", gap: 2, children: [_jsx(Button, { variant: "primary", disabled: decide.isPending, onClick: () => decide.mutate({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Button, Heading, Label, SegmentedControl, Spinner, Text, } from '@primer/react';
|
|
3
3
|
import { Blankslate, DataTable, Table } from '@primer/react/experimental';
|
|
4
|
-
import { Box } from '@datalayer/primer-addons';
|
|
4
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
5
5
|
import { AlertIcon, CheckCircleIcon, LockIcon, PlugIcon, } from '@primer/octicons-react';
|
|
6
6
|
import { ClientBadge, McpErrorBlankslate, ScopeList, } from '../../components/mcp/index.js';
|
|
7
7
|
import { useDisconnectAgent, useMcpActivity, useOrgMcpOverview, } from '../../hooks/useMcp.js';
|
|
@@ -90,7 +90,7 @@ export const pagesForRoles = (roles) => {
|
|
|
90
90
|
}
|
|
91
91
|
return [];
|
|
92
92
|
};
|
|
93
|
-
const Metric = ({ label, value, note, tone = 'neutral', }) => (_jsxs(
|
|
93
|
+
const Metric = ({ label, value, note, tone = 'neutral', }) => (_jsxs(Card, { border: true, shadow: "none", display: "block", p: 3, minWidth: 0, children: [_jsx(Text, { sx: {
|
|
94
94
|
display: 'block',
|
|
95
95
|
fontSize: 4,
|
|
96
96
|
fontWeight: 'bold',
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { ActionList, ActionMenu, Button, Heading, Label, Link, RelativeTime, Select, Spinner, Text, } from '@primer/react';
|
|
4
4
|
import { Blankslate, DataTable, Dialog, Table, } from '@primer/react/experimental';
|
|
5
|
-
import { Box } from '@datalayer/primer-addons';
|
|
5
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
6
6
|
import { ChecklistIcon, KebabHorizontalIcon, PlayIcon, PlugIcon, StackIcon, } from '@primer/octicons-react';
|
|
7
7
|
import { ShareAccessDialog, sandboxSharingUrl } from '../../components/sharing/index.js';
|
|
8
8
|
import { ClientBadge, McpErrorBlankslate } from '../../components/mcp/index.js';
|
|
@@ -54,7 +54,7 @@ const boundTo = (client) => {
|
|
|
54
54
|
return '';
|
|
55
55
|
};
|
|
56
56
|
const countLook = (tone) => tone === 'danger' ? 'danger.fg' : 'fg.default';
|
|
57
|
-
const Count = ({ label, value, tone = 'neutral', note, to, onOpen, }) => (_jsxs(
|
|
57
|
+
const Count = ({ label, value, tone = 'neutral', note, to, onOpen, }) => (_jsxs(Card, { border: true, shadow: "none", display: "block", p: 3, minWidth: 0, children: [_jsx(Text, { sx: {
|
|
58
58
|
display: 'block',
|
|
59
59
|
fontSize: 4,
|
|
60
60
|
fontWeight: 'bold',
|
package/lib/views/mcp/McpHome.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { AiAgentIcon } from '@datalayer/icons-react';
|
|
4
4
|
import { Button, Heading, IconButton, Link, Text } from '@primer/react';
|
|
5
|
-
import { Box } from '@datalayer/primer-addons';
|
|
5
|
+
import { Box, Card } from '@datalayer/primer-addons';
|
|
6
6
|
import { CheckIcon, CopyIcon, KeyIcon, PulseIcon, RssIcon, ShieldCheckIcon, TelescopeIcon, ToolsIcon, } from '@primer/octicons-react';
|
|
7
7
|
import { useConnectedAgents, useMcpActivity, useMcpMetrics, useTasks, } from '../../hooks/useMcp.js';
|
|
8
8
|
import { useNavigate } from '../../hooks/index.js';
|
|
@@ -56,7 +56,7 @@ export const Copyable = ({ text }) => {
|
|
|
56
56
|
} })] }));
|
|
57
57
|
};
|
|
58
58
|
/** One of the five summaries: a heading, what it holds, and the way in. */
|
|
59
|
-
const Summary = ({ icon: Icon, title, children, action, to, onOpen, }) => (_jsxs(
|
|
59
|
+
const Summary = ({ icon: Icon, title, children, action, to, onOpen, }) => (_jsxs(Card, { border: true, shadow: "none", p: 3, display: "grid", gap: 2, alignContent: "start", minWidth: 0, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(Icon, { size: 16 }), to ? (_jsx(Link, { href: to, onClick: event => {
|
|
60
60
|
// A modified click asks for a new tab; `href` is real, so let it.
|
|
61
61
|
if (event.metaKey || event.ctrlKey || event.shiftKey) {
|
|
62
62
|
return;
|
|
@@ -77,11 +77,11 @@ export const McpHome = ({ routes, clientDocsBase = '/docs/mcp-clients', showTitl
|
|
|
77
77
|
const connected = agents.data ?? [];
|
|
78
78
|
const runs = tasks.data?.items ?? [];
|
|
79
79
|
const live = activity.data?.clients.length ?? 0;
|
|
80
|
-
return (_jsxs(Box, { display: "grid", gap: 4, minWidth: 0, children: [showTitle && (_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "MCP" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "Connect your agents to your notebooks, data and sandboxes through the Model Context Protocol." })] })), showSetup && (_jsxs(_Fragment, { children: [_jsxs(
|
|
80
|
+
return (_jsxs(Box, { display: "grid", gap: 4, minWidth: 0, children: [showTitle && (_jsxs(Box, { children: [_jsx(Heading, { as: "h2", sx: { fontSize: 3, mb: 1 }, children: "MCP" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: "Connect your agents to your notebooks, data and sandboxes through the Model Context Protocol." })] })), showSetup && (_jsxs(_Fragment, { children: [_jsxs(Card, { border: true, shadow: "none", variant: "subtle", flexDirection: "row", display: "flex", alignItems: "center", gap: 3, flexWrap: "wrap", p: 3, children: [_jsx(Text, { sx: { fontSize: 1 }, children: live > 0
|
|
81
81
|
? `${plural(live, 'client')} connected right now.`
|
|
82
82
|
: 'No client is connected right now.' }), _jsx(Button, { size: "small", leadingVisual: PulseIcon, onClick: () => navigate(routes.dashboard), children: "What is going on" })] }), _jsxs(Box, { display: "grid", gap: 2, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "The endpoint" }), _jsx(Copyable, { text: endpoint || 'https://mcp.datalayer.run/mcp' }), _jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(KeyIcon, { size: 14 }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: signedIn
|
|
83
83
|
? 'You are signed in, so a client you set up from here authorizes through OAuth without a shared token.'
|
|
84
|
-
: 'Sign in to authorize a client through OAuth; without it a client can only use a personal access token.' })] })] }), _jsxs(Box, { display: "grid", gap: 2, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "Set up a client" }), _jsx(Box, { display: "grid", gap: 3, gridTemplateColumns: ['1fr', 'repeat(2, 1fr)', 'repeat(3, 1fr)'], children: MCP_CLIENTS.map(client => (_jsxs(
|
|
84
|
+
: 'Sign in to authorize a client through OAuth; without it a client can only use a personal access token.' })] })] }), _jsxs(Box, { display: "grid", gap: 2, children: [_jsx(Heading, { as: "h3", sx: { fontSize: 2 }, children: "Set up a client" }), _jsx(Box, { display: "grid", gap: 3, gridTemplateColumns: ['1fr', 'repeat(2, 1fr)', 'repeat(3, 1fr)'], children: MCP_CLIENTS.map(client => (_jsxs(Card, { border: true, shadow: "none", p: 3, display: "grid", gap: 2, minWidth: 0, children: [_jsx(Text, { sx: { fontSize: 1, fontWeight: 'semibold' }, children: client.name }), _jsx(Copyable, { text: mcpSetupCommand(client.setup) }), _jsx(Link, { href: `${clientDocsBase}/${client.setup}`, sx: { fontSize: 0, cursor: 'pointer', textAlign: 'left' }, onClick: event => {
|
|
85
85
|
// A modified click is the reader asking for a new tab or
|
|
86
86
|
// window, and `href` is real so it still leads to the
|
|
87
87
|
// full page — for that, for the status bar and for the
|