@oceanum/oceanumlab 4.7.0 → 4.7.2
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/README.md +102 -11
- package/lib/DatameshUI.d.ts +1 -6
- package/lib/DatameshUI.js +1 -9
- package/lib/DatameshWidget.d.ts +18 -2
- package/lib/DatameshWidget.js +80 -12
- package/lib/StoredNotebooks.d.ts +20 -0
- package/lib/StoredNotebooks.js +112 -0
- package/lib/Tabs.d.ts +26 -0
- package/lib/Tabs.js +45 -0
- package/lib/aiBackend.d.ts +1 -1
- package/lib/aiBackend.js +1 -1
- package/lib/auth/accountWidget.d.ts +35 -0
- package/lib/auth/accountWidget.js +95 -0
- package/lib/auth/claims.d.ts +3 -0
- package/lib/auth/claims.js +25 -0
- package/lib/auth/plugin.d.ts +9 -0
- package/lib/auth/plugin.js +126 -0
- package/lib/auth/serverAuth.d.ts +79 -0
- package/lib/auth/serverAuth.js +254 -0
- package/lib/auth/startSignIn.d.ts +11 -0
- package/lib/auth/startSignIn.js +16 -0
- package/lib/auth/tokens.d.ts +102 -0
- package/lib/auth/tokens.js +9 -0
- package/lib/chatRouter.d.ts +13 -1
- package/lib/chatRouter.js +9 -2
- package/lib/constants.d.ts +1 -1
- package/lib/constants.js +1 -1
- package/lib/datameshUiUrl.d.ts +2 -2
- package/lib/datameshUiUrl.js +6 -6
- package/lib/index.d.ts +8 -1
- package/lib/index.js +58 -17
- package/lib/share/client.d.ts +84 -0
- package/lib/share/client.js +205 -0
- package/lib/share/events.d.ts +7 -0
- package/lib/share/events.js +9 -0
- package/lib/share/notebook.d.ts +184 -0
- package/lib/share/notebook.js +277 -0
- package/lib/share/oceanumOnly.d.ts +49 -0
- package/lib/share/oceanumOnly.js +90 -0
- package/lib/share/plugin.d.ts +21 -0
- package/lib/share/plugin.js +883 -0
- package/lib/share/widgets.d.ts +25 -0
- package/lib/share/widgets.js +154 -0
- package/package.json +13 -1
- package/schema/datamesh-connect.json +3 -12
- package/src/DatameshUI.tsx +1 -10
- package/src/DatameshWidget.tsx +143 -19
- package/src/StoredNotebooks.tsx +206 -0
- package/src/Tabs.tsx +102 -0
- package/src/__tests__/DatameshWidget.spec.tsx +33 -20
- package/src/__tests__/OceanumPanel.spec.tsx +157 -0
- package/src/__tests__/StoredNotebooks.spec.tsx +157 -0
- package/src/__tests__/Tabs.spec.tsx +168 -0
- package/src/__tests__/aiBackend.spec.ts +5 -6
- package/src/__tests__/chatRouter.spec.ts +9 -15
- package/src/__tests__/datameshUiUrl.spec.ts +4 -3
- package/src/__tests__/oceanumOnly.spec.ts +193 -0
- package/src/__tests__/pluginArity.spec.ts +24 -0
- package/src/__tests__/serverAuth.spec.ts +277 -0
- package/src/__tests__/settingsSchema.spec.ts +73 -0
- package/src/__tests__/shareClient.spec.ts +314 -0
- package/src/__tests__/shareNotebook.spec.ts +451 -0
- package/src/__tests__/sharePlugin.spec.ts +426 -0
- package/src/aiBackend.ts +1 -1
- package/src/auth/accountWidget.ts +113 -0
- package/src/auth/claims.ts +30 -0
- package/src/auth/plugin.ts +155 -0
- package/src/auth/serverAuth.ts +342 -0
- package/src/auth/startSignIn.ts +23 -0
- package/src/auth/tokens.ts +111 -0
- package/src/chatRouter.ts +8 -4
- package/src/constants.ts +1 -1
- package/src/datameshUiUrl.ts +6 -6
- package/src/index.ts +87 -36
- package/src/share/client.ts +288 -0
- package/src/share/events.ts +10 -0
- package/src/share/notebook.ts +416 -0
- package/src/share/oceanumOnly.ts +126 -0
- package/src/share/plugin.ts +1096 -0
- package/src/share/widgets.ts +217 -0
- package/style/index.css +266 -13
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Token } from '@lumino/coreutils';
|
|
2
|
+
import { ISignal } from '@lumino/signaling';
|
|
3
|
+
/**
|
|
4
|
+
* One Oceanum deployment (production or development).
|
|
5
|
+
*/
|
|
6
|
+
export interface IOceanumEnvironment {
|
|
7
|
+
/** Page hostnames served by this environment, e.g. `notebook.oceanum.io`. */
|
|
8
|
+
readonly hosts: readonly string[];
|
|
9
|
+
/** Auth0 tenant domain, e.g. `auth.oceanum.io`. */
|
|
10
|
+
readonly auth0Domain: string;
|
|
11
|
+
/** Auth0 SPA client id. */
|
|
12
|
+
readonly clientId: string;
|
|
13
|
+
/**
|
|
14
|
+
* The Oceanum apps domain, e.g. `oceanum.io`. The Oceanum nav builds its app, account,
|
|
15
|
+
* docs and sign-out URLs from it (`https://platform.oceanum.io/account`, ...).
|
|
16
|
+
*/
|
|
17
|
+
readonly oceanumDomain: string;
|
|
18
|
+
/** Service base URLs for this environment. */
|
|
19
|
+
readonly urls: IOceanumServiceUrls;
|
|
20
|
+
/**
|
|
21
|
+
* Whether to check for an existing Oceanum.io session with a full-page redirect when the
|
|
22
|
+
* silent iframe check finds none. Only for a tenant on another site than the notebook
|
|
23
|
+
* (browsers that block third-party cookies hide the session from the iframe), and only where
|
|
24
|
+
* the site origin is an allowed callback URL.
|
|
25
|
+
*/
|
|
26
|
+
readonly signInRedirect: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Whether Oceanum.io is the only file management this host shows, as on
|
|
29
|
+
* notebook.oceanum.io: the file browser and the File menu's local-file entries go,
|
|
30
|
+
* and every local save is pushed to the spec store. `'local'` (the default when
|
|
31
|
+
* absent) keeps JupyterLab's file management alongside the Oceanum commands.
|
|
32
|
+
*/
|
|
33
|
+
readonly fileManagement?: 'local' | 'oceanum';
|
|
34
|
+
}
|
|
35
|
+
/** Service base URLs for an environment, without trailing slashes. */
|
|
36
|
+
export interface IOceanumServiceUrls {
|
|
37
|
+
/** Datamesh gateway, e.g. `https://datamesh.oceanum.io`. */
|
|
38
|
+
readonly datamesh: string;
|
|
39
|
+
/** Spec store, e.g. `https://specs.oceanum.io`. */
|
|
40
|
+
readonly specs: string;
|
|
41
|
+
/** User management service (Auth0 user metadata), e.g. `https://manage.oceanum.io`. */
|
|
42
|
+
readonly manage: string;
|
|
43
|
+
/**
|
|
44
|
+
* Datamesh UI, e.g. `https://ui.datamesh.oceanum.io`, shown by oceanumlab's Datamesh panel.
|
|
45
|
+
* Optional: without it, Oceanum production applies.
|
|
46
|
+
*/
|
|
47
|
+
readonly datameshUi?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Oceanum AI backend, e.g. `https://ai.oceanum.io`, called by oceanumlab's AI chat.
|
|
50
|
+
* Optional: without it, Oceanum production applies.
|
|
51
|
+
*/
|
|
52
|
+
readonly ai?: string;
|
|
53
|
+
}
|
|
54
|
+
export type ColorScheme = 'light' | 'dark' | 'auto';
|
|
55
|
+
/** The signed-in user, from the Auth0 ID token claims. */
|
|
56
|
+
export interface IOceanumUser {
|
|
57
|
+
readonly sub: string;
|
|
58
|
+
readonly email: string;
|
|
59
|
+
readonly name: string | null;
|
|
60
|
+
/** `https://oceanum.io/active_org`. */
|
|
61
|
+
readonly activeOrg: string | null;
|
|
62
|
+
/** `https://oceanum.io/color_scheme`. */
|
|
63
|
+
readonly colorScheme: ColorScheme | null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Oceanum.io sign-in state, shared with other Oceanum Notebook extensions.
|
|
67
|
+
*/
|
|
68
|
+
export interface IOceanumAuth {
|
|
69
|
+
/** The environment for this page, or `null` if sign-in is not configured for this host. */
|
|
70
|
+
readonly environment: IOceanumEnvironment | null;
|
|
71
|
+
/** Service URLs for `environment`, or `null` when it is `null`. */
|
|
72
|
+
readonly urls: IOceanumServiceUrls | null;
|
|
73
|
+
/** Resolves once any login redirect has been handled and the session restored. */
|
|
74
|
+
readonly ready: Promise<void>;
|
|
75
|
+
/** The signed-in user, or `null`. */
|
|
76
|
+
readonly user: IOceanumUser | null;
|
|
77
|
+
/** Emits the new user (or `null`) on sign-in and sign-out. */
|
|
78
|
+
readonly userChanged: ISignal<IOceanumAuth, IOceanumUser | null>;
|
|
79
|
+
/** Emits the new access token (or `null`) whenever it changes, including refreshes. */
|
|
80
|
+
readonly tokenChanged: ISignal<IOceanumAuth, string | null>;
|
|
81
|
+
/**
|
|
82
|
+
* Start sign-in without leaving the page, so its kernels survive. Resolves once sign-in has
|
|
83
|
+
* started, not when it completes; watch `userChanged` for the result. Does nothing while a
|
|
84
|
+
* user is signed in.
|
|
85
|
+
*/
|
|
86
|
+
signIn(): Promise<void>;
|
|
87
|
+
/** Sign out of Oceanum.io and clear the session. */
|
|
88
|
+
signOut(): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* A current access token, refreshed if it is near expiry, or `null` when signed out.
|
|
91
|
+
* Never cache the result: Auth0 access tokens are short-lived.
|
|
92
|
+
*/
|
|
93
|
+
getAccessToken(): Promise<string | null>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The sign-in contract. oceanumlab owns it and exports it from its package entry, because a
|
|
97
|
+
* Lumino token is matched by object identity: every extension that provides or consumes it
|
|
98
|
+
* has to import this one object, which JupyterLab arranges by sharing `@oceanum/oceanumlab`
|
|
99
|
+
* as a singleton module. On an ordinary JupyterLab oceanumlab's own device sign-in provides
|
|
100
|
+
* it; on notebook.oceanum.io the notebook's Oceanum widget extension does.
|
|
101
|
+
*/
|
|
102
|
+
export declare const IOceanumAuth: Token<IOceanumAuth>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Token } from '@lumino/coreutils';
|
|
2
|
+
/**
|
|
3
|
+
* The sign-in contract. oceanumlab owns it and exports it from its package entry, because a
|
|
4
|
+
* Lumino token is matched by object identity: every extension that provides or consumes it
|
|
5
|
+
* has to import this one object, which JupyterLab arranges by sharing `@oceanum/oceanumlab`
|
|
6
|
+
* as a singleton module. On an ordinary JupyterLab oceanumlab's own device sign-in provides
|
|
7
|
+
* it; on notebook.oceanum.io the notebook's Oceanum widget extension does.
|
|
8
|
+
*/
|
|
9
|
+
export const IOceanumAuth = new Token('@oceanum/oceanumlab:IOceanumAuth', 'Oceanum.io sign-in state and Datamesh access tokens.');
|
package/lib/chatRouter.d.ts
CHANGED
|
@@ -39,6 +39,12 @@ export declare class ChatRouter {
|
|
|
39
39
|
private _settings;
|
|
40
40
|
private _commands;
|
|
41
41
|
private _notebook;
|
|
42
|
+
/**
|
|
43
|
+
* The Oceanum AI backend's address: the signed-in deployment's, or empty for the
|
|
44
|
+
* production default. Deployment configuration, not a setting -- a user-editable
|
|
45
|
+
* address is somewhere to send the user's credential.
|
|
46
|
+
*/
|
|
47
|
+
private _backend;
|
|
42
48
|
/**
|
|
43
49
|
* @param _commands Where the host's Oceanum.io sign-in is asked for, when
|
|
44
50
|
* there is no pasted Datamesh token.
|
|
@@ -47,7 +53,13 @@ export declare class ChatRouter {
|
|
|
47
53
|
* every request, so it follows the conversation rather than whichever tab
|
|
48
54
|
* happens to be active.
|
|
49
55
|
*/
|
|
50
|
-
constructor(_settings: ISettingRegistry.ISettings, _commands: AuthCommands, _notebook: () => Promise<INotebookSnapshot | null
|
|
56
|
+
constructor(_settings: ISettingRegistry.ISettings, _commands: AuthCommands, _notebook: () => Promise<INotebookSnapshot | null>,
|
|
57
|
+
/**
|
|
58
|
+
* The Oceanum AI backend's address: the signed-in deployment's, or empty for the
|
|
59
|
+
* production default. Deployment configuration, not a setting -- a user-editable
|
|
60
|
+
* address is somewhere to send the user's credential.
|
|
61
|
+
*/
|
|
62
|
+
_backend?: () => string);
|
|
51
63
|
route(prompt: string, chatHistory?: ChatMessage[], signal?: AbortSignal, onProgress?: (progress: Progress) => void): Promise<RouteResult>;
|
|
52
64
|
/**
|
|
53
65
|
* The notebook half of the execute loop: the code ran in the user's own
|
package/lib/chatRouter.js
CHANGED
|
@@ -38,10 +38,17 @@ export class ChatRouter {
|
|
|
38
38
|
* every request, so it follows the conversation rather than whichever tab
|
|
39
39
|
* happens to be active.
|
|
40
40
|
*/
|
|
41
|
-
constructor(_settings, _commands, _notebook
|
|
41
|
+
constructor(_settings, _commands, _notebook,
|
|
42
|
+
/**
|
|
43
|
+
* The Oceanum AI backend's address: the signed-in deployment's, or empty for the
|
|
44
|
+
* production default. Deployment configuration, not a setting -- a user-editable
|
|
45
|
+
* address is somewhere to send the user's credential.
|
|
46
|
+
*/
|
|
47
|
+
_backend = () => '') {
|
|
42
48
|
this._settings = _settings;
|
|
43
49
|
this._commands = _commands;
|
|
44
50
|
this._notebook = _notebook;
|
|
51
|
+
this._backend = _backend;
|
|
45
52
|
}
|
|
46
53
|
async route(prompt, chatHistory = [], signal, onProgress) {
|
|
47
54
|
const { payload, isCodeCell, context } = await this._gather(prompt, chatHistory);
|
|
@@ -110,7 +117,7 @@ export class ChatRouter {
|
|
|
110
117
|
? 'Not signed in. Sign in to Oceanum.io to use Oceanum AI, or set a Datamesh token in Settings → Oceanum.io.'
|
|
111
118
|
: 'Datamesh token not configured. Set your token in Settings → Oceanum.io.');
|
|
112
119
|
}
|
|
113
|
-
const backend = aiBackendUrl(this.
|
|
120
|
+
const backend = aiBackendUrl(this._backend());
|
|
114
121
|
let response;
|
|
115
122
|
try {
|
|
116
123
|
response = await fetch(`${backend}${path}`, {
|
package/lib/constants.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Build-time constants for Oceanum AI integration.
|
|
3
3
|
* These values are compiled into the extension.
|
|
4
4
|
*/
|
|
5
|
-
/** Default URL of the Oceanum AI backend service
|
|
5
|
+
/** Default URL of the Oceanum AI backend service, where the deployment names none. */
|
|
6
6
|
export declare const OCEANUM_AI_BACKEND_URL = "https://ai.oceanum.io";
|
|
7
7
|
/** URL of the Datamesh UI embedded in the Datamesh panel */
|
|
8
8
|
export declare const DATAMESH_UI_SERVICE = "https://ui.datamesh.oceanum.io";
|
package/lib/constants.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Build-time constants for Oceanum AI integration.
|
|
3
3
|
* These values are compiled into the extension.
|
|
4
4
|
*/
|
|
5
|
-
/** Default URL of the Oceanum AI backend service
|
|
5
|
+
/** Default URL of the Oceanum AI backend service, where the deployment names none. */
|
|
6
6
|
export const OCEANUM_AI_BACKEND_URL = 'https://ai.oceanum.io';
|
|
7
7
|
/** URL of the Datamesh UI embedded in the Datamesh panel */
|
|
8
8
|
export const DATAMESH_UI_SERVICE = 'https://ui.datamesh.oceanum.io';
|
package/lib/datameshUiUrl.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** The Datamesh UI address to use for a
|
|
1
|
+
/** The Datamesh UI address to use for a configured value. */
|
|
2
2
|
export declare function validDatameshUiUrl(url: string | null | undefined): URL;
|
|
3
3
|
/**
|
|
4
4
|
* The panel's iframe `src`: the Datamesh UI in embed mode, which hides the
|
|
5
|
-
* Oceanum.io navigator. Any query the
|
|
5
|
+
* Oceanum.io navigator. Any query the address already has is kept.
|
|
6
6
|
*/
|
|
7
7
|
export declare function datameshUiSrc(url: string | null | undefined): string;
|
|
8
8
|
/**
|
package/lib/datameshUiUrl.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The Datamesh UI panel's address, and which messages may come from it.
|
|
3
3
|
*
|
|
4
|
-
* The address
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* rather than ending up in an iframe's `src`.
|
|
4
|
+
* The address comes from the deployment's sign-in environment, so a development
|
|
5
|
+
* deployment can point the panel at its own Datamesh UI. It is still written by a
|
|
6
|
+
* person in a config file, so anything that is not an http(s) URL falls back to the
|
|
7
|
+
* default rather than ending up in an iframe's `src`.
|
|
8
8
|
*/
|
|
9
9
|
import { DATAMESH_UI_SERVICE } from './constants';
|
|
10
10
|
import { validHttpUrl } from './httpUrl';
|
|
11
|
-
/** The Datamesh UI address to use for a
|
|
11
|
+
/** The Datamesh UI address to use for a configured value. */
|
|
12
12
|
export function validDatameshUiUrl(url) {
|
|
13
13
|
return validHttpUrl(url, DATAMESH_UI_SERVICE);
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* The panel's iframe `src`: the Datamesh UI in embed mode, which hides the
|
|
17
|
-
* Oceanum.io navigator. Any query the
|
|
17
|
+
* Oceanum.io navigator. Any query the address already has is kept.
|
|
18
18
|
*/
|
|
19
19
|
export function datameshUiSrc(url) {
|
|
20
20
|
const src = validDatameshUiUrl(url);
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
2
2
|
import { LabIcon } from '@jupyterlab/ui-components';
|
|
3
|
+
import { IOceanumAuth } from './auth/tokens';
|
|
3
4
|
import '../style/index.css';
|
|
4
5
|
export declare const oceanumIcon: LabIcon;
|
|
5
6
|
/**
|
|
@@ -12,5 +13,11 @@ export declare const datamesh_connect_extension: JupyterFrontEndPlugin<void>;
|
|
|
12
13
|
* `oceanum-ai:submit-prompt` command.
|
|
13
14
|
*/
|
|
14
15
|
export declare const oceanum_ai_extension: JupyterFrontEndPlugin<void>;
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* The sign-in contract, exported so another extension can provide or consume it: a Lumino
|
|
18
|
+
* token is matched by object identity, so everyone has to import this one object, and
|
|
19
|
+
* JupyterLab shares `@oceanum/oceanumlab` between extensions as a singleton module.
|
|
20
|
+
*/
|
|
21
|
+
export * from './auth/tokens';
|
|
22
|
+
declare const _default: (JupyterFrontEndPlugin<void> | JupyterFrontEndPlugin<IOceanumAuth>)[];
|
|
16
23
|
export default _default;
|
package/lib/index.js
CHANGED
|
@@ -12,6 +12,9 @@ import { requestAPI } from './handler';
|
|
|
12
12
|
import { ChatRouter, ChatRouterError } from './chatRouter';
|
|
13
13
|
import { reporterFor } from './progress';
|
|
14
14
|
import { ConversationPin } from './conversationPin';
|
|
15
|
+
import { deviceAuthPlugin } from './auth/plugin';
|
|
16
|
+
import { IOceanumAuth } from './auth/tokens';
|
|
17
|
+
import { CommandIDs as ShareCommandIDs, sharePlugin } from './share/plugin';
|
|
15
18
|
import { snapshotFromIpynb, snapshotOf } from './notebookContext';
|
|
16
19
|
import { notebookHost } from './notebookHost';
|
|
17
20
|
import { KernelHandoff } from './kernelHandoff';
|
|
@@ -38,13 +41,19 @@ export const datamesh_connect_extension = {
|
|
|
38
41
|
ISettingRegistry,
|
|
39
42
|
IStateDB
|
|
40
43
|
],
|
|
41
|
-
|
|
44
|
+
// Optional so the panel still loads on a host with no Oceanum environment; the
|
|
45
|
+
// Notebooks tab then explains why it is empty instead of the panel failing to start.
|
|
46
|
+
optional: [IOceanumAuth],
|
|
47
|
+
activate: (app, palette, restorer, status, settingRegistry, stateDB, auth) => {
|
|
48
|
+
var _a, _b, _c, _d;
|
|
42
49
|
console.log('Oceanum datamesh connect extension is loaded');
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
50
|
+
// Where the Datamesh UI and Oceanum AI live is the deployment's business, so it comes
|
|
51
|
+
// from the sign-in environment (the Jupyter server's configuration, or the notebook
|
|
52
|
+
// site's) and falls back to production. These were user settings until 4.7.1; a
|
|
53
|
+
// user-editable service address is somewhere to send the user's credential.
|
|
54
|
+
const datameshUiUrl = (_b = (_a = auth === null || auth === void 0 ? void 0 : auth.urls) === null || _a === void 0 ? void 0 : _a.datameshUi) !== null && _b !== void 0 ? _b : DATAMESH_UI_SERVICE;
|
|
55
|
+
const aiBackendUrl = (_d = (_c = auth === null || auth === void 0 ? void 0 : auth.urls) === null || _c === void 0 ? void 0 : _c.ai) !== null && _d !== void 0 ? _d : OCEANUM_AI_BACKEND_URL;
|
|
56
|
+
// The `datameshToken` setting for the AI chat; empty until the settings load.
|
|
48
57
|
let datameshTokenSetting = '';
|
|
49
58
|
// Tells the sidebar the settings above have changed.
|
|
50
59
|
const settingsChanged = new Signal(app);
|
|
@@ -60,14 +69,7 @@ export const datamesh_connect_extension = {
|
|
|
60
69
|
}).then(res => console.log(res));
|
|
61
70
|
}
|
|
62
71
|
window.injectToken = set.get('injectToken').user;
|
|
63
|
-
aiBackendUrl = set.get('aiBackendUrl').composite;
|
|
64
72
|
datameshTokenSetting = set.get('datameshToken').composite;
|
|
65
|
-
// An open Datamesh UI panel follows a change of address.
|
|
66
|
-
datameshUiUrl = set.get('datameshUiUrl').composite;
|
|
67
|
-
const datameshUI = findDatameshUI();
|
|
68
|
-
if (datameshUI) {
|
|
69
|
-
datameshUI.url = datameshUiUrl;
|
|
70
|
-
}
|
|
71
73
|
settingsChanged.emit();
|
|
72
74
|
};
|
|
73
75
|
//Try to get the datamesh token from the envars
|
|
@@ -109,14 +111,41 @@ export const datamesh_connect_extension = {
|
|
|
109
111
|
aiBackendUrl: () => aiBackendUrl,
|
|
110
112
|
settingsChanged,
|
|
111
113
|
commands: app.commands,
|
|
112
|
-
getCurrentWidget
|
|
114
|
+
getCurrentWidget,
|
|
115
|
+
auth,
|
|
116
|
+
openStoredNotebook: id => {
|
|
117
|
+
app.commands.execute(ShareCommandIDs.open, { id }).catch(error => {
|
|
118
|
+
console.error('Oceanum: could not open the stored notebook.', error);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
113
121
|
});
|
|
114
122
|
datameshConnectWidget.id = 'datamesh-connect';
|
|
115
123
|
datameshConnectWidget.title.icon = oceanumIcon;
|
|
116
124
|
datameshConnectWidget.title.caption = 'Datamesh Connect';
|
|
117
125
|
restorer.add(datameshConnectWidget, 'datamesh-connect');
|
|
126
|
+
// The layout restorer restores the widget's place in the shell, not fields on it,
|
|
127
|
+
// so the chosen tab is kept here. Restoring is best effort: a missing or unreadable
|
|
128
|
+
// entry just leaves the default.
|
|
129
|
+
const TAB_STATE_KEY = `${PLUGIN_ID}:tab`;
|
|
130
|
+
void stateDB
|
|
131
|
+
.fetch(TAB_STATE_KEY)
|
|
132
|
+
.then(value => {
|
|
133
|
+
if (typeof value === 'string') {
|
|
134
|
+
datameshConnectWidget.selectTab(value);
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
.catch(() => undefined);
|
|
138
|
+
datameshConnectWidget.tabChanged.connect((_, id) => {
|
|
139
|
+
void stateDB.save(TAB_STATE_KEY, id);
|
|
140
|
+
});
|
|
118
141
|
// Rank has been chosen somewhat arbitrarily to give priority to the running
|
|
119
142
|
// sessions widget in the sidebar.
|
|
143
|
+
//
|
|
144
|
+
// Oceanum Notebook wants this panel first instead, but that is a property of that
|
|
145
|
+
// distribution rather than of the extension: in a plain JupyterLab the file browser
|
|
146
|
+
// is the primary navigation surface and displacing it would be a regression. A
|
|
147
|
+
// distribution moves it with JupyterLab's own shell user-layout settings, keyed on
|
|
148
|
+
// this widget's id, rather than this extension hard-coding one host's preference.
|
|
120
149
|
app.shell.add(datameshConnectWidget, 'left', { rank: 900 });
|
|
121
150
|
app.commands.addCommand('datamesh-ui:open', {
|
|
122
151
|
execute: (args) => {
|
|
@@ -134,7 +163,8 @@ export const oceanum_ai_extension = {
|
|
|
134
163
|
id: '@oceanum/oceanumlab:ai-chat',
|
|
135
164
|
autoStart: true,
|
|
136
165
|
requires: [INotebookTracker, ISettingRegistry],
|
|
137
|
-
|
|
166
|
+
optional: [IOceanumAuth],
|
|
167
|
+
activate: (app, notebookTracker, settingRegistry, auth) => {
|
|
138
168
|
console.log('Oceanum AI chat extension is loaded');
|
|
139
169
|
const SETTINGS_ID = '@oceanum/oceanumlab:datamesh-connect';
|
|
140
170
|
settingRegistry
|
|
@@ -170,7 +200,7 @@ export const oceanum_ai_extension = {
|
|
|
170
200
|
// Gone: an answer's cells will go into a new notebook instead.
|
|
171
201
|
return null;
|
|
172
202
|
}
|
|
173
|
-
});
|
|
203
|
+
}, () => { var _a, _b; return (_b = (_a = auth === null || auth === void 0 ? void 0 : auth.urls) === null || _a === void 0 ? void 0 : _a.ai) !== null && _b !== void 0 ? _b : ''; });
|
|
174
204
|
// Asked only when an answer has blocks to place: brings the
|
|
175
205
|
// conversation's notebook to the front, opening it again if closed.
|
|
176
206
|
const handoff = new KernelHandoff(() => pin.show(), () => readFrom);
|
|
@@ -290,4 +320,15 @@ export const oceanum_ai_extension = {
|
|
|
290
320
|
});
|
|
291
321
|
}
|
|
292
322
|
};
|
|
293
|
-
|
|
323
|
+
/**
|
|
324
|
+
* The sign-in contract, exported so another extension can provide or consume it: a Lumino
|
|
325
|
+
* token is matched by object identity, so everyone has to import this one object, and
|
|
326
|
+
* JupyterLab shares `@oceanum/oceanumlab` between extensions as a singleton module.
|
|
327
|
+
*/
|
|
328
|
+
export * from './auth/tokens';
|
|
329
|
+
export default [
|
|
330
|
+
deviceAuthPlugin,
|
|
331
|
+
sharePlugin,
|
|
332
|
+
datamesh_connect_extension,
|
|
333
|
+
oceanum_ai_extension
|
|
334
|
+
];
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ISpecBody, ISpecPatch, ISpecRecord, ISpecSummary } from './notebook';
|
|
2
|
+
export type ErrorKind = 'signed-out' | 'expired' | 'forbidden' | 'not-found' | 'too-large' | 'network' | 'invalid' | 'unsupported' | 'server';
|
|
3
|
+
/** A spec store failure with a message that is safe to show to the user. */
|
|
4
|
+
export declare class SpecStoreError extends Error {
|
|
5
|
+
readonly kind: ErrorKind;
|
|
6
|
+
readonly status: number | null;
|
|
7
|
+
constructor(kind: ErrorKind, status?: number | null);
|
|
8
|
+
}
|
|
9
|
+
/** Map a failed HTTP status to an error. */
|
|
10
|
+
export declare function errorForStatus(status: number): SpecStoreError;
|
|
11
|
+
export type SharePermission = 'read' | 'write';
|
|
12
|
+
/** A permission grant, as accepted by `POST /specs/notebook/{id}/permissions`. */
|
|
13
|
+
export type PermissionGrant = {
|
|
14
|
+
type: 'user';
|
|
15
|
+
entity: string;
|
|
16
|
+
permission: SharePermission;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'public';
|
|
19
|
+
entity: '';
|
|
20
|
+
permission: 'read';
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* What the share dialog asks for: add grants, or remove one (e.g. stop public access).
|
|
24
|
+
*
|
|
25
|
+
* Granting carries a list because the dialog shares with several people at once, while
|
|
26
|
+
* the spec store takes one grant per request. `invalid` carries back anything typed that
|
|
27
|
+
* is not a shareable address, so the caller can name it rather than failing with
|
|
28
|
+
* "invalid email".
|
|
29
|
+
*/
|
|
30
|
+
export type ShareChoice = {
|
|
31
|
+
action: 'grant';
|
|
32
|
+
grants: PermissionGrant[];
|
|
33
|
+
invalid: string[];
|
|
34
|
+
} | {
|
|
35
|
+
action: 'revoke';
|
|
36
|
+
grant: PermissionGrant;
|
|
37
|
+
};
|
|
38
|
+
export interface ISpecStoreClientOptions {
|
|
39
|
+
/** Spec store base URL, e.g. `https://specs.oceanum.io`. */
|
|
40
|
+
specsUrl: string;
|
|
41
|
+
/** Returns a current access token or `null`. Called for every request. */
|
|
42
|
+
getAccessToken: () => Promise<string | null>;
|
|
43
|
+
fetch?: typeof fetch;
|
|
44
|
+
}
|
|
45
|
+
/** Client for the `notebook` spec type of the Oceanum spec store. */
|
|
46
|
+
export declare class SpecStoreClient {
|
|
47
|
+
constructor(options: ISpecStoreClientOptions);
|
|
48
|
+
/** Every notebook the user may read, including public ones. */
|
|
49
|
+
list(): Promise<ISpecSummary[]>;
|
|
50
|
+
/** Read a record, anonymously when signed out. */
|
|
51
|
+
get(id: string): Promise<ISpecRecord>;
|
|
52
|
+
/** Create a record. The server mints the id: use the returned record's. */
|
|
53
|
+
create(body: ISpecBody): Promise<ISpecRecord>;
|
|
54
|
+
/** Replace a record's name, description and notebook. */
|
|
55
|
+
update(id: string, body: ISpecBody): Promise<ISpecRecord>;
|
|
56
|
+
/**
|
|
57
|
+
* Change some of a record's fields and leave the rest as they are. Needs the same
|
|
58
|
+
* write access as `update`, which the store enforces a patch as.
|
|
59
|
+
*/
|
|
60
|
+
patch(id: string, changes: ISpecPatch): Promise<ISpecRecord>;
|
|
61
|
+
/**
|
|
62
|
+
* Give a record a new name, without sending the notebook back with it.
|
|
63
|
+
*
|
|
64
|
+
* Falls back to reading the record and putting it back where the store has no PATCH
|
|
65
|
+
* (production had none when this was written). That fallback races another writer's
|
|
66
|
+
* change to the notebook, which is the whole reason to prefer a patch.
|
|
67
|
+
*/
|
|
68
|
+
rename(id: string, name: string): Promise<ISpecRecord>;
|
|
69
|
+
/** Delete a record (requires admin access to it). */
|
|
70
|
+
remove(id: string): Promise<void>;
|
|
71
|
+
/** Grant a permission on a record (requires admin access to it). */
|
|
72
|
+
addPermission(id: string, grant: PermissionGrant): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Remove a permission (requires admin access). The server matches the exact grant and
|
|
75
|
+
* answers 404 when there is no such permission.
|
|
76
|
+
*/
|
|
77
|
+
removePermission(id: string, grant: PermissionGrant): Promise<void>;
|
|
78
|
+
private _url;
|
|
79
|
+
private _record;
|
|
80
|
+
private _request;
|
|
81
|
+
private readonly _base;
|
|
82
|
+
private readonly _getAccessToken;
|
|
83
|
+
private readonly _fetch;
|
|
84
|
+
}
|