@cognite/dune 0.3.2 → 0.3.4
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/_templates/app/new/root/package.json.ejs.t +2 -2
- package/dist/deploy/index.d.ts +9 -1
- package/dist/deploy/index.js +89 -7
- package/dist/vite/index.d.ts +1 -0
- package/dist/vite/index.js +5 -2
- package/package.json +1 -1
- package/src/deploy/get-sdk.ts +1 -1
- package/src/deploy/login.test.ts +49 -0
- package/src/deploy/login.ts +132 -9
- package/src/deploy/types.ts +4 -0
- package/src/vite/fusion-open-plugin.ts +6 -4
|
@@ -21,9 +21,9 @@ to: '<%= useCurrentDir ? "" : ((directoryName || name) + "/") %>package.json'
|
|
|
21
21
|
"deploy-preview": "dune deploy:interactive"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@cognite/aura": "^0.1.
|
|
24
|
+
"@cognite/aura": "^0.1.2",
|
|
25
25
|
"@cognite/sdk": "^10.3.0",
|
|
26
|
-
"@cognite/dune": "^0.
|
|
26
|
+
"@cognite/dune": "^0.3.4",
|
|
27
27
|
"@tanstack/react-query": "^5.90.10",
|
|
28
28
|
"clsx": "^2.1.1",
|
|
29
29
|
"react": "^19.2.0",
|
package/dist/deploy/index.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ type Deployment = {
|
|
|
7
7
|
deployClientId: string;
|
|
8
8
|
deploySecretName: string;
|
|
9
9
|
published: boolean;
|
|
10
|
+
/** Identity provider type. Defaults to "cdf" if not specified */
|
|
11
|
+
idpType?: "cdf" | "entra_id";
|
|
12
|
+
/** Tenant ID for Entra ID authentication. Required when idpType is "entra_id" */
|
|
13
|
+
tenantId?: string;
|
|
10
14
|
};
|
|
11
15
|
type App = {
|
|
12
16
|
externalId: string;
|
|
@@ -85,6 +89,10 @@ declare class ApplicationPackager {
|
|
|
85
89
|
|
|
86
90
|
declare const getSdk: (deployment: Deployment, folder: string) => Promise<CogniteClient>;
|
|
87
91
|
|
|
88
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Get access token for deployment using the appropriate identity provider.
|
|
94
|
+
* Supports both CDF OAuth and Entra ID (Azure AD) authentication.
|
|
95
|
+
*/
|
|
96
|
+
declare const getToken: (deployment: Deployment) => Promise<string>;
|
|
89
97
|
|
|
90
98
|
export { type App, ApplicationPackager, CdfApplicationDeployer, type Deployment, deploy, getSdk, getToken };
|
package/dist/deploy/index.js
CHANGED
|
@@ -187,22 +187,39 @@ var loadSecretsFromEnv = () => {
|
|
|
187
187
|
return {};
|
|
188
188
|
}
|
|
189
189
|
};
|
|
190
|
-
var
|
|
190
|
+
var getSecretFromEnv = (secretEnvVarName) => {
|
|
191
191
|
let deploySecret;
|
|
192
192
|
if (process.env.DEPLOYMENT_SECRET) {
|
|
193
193
|
deploySecret = process.env.DEPLOYMENT_SECRET;
|
|
194
194
|
}
|
|
195
195
|
if (!deploySecret) {
|
|
196
196
|
const secrets = loadSecretsFromEnv();
|
|
197
|
-
deploySecret = secrets[
|
|
197
|
+
deploySecret = secrets[secretEnvVarName];
|
|
198
198
|
}
|
|
199
199
|
if (!deploySecret) {
|
|
200
|
-
deploySecret = process.env[
|
|
200
|
+
deploySecret = process.env[secretEnvVarName];
|
|
201
201
|
}
|
|
202
202
|
if (!deploySecret) {
|
|
203
|
-
throw new Error(`
|
|
203
|
+
throw new Error(`Secret not found in environment: ${secretEnvVarName}`);
|
|
204
204
|
}
|
|
205
|
-
|
|
205
|
+
return deploySecret;
|
|
206
|
+
};
|
|
207
|
+
var extractClusterFromUrl = (url) => {
|
|
208
|
+
if (!url) return "";
|
|
209
|
+
try {
|
|
210
|
+
const urlObj = new URL(url);
|
|
211
|
+
const hostname = urlObj.hostname;
|
|
212
|
+
return hostname.replace(/\.cognitedata\.com$/, "");
|
|
213
|
+
} catch {
|
|
214
|
+
let cluster = url.replace(/^https?:\/\//, "");
|
|
215
|
+
cluster = cluster.split("/")[0];
|
|
216
|
+
cluster = cluster.split(":")[0];
|
|
217
|
+
cluster = cluster.replace(/\.cognitedata\.com$/, "");
|
|
218
|
+
return cluster;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
var getTokenCdf = async (clientId, clientSecret) => {
|
|
222
|
+
const header = `Basic ${btoa(`${clientId}:${clientSecret}`)}`;
|
|
206
223
|
const response = await fetch("https://auth.cognite.com/oauth2/token", {
|
|
207
224
|
method: "POST",
|
|
208
225
|
headers: {
|
|
@@ -212,15 +229,80 @@ var getToken = async (deployClientId, deploySecretName) => {
|
|
|
212
229
|
body: new URLSearchParams({ grant_type: "client_credentials" })
|
|
213
230
|
});
|
|
214
231
|
if (!response.ok) {
|
|
215
|
-
|
|
232
|
+
const errorText = await response.text();
|
|
233
|
+
throw new Error(
|
|
234
|
+
`Failed to get token from CDF: ${response.status} ${response.statusText}
|
|
235
|
+
${errorText}`
|
|
236
|
+
);
|
|
216
237
|
}
|
|
217
238
|
const data = await response.json();
|
|
239
|
+
if (!data.access_token) {
|
|
240
|
+
throw new Error("No access token returned from CDF authentication");
|
|
241
|
+
}
|
|
218
242
|
return data.access_token;
|
|
219
243
|
};
|
|
244
|
+
var getTokenEntra = async (clientId, clientSecret, tenantId, baseUrl) => {
|
|
245
|
+
if (!baseUrl) {
|
|
246
|
+
throw new Error(
|
|
247
|
+
"Entra ID authentication requires 'baseUrl' to be set in deployment configuration"
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const cluster = extractClusterFromUrl(baseUrl);
|
|
251
|
+
if (!cluster) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`Entra ID authentication requires 'baseUrl' to be a valid CDF URL (e.g., https://cluster.cognitedata.com), got: ${baseUrl}`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
|
|
257
|
+
const scope = `https://${cluster}.cognitedata.com/.default`;
|
|
258
|
+
const response = await fetch(tokenUrl, {
|
|
259
|
+
method: "POST",
|
|
260
|
+
headers: {
|
|
261
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
262
|
+
},
|
|
263
|
+
body: new URLSearchParams({
|
|
264
|
+
client_id: clientId,
|
|
265
|
+
client_secret: clientSecret,
|
|
266
|
+
scope,
|
|
267
|
+
grant_type: "client_credentials"
|
|
268
|
+
})
|
|
269
|
+
});
|
|
270
|
+
if (!response.ok) {
|
|
271
|
+
const errorText = await response.text();
|
|
272
|
+
throw new Error(
|
|
273
|
+
`Failed to get token from Entra ID: ${response.status} ${response.statusText}
|
|
274
|
+
${errorText}`
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
const data = await response.json();
|
|
278
|
+
if (!data.access_token) {
|
|
279
|
+
throw new Error("No access token returned from Entra ID authentication");
|
|
280
|
+
}
|
|
281
|
+
return data.access_token;
|
|
282
|
+
};
|
|
283
|
+
var getToken = async (deployment) => {
|
|
284
|
+
const {
|
|
285
|
+
deployClientId,
|
|
286
|
+
deploySecretName,
|
|
287
|
+
idpType = "cdf",
|
|
288
|
+
tenantId,
|
|
289
|
+
baseUrl
|
|
290
|
+
} = deployment;
|
|
291
|
+
const deploySecret = getSecretFromEnv(deploySecretName);
|
|
292
|
+
if (idpType === "entra_id") {
|
|
293
|
+
if (!tenantId) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
"Entra ID authentication requires 'tenantId' in deployment configuration"
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
return getTokenEntra(deployClientId, deploySecret, tenantId, baseUrl);
|
|
299
|
+
}
|
|
300
|
+
return getTokenCdf(deployClientId, deploySecret);
|
|
301
|
+
};
|
|
220
302
|
|
|
221
303
|
// src/deploy/get-sdk.ts
|
|
222
304
|
var getSdk = async (deployment, folder) => {
|
|
223
|
-
const token = await getToken(deployment
|
|
305
|
+
const token = await getToken(deployment);
|
|
224
306
|
const sdk = new CogniteClient({
|
|
225
307
|
appId: folder,
|
|
226
308
|
project: deployment.project,
|
package/dist/vite/index.d.ts
CHANGED
package/dist/vite/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var fusionOpenPlugin = () => {
|
|
|
14
14
|
return {
|
|
15
15
|
name: "fusion-open",
|
|
16
16
|
configureServer(server) {
|
|
17
|
-
server.
|
|
17
|
+
server.printUrls = () => {
|
|
18
18
|
const address = server.httpServer?.address();
|
|
19
19
|
const port = address && typeof address === "object" ? address.port : 3001;
|
|
20
20
|
const appJsonPath = path.join(process.cwd(), "app.json");
|
|
@@ -26,13 +26,16 @@ var fusionOpenPlugin = () => {
|
|
|
26
26
|
const parsedBaseUrl = baseUrl?.split("//")[1];
|
|
27
27
|
if (org && project && baseUrl) {
|
|
28
28
|
const fusionUrl = `https://${org}.fusion.cognite.com/${project}/streamlit-apps/dune/development/${port}?cluster=${parsedBaseUrl}&workspace=industrial-tools`;
|
|
29
|
+
console.log(` \u279C Fusion: ${fusionUrl}`);
|
|
29
30
|
openUrl(fusionUrl);
|
|
31
|
+
return;
|
|
30
32
|
}
|
|
31
33
|
} catch (error) {
|
|
32
34
|
console.warn("Failed to read app.json for Fusion URL", error);
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
|
-
|
|
37
|
+
console.warn(" \u279C No valid app.json found \u2014 cannot determine Fusion URL");
|
|
38
|
+
};
|
|
36
39
|
}
|
|
37
40
|
};
|
|
38
41
|
};
|
package/package.json
CHANGED
package/src/deploy/get-sdk.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { getToken } from './login';
|
|
|
4
4
|
import type { Deployment } from './types';
|
|
5
5
|
|
|
6
6
|
export const getSdk = async (deployment: Deployment, folder: string) => {
|
|
7
|
-
const token = await getToken(deployment
|
|
7
|
+
const token = await getToken(deployment);
|
|
8
8
|
const sdk = new CogniteClient({
|
|
9
9
|
appId: folder,
|
|
10
10
|
project: deployment.project,
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { extractClusterFromUrl } from "./login";
|
|
3
|
+
|
|
4
|
+
describe("extractClusterFromUrl", () => {
|
|
5
|
+
it("extracts cluster name from standard CDF URL", () => {
|
|
6
|
+
expect(extractClusterFromUrl("https://az-ams-sp-002.cognitedata.com")).toBe(
|
|
7
|
+
"az-ams-sp-002"
|
|
8
|
+
);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("extracts cluster name from URL with path", () => {
|
|
12
|
+
expect(
|
|
13
|
+
extractClusterFromUrl("https://westeurope-1.cognitedata.com/api/v1")
|
|
14
|
+
).toBe("westeurope-1");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("extracts cluster name from greenfield URL", () => {
|
|
18
|
+
expect(
|
|
19
|
+
extractClusterFromUrl("https://greenfield.cognitedata.com")
|
|
20
|
+
).toBe("greenfield");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("handles URL with port", () => {
|
|
24
|
+
expect(
|
|
25
|
+
extractClusterFromUrl("https://az-ams-sp-002.cognitedata.com:443")
|
|
26
|
+
).toBe("az-ams-sp-002");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("handles URL without protocol", () => {
|
|
30
|
+
expect(extractClusterFromUrl("az-ams-sp-002.cognitedata.com")).toBe(
|
|
31
|
+
"az-ams-sp-002"
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("returns empty string for empty input", () => {
|
|
36
|
+
expect(extractClusterFromUrl("")).toBe("");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("handles malformed URL gracefully", () => {
|
|
40
|
+
// Should use fallback string manipulation
|
|
41
|
+
expect(extractClusterFromUrl("not-a-valid-url")).toBe("not-a-valid-url");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("extracts cluster from http URL", () => {
|
|
45
|
+
expect(extractClusterFromUrl("http://az-ams-sp-002.cognitedata.com")).toBe(
|
|
46
|
+
"az-ams-sp-002"
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
});
|
package/src/deploy/login.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Deployment } from "./types";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Load secrets from DEPLOYMENT_SECRETS environment variable (JSON)
|
|
3
5
|
*/
|
|
@@ -27,7 +29,10 @@ const loadSecretsFromEnv = (): Record<string, string> => {
|
|
|
27
29
|
}
|
|
28
30
|
};
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Get the deployment secret from environment variables
|
|
34
|
+
*/
|
|
35
|
+
const getSecretFromEnv = (secretEnvVarName: string): string => {
|
|
31
36
|
let deploySecret: string | undefined;
|
|
32
37
|
|
|
33
38
|
// First try DEPLOYMENT_SECRET (for matrix-based deployments)
|
|
@@ -38,21 +43,54 @@ export const getToken = async (deployClientId: string, deploySecretName: string)
|
|
|
38
43
|
// Then try to get from DEPLOYMENT_SECRETS JSON
|
|
39
44
|
if (!deploySecret) {
|
|
40
45
|
const secrets = loadSecretsFromEnv();
|
|
41
|
-
deploySecret = secrets[
|
|
46
|
+
deploySecret = secrets[secretEnvVarName];
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
// Fall back to direct environment variable for local development
|
|
45
50
|
if (!deploySecret) {
|
|
46
|
-
deploySecret = process.env[
|
|
51
|
+
deploySecret = process.env[secretEnvVarName];
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
if (!deploySecret) {
|
|
50
|
-
throw new Error(`
|
|
55
|
+
throw new Error(`Secret not found in environment: ${secretEnvVarName}`);
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
return deploySecret;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Extract cluster name from base URL
|
|
63
|
+
* @param url - Full URL like "https://az-ams-sp-002.cognitedata.com"
|
|
64
|
+
* @returns Cluster name like "az-ams-sp-002"
|
|
65
|
+
*/
|
|
66
|
+
export const extractClusterFromUrl = (url: string): string => {
|
|
67
|
+
if (!url) return "";
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const urlObj = new URL(url);
|
|
71
|
+
const hostname = urlObj.hostname;
|
|
72
|
+
// Remove .cognitedata.com suffix
|
|
73
|
+
return hostname.replace(/\.cognitedata\.com$/, "");
|
|
74
|
+
} catch {
|
|
75
|
+
// Fallback to simple string manipulation
|
|
76
|
+
let cluster = url.replace(/^https?:\/\//, "");
|
|
77
|
+
cluster = cluster.split("/")[0]; // Remove path
|
|
78
|
+
cluster = cluster.split(":")[0]; // Remove port
|
|
79
|
+
cluster = cluster.replace(/\.cognitedata\.com$/, "");
|
|
80
|
+
return cluster;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get access token using CDF OAuth provider
|
|
86
|
+
*/
|
|
87
|
+
const getTokenCdf = async (
|
|
88
|
+
clientId: string,
|
|
89
|
+
clientSecret: string
|
|
90
|
+
): Promise<string> => {
|
|
91
|
+
const header = `Basic ${btoa(`${clientId}:${clientSecret}`)}`;
|
|
92
|
+
const response = await fetch("https://auth.cognite.com/oauth2/token", {
|
|
93
|
+
method: "POST",
|
|
56
94
|
headers: {
|
|
57
95
|
Authorization: header,
|
|
58
96
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
@@ -61,9 +99,94 @@ export const getToken = async (deployClientId: string, deploySecretName: string)
|
|
|
61
99
|
});
|
|
62
100
|
|
|
63
101
|
if (!response.ok) {
|
|
64
|
-
|
|
102
|
+
const errorText = await response.text();
|
|
103
|
+
throw new Error(
|
|
104
|
+
`Failed to get token from CDF: ${response.status} ${response.statusText}\n${errorText}`
|
|
105
|
+
);
|
|
65
106
|
}
|
|
66
107
|
|
|
67
|
-
const data = await response.json();
|
|
108
|
+
const data = (await response.json()) satisfies { access_token?: string };
|
|
109
|
+
if (!data.access_token) {
|
|
110
|
+
throw new Error("No access token returned from CDF authentication");
|
|
111
|
+
}
|
|
68
112
|
return data.access_token;
|
|
69
113
|
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get access token using Entra ID (Azure AD) OAuth provider
|
|
117
|
+
*/
|
|
118
|
+
const getTokenEntra = async (
|
|
119
|
+
clientId: string,
|
|
120
|
+
clientSecret: string,
|
|
121
|
+
tenantId: string,
|
|
122
|
+
baseUrl: string
|
|
123
|
+
): Promise<string> => {
|
|
124
|
+
if (!baseUrl) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
"Entra ID authentication requires 'baseUrl' to be set in deployment configuration"
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
const cluster = extractClusterFromUrl(baseUrl);
|
|
130
|
+
if (!cluster) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Entra ID authentication requires 'baseUrl' to be a valid CDF URL (e.g., https://cluster.cognitedata.com), got: ${baseUrl}`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
|
|
137
|
+
const scope = `https://${cluster}.cognitedata.com/.default`;
|
|
138
|
+
|
|
139
|
+
const response = await fetch(tokenUrl, {
|
|
140
|
+
method: "POST",
|
|
141
|
+
headers: {
|
|
142
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
143
|
+
},
|
|
144
|
+
body: new URLSearchParams({
|
|
145
|
+
client_id: clientId,
|
|
146
|
+
client_secret: clientSecret,
|
|
147
|
+
scope: scope,
|
|
148
|
+
grant_type: "client_credentials",
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
const errorText = await response.text();
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Failed to get token from Entra ID: ${response.status} ${response.statusText}\n${errorText}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const data = (await response.json()) satisfies { access_token?: string };
|
|
160
|
+
if (!data.access_token) {
|
|
161
|
+
throw new Error("No access token returned from Entra ID authentication");
|
|
162
|
+
}
|
|
163
|
+
return data.access_token;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get access token for deployment using the appropriate identity provider.
|
|
168
|
+
* Supports both CDF OAuth and Entra ID (Azure AD) authentication.
|
|
169
|
+
*/
|
|
170
|
+
export const getToken = async (deployment: Deployment): Promise<string> => {
|
|
171
|
+
const {
|
|
172
|
+
deployClientId,
|
|
173
|
+
deploySecretName,
|
|
174
|
+
idpType = "cdf",
|
|
175
|
+
tenantId,
|
|
176
|
+
baseUrl,
|
|
177
|
+
} = deployment;
|
|
178
|
+
|
|
179
|
+
const deploySecret = getSecretFromEnv(deploySecretName);
|
|
180
|
+
|
|
181
|
+
if (idpType === "entra_id") {
|
|
182
|
+
if (!tenantId) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
"Entra ID authentication requires 'tenantId' in deployment configuration"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return getTokenEntra(deployClientId, deploySecret, tenantId, baseUrl);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Default: CDF provider
|
|
191
|
+
return getTokenCdf(deployClientId, deploySecret);
|
|
192
|
+
};
|
package/src/deploy/types.ts
CHANGED
|
@@ -5,6 +5,10 @@ export type Deployment = {
|
|
|
5
5
|
deployClientId: string;
|
|
6
6
|
deploySecretName: string;
|
|
7
7
|
published: boolean;
|
|
8
|
+
/** Identity provider type. Defaults to "cdf" if not specified */
|
|
9
|
+
idpType?: "cdf" | "entra_id";
|
|
10
|
+
/** Tenant ID for Entra ID authentication. Required when idpType is "entra_id" */
|
|
11
|
+
tenantId?: string;
|
|
8
12
|
};
|
|
9
13
|
|
|
10
14
|
export type App = {
|
|
@@ -17,13 +17,14 @@ interface ViteDevServer {
|
|
|
17
17
|
address: () => { port: number } | string | null;
|
|
18
18
|
on: (event: string, callback: () => void) => void;
|
|
19
19
|
} | null;
|
|
20
|
+
printUrls: () => void;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export const fusionOpenPlugin = () => {
|
|
23
24
|
return {
|
|
24
25
|
name: 'fusion-open',
|
|
25
26
|
configureServer(server: ViteDevServer) {
|
|
26
|
-
server.
|
|
27
|
+
server.printUrls = () => {
|
|
27
28
|
const address = server.httpServer?.address();
|
|
28
29
|
const port = address && typeof address === 'object' ? address.port : 3001;
|
|
29
30
|
|
|
@@ -33,19 +34,20 @@ export const fusionOpenPlugin = () => {
|
|
|
33
34
|
const appJson = JSON.parse(fs.readFileSync(appJsonPath, 'utf-8'));
|
|
34
35
|
const firstDeployment = appJson.deployments?.[0];
|
|
35
36
|
const { org, project, baseUrl } = firstDeployment || {};
|
|
36
|
-
|
|
37
37
|
const parsedBaseUrl = baseUrl?.split('//')[1];
|
|
38
38
|
|
|
39
39
|
if (org && project && baseUrl) {
|
|
40
40
|
const fusionUrl = `https://${org}.fusion.cognite.com/${project}/streamlit-apps/dune/development/${port}?cluster=${parsedBaseUrl}&workspace=industrial-tools`;
|
|
41
|
-
|
|
41
|
+
console.log(` ➜ Fusion: ${fusionUrl}`);
|
|
42
42
|
openUrl(fusionUrl);
|
|
43
|
+
return;
|
|
43
44
|
}
|
|
44
45
|
} catch (error) {
|
|
45
46
|
console.warn('Failed to read app.json for Fusion URL', error);
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
|
|
49
|
+
console.warn(' ➜ No valid app.json found — cannot determine Fusion URL');
|
|
50
|
+
};
|
|
49
51
|
},
|
|
50
52
|
};
|
|
51
53
|
};
|