@meshxdata/fops 0.1.55 → 0.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +4 -4
  2. package/package.json +1 -2
  3. package/src/commands/index.js +2 -0
  4. package/src/commands/k3s-cmd.js +124 -0
  5. package/src/commands/lifecycle.js +7 -0
  6. package/src/plugins/builtins/docker-compose.js +17 -35
  7. package/src/plugins/bundled/fops-plugin-azure/lib/azure-openai.js +0 -3
  8. package/src/plugins/bundled/fops-plugin-azure/lib/commands/vm-cmds.js +5 -2
  9. package/src/plugins/bundled/fops-plugin-cloud/api.js +14 -0
  10. package/src/project.js +12 -7
  11. package/src/plugins/bundled/fops-plugin-cloud/ui/postcss.config.cjs +0 -5
  12. package/src/plugins/bundled/fops-plugin-cloud/ui/src/App.jsx +0 -32
  13. package/src/plugins/bundled/fops-plugin-cloud/ui/src/api/client.js +0 -114
  14. package/src/plugins/bundled/fops-plugin-cloud/ui/src/api/queries.js +0 -111
  15. package/src/plugins/bundled/fops-plugin-cloud/ui/src/components/LogPanel.jsx +0 -162
  16. package/src/plugins/bundled/fops-plugin-cloud/ui/src/components/ThemeToggle.jsx +0 -46
  17. package/src/plugins/bundled/fops-plugin-cloud/ui/src/css/additional-styles/utility-patterns.css +0 -147
  18. package/src/plugins/bundled/fops-plugin-cloud/ui/src/css/style.css +0 -138
  19. package/src/plugins/bundled/fops-plugin-cloud/ui/src/favicon.svg +0 -15
  20. package/src/plugins/bundled/fops-plugin-cloud/ui/src/lib/utils.ts +0 -19
  21. package/src/plugins/bundled/fops-plugin-cloud/ui/src/main.jsx +0 -25
  22. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Audit.jsx +0 -164
  23. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Costs.jsx +0 -305
  24. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/CreateResource.jsx +0 -285
  25. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Fleet.jsx +0 -307
  26. package/src/plugins/bundled/fops-plugin-cloud/ui/src/pages/Resources.jsx +0 -229
  27. package/src/plugins/bundled/fops-plugin-cloud/ui/src/partials/Header.jsx +0 -132
  28. package/src/plugins/bundled/fops-plugin-cloud/ui/src/partials/Sidebar.jsx +0 -174
  29. package/src/plugins/bundled/fops-plugin-cloud/ui/src/partials/SidebarLinkGroup.jsx +0 -21
  30. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/AuthContext.jsx +0 -170
  31. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/Info.jsx +0 -49
  32. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/ThemeContext.jsx +0 -37
  33. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/Transition.jsx +0 -116
  34. package/src/plugins/bundled/fops-plugin-cloud/ui/src/utils/Utils.js +0 -63
