@cognite/dune 0.1.1 → 0.2.0
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/config/biome.json.ejs.t +54 -0
- package/_templates/app/new/config/components.json.ejs.t +28 -0
- package/_templates/app/new/config/tailwind.config.js.ejs.t +15 -0
- package/_templates/app/new/{tsconfig.json.ejs.t → config/tsconfig.json.ejs.t} +4 -1
- package/_templates/app/new/config/vite.config.ts.ejs.t +27 -0
- package/_templates/app/new/cursor/data-modeling.mdc.ejs.t +1994 -0
- package/_templates/app/new/cursor/mcp.json.ejs.t +15 -0
- package/_templates/app/new/cursor/rules.mdc.ejs.t +12 -0
- package/_templates/app/new/root/PRD.md.ejs.t +5 -0
- package/_templates/app/new/{package.json.ejs.t → root/package.json.ejs.t} +12 -3
- package/_templates/app/new/{App.test.tsx.ejs.t → src/App.test.tsx.ejs.t} +5 -5
- package/_templates/app/new/{App.tsx.ejs.t → src/App.tsx.ejs.t} +2 -3
- package/_templates/app/new/src/lib/utils.ts.ejs.t +10 -0
- package/_templates/app/new/{main.tsx.ejs.t → src/main.tsx.ejs.t} +2 -0
- package/_templates/app/new/src/styles.css.ejs.t +25 -0
- package/bin/auth/authentication-flow.js +89 -0
- package/bin/auth/callback-server.js +181 -0
- package/bin/auth/certificate-manager.js +81 -0
- package/bin/auth/client-credentials.js +240 -0
- package/bin/auth/oauth-client.js +92 -0
- package/bin/cli.js +45 -5
- package/bin/deploy-command.js +246 -0
- package/bin/deploy-interactive-command.js +382 -0
- package/bin/utils/crypto.js +35 -0
- package/dist/deploy/index.d.ts +7 -0
- package/dist/deploy/index.js +43 -1
- package/package.json +12 -7
- package/src/auth/dune-auth-provider.tsx +97 -0
- package/src/auth/index.ts +11 -0
- package/src/auth/use-dune.ts +12 -0
- package/src/auth/utils.ts +91 -0
- package/src/deploy/application-deployer.ts +137 -0
- package/src/deploy/application-packager.ts +73 -0
- package/src/deploy/deploy.ts +33 -0
- package/src/deploy/get-sdk.ts +17 -0
- package/src/deploy/index.ts +9 -0
- package/src/deploy/login.ts +69 -0
- package/src/deploy/types.ts +15 -0
- package/src/index.ts +2 -0
- package/src/vite/fusion-open-plugin.ts +51 -0
- package/src/vite/index.ts +2 -0
- package/_templates/app/new/biome.json.ejs.t +0 -25
- package/_templates/app/new/vite.config.ts.ejs.t +0 -15
- /package/_templates/app/new/{tsconfig.node.json.ejs.t → config/tsconfig.node.json.ejs.t} +0 -0
- /package/_templates/app/new/{vitest.config.ts.ejs.t → config/vitest.config.ts.ejs.t} +0 -0
- /package/_templates/app/new/{vitest.setup.ts.ejs.t → config/vitest.setup.ts.ejs.t} +0 -0
- /package/_templates/app/new/{app.json.ejs.t → root/app.json.ejs.t} +0 -0
- /package/_templates/app/new/{gitignore.ejs.t → root/gitignore.ejs.t} +0 -0
- /package/_templates/app/new/{index.html.ejs.t → root/index.html.ejs.t} +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { CogniteClient } from "@cognite/sdk";
|
|
2
|
+
|
|
3
|
+
export const MESSAGE_TYPES = {
|
|
4
|
+
APP_HOST_READY: "APP_HOST_READY",
|
|
5
|
+
APP_READY: "APP_READY",
|
|
6
|
+
CREDENTIALS: "CREDENTIALS",
|
|
7
|
+
REQUEST_CREDENTIALS: "REQUEST_CREDENTIALS",
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
export interface CDFConfig {
|
|
11
|
+
project: string;
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
clientId: string;
|
|
14
|
+
clientSecret: string;
|
|
15
|
+
appId?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const getToken = async (clientId: string, clientSecret: string) => {
|
|
19
|
+
const header = `Basic ${btoa(`${clientId}:${clientSecret}`)}`;
|
|
20
|
+
|
|
21
|
+
const response = await fetch("https://auth.cognite.com/oauth2/token", {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: header,
|
|
25
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
26
|
+
},
|
|
27
|
+
body: new URLSearchParams({
|
|
28
|
+
grant_type: "client_credentials",
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const data = await response.json();
|
|
33
|
+
return data.access_token;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const createCDFSDK = async (config: CDFConfig) => {
|
|
37
|
+
const { project, baseUrl, clientId, clientSecret, appId = "cdf-authentication-package" } = config;
|
|
38
|
+
|
|
39
|
+
if (!project || !baseUrl || !clientId || !clientSecret) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"Missing required configuration. Please provide: project, baseUrl, clientId, clientSecret"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sdk = new CogniteClient({
|
|
46
|
+
appId,
|
|
47
|
+
project,
|
|
48
|
+
baseUrl,
|
|
49
|
+
oidcTokenProvider: async () => {
|
|
50
|
+
return await getToken(clientId, clientSecret);
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
await sdk.authenticate();
|
|
55
|
+
return sdk;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const EMPTY_SDK = new CogniteClient({
|
|
59
|
+
appId: "cdf-authentication-package",
|
|
60
|
+
project: "",
|
|
61
|
+
oidcTokenProvider: async () => "",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
interface Credentials {
|
|
65
|
+
project: string;
|
|
66
|
+
baseUrl: string;
|
|
67
|
+
token: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const requestCredentials = () => {
|
|
71
|
+
console.log("🔑 Requesting credentials from parent...");
|
|
72
|
+
if (window.parent && window.parent !== window) {
|
|
73
|
+
window.parent.postMessage({ type: "REQUEST_CREDENTIALS" }, "*");
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const handleCredentialsResponse = (event: MessageEvent) => {
|
|
78
|
+
// Check if this is a credentials message (wrapped in type/credentials format)
|
|
79
|
+
if (event.data?.type === "PROVIDE_CREDENTIALS" && event.data?.credentials) {
|
|
80
|
+
const creds = event.data.credentials;
|
|
81
|
+
if (creds.token && creds.baseUrl && creds.project) {
|
|
82
|
+
console.log("🎉 useCredentials received credentials:", {
|
|
83
|
+
hasToken: !!creds.token,
|
|
84
|
+
tokenLength: creds.token?.length || 0,
|
|
85
|
+
project: creds.project,
|
|
86
|
+
baseUrl: creds.baseUrl,
|
|
87
|
+
});
|
|
88
|
+
return creds as Credentials;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CDF Application Deployment
|
|
3
|
+
*
|
|
4
|
+
* Handles deployment of packaged applications to CDF.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import type { CogniteClient } from "@cognite/sdk";
|
|
9
|
+
|
|
10
|
+
export class CdfApplicationDeployer {
|
|
11
|
+
private client: CogniteClient;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {CogniteClient} client - Cognite SDK client
|
|
15
|
+
*/
|
|
16
|
+
constructor(client: CogniteClient) {
|
|
17
|
+
this.client = client;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private DATA_SET_EXTERNAL_ID = "published-custom-apps";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validate that the required data set exists and is accessible
|
|
24
|
+
* @returns {Promise<number>} Data set ID (numerical)
|
|
25
|
+
* @throws {Error} If data set doesn't exist or user doesn't have access
|
|
26
|
+
*/
|
|
27
|
+
async validateDataSet() {
|
|
28
|
+
try {
|
|
29
|
+
const dataSets = await this.client.datasets.retrieve([
|
|
30
|
+
{ externalId: this.DATA_SET_EXTERNAL_ID },
|
|
31
|
+
]);
|
|
32
|
+
return dataSets[0].id;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
try {
|
|
35
|
+
this.client.datasets.create([
|
|
36
|
+
{
|
|
37
|
+
externalId: this.DATA_SET_EXTERNAL_ID,
|
|
38
|
+
name: "Published Custom Apps",
|
|
39
|
+
description: "Published Custom Apps",
|
|
40
|
+
writeProtected: false,
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
console.log(`✅ Data set '${this.DATA_SET_EXTERNAL_ID}' created`);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Failed to create data set '${this.DATA_SET_EXTERNAL_ID}'. Please ask your IT admin to create it or grant access.`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Upload application package to CDF Files API
|
|
54
|
+
* @param {string} appExternalId - Application external ID
|
|
55
|
+
* @param {string} name - Application name
|
|
56
|
+
* @param {string} description - Application description
|
|
57
|
+
* @param {string} versionTag - Version tag
|
|
58
|
+
* @param {string} zipFilename - Path to zip file
|
|
59
|
+
* @param {boolean} published - Whether the application should be published
|
|
60
|
+
*/
|
|
61
|
+
async uploadToFilesApi(
|
|
62
|
+
appExternalId: string,
|
|
63
|
+
name: string,
|
|
64
|
+
description: string,
|
|
65
|
+
versionTag: string,
|
|
66
|
+
zipFilename: string,
|
|
67
|
+
published = false
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
// Validate data set exists and get its ID
|
|
70
|
+
console.log("🔍 Validating data set access...");
|
|
71
|
+
const dataSetId = await this.validateDataSet();
|
|
72
|
+
console.log(`✅ Data set '${this.DATA_SET_EXTERNAL_ID}' validated (ID: ${dataSetId})\n`);
|
|
73
|
+
|
|
74
|
+
console.log("📁 Creating file record...");
|
|
75
|
+
|
|
76
|
+
const fileContent = fs.readFileSync(zipFilename);
|
|
77
|
+
const metadata = {
|
|
78
|
+
published: String(published),
|
|
79
|
+
name: name,
|
|
80
|
+
description: description,
|
|
81
|
+
externalId: `${appExternalId}-${versionTag}`,
|
|
82
|
+
version: versionTag,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
await this.client.files.upload(
|
|
86
|
+
{
|
|
87
|
+
name: `${appExternalId}-${versionTag}.zip`,
|
|
88
|
+
externalId: `${appExternalId}-${versionTag}`,
|
|
89
|
+
directory: "/dune-apps",
|
|
90
|
+
metadata: metadata,
|
|
91
|
+
dataSetId: dataSetId,
|
|
92
|
+
},
|
|
93
|
+
fileContent,
|
|
94
|
+
true, // overwrite
|
|
95
|
+
true // waitUntilAcknowledged
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
console.log("✅ File record created");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Execute complete deployment to CDF
|
|
103
|
+
* @param {string} appExternalId - Application external ID
|
|
104
|
+
* @param {string} name - Application name
|
|
105
|
+
* @param {string} description - Application description
|
|
106
|
+
* @param {string} versionTag - Version tag
|
|
107
|
+
* @param {string} zipFilename - Path to zip file
|
|
108
|
+
* @param {boolean} published - Whether the application should be published
|
|
109
|
+
*/
|
|
110
|
+
async deploy(
|
|
111
|
+
appExternalId: string,
|
|
112
|
+
name: string,
|
|
113
|
+
description: string,
|
|
114
|
+
versionTag: string,
|
|
115
|
+
zipFilename: string,
|
|
116
|
+
published = false
|
|
117
|
+
): Promise<void> {
|
|
118
|
+
console.log("\n🚀 Deploying application to CDF...\n");
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
// Upload to Files API
|
|
122
|
+
await this.uploadToFilesApi(
|
|
123
|
+
appExternalId,
|
|
124
|
+
name,
|
|
125
|
+
description,
|
|
126
|
+
versionTag,
|
|
127
|
+
zipFilename,
|
|
128
|
+
published
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
console.log("\n✅ Deployment successful!");
|
|
132
|
+
} catch (error: unknown) {
|
|
133
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
134
|
+
throw new Error(`Deployment failed: ${message}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application Packaging
|
|
3
|
+
*
|
|
4
|
+
* Handles packaging of build directories into deployment-ready zip files.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import archiver from "archiver";
|
|
10
|
+
|
|
11
|
+
export class ApplicationPackager {
|
|
12
|
+
private distPath: string;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} distDirectory - Build directory to package (can be relative or absolute)
|
|
16
|
+
*/
|
|
17
|
+
constructor(distDirectory = "dist") {
|
|
18
|
+
// If distDirectory is already an absolute path, use it as-is
|
|
19
|
+
// Otherwise, join it with the current working directory
|
|
20
|
+
this.distPath = path.isAbsolute(distDirectory)
|
|
21
|
+
? distDirectory
|
|
22
|
+
: path.join(process.cwd(), distDirectory);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Validate that build directory exists
|
|
27
|
+
* @throws {Error} If build directory not found
|
|
28
|
+
*/
|
|
29
|
+
validateBuildDirectory(): void {
|
|
30
|
+
if (!fs.existsSync(this.distPath)) {
|
|
31
|
+
throw new Error(`Build directory "${this.distPath}" not found. Run build first.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create zip file from build directory
|
|
37
|
+
* @param {string} outputFilename - Output zip filename
|
|
38
|
+
* @param {boolean} verbose - Enable verbose logging
|
|
39
|
+
* @returns {Promise<string>} Path to created zip file
|
|
40
|
+
*/
|
|
41
|
+
async createZip(outputFilename = "app.zip", verbose = false): Promise<string> {
|
|
42
|
+
this.validateBuildDirectory();
|
|
43
|
+
|
|
44
|
+
console.log("📦 Packaging application...");
|
|
45
|
+
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const output = fs.createWriteStream(outputFilename);
|
|
48
|
+
const archive = archiver("zip", {
|
|
49
|
+
zlib: { level: 9 }, // Maximum compression
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
output.on("close", () => {
|
|
53
|
+
const sizeMB = (archive.pointer() / 1024 / 1024).toFixed(2);
|
|
54
|
+
console.log(`✅ App packaged: ${outputFilename} (${sizeMB} MB)`);
|
|
55
|
+
resolve(outputFilename);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
archive.on("error", (err: Error) => {
|
|
59
|
+
reject(new Error(`Failed to create zip: ${err.message}`));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (verbose) {
|
|
63
|
+
archive.on("entry", (entry: archiver.EntryData) => {
|
|
64
|
+
console.log(` 📄 ${entry.name}`);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
archive.pipe(output);
|
|
69
|
+
archive.directory(this.distPath, false);
|
|
70
|
+
archive.finalize();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { CdfApplicationDeployer } from "./application-deployer";
|
|
3
|
+
import { ApplicationPackager } from "./application-packager";
|
|
4
|
+
import { getSdk } from "./get-sdk";
|
|
5
|
+
import type { App, Deployment } from "./types";
|
|
6
|
+
|
|
7
|
+
export const deploy = async (deployment: Deployment, app: App, folder: string) => {
|
|
8
|
+
// Step 1: Get an SDK instance
|
|
9
|
+
const sdk = await getSdk(deployment, folder);
|
|
10
|
+
|
|
11
|
+
// Step 2: Package application (from the dist subdirectory)
|
|
12
|
+
const distPath = `${folder}/dist`;
|
|
13
|
+
const packager = new ApplicationPackager(distPath);
|
|
14
|
+
const zipFilename = await packager.createZip("app.zip", true);
|
|
15
|
+
|
|
16
|
+
// Step 3: Deploy to CDF
|
|
17
|
+
const deployer = new CdfApplicationDeployer(sdk);
|
|
18
|
+
await deployer.deploy(
|
|
19
|
+
app.externalId,
|
|
20
|
+
app.name,
|
|
21
|
+
app.description,
|
|
22
|
+
app.versionTag,
|
|
23
|
+
zipFilename,
|
|
24
|
+
deployment.published
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Step 4: Clean up zip file
|
|
28
|
+
try {
|
|
29
|
+
fs.unlinkSync(zipFilename);
|
|
30
|
+
} catch {
|
|
31
|
+
// Ignore cleanup errors
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CogniteClient } from "@cognite/sdk";
|
|
2
|
+
import { getToken } from "./login";
|
|
3
|
+
import type { Deployment } from "./types";
|
|
4
|
+
|
|
5
|
+
export const getSdk = async (deployment: Deployment, folder: string) => {
|
|
6
|
+
const token = await getToken(deployment.deployClientId, deployment.deploySecretName);
|
|
7
|
+
const sdk = new CogniteClient({
|
|
8
|
+
appId: folder,
|
|
9
|
+
project: deployment.project,
|
|
10
|
+
baseUrl: deployment.baseUrl,
|
|
11
|
+
oidcTokenProvider: async () => {
|
|
12
|
+
return token;
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
await sdk.authenticate();
|
|
16
|
+
return sdk;
|
|
17
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Deploy exports for CI/CD and programmatic deployment
|
|
2
|
+
export { deploy } from "./deploy";
|
|
3
|
+
export { CdfApplicationDeployer } from "./application-deployer";
|
|
4
|
+
export { ApplicationPackager } from "./application-packager";
|
|
5
|
+
export { getSdk } from "./get-sdk";
|
|
6
|
+
export { getToken } from "./login";
|
|
7
|
+
|
|
8
|
+
// Type exports
|
|
9
|
+
export type { Deployment, App } from "./types";
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load secrets from DEPLOYMENT_SECRETS environment variable (JSON)
|
|
3
|
+
*/
|
|
4
|
+
const loadSecretsFromEnv = (): Record<string, string> => {
|
|
5
|
+
const secretsJson = process.env.DEPLOYMENT_SECRETS;
|
|
6
|
+
if (!secretsJson) {
|
|
7
|
+
return {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const secrets = JSON.parse(secretsJson);
|
|
12
|
+
// Convert GitHub secret names (UPPER_CASE) to deployment secret names (lower-case)
|
|
13
|
+
const normalizedSecrets: Record<string, string> = {};
|
|
14
|
+
|
|
15
|
+
for (const [key, value] of Object.entries(secrets)) {
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
// Convert UPPER_CASE to lower-case-with-dashes
|
|
18
|
+
const normalizedKey = key.toLowerCase().replace(/_/g, "-");
|
|
19
|
+
normalizedSecrets[normalizedKey] = value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return normalizedSecrets;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.error("Error parsing DEPLOYMENT_SECRETS:", error);
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const getToken = async (deployClientId: string, deploySecretName: string) => {
|
|
31
|
+
let deploySecret: string | undefined;
|
|
32
|
+
|
|
33
|
+
// First try DEPLOYMENT_SECRET (for matrix-based deployments)
|
|
34
|
+
if (process.env.DEPLOYMENT_SECRET) {
|
|
35
|
+
deploySecret = process.env.DEPLOYMENT_SECRET;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Then try to get from DEPLOYMENT_SECRETS JSON
|
|
39
|
+
if (!deploySecret) {
|
|
40
|
+
const secrets = loadSecretsFromEnv();
|
|
41
|
+
deploySecret = secrets[deploySecretName];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Fall back to direct environment variable for local development
|
|
45
|
+
if (!deploySecret) {
|
|
46
|
+
deploySecret = process.env[deploySecretName];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!deploySecret) {
|
|
50
|
+
throw new Error(`Deployment secret not found in environment: ${deploySecretName}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const header = `Basic ${btoa(`${deployClientId}:${deploySecret}`)}`;
|
|
54
|
+
const response = await fetch("https://auth.cognite.com/oauth2/token", {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers: {
|
|
57
|
+
Authorization: header,
|
|
58
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
59
|
+
},
|
|
60
|
+
body: new URLSearchParams({ grant_type: "client_credentials" }),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(`Failed to get token: ${response.status} ${response.statusText}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const data = await response.json();
|
|
68
|
+
return data.access_token;
|
|
69
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type Deployment = {
|
|
2
|
+
org: string;
|
|
3
|
+
project: string;
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
deployClientId: string;
|
|
6
|
+
deploySecretName: string;
|
|
7
|
+
published: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type App = {
|
|
11
|
+
externalId: string;
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
versionTag: string;
|
|
15
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const openUrl = (url: string) => {
|
|
6
|
+
const start =
|
|
7
|
+
process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
8
|
+
if (process.platform === "win32") {
|
|
9
|
+
exec(`start "" "${url}"`);
|
|
10
|
+
} else {
|
|
11
|
+
exec(`${start} "${url}"`);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
interface ViteDevServer {
|
|
16
|
+
httpServer?: {
|
|
17
|
+
address: () => { port: number } | string | null;
|
|
18
|
+
on: (event: string, callback: () => void) => void;
|
|
19
|
+
} | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const fusionOpenPlugin = () => {
|
|
23
|
+
return {
|
|
24
|
+
name: "fusion-open",
|
|
25
|
+
configureServer(server: ViteDevServer) {
|
|
26
|
+
server.httpServer?.on("listening", () => {
|
|
27
|
+
const address = server.httpServer?.address();
|
|
28
|
+
const port = address && typeof address === "object" ? address.port : 3000;
|
|
29
|
+
|
|
30
|
+
const appJsonPath = path.join(process.cwd(), "app.json");
|
|
31
|
+
if (fs.existsSync(appJsonPath)) {
|
|
32
|
+
try {
|
|
33
|
+
const appJson = JSON.parse(fs.readFileSync(appJsonPath, "utf-8"));
|
|
34
|
+
const firstDeployment = appJson.deployments?.[0];
|
|
35
|
+
const { org, project, baseUrl } = firstDeployment || {};
|
|
36
|
+
|
|
37
|
+
const parsedBaseUrl = baseUrl?.split("//")[1];
|
|
38
|
+
|
|
39
|
+
if (org && project && baseUrl) {
|
|
40
|
+
const fusionUrl = `https://${org}.fusion.cognite.com/${project}/streamlit-apps/dune/development/${port}?cluster=${parsedBaseUrl}&workspace=industrial-tools`;
|
|
41
|
+
|
|
42
|
+
openUrl(fusionUrl);
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.warn("Failed to read app.json for Fusion URL", error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
to: <%= name %>/biome.json
|
|
3
|
-
---
|
|
4
|
-
{
|
|
5
|
-
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
6
|
-
"vcs": {
|
|
7
|
-
"enabled": true,
|
|
8
|
-
"clientKind": "git",
|
|
9
|
-
"useIgnoreFile": true
|
|
10
|
-
},
|
|
11
|
-
"organizeImports": {
|
|
12
|
-
"enabled": true
|
|
13
|
-
},
|
|
14
|
-
"linter": {
|
|
15
|
-
"enabled": true,
|
|
16
|
-
"rules": {
|
|
17
|
-
"recommended": true
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"formatter": {
|
|
21
|
-
"enabled": true,
|
|
22
|
-
"indentStyle": "tab"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
to: <%= name %>/vite.config.ts
|
|
3
|
-
---
|
|
4
|
-
import react from "@vitejs/plugin-react";
|
|
5
|
-
import { defineConfig } from "vite";
|
|
6
|
-
import mkcert from "vite-plugin-mkcert";
|
|
7
|
-
|
|
8
|
-
export default defineConfig({
|
|
9
|
-
plugins: [react(), mkcert()],
|
|
10
|
-
server: {
|
|
11
|
-
https: true,
|
|
12
|
-
port: 3000,
|
|
13
|
-
},
|
|
14
|
-
});
|
|
15
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|