@datalayer/agent-runtimes 1.0.26 → 1.0.29

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 (34) hide show
  1. package/lib/api/index.js +16 -4
  2. package/lib/components/code-sandboxes/CodeSandboxEnvironmentSelect.d.ts +41 -0
  3. package/lib/components/code-sandboxes/CodeSandboxEnvironmentSelect.js +83 -0
  4. package/lib/components/code-sandboxes/CodeSandboxLauncher.d.ts +11 -0
  5. package/lib/components/code-sandboxes/CodeSandboxLauncher.js +190 -17
  6. package/lib/components/code-sandboxes/CodeSandboxNames.d.ts +47 -0
  7. package/lib/components/code-sandboxes/CodeSandboxNames.js +211 -0
  8. package/lib/components/code-sandboxes/CodeSandboxPicker.d.ts +14 -1
  9. package/lib/components/code-sandboxes/CodeSandboxPicker.js +203 -18
  10. package/lib/components/code-sandboxes/CodeSandboxTransfer.d.ts +6 -0
  11. package/lib/components/code-sandboxes/CodeSandboxUtils.d.ts +3 -0
  12. package/lib/components/code-sandboxes/CodeSandboxUtils.js +76 -20
  13. package/lib/components/code-sandboxes/NewCodeSandboxControls.d.ts +17 -0
  14. package/lib/components/code-sandboxes/NewCodeSandboxControls.js +4 -4
  15. package/lib/components/code-sandboxes/codeSandboxProviders.d.ts +29 -0
  16. package/lib/components/code-sandboxes/codeSandboxProviders.js +81 -0
  17. package/lib/components/code-sandboxes/index.d.ts +3 -0
  18. package/lib/components/code-sandboxes/index.js +3 -0
  19. package/lib/examples/AgentspecsExample.js +3 -3
  20. package/lib/identity/index.d.ts +1 -1
  21. package/lib/identity/index.js +1 -1
  22. package/lib/identity/types.d.ts +1 -1
  23. package/lib/identity/types.js +1 -1
  24. package/lib/models/CodeSandboxVariant.d.ts +45 -0
  25. package/lib/models/CodeSandboxVariant.js +65 -0
  26. package/lib/models/index.d.ts +1 -0
  27. package/lib/models/index.js +1 -0
  28. package/lib/runtimes/actions.js +14 -2
  29. package/lib/specs/envvars.d.ts +1 -1
  30. package/lib/specs/envvars.js +3 -3
  31. package/lib/specs/mcpServers.js +2 -2
  32. package/lib/state/substates/RuntimesState.js +4 -1
  33. package/package.json +1 -1
  34. package/scripts/gen_workers.py +1 -1
@@ -2,11 +2,13 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- import { PathExt } from '@jupyterlab/coreutils';
6
5
  import { nullTranslator } from '@jupyterlab/translation';
7
- const ASSIGN_NEW_RUNTIME_LABEL = 'Assign a new Runtime';
8
- const ASSIGN_EXISTING_REMOTE_RUNTIME_LABEL = 'Assign an existing Remote Runtime';
9
- const ASSIGN_EXISTING_RUNTIME_LABEL = 'Assign an existing Runtime';
6
+ const ASSIGN_NEW_RUNTIME_LABEL = 'Assign a new Code Sandbox';
7
+ import { ensureCodeSandboxGivenName } from './CodeSandboxNames';
8
+ import { isCodeSandboxProviderAvailable } from './codeSandboxProviders';
9
+ import { CodeSandboxVariant, codeSandboxVariantOf, } from '../../models/CodeSandboxVariant';
10
+ const ASSIGN_EXISTING_REMOTE_RUNTIME_LABEL = 'Assign an existing Remote Code Sandbox';
11
+ const ASSIGN_EXISTING_RUNTIME_LABEL = 'Assign an existing Code Sandbox';
10
12
  /**
11
13
  * Create the grouped runtime descriptions.
12
14
  */