@@ -1,170 +0,0 @@
1
- import { createContext, useContext, useEffect, useState } from "react";
2
- import { Auth0Provider, useAuth0 } from "@auth0/auth0-react";
3
- import { setTokenGetter } from "../api/client";
4
-
5
- const AuthContext = createContext({
6
- user: null,
7
- roles: [],
8
- permissions: [],
9
- isAdmin: false,
10
- canWrite: false,
11
- canDeploy: false,
12
- getToken: async () => "",
13
- });
14
-
15
- /**
16
- * Inner provider that fetches user profile + roles from the API
17
- * once authenticated.
18
- */
19
- function AuthGate({ children }) {
20
- const { isAuthenticated, isLoading, loginWithRedirect, getAccessTokenSilently, user } = useAuth0();
21
- const [profile, setProfile] = useState(null);
22
- const [loadingProfile, setLoadingProfile] = useState(true);
23
-
24
- // Wire token getter into the API client so all fetches are authenticated
25
- useEffect(() => {
26
- if (isAuthenticated) setTokenGetter(() => getAccessTokenSilently());
27
- }, [isAuthenticated, getAccessTokenSilently]);
28
-
29
- useEffect(() => {
30
- if (!isAuthenticated) return;
31
- let cancelled = false;
32
-
33
- (async () => {
34
- try {
35
- const token = await getAccessTokenSilently();
36
- const res = await fetch("/cloud/api/me", {
37
- headers: { Authorization: `Bearer ${token}` },
38
- });
39
- if (res.ok && !cancelled) {
40
- setProfile(await res.json());
41
- }
42
- } catch {
43
- // token or fetch failed — will show login
44
- } finally {
45
- if (!cancelled) setLoadingProfile(false);
46
- }
47
- })();
48
-
49
- return () => { cancelled = true; };
50
- }, [isAuthenticated, getAccessTokenSilently]);
51
-
52
- if (isLoading) {
53
- return (
54
- <div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-900">
55
- <div className="text-gray-500 dark:text-gray-400 text-sm">Loading...</div>
56
- </div>
57
- );
58
- }
59
-
60
- if (!isAuthenticated) {
61
- return (
62
- <div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-900">
63
- <div className="text-center">
64
- <h1 className="text-2xl font-bold text-gray-800 dark:text-gray-100 mb-2">meshXcloud</h1>
65
- <p className="text-gray-500 dark:text-gray-400 mb-6">Sign in to access the cloud panel</p>
66
- <button
67
- onClick={() => loginWithRedirect()}
68
- className="px-6 py-2.5 bg-violet-500 hover:bg-violet-600 text-white rounded-lg font-medium transition-colors"
69
- >
70
- Sign in with Auth0
71
- </button>
72
- </div>
73
- </div>
74
- );
75
- }
76
-
77
- if (loadingProfile) {
78
- return (
79
- <div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-900">
80
- <div className="text-gray-500 dark:text-gray-400 text-sm">Loading profile...</div>
81
- </div>
82
- );
83
- }
84
-
85
- const roles = profile?.roles || [];
86
- const permissions = profile?.permissions || [];
87
- const isAdmin = roles.includes("admin");
88
-
89
- const value = {
90
- user: { ...user, ...profile },
91
- roles,
92
- permissions,
93
- isAdmin,
94
- canWrite: isAdmin || permissions.includes("cloud:write") || permissions.includes("cloud:admin"),
95
- canDeploy: isAdmin || permissions.includes("cloud:deploy") || permissions.includes("cloud:admin"),
96
- getToken: getAccessTokenSilently,
97
- };
98
-
99
- return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
100
- }
101
-
102
- /**
103
- * Passthrough provider when auth is disabled — grants full admin access.
104
- */
105
- function NoAuthProvider({ children }) {
106
- const value = {
107
- user: { sub: "local", name: "Local User", email: "" },
108
- roles: ["admin"],
109
- permissions: ["cloud:admin"],
110
- isAdmin: true,
111
- canWrite: true,
112
- canDeploy: true,
113
- getToken: async () => "",
114
- };
115
- return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
116
- }
117
-
118
- /**
119
- * Top-level wrapper that fetches Auth0 config from the API.
120
- * If auth is disabled (CLOUD_AUTH != 1), renders children directly
121
- * with full permissions. Otherwise, wraps in Auth0Provider + AuthGate.
122
- */
123
- export default function AuthProvider({ children }) {
124
- const [config, setConfig] = useState(null);
125
- const [error, setError] = useState(null);
126
-
127
- useEffect(() => {
128
- fetch("/cloud/api/auth-config")
129
- .then((r) => r.json())
130
- .then(setConfig)
131
- .catch((e) => setError(e.message));
132
- }, []);
133
-
134
- if (error) {
135
- return (
136
- <div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-900">
137
- <div className="text-red-500 text-sm">Auth config error: {error}</div>
138
- </div>
139
- );
140
- }
141
-
142
- if (!config) {
143
- return (
144
- <div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-900">
145
- <div className="text-gray-500 dark:text-gray-400 text-sm">Initializing...</div>
146
- </div>
147
- );
148
- }
149
-
150
- // Auth disabled — bypass Auth0 entirely
151
- if (!config.enabled) {
152
- return <NoAuthProvider>{children}</NoAuthProvider>;
153
- }
154
-
155
- return (
156
- <Auth0Provider
157
- domain={config.domain}
158
- clientId={config.clientId}
159
- authorizationParams={{
160
- redirect_uri: window.location.origin + "/cloud",
161
- audience: config.audience,
162
- }}
163
- cacheLocation="localstorage"
164
- >
165
- <AuthGate>{children}</AuthGate>
166
- </Auth0Provider>
167
- );
168
- }
169
-
170
- export const useAuthContext = () => useContext(AuthContext);
@@ -1,49 +0,0 @@
1
- import React, { useState } from 'react';
2
- import Transition from './Transition';
3
-
4
- function Info({
5
- children,
6
- className,
7
- containerClassName
8
- }) {
9
-
10
- const [infoOpen, setInfoOpen] = useState(false);
11
-
12
- return (
13
- <div
14
- className={`relative ${className}`}
15
- onMouseEnter={() => setInfoOpen(true)}
16
- onMouseLeave={() => setInfoOpen(false)}
17
- onFocus={() => setInfoOpen(true)}
18
- onBlur={() => setInfoOpen(false)}
19
- >
20
- <button
21
- className="block"
22
- aria-haspopup="true"
23
- aria-expanded={infoOpen}
24
- onClick={(e) => e.preventDefault()}
25
- >
26
- <svg className="w-4 h-4 fill-current text-gray-400" viewBox="0 0 16 16">
27
- <path d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm0 12c-.6 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1zm1-3H7V4h2v5z" />
28
- </svg>
29
- </button>
30
- <div className="z-10 absolute bottom-full left-1/2 transform -translate-x-1/2">
31
- <Transition
32
- show={infoOpen}
33
- tag="div"
34
- className={`bg-white border border-gray-200 p-3 rounded-sm shadow-lg overflow-hidden mb-2 ${containerClassName}`}
35
- enter="transition ease-out duration-200 transform"
36
- enterStart="opacity-0 -translate-y-2"
37
- enterEnd="opacity-100 translate-y-0"
38
- leave="transition ease-out duration-200"
39
- leaveStart="opacity-100"
40
- leaveEnd="opacity-0"
41
- >
42
- {children}
43
- </Transition>
44
- </div>
45
- </div>
46
- );
47
- }
48
-
49
- export default Info;
@@ -1,37 +0,0 @@
1
- import { createContext, useContext, useState, useEffect } from 'react';
2
-
3
- const ThemeContext = createContext({
4
- currentTheme: 'light',
5
- changeCurrentTheme: () => {},
6
- });
7
-
8
- export default function ThemeProvider({children}) {
9
- const persistedTheme = localStorage.getItem('theme');
10
- const [theme, setTheme] = useState(persistedTheme || 'light');
11
-
12
- const changeCurrentTheme = (newTheme) => {
13
- setTheme(newTheme);
14
- localStorage.setItem('theme', newTheme);
15
- };
16
-
17
- useEffect(() => {
18
- document.documentElement.classList.add('**:transition-none!');
19
- if (theme === 'light') {
20
- document.documentElement.classList.remove('dark');
21
- document.documentElement.style.colorScheme = 'light';
22
- } else {
23
- document.documentElement.classList.add('dark');
24
- document.documentElement.style.colorScheme = 'dark';
25
- }
26
-
27
- const transitionTimeout = setTimeout(() => {
28
- document.documentElement.classList.remove('**:transition-none!');
29
- }, 1);
30
-
31
- return () => clearTimeout(transitionTimeout);
32
- }, [theme]);
33
-
34
- return <ThemeContext.Provider value={{ currentTheme: theme, changeCurrentTheme }}>{children}</ThemeContext.Provider>;
35
- }
36
-
37
- export const useThemeProvider = () => useContext(ThemeContext);
@@ -1,116 +0,0 @@
1
- import React, { useRef, useEffect, useContext } from 'react';
2
- import { CSSTransition as ReactCSSTransition } from 'react-transition-group';
3
-
4
- const TransitionContext = React.createContext({
5
- parent: {},
6
- })
7
-
8
- function useIsInitialRender() {
9
- const isInitialRender = useRef(true);
10
- useEffect(() => {
11
- isInitialRender.current = false;
12
- }, [])
13
- return isInitialRender.current;
14
- }
15
-
16
- function CSSTransition({
17
- show,
18
- enter = '',
19
- enterStart = '',
20
- enterEnd = '',
21
- leave = '',
22
- leaveStart = '',
23
- leaveEnd = '',
24
- appear,
25
- unmountOnExit,
26
- tag = 'div',
27
- children,
28
- ...rest
29
- }) {
30
- const enterClasses = enter.split(' ').filter((s) => s.length);
31
- const enterStartClasses = enterStart.split(' ').filter((s) => s.length);
32
- const enterEndClasses = enterEnd.split(' ').filter((s) => s.length);
33
- const leaveClasses = leave.split(' ').filter((s) => s.length);
34
- const leaveStartClasses = leaveStart.split(' ').filter((s) => s.length);
35
- const leaveEndClasses = leaveEnd.split(' ').filter((s) => s.length);
36
- const removeFromDom = unmountOnExit;
37
-
38
- function addClasses(node, classes) {
39
- classes.length && node.classList.add(...classes);
40
- }
41
-
42
- function removeClasses(node, classes) {
43
- classes.length && node.classList.remove(...classes);
44
- }
45
-
46
- const nodeRef = React.useRef(null);
47
- const Component = tag;
48
-
49
- return (
50
- <ReactCSSTransition
51
- appear={appear}
52
- nodeRef={nodeRef}
53
- unmountOnExit={removeFromDom}
54
- in={show}
55
- addEndListener={(done) => {
56
- nodeRef.current.addEventListener('transitionend', done, false)
57
- }}
58
- onEnter={() => {
59
- if (!removeFromDom) nodeRef.current.style.display = null;
60
- addClasses(nodeRef.current, [...enterClasses, ...enterStartClasses])
61
- }}
62
- onEntering={() => {
63
- removeClasses(nodeRef.current, enterStartClasses)
64
- addClasses(nodeRef.current, enterEndClasses)
65
- }}
66
- onEntered={() => {
67
- removeClasses(nodeRef.current, [...enterEndClasses, ...enterClasses])
68
- }}
69
- onExit={() => {
70
- addClasses(nodeRef.current, [...leaveClasses, ...leaveStartClasses])
71
- }}
72
- onExiting={() => {
73
- removeClasses(nodeRef.current, leaveStartClasses)
74
- addClasses(nodeRef.current, leaveEndClasses)
75
- }}
76
- onExited={() => {
77
- removeClasses(nodeRef.current, [...leaveEndClasses, ...leaveClasses])
78
- if (!removeFromDom) nodeRef.current.style.display = 'none';
79
- }}
80
- >
81
- <Component ref={nodeRef} {...rest} style={{ display: !removeFromDom ? 'none': null }}>{children}</Component>
82
- </ReactCSSTransition>
83
- )
84
- }
85
-
86
- function Transition({ show, appear, ...rest }) {
87
- const { parent } = useContext(TransitionContext);
88
- const isInitialRender = useIsInitialRender();
89
- const isChild = show === undefined;
90
-
91
- if (isChild) {
92
- return (
93
- <CSSTransition
94
- appear={parent.appear || !parent.isInitialRender}
95
- show={parent.show}
96
- {...rest}
97
- />
98
- )
99
- }
100
-
101
- return (
102
- <TransitionContext.Provider
103
- value={{
104
- parent: {
105
- show,
106
- isInitialRender,
107
- appear,
108
- },
109
- }}
110
- >
111
- <CSSTransition appear={appear} show={show} {...rest} />
112
- </TransitionContext.Provider>
113
- )
114
- }
115
-
116
- export default Transition;
@@ -1,63 +0,0 @@
1
- export const formatValue = (value) => Intl.NumberFormat('en-US', {
2
- style: 'currency',
3
- currency: 'USD',
4
- maximumSignificantDigits: 3,
5
- notation: 'compact',
6
- }).format(value);
7
-
8
- export const formatThousands = (value) => Intl.NumberFormat('en-US', {
9
- maximumSignificantDigits: 3,
10
- notation: 'compact',
11
- }).format(value);
12
-
13
- export const getCssVariable = (variable) => {
14
- return getComputedStyle(document.documentElement).getPropertyValue(variable).trim();
15
- };
16
-
17
- const adjustHexOpacity = (hexColor, opacity) => {
18
- // Remove the '#' if it exists
19
- hexColor = hexColor.replace('#', '');
20
-
21
- // Convert hex to RGB
22
- const r = parseInt(hexColor.substring(0, 2), 16);
23
- const g = parseInt(hexColor.substring(2, 4), 16);
24
- const b = parseInt(hexColor.substring(4, 6), 16);
25
-
26
- // Return RGBA string
27
- return `rgba(${r}, ${g}, ${b}, ${opacity})`;
28
- };
29
-
30
- const adjustHSLOpacity = (hslColor, opacity) => {
31
- // Convert HSL to HSLA
32
- return hslColor.replace('hsl(', 'hsla(').replace(')', `, ${opacity})`);
33
- };
34
-
35
- const adjustOKLCHOpacity = (oklchColor, opacity) => {
36
- // Add alpha value to OKLCH color
37
- return oklchColor.replace(/oklch\((.*?)\)/, (match, p1) => `oklch(${p1} / ${opacity})`);
38
- };
39
-
40
- export const adjustColorOpacity = (color, opacity) => {
41
- if (color.startsWith('#')) {
42
- return adjustHexOpacity(color, opacity);
43
- } else if (color.startsWith('hsl')) {
44
- return adjustHSLOpacity(color, opacity);
45
- } else if (color.startsWith('oklch')) {
46
- return adjustOKLCHOpacity(color, opacity);
47
- } else {
48
- throw new Error('Unsupported color format');
49
- }
50
- };
51
-
52
- export const oklchToRGBA = (oklchColor) => {
53
- // Create a temporary div to use for color conversion
54
- const tempDiv = document.createElement('div');
55
- tempDiv.style.color = oklchColor;
56
- document.body.appendChild(tempDiv);
57
-
58
- // Get the computed style and convert to RGB
59
- const computedColor = window.getComputedStyle(tempDiv).color;
60
- document.body.removeChild(tempDiv);
61
-
62
- return computedColor;
63
- };