@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.
- package/lib/api/index.js +16 -4
- package/lib/components/code-sandboxes/CodeSandboxEnvironmentSelect.d.ts +41 -0
- package/lib/components/code-sandboxes/CodeSandboxEnvironmentSelect.js +83 -0
- package/lib/components/code-sandboxes/CodeSandboxLauncher.d.ts +11 -0
- package/lib/components/code-sandboxes/CodeSandboxLauncher.js +190 -17
- package/lib/components/code-sandboxes/CodeSandboxNames.d.ts +47 -0
- package/lib/components/code-sandboxes/CodeSandboxNames.js +211 -0
- package/lib/components/code-sandboxes/CodeSandboxPicker.d.ts +14 -1
- package/lib/components/code-sandboxes/CodeSandboxPicker.js +203 -18
- package/lib/components/code-sandboxes/CodeSandboxTransfer.d.ts +6 -0
- package/lib/components/code-sandboxes/CodeSandboxUtils.d.ts +3 -0
- package/lib/components/code-sandboxes/CodeSandboxUtils.js +76 -20
- package/lib/components/code-sandboxes/NewCodeSandboxControls.d.ts +17 -0
- package/lib/components/code-sandboxes/NewCodeSandboxControls.js +4 -4
- package/lib/components/code-sandboxes/codeSandboxProviders.d.ts +29 -0
- package/lib/components/code-sandboxes/codeSandboxProviders.js +81 -0
- package/lib/components/code-sandboxes/index.d.ts +3 -0
- package/lib/components/code-sandboxes/index.js +3 -0
- package/lib/examples/AgentspecsExample.js +3 -3
- package/lib/identity/index.d.ts +1 -1
- package/lib/identity/index.js +1 -1
- package/lib/identity/types.d.ts +1 -1
- package/lib/identity/types.js +1 -1
- package/lib/models/CodeSandboxVariant.d.ts +45 -0
- package/lib/models/CodeSandboxVariant.js +65 -0
- package/lib/models/index.d.ts +1 -0
- package/lib/models/index.js +1 -0
- package/lib/runtimes/actions.js +14 -2
- package/lib/specs/envvars.d.ts +1 -1
- package/lib/specs/envvars.js +3 -3
- package/lib/specs/mcpServers.js +2 -2
- package/lib/state/substates/RuntimesState.js +4 -1
- package/package.json +1 -1
- 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
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
|
|
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,
|
|
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
|
|
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
|
-
...(
|
|
255
|
+
...(KAGGLE_API_TOKEN
|
|
256
256
|
? {
|
|
257
257
|
kaggle: {
|
|
258
258
|
type: 'token',
|
|
259
|
-
token:
|
|
259
|
+
token: KAGGLE_API_TOKEN,
|
|
260
260
|
displayName: 'Kaggle',
|
|
261
261
|
iconUrl: 'https://www.kaggle.com/static/images/favicon.ico',
|
|
262
262
|
},
|
package/lib/identity/index.d.ts
CHANGED
|
@@ -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,
|
|
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';
|
package/lib/identity/index.js
CHANGED
|
@@ -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,
|
|
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)
|
package/lib/identity/types.d.ts
CHANGED
|
@@ -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
|
|
240
|
+
export declare const KAGGLE_API_TOKEN_PROVIDER: TokenProviderConfig;
|
|
241
241
|
/**
|
|
242
242
|
* OAuth provider configuration for identity providers.
|
|
243
243
|
*/
|
package/lib/identity/types.js
CHANGED
|
@@ -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
|
|
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;
|
package/lib/models/index.d.ts
CHANGED
package/lib/models/index.js
CHANGED
package/lib/runtimes/actions.js
CHANGED
|
@@ -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
|
|
47
|
+
options.creditsLimit < 0) {
|
|
36
48
|
throw new Error(`Invalid runtime creditsLimit for environment ${options.environmentName}. ` +
|
|
37
|
-
'A
|
|
49
|
+
'A number of zero or more is required.');
|
|
38
50
|
}
|
|
39
51
|
const body = {
|
|
40
52
|
environment: {
|
package/lib/specs/envvars.d.ts
CHANGED
|
@@ -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
|
|
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;
|
package/lib/specs/envvars.js
CHANGED
|
@@ -62,8 +62,8 @@ export const HF_TOKEN_SPEC_0_0_1 = {
|
|
|
62
62
|
icon: 'key',
|
|
63
63
|
emoji: '🔑',
|
|
64
64
|
};
|
|
65
|
-
export const
|
|
66
|
-
id: '
|
|
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
|
-
|
|
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,
|