@@ -18,18 +20,31 @@ export function getGroupedCodeSandboxDescs(multiServiceManager, kernelId, transl
18
20
  // identifier, the specification only names it. Only the environments to
19
21
  // start a new runtime in come from the specifications, and they wait.
20
22
  const specs = multiServiceManager.local.kernelspecs.specs;
21
- const sessions = multiServiceManager.local.sessions.running();
23
+ /*
24
+ * The kernels of a Jupyter Server belong to the page that talks to one —
25
+ * inside JupyterLab. The web application's "local" manager points at
26
+ * whatever server its configuration names, and offering those kernels
27
+ * there lists sandboxes the user never started and cannot reason about.
28
+ * One rule for the whole file: the provider registry answers it, for the
29
+ * existing sandboxes here exactly as for the new-sandbox group below.
30
+ */
31
+ const jupyterProviderAvailable = isCodeSandboxProviderAvailable('local', multiServiceManager);
32
+ const sessions = jupyterProviderAvailable
33
+ ? multiServiceManager.local.sessions.running()
34
+ : [];
22
35
  const kernels = {};
23
36
  // Add the sessions.
24
37
  const runningSessions = Array.from(sessions)
25
38
  .filter(session => session.kernel && session.kernel.id !== kernelId)
26
- .map(session => {
39
+ // Annotated for the same reason as the chain below: what is pushed into
40
+ // this list includes sandboxes that carry a pod rather than a kernel.
41
+ .map((session) => {
27
42
  const spec = specs?.kernelspecs[session.kernel.name];
28
43
  return {
29
44
  kernelId: session.kernel.id,
30
45
  name: spec?.name ?? session.kernel.name,
31
46
  language: spec?.language ?? '',
32
- displayName: session.name || PathExt.basename(session.path),
47
+ displayName: ensureCodeSandboxGivenName(session.kernel.id, spec?.display_name ?? session.kernel.name),
33
48
  location: 'local',
34
49
  };
35
50
  })
@@ -43,36 +58,56 @@ export function getGroupedCodeSandboxDescs(multiServiceManager, kernelId, transl
43
58
  kernelId: session.kernel.id,
44
59
  name: spec?.name ?? session.kernel.name,
45
60
  language: spec?.language ?? '',
46
- displayName: session.name || PathExt.basename(session.path),
61
+ displayName: ensureCodeSandboxGivenName(session.kernel.id, spec?.display_name ?? session.kernel.name),
47
62
  location: 'browser',
48
63
  };
49
64
  }))
50
65
  .filter(filterKernels);
51
66
  // Add the running runtimes.
52
67
  const listedAsSession = runningSessions.map(s => s.kernelId);
53
- const runningKernels = Array.from(multiServiceManager.local.kernels.running())
68
+ const runningKernels = Array.from(jupyterProviderAvailable ? multiServiceManager.local.kernels.running() : [])
54
69
  .filter(k => !listedAsSession.includes(k.id))
