@datalayer/core 1.1.66 → 1.2.10

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.
Files changed (210) hide show
  1. package/README.md +41 -18
  2. package/lib/api/constants.d.ts +1 -1
  3. package/lib/api/constants.js +1 -1
  4. package/lib/api/contents/attachments.js +3 -7
  5. package/lib/api/contents/datasets.d.ts +48 -0
  6. package/lib/api/contents/datasets.js +44 -7
  7. package/lib/api/contents/runtimeMounts.js +1 -1
  8. package/lib/api/contents/sandboxUid.d.ts +13 -45
  9. package/lib/api/contents/sandboxUid.js +14 -65
  10. package/lib/api/evals/client.d.ts +402 -0
  11. package/lib/api/evals/client.js +359 -0
  12. package/lib/api/evals/index.d.ts +10 -0
  13. package/lib/api/evals/index.js +18 -0
  14. package/lib/api/evals/request.d.ts +43 -0
  15. package/lib/api/evals/request.js +67 -0
  16. package/lib/api/evals/status.d.ts +27 -0
  17. package/lib/api/evals/status.js +122 -0
  18. package/lib/api/evals/types.d.ts +1335 -0
  19. package/lib/api/evals/types.js +5 -0
  20. package/lib/api/iam/connectedAgents.d.ts +29 -0
  21. package/lib/api/iam/connectedAgents.js +35 -0
  22. package/lib/api/iam/identityProviders.d.ts +76 -0
  23. package/lib/api/iam/identityProviders.js +187 -0
  24. package/lib/api/iam/index.d.ts +1 -0
  25. package/lib/api/iam/index.js +1 -0
  26. package/lib/api/iam/mcpPolicy.d.ts +102 -1
  27. package/lib/api/iam/mcpPolicy.js +38 -2
  28. package/lib/api/iam/profile.d.ts +17 -1
  29. package/lib/api/iam/profile.js +27 -0
  30. package/lib/api/iam/scimTokens.d.ts +79 -0
  31. package/lib/api/iam/scimTokens.js +141 -0
  32. package/lib/api/iam/secrets.js +9 -21
  33. package/lib/api/iam/trials.d.ts +38 -0
  34. package/lib/api/iam/trials.js +66 -0
  35. package/lib/api/index.d.ts +4 -0
  36. package/lib/api/index.js +4 -0
  37. package/lib/api/mcp/observability.d.ts +25 -9
  38. package/lib/api/mcp/observability.js +63 -50
  39. package/lib/api/orchestration/client.d.ts +53 -0
  40. package/lib/api/orchestration/client.js +105 -0
  41. package/lib/api/orchestration/events.d.ts +88 -0
  42. package/lib/api/orchestration/events.js +245 -0
  43. package/lib/api/orchestration/generated.d.ts +693 -0
  44. package/lib/api/orchestration/generated.js +687 -0
  45. package/lib/api/orchestration/index.d.ts +30 -0
  46. package/lib/api/orchestration/index.js +34 -0
  47. package/lib/api/orchestration/lifecycle.d.ts +39 -0
  48. package/lib/api/orchestration/lifecycle.js +57 -0
  49. package/lib/api/orchestration/measures.d.ts +55 -0
  50. package/lib/api/orchestration/measures.js +99 -0
  51. package/lib/api/otel/dashboards.d.ts +56 -0
  52. package/lib/api/otel/dashboards.js +50 -0
  53. package/lib/api/otel/index.d.ts +1 -0
  54. package/lib/api/otel/index.js +1 -0
  55. package/lib/api/otel/metrics.d.ts +16 -0
  56. package/lib/api/otel/metrics.js +15 -0
  57. package/lib/api/scheduler/client.d.ts +32 -0
  58. package/lib/api/scheduler/client.js +42 -0
  59. package/lib/api/scheduler/index.d.ts +8 -0
  60. package/lib/api/scheduler/index.js +16 -0
  61. package/lib/api/scheduler/request.d.ts +28 -0
  62. package/lib/api/scheduler/request.js +50 -0
  63. package/lib/api/scheduler/types.d.ts +88 -0
  64. package/lib/api/scheduler/types.js +5 -0
  65. package/lib/api/spacer/comments.d.ts +97 -0
  66. package/lib/api/spacer/comments.js +45 -0
  67. package/lib/api/spacer/index.d.ts +9 -0
  68. package/lib/api/spacer/index.js +17 -0
  69. package/lib/api/spacer/notebooks.d.ts +33 -0
  70. package/lib/api/spacer/notebooks.js +39 -0
  71. package/lib/api/spacer/request.d.ts +25 -0
  72. package/lib/api/spacer/request.js +46 -0
  73. package/lib/api/spacer/spaces.d.ts +68 -0
  74. package/lib/api/spacer/spaces.js +45 -0
  75. package/lib/api/utils/validation.d.ts +4 -4
  76. package/lib/api/utils/validation.js +10 -7
  77. package/lib/client/constants.d.ts +1 -0
  78. package/lib/client/constants.js +1 -0
  79. package/lib/components/animation/AnimatedText.d.ts +1 -44
  80. package/lib/components/animation/AnimatedText.js +6 -122
  81. package/lib/components/anonymous/AnonymousKeyExpired.d.ts +56 -0
  82. package/lib/components/anonymous/AnonymousKeyExpired.js +96 -0
  83. package/lib/components/anonymous/AnonymousKeyTimer.d.ts +63 -0
  84. package/lib/components/anonymous/AnonymousKeyTimer.js +140 -0
  85. package/lib/components/anonymous/index.d.ts +7 -0
  86. package/lib/components/anonymous/index.js +15 -0
  87. package/lib/components/avatars/UserAvatar.d.ts +7 -1
  88. package/lib/components/avatars/UserAvatar.js +15 -1
  89. package/lib/components/billing/BillingEntitySelect.d.ts +6 -0
  90. package/lib/components/billing/BillingEntitySelect.js +49 -10
  91. package/lib/components/billing/eligibility.d.ts +22 -0
  92. package/lib/components/billing/eligibility.js +27 -0
  93. package/lib/components/billing/index.d.ts +1 -0
  94. package/lib/components/billing/index.js +1 -0
  95. package/lib/components/collaboration/LiveEditorCollaborators.d.ts +5 -0
  96. package/lib/components/collaboration/LiveEditorCollaborators.js +3 -1
  97. package/lib/components/display/DatalayerBox.d.ts +2 -3
  98. package/lib/components/display/DatalayerBox.js +1 -1
  99. package/lib/components/display/NavLink.js +3 -2
  100. package/lib/components/labels/StatusLabels.d.ts +2 -10
  101. package/lib/components/labels/StatusLabels.js +2 -26
  102. package/lib/components/principal/PrincipalAppearance.d.ts +21 -42
  103. package/lib/components/principal/PrincipalAppearance.js +232 -174
  104. package/lib/components/principal/PrincipalAvatar.d.ts +8 -1
  105. package/lib/components/principal/PrincipalAvatar.js +2 -2
  106. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +8 -1
  107. package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
  108. package/lib/components/screencapture/Screencapture.js +46 -3
  109. package/lib/components/sharing/ShareAccessComponent.d.ts +45 -4
  110. package/lib/components/sharing/ShareAccessComponent.js +110 -78
  111. package/lib/components/sharing/SharingEditor.d.ts +5 -1
  112. package/lib/components/sharing/SharingEditor.js +9 -4
  113. package/lib/components/sharing/index.d.ts +1 -0
  114. package/lib/components/sharing/index.js +1 -0
  115. package/lib/components/sharing/sandboxSharing.d.ts +26 -0
  116. package/lib/components/sharing/sandboxSharing.js +41 -0
  117. package/lib/components/spaces/SpaceDetailsOverlay.d.ts +39 -0
  118. package/lib/components/spaces/SpaceDetailsOverlay.js +80 -0
  119. package/lib/components/spaces/SpaceDisplay.d.ts +38 -0
  120. package/lib/components/spaces/SpaceDisplay.js +92 -0
  121. package/lib/components/spaces/index.d.ts +4 -0
  122. package/lib/components/spaces/index.js +7 -0
  123. package/lib/components/spaces/spaceDisplayModel.d.ts +93 -0
  124. package/lib/components/spaces/spaceDisplayModel.js +170 -0
  125. package/lib/hooks/cacheConverters.d.ts +0 -7
  126. package/lib/hooks/cacheConverters.js +0 -25
  127. package/lib/hooks/index.d.ts +1 -0
  128. package/lib/hooks/index.js +1 -0
  129. package/lib/hooks/useBillingEntityStore.js +3 -0
  130. package/lib/hooks/useCache.d.ts +168 -25
  131. package/lib/hooks/useCache.js +1424 -307
  132. package/lib/hooks/useContents.d.ts +22 -1
  133. package/lib/hooks/useContents.js +90 -21
  134. package/lib/hooks/useMcp.d.ts +87 -1
  135. package/lib/hooks/useMcp.js +247 -12
  136. package/lib/hooks/useNavigate.d.ts +41 -3
  137. package/lib/hooks/useNavigate.js +106 -52
  138. package/lib/hooks/usePrincipalCacheStore.js +3 -0
  139. package/lib/hooks/usePrincipalStore.js +3 -0
  140. package/lib/hooks/useSpaceCacheStore.d.ts +50 -0
  141. package/lib/hooks/useSpaceCacheStore.js +85 -0
  142. package/lib/models/CreditsDTO.d.ts +6 -5
  143. package/lib/models/CreditsDTO.js +7 -5
  144. package/lib/models/McpBinding.d.ts +6 -0
  145. package/lib/models/Organization.d.ts +69 -0
  146. package/lib/models/Organization.js +96 -0
  147. package/lib/models/Secret.d.ts +1 -2
  148. package/lib/models/Secret.js +2 -18
  149. package/lib/models/StartedBy.d.ts +34 -0
  150. package/lib/models/StartedBy.js +36 -0
  151. package/lib/models/Team.d.ts +2 -0
  152. package/lib/models/Team.js +2 -0
  153. package/lib/models/User.d.ts +4 -0
  154. package/lib/models/User.js +3 -0
  155. package/lib/models/UserSettings.d.ts +13 -0
  156. package/lib/models/UserSettings.js +9 -0
  157. package/lib/models/contents/__fixtures__/v1-contracts.json +158 -30
  158. package/lib/navigation/components.js +2 -3
  159. package/lib/routes/publicPaths.js +4 -0
  160. package/lib/state/index.d.ts +1 -0
  161. package/lib/state/index.js +1 -0
  162. package/lib/state/sessionEnd.d.ts +55 -0
  163. package/lib/state/sessionEnd.js +162 -0
  164. package/lib/state/substates/CoreState.js +3 -3
  165. package/lib/state/substates/IAMState.js +7 -0
  166. package/lib/state/substates/LayoutState.js +12 -0
  167. package/lib/state/substates/ProfileState.d.ts +1 -0
  168. package/lib/state/substates/ProfileState.js +1 -0
  169. package/lib/utils/Lazy.d.ts +20 -0
  170. package/lib/utils/Lazy.js +32 -6
  171. package/lib/utils/Screencapture.js +3 -0
  172. package/lib/utils/WithSuspense.d.ts +11 -1
  173. package/lib/utils/WithSuspense.js +11 -1
  174. package/lib/views/mcp/AdmittedClients.d.ts +44 -0
  175. package/lib/views/mcp/AdmittedClients.js +59 -0
  176. package/lib/views/mcp/ApprovalQueue.d.ts +66 -0
  177. package/lib/views/mcp/ApprovalQueue.js +95 -0
  178. package/lib/views/mcp/ConnectedAgents.js +38 -3
  179. package/lib/views/mcp/EnterpriseConsole.d.ts +1 -1
  180. package/lib/views/mcp/EnterpriseConsole.js +23 -1
  181. package/lib/views/mcp/IdentityProviders.d.ts +33 -0
  182. package/lib/views/mcp/IdentityProviders.js +299 -0
  183. package/lib/views/mcp/McpDashboard.js +90 -7
  184. package/lib/views/mcp/McpObservability.d.ts +30 -1
  185. package/lib/views/mcp/McpObservability.js +105 -19
  186. package/lib/views/mcp/NotebookRuns.d.ts +39 -0
  187. package/lib/views/mcp/NotebookRuns.js +44 -0
  188. package/lib/views/mcp/OrganizationPolicy.js +2 -1
  189. package/lib/views/mcp/PersonalPolicy.js +2 -1
  190. package/lib/views/mcp/PolicyForm.d.ts +17 -1
  191. package/lib/views/mcp/PolicyForm.js +25 -3
  192. package/lib/views/mcp/PolicyHistory.d.ts +13 -0
  193. package/lib/views/mcp/PolicyHistory.js +14 -1
  194. package/lib/views/mcp/RunDetail.d.ts +87 -0
  195. package/lib/views/mcp/RunDetail.js +186 -0
  196. package/lib/views/mcp/ScimProvisioning.d.ts +54 -0
  197. package/lib/views/mcp/ScimProvisioning.js +173 -0
  198. package/lib/views/mcp/TeamPolicies.js +66 -5
  199. package/lib/views/mcp/ToolAccess.d.ts +60 -0
  200. package/lib/views/mcp/ToolAccess.js +101 -0
  201. package/lib/views/mcp/TraceTimeline.d.ts +81 -0
  202. package/lib/views/mcp/TraceTimeline.js +132 -0
  203. package/lib/views/mcp/index.d.ts +5 -0
  204. package/lib/views/mcp/index.js +5 -0
  205. package/lib/views/otel/simpleAuthStore.js +3 -0
  206. package/lib/views/secrets/SecretEdit.js +3 -2
  207. package/lib/views/secrets/SecretNew.js +5 -1
  208. package/package.json +269 -264
  209. package/scripts/generate-mcp-types.py +1 -1
  210. package/scripts/generate-orchestration-types.py +920 -0