55
- .map(k => {
70
+ // Annotated, because the element type of the chain below is taken from
71
+ // this first link: a sandbox with no kernel joins it further down.
72
+ .map((k) => {
56
73
  const spec = specs?.kernelspecs[k.name];
57
74
  return {
58
75
  kernelId: k.id,
59
76
  name: spec?.name ?? k.name,
60
77
  language: spec?.language ?? '',
61
- displayName: spec?.display_name ?? k.name,
78
+ displayName: ensureCodeSandboxGivenName(k.id, spec?.display_name ?? k.name),
62
79
  location: 'local',
63
80
  };
64
81
  })
65
82
  .concat((multiServiceManager.remote?.runtimesManager.get() ?? [])
66
- .filter(k => k.id && !listedAsSession.includes(k.id))
83
+ /*
84
+ * A sandbox with no KERNEL is still a sandbox that runs.
85
+ *
86
+ * The ones of an external provider — Kaggle, Modal — run at their
87
+ * provider and carry no kernel of this platform, so their `id` is
88
+ * empty. Asking for a truthy one dropped every one of them: they
89
+ * appeared in the table of the Code Sandboxes and nowhere in the
90
+ * picker, which is the one place a notebook can be pointed at them.
91
+ * What they always have is the pod that stands for them.
92
+ */
93
+ .filter(k => (k.id || k.pod_name) && !listedAsSession.includes(k.id))
67
94
  .map(runtime => {
68
95
  const environment = multiServiceManager
69
96
  .remote.environments.get()
70
97
  .find(env => env.name === runtime.environment.name);
71
98
  return {
72
- kernelId: runtime.id,
99
+ // Empty for an external sandbox: it has none, and saying so is
100
+ // what keeps the picker from trying to connect to one.
101
+ kernelId: runtime.id || undefined,
73
102
  name: environment.name,
74
103
  language: environment.language,
75
- displayName: environment.title || runtime.given_name || environment.name,
104
+ provider: codeSandboxVariantOf(environment?.owner ?? runtime.environment?.owner),
105
+ displayName:
106
+ // The name it was GIVEN first: the title of the environment is
107
+ // what it runs, and every sandbox of that environment answers
108
+ // it — the launcher asks for a name precisely so two of them
109
+ // can be told apart here.
110
+ runtime.given_name || environment.title || environment.name,
76
111
  location: 'remote',
77
112
  podName: runtime.pod_name,
78
113
  gpu: environment.resources?.['nvidia.com/gpu'],
@@ -86,7 +121,7 @@ export function getGroupedCodeSandboxDescs(multiServiceManager, kernelId, transl
86
121
  kernelId: k.id,
87
122
  name: spec?.name ?? k.name,
88
123
  language: spec?.language ?? '',
89
- displayName: spec?.display_name ?? k.name,
124
+ displayName: ensureCodeSandboxGivenName(k.id, spec?.display_name ?? k.name),
90
125
  location: 'browser',
91
126
  };
92
127
  }))
@@ -96,7 +131,9 @@ export function getGroupedCodeSandboxDescs(multiServiceManager, kernelId, transl
96
131
  // another — and a runtime listed twice would be picked twice.
97
132
  const seen = new Set();
98
133
  const distinctRunning = runningSessions.filter(desc => {
99
- const key = `${desc.location}:${desc.kernelId ?? desc.name}`;
134
+ // A kernel-less sandbox is named by its pod; without it every external
135
+ // sandbox of one environment collapsed into a single entry.
136
+ const key = `${desc.location}:${desc.kernelId ?? desc.podName ?? desc.name}`;
100
137
  if (seen.has(key)) {
101
138
  return false;
102
139
  }
@@ -112,7 +149,14 @@ export function getGroupedCodeSandboxDescs(multiServiceManager, kernelId, transl
112
149
  kernels[key] = runningSessions;
113
150
  }
114
151
  // Environments.
115
- const environments = Object.values(specs?.kernelspecs ?? {})
152
+ /*
153
+ * The sandboxes of a Jupyter Server, which is a provider like the others.
154
+ *
155
+ * It is the one provider that only exists where the page is talking to such
156
+ * a server: inside JupyterLab. Elsewhere its kernels are somebody else's,
157
+ * and offering them promises what cannot be reached.
158
+ */
159
+ const environments = Object.values(jupyterProviderAvailable ? (specs?.kernelspecs ?? {}) : {})
116
160
  .filter(spec => !!spec)
117
161
  .map(spec => ({
118
162
  name: spec.name,
@@ -120,10 +164,21 @@ export function getGroupedCodeSandboxDescs(multiServiceManager, kernelId, transl
120
164
  displayName: spec.display_name,
121
165
  gpu: spec.resources?.['nvidia.com/gpu'],
122
166
  location: 'local',
167
+ provider: CodeSandboxVariant.JupyterServer,
123
168
  }))
124
169
  .filter(filterKernels);
125
- environments.push(...(multiServiceManager.remote?.environments
126
- .get()
170
+ /*
171
+ * Every provider that can be used from here, and only those.
172
+ *
173
+ * A provider is a place a sandbox runs — the platform, this Jupyter Server,
174
+ * the browser — and each is offered when it is REACHABLE, not according to
175
+ * which application is asking. Withholding the platform inside JupyterLab
176
+ * took `ai-agents-env` off the list and left no way to launch a remote
177
+ * sandbox from there at all; the rule that was wanted is the other one, and
178
+ * it is stated where the Jupyter Server is read below.
179
+ */
180
+ const remoteEnvironments = multiServiceManager.remote?.environments.get() ?? [];
181
+ environments.push(...remoteEnvironments
127
182
  .map(spec => ({
128
183
  name: spec.name,
129
184
  language: spec.language,
@@ -131,8 +186,9 @@ export function getGroupedCodeSandboxDescs(multiServiceManager, kernelId, transl
131
186
  location: 'remote',
132
187
  gpu: spec.resources?.['nvidia.com/gpu'],
133
188
  burningRate: spec.burning_rate,
189
+ provider: codeSandboxVariantOf(spec?.owner),
134
190
  }))
135
- .filter(filterKernels) ?? []));
191
+ .filter(filterKernels));
136
192
  environments.push(...Object.values(multiServiceManager.browser?.kernelspecs.specs?.kernelspecs ?? {})
137
193
  .filter(spec => !!spec)
138
194
  .map(spec => ({
@@ -34,6 +34,23 @@ export interface INewCodeSandboxControlsProps {
34
34
  userStorage: boolean;
35
35
  onUserStorageToggle: () => void;
36
36
  storageDisabled?: boolean;
37
+ /**
38
+ * The name the sandbox is given, when the caller asks for that question.
39
+ *
40
+ * A sandbox of the platform is named through `given_name`; one of a server
41
+ * or of the browser has the name held for it by `CodeSandboxNames`. The
42
+ * question is the same either way, so it is asked in one place.
43
+ */
44
+ givenName?: string;
45
+ onGivenNameChange?: (givenName: string) => void;
46
+ /**
47
+ * Whether the storage of the user is offered.
48
+ *
49
+ * A sandbox of this Jupyter Server runs on this machine, with the files of
50
+ * this machine: it reserves no time and mounts no storage of the platform,
51
+ * so neither question applies to it.
52
+ */
53
+ withUserStorage?: boolean;
37
54
  }
38
55
  /**
39
56
  * The two questions of a new sandbox: the time reservation, and the storage.
@@ -15,7 +15,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
15
15
  *
16
16
  * @module components/code-sandboxes/NewCodeSandboxControls
17
17
  */
18
- import { FormControl, ToggleSwitch, Tooltip, IconButton } from '@primer/react';
18
+ import { FormControl, IconButton, TextInput, ToggleSwitch, Tooltip, } from '@primer/react';
19
19
  import { AlertIcon } from '@primer/octicons-react';
20
20
  import { useCoreStore, useIAMStore } from '../../state/substates';
21
21
  import { CodeSandboxReservationControl, MAXIMAL_CODE_SANDBOX_TIME_RESERVATION_MINUTES, } from './CodeSandboxReservationControl';
@@ -67,10 +67,10 @@ export function creditsLimitFor(timeLimitMinutes, burningRate) {
67
67
  * The storage is withheld on white-label deployments, as it always was.
68
68
  */
69
69
  export function NewCodeSandboxControls(props) {
70
- const { addCredits, burningRate, withReservation = true, disabled, error, max, onTimeChange, onUserStorageToggle, storageDisabled, timeLimit, userStorage, } = props;
70
+ const { addCredits, burningRate, withReservation = true, disabled, error, givenName, max, onGivenNameChange, onTimeChange, onUserStorageToggle, storageDisabled, timeLimit, userStorage, withUserStorage = true, } = props;
71
71
  const { configuration } = useCoreStore();
72
- return (_jsxs(_Fragment, { children: [withReservation && (_jsx(CodeSandboxReservationControl, { addCredits: addCredits, disabled: disabled, label: 'Time reservation', max: max, time: timeLimit, burningRate: burningRate, onTimeChange: onTimeChange, error: error })), !configuration.whiteLabel && (_jsxs(FormControl, { disabled: storageDisabled, layout: "horizontal", children: [_jsxs(FormControl.Label, { id: "new-sandbox-user-storage-label", children: ["User storage", _jsx(Tooltip, { text: "The runtime will be slower to start.", direction: "e", style: { marginLeft: 3 }, children: _jsx(IconButton, { icon: AlertIcon, "aria-label": "", variant: "invisible" }) })] }), _jsx(ToggleSwitch, { disabled: storageDisabled, checked: userStorage, size: "small", onClick: event => {
72
+ return (_jsxs(_Fragment, { children: [withReservation && (_jsx(CodeSandboxReservationControl, { addCredits: addCredits, disabled: disabled, label: 'Time reservation', max: max, time: timeLimit, burningRate: burningRate, onTimeChange: onTimeChange, error: error })), withUserStorage && !configuration.whiteLabel && (_jsxs(FormControl, { disabled: storageDisabled, layout: "horizontal", children: [_jsxs(FormControl.Label, { id: "new-sandbox-user-storage-label", children: ["User storage", _jsx(Tooltip, { text: "The runtime will be slower to start.", direction: "e", style: { marginLeft: 3 }, children: _jsx(IconButton, { icon: AlertIcon, "aria-label": "", variant: "invisible" }) })] }), _jsx(ToggleSwitch, { disabled: storageDisabled, checked: userStorage, size: "small", onClick: event => {
73
73
  event.preventDefault();
74
74
  onUserStorageToggle();
75
- }, "aria-labelledby": "new-sandbox-user-storage-label" })] }))] }));
75
+ }, "aria-labelledby": "new-sandbox-user-storage-label" })] })), onGivenNameChange && (_jsxs(FormControl, { disabled: disabled, children: [_jsx(FormControl.Label, { children: "Given Name" }), _jsx(TextInput, { block: true, name: "given-name", value: givenName ?? '', onChange: event => onGivenNameChange(event.target.value) }), _jsx(FormControl.Caption, { children: "What this sandbox is called, wherever it is listed." })] }))] }));
76
76
  }
@@ -0,0 +1,29 @@
1
+ import type { IMultiServiceManager } from '../../api';
2
+ import type { IRuntimeLocation } from '../../models';
3
+ /** A place sandboxes run, as this page knows it. */
4
+ export type ICodeSandboxProvider = {
5
+ /** Identifier of the provider, which is where its sandboxes run. */
6
+ name: IRuntimeLocation;
7
+ /** What it is called, for a person. */
8
+ title: string;
9
+ /** Whether this page can reach it at all. */
10
+ available: boolean;
11
+ /** Why not, when it cannot. */
12
+ unavailableReason?: string;
13
+ };
14
+ /**
15
+ * Every provider this page knows, and whether it can be used.
16
+ *
17
+ * @param multiServiceManager The services of the page, which say what is
18
+ * reachable: the platform when the account has it, the Jupyter Server when
19
+ * the page is one, the browser when kernels run in it
20
+ */
21
+ export declare function codeSandboxProviders(multiServiceManager: IMultiServiceManager | undefined): ICodeSandboxProvider[];
22
+ /**
23
+ * Whether one provider can be used from this page.
24
+ *
25
+ * @param location Where the sandboxes of that provider run
26
+ * @param multiServiceManager The services of the page
27
+ */
28
+ export declare function isCodeSandboxProviderAvailable(location: IRuntimeLocation, multiServiceManager: IMultiServiceManager | undefined): boolean;
29
+ export default codeSandboxProviders;
@@ -0,0 +1,81 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The places a code sandbox can run, and which of them this page can use.
7
+ *
8
+ * A sandbox is not one kind of thing: it is a runtime of the platform, a
9
+ * kernel of a Jupyter Server, a kernel of the browser — and, on the Python
10
+ * side, a Kaggle session or a Modal container as well. Each of those is a
11
+ * PROVIDER, each ships the environments it offers (the platform ships
12
+ * `ai-agents-env`, Kaggle a CPU and a GPU session), and each is usable only
13
+ * where what it needs is at hand.
14
+ *
15
+ * That last rule was written three times over — in the picker, in the
16
+ * launcher, in the table of the sandboxes — and drifted every time it was
17
+ * touched: withholding the platform inside JupyterLab took `ai-agents-env`
18
+ * off the list and left no way to launch a remote sandbox there at all. It is
19
+ * stated once here, and the three read it.
20
+ *
21
+ * The same concept lives in `code_sandboxes.providers` on the Python side,
22
+ * where availability is decided by secrets rather than by services. Both
23
+ * answer the same question — which providers can be used from here — so the
24
+ * CLI, the web application and JupyterLab offer the same thing.
25
+ *
26
+ * @module components/code-sandboxes/codeSandboxProviders
27
+ */
28
+ import { loadJupyterConfig } from '@datalayer/jupyter-react';
29
+ /**
30
+ * Every provider this page knows, and whether it can be used.
31
+ *
32
+ * @param multiServiceManager The services of the page, which say what is
33
+ * reachable: the platform when the account has it, the Jupyter Server when
34
+ * the page is one, the browser when kernels run in it
35
+ */
36
+ export function codeSandboxProviders(multiServiceManager) {
37
+ /*
38
+ * The Jupyter Server is the provider bound to WHERE the page runs.
39
+ *
40
+ * Its kernels belong to the server the page is talking to. Outside
41
+ * JupyterLab there is no such server — what a manager reports as "local" is
42
+ * somebody else's — so offering them promises what cannot be reached.
43
+ */
44
+ const insideJupyterLab = loadJupyterConfig().insideJupyterLab;
45
+ return [
46
+ {
47
+ name: 'remote',
48
+ title: 'Datalayer',
49
+ available: Boolean(multiServiceManager?.remote),
50
+ unavailableReason: multiServiceManager?.remote
51
+ ? undefined
52
+ : 'Sign in to Datalayer to launch a sandbox on the platform.'
53
+ },
54
+ {
55
+ name: 'local',
56
+ title: 'Jupyter Server',
57
+ available: insideJupyterLab && Boolean(multiServiceManager?.local),
58
+ unavailableReason: insideJupyterLab
59
+ ? undefined
60
+ : 'The kernels of a Jupyter Server are offered inside JupyterLab.'
61
+ },
62
+ {
63
+ name: 'browser',
64
+ title: 'Browser',
65
+ available: Boolean(multiServiceManager?.browser),
66
+ unavailableReason: multiServiceManager?.browser
67
+ ? undefined
68
+ : 'No kernel runs in this browser.'
69
+ }
70
+ ];
71
+ }
72
+ /**
73
+ * Whether one provider can be used from this page.
74
+ *
75
+ * @param location Where the sandboxes of that provider run
76
+ * @param multiServiceManager The services of the page
77
+ */
78
+ export function isCodeSandboxProviderAvailable(location, multiServiceManager) {
79
+ return Boolean(codeSandboxProviders(multiServiceManager).find(provider => provider.name === location)?.available);
80
+ }
81
+ export default codeSandboxProviders;
@@ -11,11 +11,14 @@
11
11
  */
12
12
  export * from './CodeSandboxCellVariables';
13
13
  export * from './CodeSandboxCellVariablesDialog';
14
+ export * from './CodeSandboxEnvironmentSelect';
14
15
  export * from './CodeSandboxLauncher';
15
16
  export * from './CodeSandboxPicker';
16
17
  export * from './CodeSandboxPickerCell';
17
18
  export * from './CodeSandboxReservationControl';
18
19
  export * from './CodeSandboxTransfer';
20
+ export * from './CodeSandboxNames';
21
+ export * from './codeSandboxProviders';
19
22
  export * from './CodeSandboxUtils';
20
23
  export * from './CodeSandboxVariables';
21
24
  export * from './NewCodeSandboxControls';
@@ -15,11 +15,14 @@
15
15
  */
16
16
  export * from './CodeSandboxCellVariables';
17
17
  export * from './CodeSandboxCellVariablesDialog';
18
+ export * from './CodeSandboxEnvironmentSelect';
18
19
  export * from './CodeSandboxLauncher';
19
20
  export * from './CodeSandboxPicker';
20
21
  export * from './CodeSandboxPickerCell';
21
22
  export * from './CodeSandboxReservationControl';
22
23
  export * from './CodeSandboxTransfer';
24
+ export * from './CodeSandboxNames';
25
+ export * from './codeSandboxProviders';
23
26
  export * from './CodeSandboxUtils';
24
27
  export * from './CodeSandboxVariables';
25
28
  export * from './NewCodeSandboxControls';
@@ -84,7 +84,7 @@ const GITHUB_CLIENT_ID = import.meta.env.VITE_GITHUB_CLIENT_ID || 'example-clien
84
84
  // Kaggle API token - set via environment variable
85
85
  // Get your token at: https://www.kaggle.com/settings/account (API section)
86
86
  // Download kaggle.json and use the "key" value
87
- const KAGGLE_TOKEN = import.meta.env.VITE_KAGGLE_TOKEN || '';
87
+ const KAGGLE_API_TOKEN = import.meta.env.VITE_KAGGLE_API_TOKEN || '';
88
88
  const isPlainObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
89
89
  const yamlScalar = (value) => {
90
90
  if (value === null || value === undefined) {
@@ -252,11 +252,11 @@ const DEFAULT_IDENTITY_PROVIDERS = {
252
252
  },
253
253
  }
254
254
  : {}),
255
- ...(KAGGLE_TOKEN
255
+ ...(KAGGLE_API_TOKEN
256
256
  ? {
257
257
  kaggle: {
258
258
  type: 'token',
259
- token: KAGGLE_TOKEN,
259
+ token: KAGGLE_API_TOKEN,
260
260
  displayName: 'Kaggle',
261
261
  iconUrl: 'https://www.kaggle.com/static/images/favicon.ico',
262
262
  },
@@ -37,7 +37,7 @@
37
37
  * ```
38
38
  */
39
39
  export type { OAuthProvider, OAuthToken, Identity, ProviderUserInfo, OAuthProviderConfig, AuthorizationRequest, AuthorizationCallback, IdentityState, IdentityActions, IdentityStore, AuthType, TokenProviderConfig, OAuthIdentityProviderConfig, TokenIdentityProviderConfig, IdentityProviderConfig, IdentityProvidersConfig, } from './types';
40
- export { GITHUB_PROVIDER, GOOGLE_PROVIDER, KAGGLE_PROVIDER, KAGGLE_TOKEN_PROVIDER, } from './types';
40
+ export { GITHUB_PROVIDER, GOOGLE_PROVIDER, KAGGLE_PROVIDER, KAGGLE_API_TOKEN_PROVIDER, } from './types';
41
41
  export { generateCodeVerifier, generateCodeChallenge, generateState, generatePKCEPair, } from './pkce';
42
42
  export { discoverAuthorizationServer, supportsDCR, registerClient, updateClientRegistration, deleteClientRegistration, getOrCreateDynamicClient, loadDynamicClient, saveDynamicClient, removeDynamicClient, getAllDynamicClients, clearAllDynamicClients, dynamicClientToProviderConfig, type AuthorizationServerMetadata, type ClientRegistrationRequest, type ClientRegistrationResponse, type ClientRegistrationError, type DynamicClient, } from './dcr';
43
43
  export { useIdentityStore, useConnectedIdentities, useConnectedProviders, useIdentity as useIdentitySelector, useIsProviderConnected, usePendingAuthorization, useIdentityLoading, useIdentityError, configureBuiltinProviders, } from './identityStore';
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- export { GITHUB_PROVIDER, GOOGLE_PROVIDER, KAGGLE_PROVIDER, KAGGLE_TOKEN_PROVIDER, } from './types';
5
+ export { GITHUB_PROVIDER, GOOGLE_PROVIDER, KAGGLE_PROVIDER, KAGGLE_API_TOKEN_PROVIDER, } from './types';
6
6
  // PKCE utilities
7
7
  export { generateCodeVerifier, generateCodeChallenge, generateState, generatePKCEPair, } from './pkce';
8
8
  // Dynamic Client Registration (DCR)
@@ -237,7 +237,7 @@ export interface TokenProviderConfig {
237
237
  * Kaggle token-based provider configuration.
238
238
  * Generate token at: https://www.kaggle.com/settings/account (API section)
239
239
  */
240
- export declare const KAGGLE_TOKEN_PROVIDER: TokenProviderConfig;
240
+ export declare const KAGGLE_API_TOKEN_PROVIDER: TokenProviderConfig;
241
241
  /**
242
242
  * OAuth provider configuration for identity providers.
243
243
  */
@@ -63,7 +63,7 @@ export const KAGGLE_PROVIDER = {
63
63
  * Kaggle token-based provider configuration.
64
64
  * Generate token at: https://www.kaggle.com/settings/account (API section)
65
65
  */
66
- export const KAGGLE_TOKEN_PROVIDER = {
66
+ export const KAGGLE_API_TOKEN_PROVIDER = {
67
67
  provider: 'kaggle',
68
68
  displayName: 'Kaggle',
69
69
  iconUrl: 'https://www.kaggle.com/static/images/favicon.ico',
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The kinds of code sandbox, as the Python `code_sandboxes` package names them.
3
+ *
4
+ * A sandbox is a place code runs, and the VARIANT names the kind of place:
5
+ * the Datalayer platform, a Jupyter Server, Kaggle, Modal, and so on. The
6
+ * values mirror `code_sandboxes.models.SandboxVariant` verbatim — one naming
7
+ * for the CLI, the services and the applications, so an environment tagged
8
+ * `kaggle` by the operator reads as `kaggle` here and nowhere becomes a
9
+ * synonym.
10
+ *
11
+ * @module models/CodeSandboxVariant
12
+ */
13
+ /** The kinds of code sandbox, by the value the platform tags them with. */
14
+ export declare enum CodeSandboxVariant {
15
+ /** In-process evaluation: tests and examples, isolates nothing. */
16
+ Eval = "eval",
17
+ /** A container on the Docker daemon of a machine. */
18
+ Docker = "docker",
19
+ /** A kernel of a Jupyter Server. */
20
+ JupyterServer = "jupyter-server",
21
+ /** A runtime of the Datalayer platform. */
22
+ Datalayer = "datalayer",
23
+ /** A Google Colab runtime. */
24
+ GoogleColab = "google_colab",
25
+ /** A Kaggle notebook session. */
26
+ Kaggle = "kaggle",
27
+ /** The Monty in-browser interpreter. */
28
+ Monty = "monty",
29
+ /** A container on Modal. */
30
+ Modal = "modal"
31
+ }
32
+ /** What each variant is called where a person reads it. */
33
+ export declare const CODE_SANDBOX_VARIANT_TITLES: Record<CodeSandboxVariant, string>;
34
+ /**
35
+ * The variant an environment belongs to, read off its `owner` tag.
36
+ *
37
+ * The operator tags an external environment with its provider through the
38
+ * environment's `owner` — `kaggle`, `modal` — while the platform's own carry
39
+ * `datalayer`. An owner nothing here recognises reads as Datalayer, which is
40
+ * what every environment was before providers existed.
41
+ */
42
+ export declare function codeSandboxVariantOf(owner?: string | null): CodeSandboxVariant;
43
+ /** A short label for the chip beside an environment's name. */
44
+ export declare function codeSandboxVariantTitle(variant: CodeSandboxVariant): string;
45
+ export default CodeSandboxVariant;
@@ -0,0 +1,65 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The kinds of code sandbox, as the Python `code_sandboxes` package names them.
7
+ *
8
+ * A sandbox is a place code runs, and the VARIANT names the kind of place:
9
+ * the Datalayer platform, a Jupyter Server, Kaggle, Modal, and so on. The
10
+ * values mirror `code_sandboxes.models.SandboxVariant` verbatim — one naming
11
+ * for the CLI, the services and the applications, so an environment tagged
12
+ * `kaggle` by the operator reads as `kaggle` here and nowhere becomes a
13
+ * synonym.
14
+ *
15
+ * @module models/CodeSandboxVariant
16
+ */
17
+ /** The kinds of code sandbox, by the value the platform tags them with. */
18
+ export var CodeSandboxVariant;
19
+ (function (CodeSandboxVariant) {
20
+ /** In-process evaluation: tests and examples, isolates nothing. */
21
+ CodeSandboxVariant["Eval"] = "eval";
22
+ /** A container on the Docker daemon of a machine. */
23
+ CodeSandboxVariant["Docker"] = "docker";
24
+ /** A kernel of a Jupyter Server. */
25
+ CodeSandboxVariant["JupyterServer"] = "jupyter-server";
26
+ /** A runtime of the Datalayer platform. */
27
+ CodeSandboxVariant["Datalayer"] = "datalayer";
28
+ /** A Google Colab runtime. */
29
+ CodeSandboxVariant["GoogleColab"] = "google_colab";
30
+ /** A Kaggle notebook session. */
31
+ CodeSandboxVariant["Kaggle"] = "kaggle";
32
+ /** The Monty in-browser interpreter. */
33
+ CodeSandboxVariant["Monty"] = "monty";
34
+ /** A container on Modal. */
35
+ CodeSandboxVariant["Modal"] = "modal";
36
+ })(CodeSandboxVariant || (CodeSandboxVariant = {}));
37
+ /** What each variant is called where a person reads it. */
38
+ export const CODE_SANDBOX_VARIANT_TITLES = {
39
+ [CodeSandboxVariant.Eval]: 'Eval',
40
+ [CodeSandboxVariant.Docker]: 'Docker',
41
+ [CodeSandboxVariant.JupyterServer]: 'Jupyter Server',
42
+ [CodeSandboxVariant.Datalayer]: 'Datalayer',
43
+ [CodeSandboxVariant.GoogleColab]: 'Google Colab',
44
+ [CodeSandboxVariant.Kaggle]: 'Kaggle',
45
+ [CodeSandboxVariant.Monty]: 'Monty',
46
+ [CodeSandboxVariant.Modal]: 'Modal'
47
+ };
48
+ /**
49
+ * The variant an environment belongs to, read off its `owner` tag.
50
+ *
51
+ * The operator tags an external environment with its provider through the
52
+ * environment's `owner` — `kaggle`, `modal` — while the platform's own carry
53
+ * `datalayer`. An owner nothing here recognises reads as Datalayer, which is
54
+ * what every environment was before providers existed.
55
+ */
56
+ export function codeSandboxVariantOf(owner) {
57
+ const normalized = (owner ?? '').replace('_', '-').toLowerCase();
58
+ const match = Object.values(CodeSandboxVariant).find(value => value.replace('_', '-') === normalized);
59
+ return match ?? CodeSandboxVariant.Datalayer;
60
+ }
61
+ /** A short label for the chip beside an environment's name. */
62
+ export function codeSandboxVariantTitle(variant) {
63
+ return CODE_SANDBOX_VARIANT_TITLES[variant] ?? variant;
64
+ }
65
+ export default CodeSandboxVariant;
@@ -22,6 +22,7 @@ export * from './NotebookDTO';
22
22
  export * from './Page';
23
23
  export * from './PageTag';
24
24
  export * from './ProjectDTO';
25
+ export * from './CodeSandboxVariant';
25
26
  export * from './Runtime';
26
27
  export * from './RuntimeDTO';
27
28
  export * from './Space';
@@ -27,6 +27,7 @@ export * from './NotebookDTO';
27
27
  export * from './Page';
28
28
  export * from './PageTag';
29
29
  export * from './ProjectDTO';
30
+ export * from './CodeSandboxVariant';
30
31
  export * from './Runtime';
31
32
  export * from './RuntimeDTO';
32
33
  export * from './Space';
@@ -30,11 +30,23 @@ export async function getEnvironments() {
30
30
  */
31
31
  export async function createRuntime(options) {
32
32
  const { externalToken, token } = iamStore.getState();
33
+ /*
34
+ * A limit is required; ZERO is one of the answers.
35
+ *
36
+ * The environments of an external provider — Kaggle, Modal — burn no
37
+ * credits of the platform: their sandbox runs on the account of the user,
38
+ * at their provider, and the platform only records it. Their burning rate
39
+ * is `0`, so the limit computed for them is `0` too, and refusing that
40
+ * threw here — before any request was made, which is why the failure
41
+ * showed as a sandbox that could not be created with nothing in the
42
+ * network log to explain it. What is still refused is a limit that is not
43
+ * a number at all, or one below zero.
44
+ */
33
45
  if (typeof options.creditsLimit !== 'number' ||
34
46
  !Number.isFinite(options.creditsLimit) ||
35
- options.creditsLimit <= 0) {
47
+ options.creditsLimit < 0) {
36
48
  throw new Error(`Invalid runtime creditsLimit for environment ${options.environmentName}. ` +
37
- 'A positive number is required.');
49
+ 'A number of zero or more is required.');
38
50
  }
39
51
  const body = {
40
52
  environment: {
@@ -12,7 +12,7 @@ export declare const GITHUB_TOKEN_SPEC_0_0_1: EnvvarSpec;
12
12
  export declare const GOOGLE_OAUTH_CLIENT_ID_SPEC_0_0_1: EnvvarSpec;
13
13
  export declare const GOOGLE_OAUTH_CLIENT_SECRET_SPEC_0_0_1: EnvvarSpec;
14
14
  export declare const HF_TOKEN_SPEC_0_0_1: EnvvarSpec;
15
- export declare const KAGGLE_TOKEN_SPEC_0_0_1: EnvvarSpec;
15
+ export declare const KAGGLE_API_TOKEN_SPEC_0_0_1: EnvvarSpec;
16
16
  export declare const SLACK_BOT_TOKEN_SPEC_0_0_1: EnvvarSpec;
17
17
  export declare const SLACK_CHANNEL_IDS_SPEC_0_0_1: EnvvarSpec;
18
18
  export declare const SLACK_TEAM_ID_SPEC_0_0_1: EnvvarSpec;
@@ -62,8 +62,8 @@ export const HF_TOKEN_SPEC_0_0_1 = {
62
62
  icon: 'key',
63
63
  emoji: '🔑',
64
64
  };
65
- export const KAGGLE_TOKEN_SPEC_0_0_1 = {
66
- id: 'KAGGLE_TOKEN',
65
+ export const KAGGLE_API_TOKEN_SPEC_0_0_1 = {
66
+ id: 'KAGGLE_API_TOKEN',
67
67
  version: '0.0.1',
68
68
  name: 'Kaggle API Token',
69
69
  description: 'API token for accessing Kaggle datasets, competitions, notebooks, and models. Required for Kaggle MCP server authentication.',
@@ -120,7 +120,7 @@ export const ENVVAR_CATALOG = {
120
120
  GOOGLE_OAUTH_CLIENT_ID: GOOGLE_OAUTH_CLIENT_ID_SPEC_0_0_1,
121
121
  GOOGLE_OAUTH_CLIENT_SECRET: GOOGLE_OAUTH_CLIENT_SECRET_SPEC_0_0_1,
122
122
  HF_TOKEN: HF_TOKEN_SPEC_0_0_1,
123
- KAGGLE_TOKEN: KAGGLE_TOKEN_SPEC_0_0_1,
123
+ KAGGLE_API_TOKEN: KAGGLE_API_TOKEN_SPEC_0_0_1,
124
124
  SLACK_BOT_TOKEN: SLACK_BOT_TOKEN_SPEC_0_0_1,
125
125
  SLACK_CHANNEL_IDS: SLACK_CHANNEL_IDS_SPEC_0_0_1,
126
126
  SLACK_TEAM_ID: SLACK_TEAM_ID_SPEC_0_0_1,