@@ -0,0 +1,79 @@
1
+ /** A SCIM credential as the console may see it — never the secret or its hash. */
2
+ export interface ScimToken {
3
+ uid: string;
4
+ orgUid: string;
5
+ /** What an administrator called it, to tell two directories apart. */
6
+ name: string;
7
+ /**
8
+ * Revoked rather than deleted, so the rows a provisioning run wrote can
9
+ * still name the token that wrote them.
10
+ */
11
+ revoked: boolean;
12
+ /**
13
+ * When the secret was last replaced. A token that has never been rotated
14
+ * carries its creation moment here.
15
+ */
16
+ rotatedAt?: string;
17
+ /**
18
+ * When it last authenticated a SCIM call, or empty for never. Written best
19
+ * effort — a provisioning call is never failed for the bookkeeping.
20
+ */
21
+ lastUsedAt?: string;
22
+ createdBy?: string;
23
+ createdAt?: string;
24
+ updatedAt?: string;
25
+ }
26
+ /**
27
+ * A freshly minted credential: the record, and the secret **this once**.
28
+ *
29
+ * The two are separate fields rather than one merged object on purpose. The
30
+ * secret does not belong in anything the console stores, renders twice, or
31
+ * puts in a list, and a shape that carried it alongside every other field
32
+ * invites exactly that.
33
+ */
34
+ export interface MintedScimToken {
35
+ token: ScimToken;
36
+ /** Shown once. Not retrievable, by construction — only a hash is stored. */
37
+ secret: string;
38
+ }
39
+ export declare const listScimTokens: (token: string, orgUid: string, baseUrl?: string) => Promise<ScimToken[]>;
40
+ /**
41
+ * Mint one. The secret is in this answer and nowhere else, ever again.
42
+ *
43
+ * The caller must put it in front of somebody before the next render. There
44
+ * is no second chance and no call that would provide one.
45
+ */
46
+ export declare const createScimToken: (token: string, orgUid: string, name: string, baseUrl?: string) => Promise<MintedScimToken>;
47
+ /**
48
+ * A new secret for the same record; the old one stops at once.
49
+ *
50
+ * Rotation rather than delete-and-recreate because the record's uid is what
51
+ * the provisioning rows name. Replacing it would leave them pointing at
52
+ * nothing.
53
+ */
54
+ export declare const rotateScimToken: (token: string, orgUid: string, uid: string, baseUrl?: string) => Promise<MintedScimToken>;
55
+ /** Stop a token, keeping the rows that name it readable. */
56
+ export declare const revokeScimToken: (token: string, orgUid: string, uid: string, baseUrl?: string) => Promise<ScimToken>;
57
+ /**
58
+ * The base URL a directory is configured with.
59
+ *
60
+ * Built here rather than typed into the page, because it is half of what an
61
+ * administrator copies into Okta or Entra and a typo in it looks like an
62
+ * outage on the directory's side. The directory appends `/Users` itself;
63
+ * RFC 7644 fixes everything after this.
64
+ */
65
+ export declare const scimBaseUrl: (baseUrl?: string) => string;
66
+ /**
67
+ * Whether this organization can actually be provisioned into right now.
68
+ *
69
+ * A list with tokens in it is not the same as a working integration, and the
70
+ * distinction is the one an administrator most needs: every token revoked
71
+ * reads, in a table, almost exactly like a healthy setup. `neverUsed` is the
72
+ * other quiet state — configured, live, and never once called, which is what
73
+ * a directory that was set up and never switched on looks like.
74
+ */
75
+ export declare const scimStatus: (tokens: ScimToken[]) => {
76
+ state: "none" | "revoked" | "never-used" | "active";
77
+ live: ScimToken[];
78
+ lastUsedAt?: string;
79
+ };
@@ -0,0 +1,141 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /*
6
+ * Copyright (c) 2023-2026 Datalayer, Inc.
7
+ * Distributed under the terms of the Modified BSD License.
8
+ */
9
+ /**
10
+ * The credentials an organization's directory provisions with, over SCIM.
11
+ *
12
+ * The identity provider registry beside this one made a directory able to
13
+ * *sign somebody in*. SCIM is the half that does not wait for them to turn
14
+ * up: the directory says who exists when they join and, far more
15
+ * importantly, who has left.
16
+ *
17
+ * **The token is the tenant.** Every SCIM call resolves its organization
18
+ * from the bearer credential before it looks at anything else, so these
19
+ * records are not a convenience — each one *is* an organization's provisioning
20
+ * authority, and there is nothing else identifying the caller.
21
+ *
22
+ * **The secret is answered once and never again.** `createScimToken` and
23
+ * `rotateScimToken` return it; nothing reads it back, because only a SHA-256
24
+ * of it is stored. A console that offered to show it later would be
25
+ * promising something the server cannot do — so this client has no call that
26
+ * could, and the UI has to say "copy it now" and mean it.
27
+ *
28
+ * `lastUsedAt` is the column worth reading. A directory configured once and
29
+ * forgotten looks exactly like a live one in every other field, and the
30
+ * difference matters most for deprovisioning: the integration that has not
31
+ * called in a month is not removing anybody.
32
+ *
33
+ * @module api/iam/scimTokens
34
+ */
35
+ import { requestDatalayerAPI } from '../DatalayerApi.js';
36
+ import { API_BASE_PATHS, DEFAULT_SERVICE_URLS } from '../constants.js';
37
+ const fromWire = (wire) => ({
38
+ uid: wire.uid ?? '',
39
+ orgUid: wire.org_uid ?? '',
40
+ name: wire.name ?? '',
41
+ // A record with no `revoked` field is a live one: absence is not
42
+ // revocation, and reading it as one would show every token as stopped.
43
+ revoked: wire.revoked === true,
44
+ rotatedAt: wire.rotated_at || undefined,
45
+ lastUsedAt: wire.last_used_at || undefined,
46
+ createdBy: wire.created_by || undefined,
47
+ createdAt: wire.created_at || undefined,
48
+ updatedAt: wire.updated_at || undefined,
49
+ });
50
+ const tokensUrl = (baseUrl, orgUid, suffix = '') => `${baseUrl}${API_BASE_PATHS.IAM}/organizations/${encodeURIComponent(orgUid)}` +
51
+ `/scim-tokens${suffix}`;
52
+ export const listScimTokens = async (token, orgUid, baseUrl = DEFAULT_SERVICE_URLS.IAM) => {
53
+ const response = await requestDatalayerAPI({
54
+ url: tokensUrl(baseUrl, orgUid),
55
+ method: 'GET',
56
+ token,
57
+ });
58
+ return (response.tokens ?? []).map(fromWire);
59
+ };
60
+ /**
61
+ * Mint one. The secret is in this answer and nowhere else, ever again.
62
+ *
63
+ * The caller must put it in front of somebody before the next render. There
64
+ * is no second chance and no call that would provide one.
65
+ */
66
+ export const createScimToken = async (token, orgUid, name, baseUrl = DEFAULT_SERVICE_URLS.IAM) => {
67
+ const response = await requestDatalayerAPI({
68
+ url: tokensUrl(baseUrl, orgUid),
69
+ method: 'POST',
70
+ token,
71
+ body: { name },
72
+ });
73
+ return {
74
+ token: fromWire(response.token ?? {}),
75
+ secret: response.token?.token ?? '',
76
+ };
77
+ };
78
+ /**
79
+ * A new secret for the same record; the old one stops at once.
80
+ *
81
+ * Rotation rather than delete-and-recreate because the record's uid is what
82
+ * the provisioning rows name. Replacing it would leave them pointing at
83
+ * nothing.
84
+ */
85
+ export const rotateScimToken = async (token, orgUid, uid, baseUrl = DEFAULT_SERVICE_URLS.IAM) => {
86
+ const response = await requestDatalayerAPI({
87
+ url: tokensUrl(baseUrl, orgUid, `/${encodeURIComponent(uid)}/rotate`),
88
+ method: 'POST',
89
+ token,
90
+ });
91
+ return {
92
+ token: fromWire(response.token ?? {}),
93
+ secret: response.token?.token ?? '',
94
+ };
95
+ };
96
+ /** Stop a token, keeping the rows that name it readable. */
97
+ export const revokeScimToken = async (token, orgUid, uid, baseUrl = DEFAULT_SERVICE_URLS.IAM) => {
98
+ const response = await requestDatalayerAPI({
99
+ url: tokensUrl(baseUrl, orgUid, `/${encodeURIComponent(uid)}`),
100
+ method: 'DELETE',
101
+ token,
102
+ });
103
+ return fromWire(response.token ?? {});
104
+ };
105
+ /**
106
+ * The base URL a directory is configured with.
107
+ *
108
+ * Built here rather than typed into the page, because it is half of what an
109
+ * administrator copies into Okta or Entra and a typo in it looks like an
110
+ * outage on the directory's side. The directory appends `/Users` itself;
111
+ * RFC 7644 fixes everything after this.
112
+ */
113
+ export const scimBaseUrl = (baseUrl = DEFAULT_SERVICE_URLS.IAM) => `${baseUrl}/api/iam/scim/v2`;
114
+ /**
115
+ * Whether this organization can actually be provisioned into right now.
116
+ *
117
+ * A list with tokens in it is not the same as a working integration, and the
118
+ * distinction is the one an administrator most needs: every token revoked
119
+ * reads, in a table, almost exactly like a healthy setup. `neverUsed` is the
120
+ * other quiet state — configured, live, and never once called, which is what
121
+ * a directory that was set up and never switched on looks like.
122
+ */
123
+ export const scimStatus = (tokens) => {
124
+ const live = tokens.filter(entry => !entry.revoked);
125
+ if (tokens.length === 0) {
126
+ return { state: 'none', live };
127
+ }
128
+ if (live.length === 0) {
129
+ return { state: 'revoked', live };
130
+ }
131
+ const used = live
132
+ .map(entry => entry.lastUsedAt)
133
+ .filter((value) => Boolean(value))
134
+ .sort();
135
+ const lastUsedAt = used.length > 0 ? used[used.length - 1] : undefined;
136
+ return {
137
+ state: lastUsedAt ? 'active' : 'never-used',
138
+ live,
139
+ lastUsedAt,
140
+ };
141
+ };
@@ -9,21 +9,6 @@
9
9
  import { requestDatalayerAPI } from '../DatalayerApi.js';
10
10
  import { API_BASE_PATHS, DEFAULT_SERVICE_URLS } from '../constants.js';
11
11
  import { validateToken, validateRequiredString } from '../utils/validation.js';
12
- /**
13
- * Helper function to Base64 encode a value (cross-platform).
14
- * @param value - Plain text value to encode
15
- * @returns Base64 encoded string
16
- */
17
- function encodeValue(value) {
18
- if (typeof Buffer !== 'undefined') {
19
- // Node.js environment
20
- return Buffer.from(value).toString('base64');
21
- }
22
- else {
23
- // Browser environment
24
- return btoa(value);
25
- }
26
- }
27
12
  /**
28
13
  * Create a new secret.
29
14
  * Creates a new encrypted secret for the authenticated user.
@@ -52,7 +37,10 @@ export const createSecret = async (token, data, baseUrl = DEFAULT_SERVICE_URLS.I
52
37
  variant: data.variant || 'generic',
53
38
  name: data.name,
54
39
  description: data.description || '',
55
- value: encodeValue(data.value),
40
+ // As it is. A secret has one representation end to end (PLAN_ENVS.md
41
+ // E3-05): this used to base64-encode, and nothing on the read side undid
42
+ // it, so a build secret arrived at its build encoded and opened nothing.
43
+ value: data.value,
56
44
  };
57
45
  try {
58
46
  return await requestDatalayerAPI({
@@ -64,7 +52,7 @@ export const createSecret = async (token, data, baseUrl = DEFAULT_SERVICE_URLS.I
64
52
  }
65
53
  catch (error) {
66
54
  if (error.response?.status === 409) {
67
- throw new Error(`Secret with name '${data.name}' already exists. Please use a different name.`);
55
+ throw new Error(`Secret with name '${data.name}' already exists. Please use a different name.`, { cause: error });
68
56
  }
69
57
  throw error;
70
58
  }
@@ -118,7 +106,7 @@ export const getSecret = async (token, secretId, baseUrl = DEFAULT_SERVICE_URLS.
118
106
  }
119
107
  catch (error) {
120
108
  if (error.response?.status === 404) {
121
- throw new Error(`Secret '${secretId}' not found.`);
109
+ throw new Error(`Secret '${secretId}' not found.`, { cause: error });
122
110
  }
123
111
  throw error;
124
112
  }
@@ -147,7 +135,7 @@ export const updateSecret = async (token, secretId, data, baseUrl = DEFAULT_SERV
147
135
  // Base64 encode value if provided
148
136
  const requestBody = { ...data };
149
137
  if (data.value) {
150
- requestBody.value = encodeValue(data.value);
138
+ requestBody.value = data.value;
151
139
  }
152
140
  try {
153
141
  return await requestDatalayerAPI({
@@ -159,7 +147,7 @@ export const updateSecret = async (token, secretId, data, baseUrl = DEFAULT_SERV
159
147
  }
160
148
  catch (error) {
161
149
  if (error.response?.status === 404) {
162
- throw new Error(`Secret '${secretId}' not found.`);
150
+ throw new Error(`Secret '${secretId}' not found.`, { cause: error });
163
151
  }
164
152
  throw error;
165
153
  }
@@ -189,7 +177,7 @@ export const deleteSecret = async (token, secretId, baseUrl = DEFAULT_SERVICE_UR
189
177
  }
190
178
  catch (error) {
191
179
  if (error.response?.status === 404) {
192
- throw new Error(`Secret '${secretId}' not found.`);
180
+ throw new Error(`Secret '${secretId}' not found.`, { cause: error });
193
181
  }
194
182
  throw error;
195
183
  }
@@ -0,0 +1,38 @@
1
+ /** Where a claim carries the trial's own key. */
2
+ export declare const TRIAL_TOKEN_HEADER = "X-Datalayer-Trial-Token";
3
+ /** What a visitor may try here, if anything. */
4
+ export interface TrialOffer {
5
+ offered: boolean;
6
+ /** The one benchmark a trial may run. */
7
+ evalsetId: string;
8
+ /** The credits it may spend. */
9
+ credits: number;
10
+ /** How long it lives, in seconds. */
11
+ ttlSeconds: number;
12
+ }
13
+ export interface Trial {
14
+ id: string;
15
+ /** The uid the trial's work is owned by, which is the trial's own. */
16
+ ownerUid: string;
17
+ state: 'open' | 'claimed' | 'expired';
18
+ evalsetId: string;
19
+ creditsLimit: number | null;
20
+ creditsSpent: number;
21
+ expiresAt: string | null;
22
+ claimedByUid: string;
23
+ claimedAt: string | null;
24
+ }
25
+ /** A trial and the key that acts as it. The key is answered once. */
26
+ export interface IssuedTrial {
27
+ trial: Trial;
28
+ token: string;
29
+ }
30
+ /**
31
+ * Whether this deployment lets a visitor try a benchmark, and which one.
32
+ * Unauthenticated: it is what a public page asks before it offers the run.
33
+ */
34
+ export declare const getTrialOffer: (baseUrl?: string) => Promise<TrialOffer>;
35
+ /** Take a trial. Unauthenticated, rate limited, and refused where none are offered. */
36
+ export declare const createTrial: (baseUrl?: string) => Promise<IssuedTrial>;
37
+ /** The trial a key acts as: what it has left, and how long it has. */
38
+ export declare const getCurrentTrial: (trialToken: string, baseUrl?: string) => Promise<Trial>;
@@ -0,0 +1,66 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /*
6
+ * Copyright (c) 2023-2026 Datalayer, Inc.
7
+ * Distributed under the terms of the Modified BSD License.
8
+ */
9
+ /**
10
+ * The anonymous trial: running the public benchmark before signing up
11
+ * (BENCHMARK.md, B2-14).
12
+ *
13
+ * A visitor with no account asks for a trial and gets a principal of its
14
+ * own with one benchmark it may run, a few credits and a short expiry. The
15
+ * work they make is owned by that principal, so when they sign up it is
16
+ * given to them rather than lost — `claimTrial` in the evals client moves
17
+ * it, and the trial's own key has to be presented alongside their new one.
18
+ *
19
+ * A deployment that has not named a public benchmark offers no trials, and
20
+ * `getTrialOffer` says so: a page asks before it shows the button.
21
+ *
22
+ * @module api/iam/trials
23
+ */
24
+ import { requestDatalayerAPI } from '../DatalayerApi.js';
25
+ import { API_BASE_PATHS, DEFAULT_SERVICE_URLS } from '../constants.js';
26
+ /** Where a claim carries the trial's own key. */
27
+ export const TRIAL_TOKEN_HEADER = 'X-Datalayer-Trial-Token';
28
+ const trialsUrl = (baseUrl, suffix = '') => `${baseUrl}${API_BASE_PATHS.IAM}/trials${suffix}`;
29
+ const asTrial = (wire) => ({
30
+ id: wire.id,
31
+ ownerUid: wire.owner_uid ?? wire.id,
32
+ state: wire.state ?? 'open',
33
+ evalsetId: wire.evalset_id ?? '',
34
+ creditsLimit: wire.credits_limit ?? null,
35
+ creditsSpent: wire.credits_spent ?? 0,
36
+ expiresAt: wire.expires_at ?? null,
37
+ claimedByUid: wire.claimed_by_uid ?? '',
38
+ claimedAt: wire.claimed_at ?? null,
39
+ });
40
+ /**
41
+ * Whether this deployment lets a visitor try a benchmark, and which one.
42
+ * Unauthenticated: it is what a public page asks before it offers the run.
43
+ */
44
+ export const getTrialOffer = async (baseUrl = DEFAULT_SERVICE_URLS.IAM) => {
45
+ const response = await requestDatalayerAPI({ url: trialsUrl(baseUrl, '/offer'), method: 'GET' });
46
+ return {
47
+ offered: !!response.offered,
48
+ evalsetId: response.evalset_id ?? '',
49
+ credits: response.credits ?? 0,
50
+ ttlSeconds: response.ttl_seconds ?? 0,
51
+ };
52
+ };
53
+ /** Take a trial. Unauthenticated, rate limited, and refused where none are offered. */
54
+ export const createTrial = async (baseUrl = DEFAULT_SERVICE_URLS.IAM) => {
55
+ const response = await requestDatalayerAPI({ url: trialsUrl(baseUrl), method: 'POST' });
56
+ return { trial: asTrial(response.trial), token: response.token };
57
+ };
58
+ /** The trial a key acts as: what it has left, and how long it has. */
59
+ export const getCurrentTrial = async (trialToken, baseUrl = DEFAULT_SERVICE_URLS.IAM) => {
60
+ const response = await requestDatalayerAPI({
61
+ url: trialsUrl(baseUrl, '/me'),
62
+ method: 'GET',
63
+ token: trialToken,
64
+ });
65
+ return asTrial(response.trial);
66
+ };
@@ -11,6 +11,10 @@ export type { IRequestDatalayerAPIOptions } from './DatalayerApi';
11
11
  export type { IDatalayerAPIResponse } from './DatalayerApi';
12
12
  export * as iam from './iam';
13
13
  export * as contents from './contents';
14
+ export * as evals from './evals';
14
15
  export * as mcp from './mcp';
16
+ export * as orchestration from './orchestration';
15
17
  export * as otel from './otel';
18
+ export * as scheduler from './scheduler';
19
+ export * as spacer from './spacer';
16
20
  export * from '../api/DatalayerApi';
package/lib/api/index.js CHANGED
@@ -15,6 +15,10 @@ export { requestDatalayerAPI, requestDatalayerAPIWithResponse, RunResponseError,
15
15
  // Domain-organized API exports
16
16
  export * as iam from './iam/index.js';
17
17
  export * as contents from './contents/index.js';
18
+ export * as evals from './evals/index.js';
18
19
  export * as mcp from './mcp/index.js';
20
+ export * as orchestration from './orchestration/index.js';
19
21
  export * as otel from './otel/index.js';
22
+ export * as scheduler from './scheduler/index.js';
23
+ export * as spacer from './spacer/index.js';
20
24
  export * from '../api/DatalayerApi.js';
@@ -1,4 +1,5 @@
1
- import type { OtelLog, OtelMetric, OtelSpan } from '../otel/types';
1
+ import { type DashboardData } from '../otel/dashboards';
2
+ import type { OtelLog, OtelSpan } from '../otel/types';
2
3
  import type { McpTask } from '../../models/McpTask';
3
4
  /**
4
5
  * The `service.name` the gateway exports under, and the one its workers use.
@@ -40,11 +41,25 @@ export declare const MCP_SLI_METRICS: {
40
41
  readonly taskSuccess: "mcp.tasks";
41
42
  readonly sandboxLaunch: "sandbox.launch_seconds";
42
43
  };
44
+ /** The OTEL service's built-in dashboard the four SLIs are read from. */
45
+ export declare const MCP_SERVICE_LEVELS_DASHBOARD = "mcp-service-levels";
46
+ /**
47
+ * Its panels, by the ids the OTEL service gives them. A contract test there
48
+ * holds them, so a panel renamed on the service fails a test rather than
49
+ * reading here as nothing measured.
50
+ */
51
+ export declare const MCP_SERVICE_LEVEL_PANELS: {
52
+ readonly callsByOutcome: "mcp-calls-by-outcome";
53
+ readonly callDurationP95: "mcp-call-duration-p95";
54
+ readonly tasksByStatus: "mcp-tasks-by-status";
55
+ readonly sandboxLaunchP95: "sandbox-launch_seconds-by-provider-p95";
56
+ readonly sandboxLaunches: "sandbox-launch_seconds-by-provider-count";
57
+ };
43
58
  export interface McpMetricsFilters {
44
59
  /** The `client_id` of one agent: read from spans, not from metrics. */
45
60
  agent?: string;
46
61
  org?: string;
47
- /** ISO 8601; points and spans before it are left out. */
62
+ /** ISO 8601; the reading starts there. */
48
63
  since?: string;
49
64
  limit?: number;
50
65
  }
@@ -66,8 +81,8 @@ export interface McpSliSummary {
66
81
  }
67
82
  export interface McpMetricsSnapshot {
68
83
  filters: McpMetricsFilters;
69
- /** The catalog points read, by metric name. */
70
- metrics: Partial<Record<McpMetricName, OtelMetric[]>>;
84
+ /** The catalog metrics the OTEL service holds points of, in catalog order. */
85
+ reporting: McpMetricName[];
71
86
  /** The `mcp.request` spans a per-agent or per-organization reading rests on. */
72
87
  spans: OtelSpan[];
73
88
  slis: McpSliSummary;
@@ -76,13 +91,14 @@ export interface McpMetricsSnapshot {
76
91
  /** The nearest-rank percentile of a sample; `null` for an empty one. */
77
92
  export declare const percentile: (values: number[], fraction: number) => number | null;
78
93
  /**
79
- * The SLIs over catalog points — the platform-wide reading.
94
+ * The SLIs from the service levels dashboard — the platform-wide reading.
80
95
  *
81
- * `mcp.calls{outcome}` gives availability, `mcp.call.duration{tool}` the
82
- * latency, `mcp.tasks{status}` the task success rate and
83
- * `sandbox.launch_seconds{provider}` the launches.
96
+ * Its panels arrive summarised: calls and tasks as how much each counter grew
97
+ * over the window, the two p95s from their histograms' buckets. The call
98
+ * duration is recorded in seconds and shown in milliseconds. A panel with
99
+ * nothing in it reads as not measured, never as a zero.
84
100
  */
85
- export declare const summarizeMetricPoints: (metrics: Partial<Record<McpMetricName, OtelMetric[]>>, filters?: McpMetricsFilters) => McpSliSummary;
101
+ export declare const summarizeServiceLevels: (data: DashboardData) => McpSliSummary;
86
102
  /**
87
103
  * The SLIs over `mcp.request` spans — the per-agent or per-organization
88
104
  * reading, since metrics carry neither label.
@@ -13,17 +13,21 @@
13
13
  * Audit answers *who did what, and was it allowed*; this answers *how did
14
14
  * it run*. A task carries its `trace_id`, so "show me this run" is the
15
15
  * task's spans; its logs are the log records of that trace; and the four
16
- * SLIs are read from the gateway's metric catalog. Metrics carry no user,
17
- * agent or organization label by design, so a per-agent reading is
18
- * computed from the `mcp.request` spans, which do carry `client.id` and
19
- * `org.uid`.
16
+ * SLIs are read from the OTEL service's service levels dashboard, which
17
+ * computes them from the metrics as they were exported — a counter's
18
+ * increase, a percentile from a histogram's buckets. Nothing here adds
19
+ * metric points up: the gateway reports running totals, and adding those
20
+ * counts every earlier report again. Metrics carry no user, agent or
21
+ * organization label by design, so a per-agent reading is computed from the
22
+ * `mcp.request` spans, which do carry `client.id` and `org.uid`.
20
23
  *
21
24
  * @module api/mcp/observability
22
25
  */
23
26
  import { DEFAULT_SERVICE_URLS } from '../constants.js';
24
27
  import { getTrace, listTraces } from '../otel/traces.js';
25
- import { queryMetrics } from '../otel/metrics.js';
28
+ import { listMetricNames } from '../otel/metrics.js';
26
29
  import { queryLogs } from '../otel/logs.js';
30
+ import { getDashboardData } from '../otel/dashboards.js';
27
31
  /**
28
32
  * The `service.name` the gateway exports under, and the one its workers use.
29
33
  *
@@ -94,6 +98,21 @@ export const MCP_SLI_METRICS = {
94
98
  taskSuccess: 'mcp.tasks',
95
99
  sandboxLaunch: 'sandbox.launch_seconds',
96
100
  };
101
+ /** The OTEL service's built-in dashboard the four SLIs are read from. */
102
+ export const MCP_SERVICE_LEVELS_DASHBOARD = 'mcp-service-levels';
103
+ /**
104
+ * Its panels, by the ids the OTEL service gives them. A contract test there
105
+ * holds them, so a panel renamed on the service fails a test rather than
106
+ * reading here as nothing measured.
107
+ */
108
+ export const MCP_SERVICE_LEVEL_PANELS = {
109
+ callsByOutcome: 'mcp-calls-by-outcome',
110
+ callDurationP95: 'mcp-call-duration-p95',
111
+ tasksByStatus: 'mcp-tasks-by-status',
112
+ sandboxLaunchP95: 'sandbox-launch_seconds-by-provider-p95',
113
+ sandboxLaunches: 'sandbox-launch_seconds-by-provider-count',
114
+ };
115
+ const TERMINAL_STATUSES = ['completed', 'failed', 'cancelled'];
97
116
  /** The nearest-rank percentile of a sample; `null` for an empty one. */
98
117
  export const percentile = (values, fraction) => {
99
118
  if (values.length === 0) {
@@ -112,45 +131,39 @@ const isUnavailable = (span) => {
112
131
  return status.startsWith('5') || rpc === '-32001';
113
132
  };
114
133
  /**
115
- * The SLIs over catalog points — the platform-wide reading.
134
+ * The SLIs from the service levels dashboard — the platform-wide reading.
116
135
  *
117
- * `mcp.calls{outcome}` gives availability, `mcp.call.duration{tool}` the
118
- * latency, `mcp.tasks{status}` the task success rate and
119
- * `sandbox.launch_seconds{provider}` the launches.
136
+ * Its panels arrive summarised: calls and tasks as how much each counter grew
137
+ * over the window, the two p95s from their histograms' buckets. The call
138
+ * duration is recorded in seconds and shown in milliseconds. A panel with
139
+ * nothing in it reads as not measured, never as a zero.
120
140
  */
121
- export const summarizeMetricPoints = (metrics, filters = {}) => {
122
- const within = (point) => notBefore(point.timestamp, filters.since);
123
- const calls = (metrics['mcp.calls'] ?? []).filter(within);
124
- const totalCalls = calls.reduce((sum, point) => sum + point.value, 0);
125
- const failedCalls = calls
126
- .filter(point => ['error', 'unavailable'].includes(attribute(point, 'outcome')))
127
- .reduce((sum, point) => sum + point.value, 0);
128
- const durations = (metrics['mcp.call.duration'] ?? []).filter(within).map(point => point.value);
129
- const tasks = (metrics['mcp.tasks'] ?? []).filter(within);
130
- const terminal = tasks.filter(point => ['completed', 'failed', 'cancelled'].includes(attribute(point, 'status')));
131
- const totalTerminal = terminal.reduce((sum, point) => sum + point.value, 0);
132
- const completed = terminal
133
- .filter(point => attribute(point, 'status') === 'completed')
134
- .reduce((sum, point) => sum + point.value, 0);
135
- const launches = (metrics['sandbox.launch_seconds'] ?? []).filter(within);
136
- const byProvider = new Map();
137
- for (const point of launches) {
138
- const provider = attribute(point, 'provider') || 'unknown';
139
- byProvider.set(provider, [...(byProvider.get(provider) ?? []), point.value]);
140
- }
141
- const p95SandboxLaunchSeconds = {};
142
- for (const [provider, values] of byProvider) {
143
- const p95 = percentile(values, 0.95);
144
- if (p95 !== null) {
145
- p95SandboxLaunchSeconds[provider] = p95;
146
- }
147
- }
141
+ export const summarizeServiceLevels = (data) => {
142
+ const panels = new Map(data.panels.map(panel => [panel.id, panel]));
143
+ const byLabel = (id) => Object.fromEntries((panels.get(id)?.series ?? [])
144
+ .filter(entry => entry.summary !== null)
145
+ .map(entry => [entry.label, entry.summary]));
146
+ const total = (values) => Object.values(values).reduce((sum, value) => sum + value, 0);
147
+ const calls = byLabel(MCP_SERVICE_LEVEL_PANELS.callsByOutcome);
148
+ const totalCalls = total(calls);
149
+ const failedCalls = (calls.error ?? 0) + (calls.unavailable ?? 0);
150
+ const tasks = byLabel(MCP_SERVICE_LEVEL_PANELS.tasksByStatus);
151
+ const terminal = TERMINAL_STATUSES.reduce((sum, status) => sum + (tasks[status] ?? 0), 0);
152
+ const p95Seconds = byLabel(MCP_SERVICE_LEVEL_PANELS.callDurationP95)[''];
153
+ const p95SandboxLaunchSeconds = Object.fromEntries(Object.entries(byLabel(MCP_SERVICE_LEVEL_PANELS.sandboxLaunchP95)).map(([provider, seconds]) => [
154
+ provider || 'unknown',
155
+ seconds,
156
+ ]));
148
157
  return {
149
158
  availability: totalCalls > 0 ? (totalCalls - failedCalls) / totalCalls : null,
150
- p95CallDurationMs: percentile(durations, 0.95),
151
- taskSuccessRate: totalTerminal > 0 ? completed / totalTerminal : null,
159
+ p95CallDurationMs: p95Seconds === undefined ? null : p95Seconds * 1000,
160
+ taskSuccessRate: terminal > 0 ? (tasks.completed ?? 0) / terminal : null,
152
161
  p95SandboxLaunchSeconds,
153
- samples: { calls: totalCalls, tasks: totalTerminal, launches: launches.length },
162
+ samples: {
163
+ calls: totalCalls,
164
+ tasks: terminal,
165
+ launches: total(byLabel(MCP_SERVICE_LEVEL_PANELS.sandboxLaunches)),
166
+ },
154
167
  };
155
168
  };
156
169
  /**
@@ -183,21 +196,21 @@ export const summarizeRequestSpans = (spans, filters = {}) => {
183
196
  /** The four SLIs and the catalog, for everyone or for one agent or organization. */
184
197
  export const fetchMcpMetrics = async (token, filters = {}, otelUrl = DEFAULT_SERVICE_URLS.OTEL) => {
185
198
  const limit = filters.limit ?? 500;
186
- const names = Object.values(MCP_SLI_METRICS);
187
- const pages = await Promise.all(names.map(name => queryMetrics(token, { metricName: name, serviceName: serviceNameFor(name), limit }, otelUrl)));
188
- const metrics = {};
189
- names.forEach((name, index) => {
190
- metrics[name] = pages[index].data ?? [];
191
- });
192
199
  const scoped = Boolean(filters.agent || filters.org);
193
- const spans = scoped
194
- ? (await listTraces(token, { serviceName: MCP_GATEWAY_SERVICE_NAME, limit }, otelUrl)).data ?? []
195
- : [];
200
+ const [levels, names, traces] = await Promise.all([
201
+ scoped
202
+ ? undefined
203
+ : getDashboardData(token, MCP_SERVICE_LEVELS_DASHBOARD, { since: filters.since ? new Date(filters.since) : undefined }, otelUrl),
204
+ listMetricNames(token, otelUrl),
205
+ scoped ? listTraces(token, { serviceName: MCP_GATEWAY_SERVICE_NAME, limit }, otelUrl) : undefined,
206
+ ]);
207
+ const reported = new Set((names.data ?? []).map(row => row.metric_name));
208
+ const spans = traces?.data ?? [];
196
209
  return {
197
210
  filters,
198
- metrics,
211
+ reporting: MCP_METRIC_CATALOG.filter(name => reported.has(name)),
199
212
  spans,
200
- slis: scoped ? summarizeRequestSpans(spans, filters) : summarizeMetricPoints(metrics, filters),
213
+ slis: levels ? summarizeServiceLevels(levels) : summarizeRequestSpans(spans, filters),
201
214
  readAt: new Date().toISOString(),
202
215
  };
203
216
  };