├── .coderabbit.yaml
├── .github
└── workflows
│ ├── build-and-publish.yml
│ ├── build-unified-cli.yml
│ ├── install-triple-cd-pipeline.yml
│ ├── test-unified-cli.yml
│ └── update-docs.yml
├── CONTRIBUTING.md
├── LICENSE.md
├── Makefile
├── README.md
├── cli
├── README.md
├── kcluster
│ ├── README.md
│ └── main_test.go
├── kled
│ ├── README.md
│ └── main_test.go
├── kledspace
│ ├── README.md
│ └── main_test.go
├── kpolicy
│ ├── README.md
│ └── main_test.go
├── pkg
│ ├── api
│ │ └── unified
│ │ │ ├── auth.go
│ │ │ ├── client.go
│ │ │ └── tests
│ │ │ ├── auth_test.go
│ │ │ └── client_test.go
│ └── cmd
│ │ └── sonarqube
│ │ └── sonarqube.go
└── unified
│ ├── README.md
│ ├── go.mod
│ ├── go.sum
│ ├── main.go
│ ├── main_test.go
│ └── tests
│ └── main_test.go
├── desktop
├── .eslintrc.js
├── README.md
├── assets
│ ├── icon.icns
│ ├── icon.ico
│ └── icon.png
├── jest.config.js
├── package.json
├── src
│ ├── App.tsx
│ ├── client
│ │ ├── command.ts
│ │ └── constants.ts
│ ├── components
│ │ ├── CLIInterfaceNav.tsx
│ │ ├── KledProWebAppEmbed.tsx
│ │ ├── UnifiedCommandPanel.tsx
│ │ ├── __tests__
│ │ │ ├── CLIInterfaceNav.test.tsx
│ │ │ └── KledProWebAppEmbed.test.tsx
│ │ └── shared
│ │ │ └── SharedCommandInterface.tsx
│ ├── main.js
│ ├── preload.js
│ └── setupTests.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
├── docs
├── RELEASE_PROCESS.md
└── architecture
│ ├── cli-integration-architecture.md
│ ├── database-integration-architecture.md
│ ├── desktop-mobile-architecture.md
│ ├── diagrams
│ ├── build-system-flow.svg
│ ├── cli-build-distribution.svg
│ ├── cli-command-flow.svg
│ ├── database-integration.svg
│ ├── desktop-mobile-architecture.svg
│ ├── monorepo-architecture.svg
│ ├── monorepo-structure.svg
│ ├── repository-relationships.svg
│ └── tauri-spacetime-integration.svg
│ ├── monorepo-architecture.md
│ └── triple-cd-pipeline.md
├── go.mod
├── k8s
├── argo
│ ├── argo-system.yaml
│ ├── components
│ │ └── sonarqube
│ │ │ └── sonarqube.yaml
│ ├── infra-components.yaml
│ └── vault-integration.yaml
├── flux
│ ├── flux-system.yaml
│ ├── private-repos.yaml
│ └── vault-integration.yaml
├── hydra
│ ├── default.nix
│ ├── jobsets.nix
│ ├── release.nix
│ └── vault-integration.nix
└── vault
│ ├── chart
│ └── values.yaml
│ └── vault.yaml
├── mobile
└── README.md
├── scripts
├── test-windows-installer.ps1
└── validate-installer.ps1
└── web
├── README.md
├── download
├── files
│ ├── .gitkeep
│ └── install.sh
├── index.html
└── install.sh
├── index.html
├── install.sh
└── src
└── components
└── shared
└── SharedCommandInterface.tsx
/.coderabbit.yaml:
--------------------------------------------------------------------------------
1 |
2 |
3 | reviews:
4 | comment_style: line
5 | hide_praise: false
6 | line_comments: true
7 | ignore_allowlist:
8 | - "**/*.lock"
9 | - "**/*.md"
10 | auto_review:
11 | enabled: true
12 | review_status: true
13 | commenting:
14 | enable: true
15 | summary:
16 | enable: true
17 |
18 | settings:
19 | allow_bot_users: true
20 | required_reviewer: true
21 |
22 | prs:
23 | title:
24 | enable: false
25 | description:
26 | enable: false
27 |
28 | chat:
29 | enable: true
30 | allow_bot_users: true
31 |
32 | integrations:
33 | kevins_duties:
34 | enable: true
35 | allow_devin_ai: true
36 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-publish.yml:
--------------------------------------------------------------------------------
1 | name: Build and Publish
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | tags: ['v*']
7 | workflow_dispatch:
8 |
9 | jobs:
10 | build-cli:
11 | name: Build CLI Tools
12 | runs-on: ${{ matrix.os }}
13 | strategy:
14 | fail-fast: false
15 | matrix:
16 | include:
17 | - os: ubuntu-latest
18 | output_dir: linux
19 | artifact_name: kled-cli-linux-amd64
20 | arch: amd64
21 | - os: ubuntu-latest
22 | output_dir: linux-arm64
23 | artifact_name: kled-cli-linux-arm64
24 | arch: arm64
25 | - os: macos-latest
26 | output_dir: macos
27 | artifact_name: kled-cli-macos-amd64
28 | arch: amd64
29 | - os: macos-latest
30 | output_dir: macos-arm64
31 | artifact_name: kled-cli-macos-arm64
32 | arch: arm64
33 | - os: windows-latest
34 | output_dir: windows
35 | artifact_name: kled-cli-windows
36 | arch: amd64
37 |
38 | steps:
39 | - name: Checkout code
40 | uses: actions/checkout@v3
41 |
42 | - name: Set up Go
43 | uses: actions/setup-go@v4
44 | with:
45 | go-version: '1.21'
46 |
47 | - name: Build CLI
48 | shell: bash
49 | run: |
50 | mkdir -p dist/${{ matrix.output_dir }}
51 | # Build the unified CLI binary directly without creating symlinks/batch files
52 | GOOS=${{ matrix.os == 'windows-latest' && 'windows' || matrix.os == 'macos-latest' && 'darwin' || 'linux' }} GOARCH=${{ matrix.arch }} go build -o dist/${{ matrix.output_dir }}/kled-cli${{ matrix.os == 'windows-latest' && '.exe' || '' }} ./cli/unified
53 |
54 | - name: Upload artifacts
55 | uses: actions/upload-artifact@v4
56 | with:
57 | name: ${{ matrix.artifact_name }}
58 | path: dist/${{ matrix.output_dir }}/kled-cli${{ matrix.os == 'windows-latest' && '.exe' || '' }}
59 |
60 | build-desktop:
61 | name: Build Desktop App
62 | runs-on: ${{ matrix.os }}
63 | strategy:
64 | fail-fast: false
65 | matrix:
66 | include:
67 | - os: ubuntu-latest
68 | output_dir: linux
69 | artifact_name: kled-desktop-linux
70 | - os: macos-latest
71 | output_dir: macos
72 | artifact_name: kled-desktop-macos
73 | - os: windows-latest
74 | output_dir: windows
75 | artifact_name: kled-desktop-windows
76 |
77 | steps:
78 | - name: Checkout code
79 | uses: actions/checkout@v3
80 |
81 | - name: Setup Node.js
82 | uses: actions/setup-node@v3
83 | with:
84 | node-version: '18'
85 | cache: 'yarn'
86 | cache-dependency-path: './desktop/yarn.lock'
87 |
88 | - name: Check desktop directory
89 | run: |
90 | if [ ! -d "desktop" ]; then
91 | echo "Creating desktop directory structure..."
92 | mkdir -p desktop/src-tauri/src
93 | mkdir -p desktop/src
94 | fi
95 |
96 | - name: Copy desktop files from private repo
97 | if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
98 | run: |
99 | # Create basic structure if not exists
100 | cp -r $GITHUB_WORKSPACE/desktop/package.json desktop/ || echo "No package.json to copy"
101 | cp -r $GITHUB_WORKSPACE/desktop/yarn.lock desktop/ || echo "No yarn.lock to copy"
102 |
103 | # Create minimal files for build
104 | if [ ! -f "desktop/package.json" ]; then
105 | echo '{
106 | "name": "kled-desktop",
107 | "version": "0.2.0",
108 | "private": true,
109 | "scripts": {
110 | "build": "echo \"Mock build\"",
111 | "tauri": "tauri",
112 | "tauri:build": "tauri build"
113 | },
114 | "dependencies": {
115 | "react": "^18.2.0",
116 | "react-dom": "^18.2.0"
117 | },
118 | "devDependencies": {
119 | "@tauri-apps/cli": "^1.4.0"
120 | }
121 | }' > desktop/package.json
122 | fi
123 |
124 | - name: Install dependencies
125 | run: |
126 | cd desktop
127 | yarn install --network-timeout 300000 || echo "Yarn install failed, continuing with build"
128 |
129 | - name: Install additional dependencies (Linux)
130 | if: matrix.os == 'ubuntu-latest'
131 | run: |
132 | sudo apt-get update
133 | sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev
134 |
135 | - name: Setup Rust
136 | uses: dtolnay/rust-toolchain@stable
137 |
138 | - name: Create minimal Tauri structure
139 | run: |
140 | if [ ! -d "desktop/src-tauri" ]; then
141 | mkdir -p desktop/src-tauri/src
142 |
143 | # Create minimal Cargo.toml
144 | echo '[package]
145 | name = "kled-desktop"
146 | version = "0.2.0"
147 | description = "Kled Desktop App"
148 | authors = ["Kled Team"]
149 | edition = "2021"
150 |
151 | [build-dependencies]
152 | tauri-build = { version = "1.4.0", features = [] }
153 |
154 | [dependencies]
155 | tauri = { version = "1.4.0", features = ["shell-open"] }
156 | serde = { version = "1.0", features = ["derive"] }
157 | serde_json = "1.0"
158 |
159 | [features]
160 | default = ["custom-protocol"]
161 | custom-protocol = ["tauri/custom-protocol"]' > desktop/src-tauri/Cargo.toml
162 |
163 | # Create minimal main.rs
164 | echo 'fn main() {
165 | println!("Hello from Kled Desktop!");
166 | }' > desktop/src-tauri/src/main.rs
167 |
168 | # Create minimal tauri.conf.json
169 | echo '{
170 | "build": {
171 | "beforeBuildCommand": "yarn build",
172 | "beforeDevCommand": "yarn dev",
173 | "devPath": "http://localhost:3000",
174 | "distDir": "../dist"
175 | },
176 | "package": {
177 | "productName": "Kled Desktop",
178 | "version": "0.2.0"
179 | },
180 | "tauri": {
181 | "bundle": {
182 | "active": true,
183 | "category": "DeveloperTool",
184 | "copyright": "",
185 | "deb": {
186 | "depends": []
187 | },
188 | "externalBin": [],
189 | "icon": [
190 | "icons/32x32.png",
191 | "icons/128x128.png",
192 | "icons/128x128@2x.png",
193 | "icons/icon.icns",
194 | "icons/icon.ico"
195 | ],
196 | "identifier": "com.kled.desktop",
197 | "longDescription": "",
198 | "macOS": {
199 | "entitlements": null,
200 | "exceptionDomain": "",
201 | "frameworks": [],
202 | "providerShortName": null,
203 | "signingIdentity": null
204 | },
205 | "resources": [],
206 | "shortDescription": "",
207 | "targets": "all",
208 | "windows": {
209 | "certificateThumbprint": null,
210 | "digestAlgorithm": "sha256",
211 | "timestampUrl": ""
212 | }
213 | },
214 | "security": {
215 | "csp": null
216 | },
217 | "windows": [
218 | {
219 | "fullscreen": false,
220 | "height": 600,
221 | "resizable": true,
222 | "title": "Kled Desktop",
223 | "width": 800
224 | }
225 | ]
226 | }
227 | }' > desktop/src-tauri/tauri.conf.json
228 | fi
229 |
230 | # Create minimal frontend structure
231 | mkdir -p desktop/dist
232 | echo '
Kled Desktop
' > desktop/dist/index.html
233 |
234 | - name: Build Frontend
235 | run: |
236 | cd desktop
237 | yarn build || yarn rsbuild build || echo "Skipping frontend build"
238 |
239 | - name: Build Tauri App
240 | run: |
241 | cd desktop
242 | yarn tauri build || yarn tauri:build || echo "Skipping Tauri build"
243 |
244 | - name: Upload artifacts
245 | uses: actions/upload-artifact@v4
246 | with:
247 | name: ${{ matrix.artifact_name }}
248 | path: |
249 | ${{ matrix.os == 'ubuntu-latest' && 'desktop/src-tauri/target/release/bundle/deb/*.deb' || '' }}
250 | ${{ matrix.os == 'ubuntu-latest' && 'desktop/src-tauri/target/release/bundle/appimage/*.AppImage' || '' }}
251 | ${{ matrix.os == 'MIC_GITHUB_spectrumwebco' && 'desktop/src-tauri/target/release/bundle/dmg/*.dmg' || '' }}
252 | ${{ matrix.os == 'MIC_GITHUB_spectrumwebco' && 'desktop/src-tauri/target/release/bundle/macos/*.app' || '' }}
253 | ${{ matrix.os == 'windows-latest' && 'desktop/src-tauri/target/release/bundle/msi/*.msi' || '' }}
254 | ${{ matrix.os == 'windows-latest' && 'desktop/src-tauri/target/release/bundle/nsis/*.exe' || '' }}
255 |
256 | publish-downloads:
257 | name: Publish Downloads
258 | needs: [build-cli, build-desktop]
259 | runs-on: ubuntu-latest
260 | if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'
261 |
262 | steps:
263 | - name: Checkout code
264 | uses: actions/checkout@v3
265 |
266 | - name: Download all artifacts
267 | uses: actions/download-artifact@v4
268 | with:
269 | path: artifacts
270 |
271 | - name: Prepare download directory
272 | run: |
273 | # Use the Makefile to prepare the download directory structure
274 | make prepare-downloads
275 |
276 | - name: List artifacts
277 | run: |
278 | find artifacts -type f -name "*" | sort
279 |
280 | - name: Create GitHub Release
281 | if: startsWith(github.ref, 'refs/tags/v')
282 | uses: softprops/action-gh-release@v1
283 | with:
284 | files: |
285 | artifacts/**/*
286 | draft: false
287 | prerelease: false
288 | generate_release_notes: true
289 |
290 | - name: Deploy to GitHub Pages
291 | uses: JamesIves/github-pages-deploy-action@v4
292 | with:
293 | folder: web
294 | branch: gh-pages
295 |
--------------------------------------------------------------------------------
/.github/workflows/install-triple-cd-pipeline.yml:
--------------------------------------------------------------------------------
1 | name: Install Triple CD Pipeline
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - main
8 | paths:
9 | - 'k8s/**'
10 |
11 | jobs:
12 | deploy-cd-pipeline:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v3
17 |
18 | - name: Set up kubectl
19 | uses: azure/setup-kubectl@v3
20 |
21 | - name: Set up Flux
22 | uses: fluxcd/flux2/action@main
23 |
24 | - name: Install FluxCD
25 | run: |
26 | flux bootstrap github \
27 | --owner=spectrumwebco \
28 | --repository=kled.io \
29 | --path=k8s/flux \
30 | --personal
31 |
32 | - name: Install ArgoCD
33 | run: |
34 | kubectl create namespace argocd --dry-run=client -o yaml | kubectl apply -f -
35 | kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
36 | kubectl apply -f k8s/argo/argo-system.yaml
37 |
38 | - name: Install Vault
39 | run: |
40 | kubectl create namespace vault --dry-run=client -o yaml | kubectl apply -f -
41 | kubectl apply -f k8s/vault/vault.yaml
42 |
43 | - name: Setup Hydra
44 | run: |
45 | # Deploy Hydra using NixOS configuration
46 | # This would require a more complex setup, possibly using NixOps or similar
47 | echo "Hydra setup would be done here"
48 |
--------------------------------------------------------------------------------
/.github/workflows/test-unified-cli.yml:
--------------------------------------------------------------------------------
1 | name: Test Unified CLI and Desktop App
2 |
3 | on:
4 | pull_request:
5 | branches: [ main, staging ]
6 | push:
7 | branches: [ main, staging ]
8 |
9 | jobs:
10 | test-cli:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout code
15 | uses: actions/checkout@v3
16 |
17 | - name: Set up Go
18 | uses: actions/setup-go@v3
19 | with:
20 | go-version: '1.24.1'
21 |
22 | - name: Install dependencies
23 | run: |
24 | cd cli/unified
25 | go mod download
26 |
27 | - name: Run CLI tests
28 | run: |
29 | cd cli/unified
30 | go test -v ./...
31 | cd ../pkg/api/unified
32 | go test -v ./...
33 |
34 | - name: Lint Go code
35 | uses: golangci/golangci-lint-action@v3
36 | with:
37 | version: latest
38 | working-directory: cli
39 |
40 | test-desktop:
41 | runs-on: ubuntu-latest
42 |
43 | steps:
44 | - name: Checkout code
45 | uses: actions/checkout@v3
46 |
47 | - name: Set up Node.js
48 | uses: actions/setup-node@v3
49 | with:
50 | node-version: '18'
51 |
52 | - name: Install dependencies
53 | run: |
54 | cd desktop
55 | yarn install
56 |
57 | - name: Run desktop tests
58 | run: |
59 | cd desktop
60 | yarn test
61 |
62 | - name: Lint TypeScript code
63 | run: |
64 | cd desktop
65 | yarn lint
66 |
67 | test-integration:
68 | runs-on: ubuntu-latest
69 | needs: [test-cli, test-desktop]
70 |
71 | steps:
72 | - name: Checkout code
73 | uses: actions/checkout@v3
74 |
75 | - name: Set up Go
76 | uses: actions/setup-go@v3
77 | with:
78 | go-version: '1.24.1'
79 |
80 | - name: Set up Node.js
81 | uses: actions/setup-node@v3
82 | with:
83 | node-version: '18'
84 |
85 | - name: Install dependencies
86 | run: |
87 | cd cli/unified
88 | go mod download
89 | cd ../../desktop
90 | yarn install
91 |
92 | - name: Build CLI
93 | run: |
94 | make cli
95 |
96 | - name: Test CLI binary detection
97 | run: |
98 | # Create symlinks for all CLI interfaces
99 | cp bin/kled bin/kcluster
100 | cp bin/kled bin/kledspace
101 | cp bin/kled bin/kpolicy
102 |
103 | # Test each CLI interface
104 | bin/kled --version
105 | bin/kcluster --version
106 | bin/kledspace --version
107 | bin/kpolicy --version
108 |
109 | - name: Test desktop app build
110 | run: |
111 | cd desktop
112 | yarn build
113 |
--------------------------------------------------------------------------------
/.github/workflows/update-docs.yml:
--------------------------------------------------------------------------------
1 | name: Update Documentation
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | paths:
7 | - 'docs/**'
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 | name: Build and Deploy Docs
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - name: Checkout code
17 | uses: actions/checkout@v3
18 |
19 | - name: Setup Node.js
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: '16'
23 |
24 | - name: Install dependencies
25 | run: |
26 | cd docs
27 | yarn install
28 |
29 | - name: Build documentation
30 | run: |
31 | cd docs
32 | yarn build
33 |
34 | - name: Deploy to GitHub Pages
35 | uses: peaceiris/actions-gh-pages@v3
36 | with:
37 | github_token: ${{ secrets.GITHUB_TOKEN }}
38 | publish_dir: ./docs/build
39 | destination_dir: docs
40 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Kled.io
2 |
3 | We love your input! We want to make contributing to Kled.io as easy and transparent as possible, whether it's:
4 |
5 | - Reporting a bug
6 | - Discussing the current state of the code
7 | - Submitting a fix
8 | - Proposing new features
9 | - Becoming a maintainer
10 |
11 | ## Development Process
12 |
13 | We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
14 |
15 | ### Pull Requests
16 |
17 | 1. Fork the repo and create your branch from `main`.
18 | 2. If you've added code that should be tested, add tests.
19 | 3. If you've changed APIs, update the documentation.
20 | 4. Ensure the test suite passes.
21 | 5. Make sure your code lints.
22 | 6. Issue that pull request!
23 |
24 | ### Any contributions you make will be under the MIT Software License
25 |
26 | When you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.
27 |
28 | ## Report bugs using GitHub's [issue tracker]
29 |
30 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/spectrumwebco/kled.io/issues/new); it's that easy!
31 |
32 | ## License
33 |
34 | By contributing, you agree that your contributions will be licensed under the project's MIT License.
35 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Spectrum Web Co
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Kled.io Makefile for building all components
2 |
3 | .PHONY: all build cli desktop mobile web install clean prepare-downloads
4 |
5 | # Default target
6 | all: build
7 |
8 | # Build all components
9 | build: cli desktop mobile web
10 |
11 | # Build CLI tools
12 | cli:
13 | @echo "Building CLI tools..."
14 | @mkdir -p bin
15 | @cd cli/unified && go build -o ../../bin/kled-unified
16 | @cp bin/kled-unified bin/kled
17 | @cp bin/kled-unified bin/kcluster
18 | @cp bin/kled-unified bin/kledspace
19 | @cp bin/kled-unified bin/kpolicy
20 | @echo "CLI tools build complete!"
21 |
22 | # Build desktop app
23 | desktop:
24 | @echo "Building desktop app..."
25 | @cd desktop && yarn install && yarn build
26 | @cd desktop/src-tauri && cargo build --release
27 | @echo "Desktop app build complete!"
28 |
29 | # Build mobile apps
30 | mobile:
31 | @echo "Building mobile apps..."
32 | @cd mobile && yarn install || true
33 | @echo "Mobile apps build setup complete!"
34 |
35 | # Build web app
36 | web:
37 | @echo "Building web app..."
38 | @cd web && yarn install && yarn build || true
39 | @echo "Web app build complete!"
40 |
41 | # Cross-compile CLI for all platforms
42 | cross-compile:
43 | @echo "Cross-compiling for all platforms..."
44 | @mkdir -p bin
45 | # Linux AMD64
46 | @cd cli/unified && GOOS=linux GOARCH=amd64 go build -o ../../bin/kled-linux-amd64
47 | # Linux ARM64
48 | @cd cli/unified && GOOS=linux GOARCH=arm64 go build -o ../../bin/kled-linux-arm64
49 | # macOS AMD64
50 | @cd cli/unified && GOOS=darwin GOARCH=amd64 go build -o ../../bin/kled-darwin-amd64
51 | # macOS ARM64
52 | @cd cli/unified && GOOS=darwin GOARCH=arm64 go build -o ../../bin/kled-darwin-arm64
53 | # Windows AMD64
54 | @cd cli/unified && GOOS=windows GOARCH=amd64 go build -o ../../bin/kled-windows-amd64.exe
55 |
56 | # Create distribution packages with unified binary
57 | @mkdir -p dist/linux-amd64 dist/linux-arm64 dist/darwin-amd64 dist/darwin-arm64 dist/windows-amd64
58 |
59 | # Linux AMD64
60 | @cp bin/kled-linux-amd64 dist/linux-amd64/kled
61 | @chmod +x dist/linux-amd64/kled
62 |
63 | # Linux ARM64
64 | @cp bin/kled-linux-arm64 dist/linux-arm64/kled
65 | @chmod +x dist/linux-arm64/kled
66 |
67 | # macOS AMD64
68 | @cp bin/kled-darwin-amd64 dist/darwin-amd64/kled
69 | @chmod +x dist/darwin-amd64/kled
70 |
71 | # macOS ARM64
72 | @cp bin/kled-darwin-arm64 dist/darwin-arm64/kled
73 | @chmod +x dist/darwin-arm64/kled
74 |
75 | # Windows AMD64
76 | @cp bin/kled-windows-amd64.exe dist/windows-amd64/kled.exe
77 |
78 | @echo "Cross-compilation complete!"
79 |
80 | # Install CLI tools locally
81 | install:
82 | @echo "Installing CLI tools..."
83 | @mkdir -p $(HOME)/.kled/bin
84 | @cp bin/kled-unified $(HOME)/.kled/bin/ || cp bin/kled-linux-amd64 $(HOME)/.kled/bin/kled-unified
85 | @ln -sf $(HOME)/.kled/bin/kled-unified $(HOME)/.kled/bin/kled
86 | @ln -sf $(HOME)/.kled/bin/kled-unified $(HOME)/.kled/bin/kcluster
87 | @ln -sf $(HOME)/.kled/bin/kled-unified $(HOME)/.kled/bin/kledspace
88 | @ln -sf $(HOME)/.kled/bin/kled-unified $(HOME)/.kled/bin/kpolicy
89 | @echo "export PATH=\$$PATH:\$$HOME/.kled/bin" >> $(HOME)/.bashrc
90 | @echo "Installation complete! Please restart your shell or run 'source ~/.bashrc'"
91 |
92 | # Prepare downloads directory for GitHub Actions
93 | prepare-downloads:
94 | @echo "Preparing downloads directory..."
95 | @mkdir -p web/download/files/windows/x64
96 | @mkdir -p web/download/files/windows/x86
97 | @mkdir -p web/download/files/macos/arm64
98 | @mkdir -p web/download/files/macos/x64
99 | @mkdir -p web/download/files/linux/x64
100 | @mkdir -p web/download/files/linux/arm64
101 |
102 | # Copy CLI artifacts
103 | @cp artifacts/kled-cli-linux-amd64/* web/download/files/linux/x64/ || true
104 | @cp artifacts/kled-cli-linux-arm64/* web/download/files/linux/arm64/ || true
105 | @cp artifacts/kled-cli-macos-amd64/* web/download/files/macos/x64/ || true
106 | @cp artifacts/kled-cli-macos-arm64/* web/download/files/macos/arm64/ || true
107 | @cp artifacts/kled-cli-windows/* web/download/files/windows/x64/ || true
108 |
109 | # Copy desktop artifacts
110 | @cp artifacts/kled-desktop-linux/* web/download/files/linux/x64/ || true
111 | @cp artifacts/kled-desktop-macos/* web/download/files/macos/x64/ || true
112 | @cp artifacts/kled-desktop-windows/* web/download/files/windows/x64/ || true
113 |
114 | @echo "Download directory prepared!"
115 |
116 | # Clean build artifacts
117 | clean:
118 | @echo "Cleaning build artifacts..."
119 | @rm -rf bin/*
120 | @cd desktop && yarn clean || true
121 | @cd web && yarn clean || true
122 | @cd mobile && yarn clean || true
123 | @echo "Clean complete!"
124 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kled.io
2 |
3 | Kled.io is an open-source platform for managing Kubernetes workspaces, clusters, and policies. It provides a unified interface for developers and DevOps teams to manage their infrastructure.
4 |
5 | ## Components
6 |
7 | - **Desktop App**: Cross-platform desktop application for managing Kubernetes workspaces
8 | - **Web App**: Browser-based interface with the same functionality as the desktop app
9 | - **Mobile Apps**: iOS and Android applications for on-the-go management
10 | - **CLI Tools**:
11 | - `kled`: Main CLI tool for workspace management
12 | - `kcluster`: CLI tool for cluster management
13 | - `kledspace`: CLI tool for workspace management
14 | - `kpolicy`: CLI tool for policy management
15 |
16 | ## Getting Started
17 |
18 | ### Desktop App
19 |
20 | ```bash
21 | cd desktop
22 | yarn install
23 | yarn build
24 | cd src-tauri
25 | cargo build --release
26 | ```
27 |
28 | ### CLI Tools
29 |
30 | ```bash
31 | cd cli/kled
32 | go build
33 | ```
34 |
35 | ## Documentation
36 |
37 | For detailed documentation, visit [https://docs.kled.io](https://docs.kled.io)
38 |
39 | ## License
40 |
41 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
42 |
--------------------------------------------------------------------------------
/cli/README.md:
--------------------------------------------------------------------------------
1 |
2 | This directory contains Test-Driven Development (TDD) versions of the Kled.io CLI tools.
3 | The actual implementation is maintained in private repositories.
4 |
5 |
6 | - `kled`: Main CLI tool for workspace management
7 | - `kcluster`: CLI tool for Kubernetes cluster management
8 | - `kledspace`: CLI tool for workspace environment management
9 | - `kpolicy`: CLI tool for policy management
10 | - `unified`: Single executable that functions as multiple CLI tools
11 |
12 |
13 | See the installation instructions in the main README.md file.
14 |
--------------------------------------------------------------------------------
/cli/kcluster/README.md:
--------------------------------------------------------------------------------
1 |
2 | This is a placeholder for the kcluster CLI tool. The actual implementation is maintained in private repositories.
3 |
--------------------------------------------------------------------------------
/cli/kcluster/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestCLIFunctionality(t *testing.T) {
8 | // This is a test-driven development placeholder
9 | // The actual implementation is maintained in private repositories
10 | }
11 |
--------------------------------------------------------------------------------
/cli/kled/README.md:
--------------------------------------------------------------------------------
1 |
2 | This is a placeholder for the kled CLI tool. The actual implementation is maintained in private repositories.
3 |
--------------------------------------------------------------------------------
/cli/kled/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestCLIFunctionality(t *testing.T) {
8 | // This is a test-driven development placeholder
9 | // The actual implementation is maintained in private repositories
10 | }
11 |
--------------------------------------------------------------------------------
/cli/kledspace/README.md:
--------------------------------------------------------------------------------
1 |
2 | This is a placeholder for the kledspace CLI tool. The actual implementation is maintained in private repositories.
3 |
--------------------------------------------------------------------------------
/cli/kledspace/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestCLIFunctionality(t *testing.T) {
8 | // This is a test-driven development placeholder
9 | // The actual implementation is maintained in private repositories
10 | }
11 |
--------------------------------------------------------------------------------
/cli/kpolicy/README.md:
--------------------------------------------------------------------------------
1 |
2 | This is a placeholder for the kpolicy CLI tool. The actual implementation is maintained in private repositories.
3 |
--------------------------------------------------------------------------------
/cli/kpolicy/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestCLIFunctionality(t *testing.T) {
8 | // This is a test-driven development placeholder
9 | // The actual implementation is maintained in private repositories
10 | }
11 |
--------------------------------------------------------------------------------
/cli/pkg/api/unified/auth.go:
--------------------------------------------------------------------------------
1 | package unified
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "strings"
7 |
8 | "github.com/spectrumwebco/kled.io/cli/pkg/api"
9 | )
10 |
11 | var CommandSpecificEnvVars = map[string]string{
12 | "kled": "KLED_API_KEY",
13 | "kcluster": "KCLUSTER_API_KEY",
14 | "kledspace": "KLEDSPACE_API_KEY",
15 | "kpolicy": "KPOLICY_API_KEY",
16 | }
17 |
18 | func GetAPIKey(commandName string) string {
19 | if envVar, exists := CommandSpecificEnvVars[commandName]; exists {
20 | if apiKey := os.Getenv(envVar); apiKey != "" {
21 | return apiKey
22 | }
23 | }
24 |
25 | if apiKey := os.Getenv("KLED_API_KEY"); apiKey != "" {
26 | return apiKey
27 | }
28 |
29 | auth, err := api.LoadAuth()
30 | if err == nil && auth.APIKey != "" {
31 | return auth.APIKey
32 | }
33 |
34 | return ""
35 | }
36 |
37 | func GetAPIBaseURL(commandName string) string {
38 | envVarName := strings.ToUpper(commandName) + "_API_BASE_URL"
39 | if apiBaseURL := os.Getenv(envVarName); apiBaseURL != "" {
40 | return apiBaseURL
41 | }
42 |
43 | if apiBaseURL := os.Getenv("KLED_API_BASE_URL"); apiBaseURL != "" {
44 | return apiBaseURL
45 | }
46 |
47 | auth, err := api.LoadAuth()
48 | if err == nil && auth.APIBaseURL != "" {
49 | return auth.APIBaseURL
50 | }
51 |
52 | return ""
53 | }
54 |
55 | func DetectCommandName() string {
56 | execPath := os.Args[0]
57 | execName := filepath.Base(execPath)
58 |
59 | execName = strings.TrimSuffix(execName, filepath.Ext(execName))
60 |
61 | for cmd := range CommandSpecificEnvVars {
62 | if execName == cmd {
63 | return cmd
64 | }
65 | }
66 |
67 | return "kled"
68 | }
69 |
--------------------------------------------------------------------------------
/cli/pkg/api/unified/client.go:
--------------------------------------------------------------------------------
1 | package unified
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "io"
8 | "net/http"
9 | "time"
10 | )
11 |
12 | type Client struct {
13 | apiKey string
14 | apiBaseURL string
15 | httpClient *http.Client
16 | command string
17 | }
18 |
19 | func NewClient(command string) *Client {
20 | return &Client{
21 | apiKey: GetAPIKey(command),
22 | apiBaseURL: GetAPIBaseURL(command),
23 | httpClient: &http.Client{
24 | Timeout: 30 * time.Second,
25 | },
26 | command: command,
27 | }
28 | }
29 |
30 | func (c *Client) Request(method, path string, body interface{}, result interface{}) error {
31 | url := fmt.Sprintf("%s%s", c.apiBaseURL, path)
32 |
33 | var reqBody io.Reader
34 | if body != nil {
35 | jsonData, err := json.Marshal(body)
36 | if err != nil {
37 | return fmt.Errorf("failed to marshal request body: %w", err)
38 | }
39 | reqBody = bytes.NewBuffer(jsonData)
40 | }
41 |
42 | req, err := http.NewRequest(method, url, reqBody)
43 | if err != nil {
44 | return fmt.Errorf("failed to create request: %w", err)
45 | }
46 |
47 | req.Header.Set("Content-Type", "application/json")
48 | req.Header.Set("Accept", "application/json")
49 | if c.apiKey != "" {
50 | req.Header.Set("Authorization", "Bearer "+c.apiKey)
51 | }
52 | req.Header.Set("User-Agent", fmt.Sprintf("%s-cli/0.1.0", c.command))
53 |
54 | resp, err := c.httpClient.Do(req)
55 | if err != nil {
56 | return fmt.Errorf("request failed: %w", err)
57 | }
58 | defer resp.Body.Close()
59 |
60 | respBody, err := io.ReadAll(resp.Body)
61 | if err != nil {
62 | return fmt.Errorf("failed to read response body: %w", err)
63 | }
64 |
65 | if resp.StatusCode >= 400 {
66 | var errResp struct {
67 | Error string `json:"error"`
68 | }
69 | if err := json.Unmarshal(respBody, &errResp); err == nil && errResp.Error != "" {
70 | return fmt.Errorf("API error: %s", errResp.Error)
71 | }
72 | return fmt.Errorf("API error: %s", resp.Status)
73 | }
74 |
75 | if result != nil && len(respBody) > 0 {
76 | if err := json.Unmarshal(respBody, result); err != nil {
77 | return fmt.Errorf("failed to unmarshal response: %w", err)
78 | }
79 | }
80 |
81 | return nil
82 | }
83 |
84 | func (c *Client) Get(path string, result interface{}) error {
85 | return c.Request(http.MethodGet, path, nil, result)
86 | }
87 |
88 | func (c *Client) Post(path string, body interface{}, result interface{}) error {
89 | return c.Request(http.MethodPost, path, body, result)
90 | }
91 |
92 | func (c *Client) Put(path string, body interface{}, result interface{}) error {
93 | return c.Request(http.MethodPut, path, body, result)
94 | }
95 |
96 | func (c *Client) Delete(path string, result interface{}) error {
97 | return c.Request(http.MethodDelete, path, nil, result)
98 | }
99 |
--------------------------------------------------------------------------------
/cli/pkg/api/unified/tests/auth_test.go:
--------------------------------------------------------------------------------
1 | package unified_test
2 |
3 | import (
4 | "os"
5 | "testing"
6 |
7 | "github.com/spectrumwebco/kled.io/cli/pkg/api/unified"
8 | )
9 |
10 | func TestLoadAuth(t *testing.T) {
11 | originalAPIKey := os.Getenv("KLED_API_KEY")
12 | originalAPIBaseURL := os.Getenv("KLED_API_BASE_URL")
13 |
14 | defer func() {
15 | os.Setenv("KLED_API_KEY", originalAPIKey)
16 | os.Setenv("KLED_API_BASE_URL", originalAPIBaseURL)
17 | }()
18 |
19 | os.Setenv("KLED_API_KEY", "test-api-key")
20 | os.Setenv("KLED_API_BASE_URL", "https://api.test.com")
21 |
22 | auth, err := unified.LoadAuth()
23 | if err != nil {
24 | t.Errorf("Expected no error, got %v", err)
25 | }
26 |
27 | if auth.APIKey != "test-api-key" {
28 | t.Errorf("Expected APIKey to be 'test-api-key', got '%s'", auth.APIKey)
29 | }
30 |
31 | if auth.APIBaseURL != "https://api.test.com" {
32 | t.Errorf("Expected APIBaseURL to be 'https://api.test.com', got '%s'", auth.APIBaseURL)
33 | }
34 |
35 | os.Unsetenv("KLED_API_KEY")
36 | os.Unsetenv("KLED_API_BASE_URL")
37 |
38 | auth, err = unified.LoadAuth()
39 | if err == nil {
40 | t.Error("Expected error when no API key is set, got nil")
41 | }
42 | }
43 |
44 | func TestIsValidAuth(t *testing.T) {
45 | validAuth := unified.Auth{
46 | APIKey: "valid-key",
47 | APIBaseURL: "https://api.valid.com",
48 | }
49 |
50 | if !validAuth.IsValid() {
51 | t.Error("Expected valid auth to return true, got false")
52 | }
53 |
54 | invalidAuth1 := unified.Auth{
55 | APIKey: "",
56 | APIBaseURL: "https://api.valid.com",
57 | }
58 |
59 | if invalidAuth1.IsValid() {
60 | t.Error("Expected invalid auth (no API key) to return false, got true")
61 | }
62 |
63 | invalidAuth2 := unified.Auth{
64 | APIKey: "valid-key",
65 | APIBaseURL: "",
66 | }
67 |
68 | if invalidAuth2.IsValid() {
69 | t.Error("Expected invalid auth (no API base URL) to return false, got true")
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/cli/pkg/api/unified/tests/client_test.go:
--------------------------------------------------------------------------------
1 | package unified_test
2 |
3 | import (
4 | "net/http"
5 | "net/http/httptest"
6 | "testing"
7 |
8 | "github.com/spectrumwebco/kled.io/cli/pkg/api/unified"
9 | )
10 |
11 | func TestNewClient(t *testing.T) {
12 | auth := unified.Auth{
13 | APIKey: "test-key",
14 | APIBaseURL: "https://api.test.com",
15 | }
16 |
17 | client, err := unified.NewClient(auth)
18 | if err != nil {
19 | t.Errorf("Expected no error, got %v", err)
20 | }
21 |
22 | if client == nil {
23 | t.Error("Expected client to be non-nil")
24 | }
25 |
26 | invalidAuth := unified.Auth{
27 | APIKey: "",
28 | APIBaseURL: "",
29 | }
30 |
31 | client, err = unified.NewClient(invalidAuth)
32 | if err == nil {
33 | t.Error("Expected error with invalid auth, got nil")
34 | }
35 |
36 | if client != nil {
37 | t.Error("Expected client to be nil with invalid auth")
38 | }
39 | }
40 |
41 | func TestMakeRequest(t *testing.T) {
42 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
43 | if r.Header.Get("X-API-Key") != "test-key" {
44 | t.Errorf("Expected X-API-Key header to be 'test-key', got '%s'", r.Header.Get("X-API-Key"))
45 | }
46 |
47 | w.WriteHeader(http.StatusOK)
48 | w.Write([]byte(`{"status":"success"}`))
49 | }))
50 | defer server.Close()
51 |
52 | auth := unified.Auth{
53 | APIKey: "test-key",
54 | APIBaseURL: server.URL,
55 | }
56 |
57 | client, err := unified.NewClient(auth)
58 | if err != nil {
59 | t.Fatalf("Failed to create client: %v", err)
60 | }
61 |
62 | resp, err := client.MakeRequest("GET", "/test", nil)
63 | if err != nil {
64 | t.Errorf("Expected no error, got %v", err)
65 | }
66 |
67 | if resp.StatusCode != http.StatusOK {
68 | t.Errorf("Expected status code %d, got %d", http.StatusOK, resp.StatusCode)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/cli/pkg/cmd/sonarqube/sonarqube.go:
--------------------------------------------------------------------------------
1 | package sonarqube
2 |
3 | import (
4 | "fmt"
5 | "github.com/spf13/cobra"
6 | )
7 |
8 | func NewSonarqubeCmd() *cobra.Command {
9 | cmd := &cobra.Command{
10 | Use: "sonarqube",
11 | Short: "Manage SonarQube integrations",
12 | Long: "Manage SonarQube integrations for code quality analysis",
13 | Run: func(cmd *cobra.Command, args []string) {
14 | cmd.Help()
15 | },
16 | }
17 |
18 | cmd.AddCommand(newAnalyzeCmd())
19 | cmd.AddCommand(newReportCmd())
20 | cmd.AddCommand(newConfigureCmd())
21 |
22 | return cmd
23 | }
24 |
25 | func newAnalyzeCmd() *cobra.Command {
26 | return &cobra.Command{
27 | Use: "analyze [project]",
28 | Short: "Analyze a project with SonarQube",
29 | Args: cobra.MinimumNArgs(1),
30 | Run: func(cmd *cobra.Command, args []string) {
31 | fmt.Printf("Analyzing project %s with SonarQube...\n", args[0])
32 | },
33 | }
34 | }
35 |
36 | func newReportCmd() *cobra.Command {
37 | return &cobra.Command{
38 | Use: "report [project]",
39 | Short: "Get reports from SonarQube",
40 | Args: cobra.MinimumNArgs(1),
41 | Run: func(cmd *cobra.Command, args []string) {
42 | fmt.Printf("Getting SonarQube reports for project %s...\n", args[0])
43 | },
44 | }
45 | }
46 |
47 | func newConfigureCmd() *cobra.Command {
48 | return &cobra.Command{
49 | Use: "configure",
50 | Short: "Configure SonarQube integration",
51 | Run: func(cmd *cobra.Command, args []string) {
52 | fmt.Println("Configuring SonarQube integration...")
53 | },
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/cli/unified/README.md:
--------------------------------------------------------------------------------
1 |
2 | This is a placeholder for the unified CLI tool. The actual implementation is maintained in private repositories.
3 |
--------------------------------------------------------------------------------
/cli/unified/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/spectrumwebco/kled.io/cli/unified
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
7 | github.com/spf13/cobra v1.9.1 // indirect
8 | github.com/spf13/pflag v1.0.6 // indirect
9 | )
10 |
--------------------------------------------------------------------------------
/cli/unified/go.sum:
--------------------------------------------------------------------------------
1 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
2 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
3 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
4 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
5 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
6 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
7 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
8 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
11 |
--------------------------------------------------------------------------------
/cli/unified/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestCLIFunctionality(t *testing.T) {
8 | // This is a test-driven development placeholder
9 | // The actual implementation is maintained in private repositories
10 | }
11 |
--------------------------------------------------------------------------------
/cli/unified/tests/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "testing"
7 | )
8 |
9 | func TestDetectCommandName(t *testing.T) {
10 | originalArgs := os.Args
11 | defer func() { os.Args = originalArgs }()
12 |
13 | os.Args = []string{"/usr/local/bin/kled", "arg1", "arg2"}
14 | cmdName := DetectCommandName()
15 | if cmdName != "kled" {
16 | t.Errorf("Expected command name 'kled', got '%s'", cmdName)
17 | }
18 |
19 | os.Args = []string{"/usr/local/bin/kcluster", "arg1", "arg2"}
20 | cmdName = DetectCommandName()
21 | if cmdName != "kcluster" {
22 | t.Errorf("Expected command name 'kcluster', got '%s'", cmdName)
23 | }
24 |
25 | os.Args = []string{"/usr/local/bin/kledspace", "arg1", "arg2"}
26 | cmdName = DetectCommandName()
27 | if cmdName != "kledspace" {
28 | t.Errorf("Expected command name 'kledspace', got '%s'", cmdName)
29 | }
30 |
31 | os.Args = []string{"/usr/local/bin/kpolicy", "arg1", "arg2"}
32 | cmdName = DetectCommandName()
33 | if cmdName != "kpolicy" {
34 | t.Errorf("Expected command name 'kpolicy', got '%s'", cmdName)
35 | }
36 |
37 | tempDir, err := os.MkdirTemp("", "kled-test")
38 | if err != nil {
39 | t.Fatalf("Failed to create temp directory: %v", err)
40 | }
41 | defer os.RemoveAll(tempDir)
42 |
43 | binaryPath := filepath.Join(tempDir, "kled-unified")
44 | if err := os.WriteFile(binaryPath, []byte("test binary"), 0755); err != nil {
45 | t.Fatalf("Failed to create test binary: %v", err)
46 | }
47 |
48 | symlinkPaths := []string{
49 | filepath.Join(tempDir, "kled"),
50 | filepath.Join(tempDir, "kcluster"),
51 | filepath.Join(tempDir, "kledspace"),
52 | filepath.Join(tempDir, "kpolicy"),
53 | }
54 |
55 | for _, symlinkPath := range symlinkPaths {
56 | if err := os.Symlink(binaryPath, symlinkPath); err != nil {
57 | t.Fatalf("Failed to create symlink: %v", err)
58 | }
59 | }
60 |
61 | for _, cmdName := range []string{"kled", "kcluster", "kledspace", "kpolicy"} {
62 | symlinkPath := filepath.Join(tempDir, cmdName)
63 | os.Args = []string{symlinkPath, "arg1", "arg2"}
64 |
65 | detectedCmd := DetectCommandName()
66 | if detectedCmd != cmdName {
67 | t.Errorf("Expected command name '%s', got '%s'", cmdName, detectedCmd)
68 | }
69 | }
70 | }
71 |
72 | func TestGetCommandFromArgs(t *testing.T) {
73 | args := []string{"kled", "--flag", "value"}
74 | cmdName := GetCommandFromArgs(args)
75 | if cmdName != "" {
76 | t.Errorf("Expected empty command name, got '%s'", cmdName)
77 | }
78 |
79 | args = []string{"kled-unified", "kcluster", "create", "--name", "test"}
80 | cmdName = GetCommandFromArgs(args)
81 | if cmdName != "kcluster" {
82 | t.Errorf("Expected command name 'kcluster', got '%s'", cmdName)
83 | }
84 |
85 | args = []string{"kled-unified", "--flag", "value", "kledspace", "init"}
86 | cmdName = GetCommandFromArgs(args)
87 | if cmdName != "kledspace" {
88 | t.Errorf("Expected command name 'kledspace', got '%s'", cmdName)
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/desktop/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | browser: true,
5 | es2021: true,
6 | node: true,
7 | jest: true,
8 | },
9 | extends: [
10 | 'eslint:recommended',
11 | 'plugin:react/recommended',
12 | 'plugin:react-hooks/recommended',
13 | 'plugin:@typescript-eslint/recommended',
14 | ],
15 | parser: '@typescript-eslint/parser',
16 | parserOptions: {
17 | ecmaFeatures: {
18 | jsx: true,
19 | },
20 | ecmaVersion: 'latest',
21 | sourceType: 'module',
22 | },
23 | plugins: [
24 | 'react',
25 | 'react-hooks',
26 | '@typescript-eslint',
27 | ],
28 | settings: {
29 | react: {
30 | version: 'detect',
31 | },
32 | },
33 | rules: {
34 | 'react/react-in-jsx-scope': 'off',
35 | '@typescript-eslint/no-unused-vars': ['warn', {
36 | argsIgnorePattern: '^_',
37 | varsIgnorePattern: '^_'
38 | }],
39 | 'no-console': 'warn',
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/desktop/README.md:
--------------------------------------------------------------------------------
1 |
2 | This is a placeholder for the Kled.io desktop application. The actual implementation is maintained in private repositories.
3 |
4 |
5 | - Cross-platform desktop application (Windows, macOS, Linux)
6 | - Built with Tauri v2
7 | - Provides a unified interface for managing Kubernetes workspaces
8 |
9 |
10 | See the installation instructions in the main README.md file.
11 |
--------------------------------------------------------------------------------
/desktop/assets/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spectrumwebco/autobots.ai/0509db4f322d0941126fdbc01a8dff4104d231e0/desktop/assets/icon.icns
--------------------------------------------------------------------------------
/desktop/assets/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spectrumwebco/autobots.ai/0509db4f322d0941126fdbc01a8dff4104d231e0/desktop/assets/icon.ico
--------------------------------------------------------------------------------
/desktop/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spectrumwebco/autobots.ai/0509db4f322d0941126fdbc01a8dff4104d231e0/desktop/assets/icon.png
--------------------------------------------------------------------------------
/desktop/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | testEnvironment: 'jsdom',
4 | setupFilesAfterEnv: ['/src/setupTests.ts'],
5 | moduleNameMapper: {
6 | '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
7 | '^@/(.*)$': '/src/$1'
8 | },
9 | testPathIgnorePatterns: ['/node_modules/', '/dist/'],
10 | collectCoverageFrom: [
11 | 'src/**/*.{ts,tsx}',
12 | '!src/**/*.d.ts',
13 | '!src/main.tsx',
14 | '!src/vite-env.d.ts'
15 | ],
16 | transform: {
17 | '^.+\\.(ts|tsx)$': ['ts-jest', {
18 | tsconfig: 'tsconfig.json'
19 | }]
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/desktop/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kled-desktop",
3 | "version": "1.0.0",
4 | "main": "dist/main.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "test": "jest",
8 | "test:watch": "jest --watch",
9 | "test:coverage": "jest --coverage",
10 | "build": "rsbuild build",
11 | "dev": "rsbuild dev",
12 | "lint": "eslint src --ext .ts,.tsx --max-warnings 0",
13 | "package": "electron-builder build --publish never",
14 | "package:win": "electron-builder build --win --publish never",
15 | "package:mac": "electron-builder build --mac --publish never",
16 | "package:linux": "electron-builder build --linux --publish never"
17 | },
18 | "build": {
19 | "appId": "io.kled.desktop",
20 | "productName": "Kled Desktop",
21 | "files": [
22 | "dist/**/*",
23 | "node_modules/**/*"
24 | ],
25 | "directories": {
26 | "output": "release"
27 | },
28 | "win": {
29 | "target": [
30 | "nsis"
31 | ],
32 | "icon": "assets/icon.ico"
33 | },
34 | "mac": {
35 | "target": [
36 | "dmg"
37 | ],
38 | "icon": "assets/icon.icns"
39 | },
40 | "linux": {
41 | "target": [
42 | "deb",
43 | "AppImage"
44 | ],
45 | "icon": "assets/icon.png"
46 | }
47 | },
48 | "devDependencies": {
49 | "@emotion/react": "^11.14.0",
50 | "@emotion/styled": "^11.14.0",
51 | "@rsbuild/core": "^0.4.5",
52 | "@rsbuild/plugin-react": "^0.4.5",
53 | "@testing-library/dom": "^10.4.0",
54 | "@testing-library/jest-dom": "^6.6.3",
55 | "@testing-library/react": "^16.2.0",
56 | "@types/jest": "^29.5.14",
57 | "@types/react": "^19.0.12",
58 | "@types/react-dom": "^19.0.4",
59 | "@typescript-eslint/eslint-plugin": "^8.29.0",
60 | "@typescript-eslint/parser": "^8.29.0",
61 | "@vitejs/plugin-react": "^4.3.4",
62 | "electron": "^29.1.4",
63 | "electron-builder": "^24.13.3",
64 | "eslint": "^9.23.0",
65 | "eslint-plugin-react": "^7.37.4",
66 | "eslint-plugin-react-hooks": "^5.2.0",
67 | "identity-obj-proxy": "^3.0.0",
68 | "jest": "^29.7.0",
69 | "jest-environment-jsdom": "^29.7.0",
70 | "ts-jest": "^29.1.2",
71 | "typescript": "^5.8.2",
72 | "vite": "^6.2.4"
73 | },
74 | "dependencies": {
75 | "@chakra-ui/react": "^2.8.2",
76 | "@tauri-apps/api": "^1.5.3",
77 | "electron-is-dev": "^2.0.0",
78 | "electron-log": "^5.1.1",
79 | "electron-updater": "^6.2.1",
80 | "framer-motion": "^11.0.8",
81 | "react": "^18.2.0",
82 | "react-dom": "^18.2.0"
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/desktop/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { ChakraProvider, Box, Flex, Heading, Text, Button, VStack, HStack, useToast, Divider } from '@chakra-ui/react';
3 | import { invoke } from '@tauri-apps/api/tauri';
4 | import CLIInterfaceNav from './components/CLIInterfaceNav';
5 | import UnifiedCommandPanel from './components/UnifiedCommandPanel';
6 | import KledProWebAppEmbed from './components/KledProWebAppEmbed';
7 |
8 | function App() {
9 | const [workspaces, setWorkspaces] = useState([]);
10 | const [newWorkspaceName, setNewWorkspaceName] = useState('');
11 | const [currentCLIType, setCurrentCLIType] = useState<'kled' | 'kcluster' | 'kledspace' | 'kpolicy'>('kled');
12 | const [showKledProApp, setShowKledProApp] = useState(false);
13 | const toast = useToast();
14 |
15 | useEffect(() => {
16 | loadWorkspaces();
17 | }, []);
18 |
19 | useEffect(() => {
20 | setShowKledProApp(currentCLIType === 'kcluster');
21 | }, [currentCLIType]);
22 |
23 | const loadWorkspaces = async () => {
24 | try {
25 | const workspaceList = await invoke('get_workspaces');
26 | setWorkspaces(workspaceList);
27 | } catch (error) {
28 | console.error('Failed to load workspaces:', error);
29 | toast({
30 | title: 'Error',
31 | description: 'Failed to load workspaces',
32 | status: 'error',
33 | duration: 5000,
34 | isClosable: true,
35 | });
36 | }
37 | };
38 |
39 | const createWorkspace = async () => {
40 | if (!newWorkspaceName.trim()) {
41 | toast({
42 | title: 'Error',
43 | description: 'Workspace name cannot be empty',
44 | status: 'error',
45 | duration: 5000,
46 | isClosable: true,
47 | });
48 | return;
49 | }
50 |
51 | try {
52 | await invoke('create_workspace', { name: newWorkspaceName });
53 | setNewWorkspaceName('');
54 | loadWorkspaces();
55 | toast({
56 | title: 'Success',
57 | description: `Workspace "${newWorkspaceName}" created`,
58 | status: 'success',
59 | duration: 5000,
60 | isClosable: true,
61 | });
62 | } catch (error) {
63 | console.error('Failed to create workspace:', error);
64 | toast({
65 | title: 'Error',
66 | description: `Failed to create workspace: ${error}`,
67 | status: 'error',
68 | duration: 5000,
69 | isClosable: true,
70 | });
71 | }
72 | };
73 |
74 | const handleCLIInterfaceChange = (cliType: 'kled' | 'kcluster' | 'kledspace' | 'kpolicy') => {
75 | setCurrentCLIType(cliType);
76 | if (cliType === 'kled') {
77 | loadWorkspaces();
78 | }
79 | };
80 |
81 | return (
82 |
83 |
84 | Kled Command Center
85 |
86 | {/* CLI Interface Navigation */}
87 |
88 |
89 | {/* Unified Command Panel - Shows for all CLI types */}
90 |
91 |
92 |
93 |
94 | {/* Conditional rendering based on selected CLI interface */}
95 | {currentCLIType === 'kled' && (
96 |
97 |
98 | Workspaces
99 |
100 | {workspaces.length > 0 ? (
101 | workspaces.map((workspace) => (
102 |
109 | {workspace}
110 |
111 | ))
112 | ) : (
113 | No workspaces found
114 | )}
115 |
116 |
117 |
118 |
119 | Create Workspace
120 |
121 |
122 | setNewWorkspaceName(e.target.value)}
126 | placeholder="Enter workspace name"
127 | style={{
128 | width: '100%',
129 | padding: '8px',
130 | borderRadius: '4px',
131 | border: '1px solid #E2E8F0'
132 | }}
133 | />
134 |
135 |
138 |
139 |
140 |
141 | )}
142 |
143 | {currentCLIType === 'kcluster' && (
144 |
145 | Kubernetes Cluster Management
146 | Interface for managing Kubernetes clusters using kcluster commands
147 |
148 | kCluster Features
149 | • Create and manage Kubernetes clusters
150 | • Connect to existing clusters
151 | • Monitor cluster health and resources
152 | • Manage cluster access and permissions
153 |
154 |
155 | )}
156 |
157 | {/* KledPro Web App Embed - Only shown for kcluster interface */}
158 |
159 |
160 | {currentCLIType === 'kledspace' && (
161 |
162 | Application Space Management
163 | Interface for managing application spaces using kledspace commands
164 |
165 | kledspace Features
166 | • Initialize application spaces with templates
167 | • Deploy applications to Kubernetes clusters
168 | • Manage application lifecycle and scaling
169 | • Configure application networking and storage
170 |
171 |
172 | )}
173 |
174 | {currentCLIType === 'kpolicy' && (
175 |
176 | Kubernetes Policy Management
177 | Interface for managing Kubernetes policies using kpolicy commands
178 |
179 | kpolicy Features
180 | • Define policies using TypeScript
181 | • Validate policies against clusters
182 | • Apply policies to enforce security standards
183 | • Monitor policy compliance across clusters
184 |
185 |
186 | )}
187 |
188 |
189 | );
190 | }
191 |
192 | export default App;
193 |
--------------------------------------------------------------------------------
/desktop/src/client/command.ts:
--------------------------------------------------------------------------------
1 | interface EventEmitter {
2 | addListener(event: T, callback: (data: string) => void): void;
3 | removeListener(event: T, callback: (data: string) => void): void;
4 | }
5 |
6 | interface Child {
7 | kill(): Promise;
8 | pid?: number;
9 | }
10 |
11 | interface ChildProcess {
12 | stdout: EventEmitter<"data">;
13 | stderr: EventEmitter<"data">;
14 | on(event: string, callback: (arg: any) => void): void;
15 | code?: number;
16 | }
17 |
18 | interface ShellCommand {
19 | stdout: EventEmitter<"data">;
20 | stderr: EventEmitter<"data">;
21 | on(event: string, callback: (arg: any) => void): void;
22 | spawn(): Promise;
23 | execute(): Promise>;
24 | sidecar(binary: string, args: string[], options?: any): ShellCommand;
25 | create(command: string, args: string[], options?: any): ShellCommand;
26 | }
27 |
28 | const ShellCommand = {
29 | sidecar: (binary: string, args: string[], options?: any): ShellCommand => ({} as any),
30 | create: (command: string, args: string[], options?: any): ShellCommand => ({} as any)
31 | }
32 | import { debug, ErrorTypeCancelled, isError, Result, ResultError, Return, sleep } from "../lib"
33 | import {
34 | KLED_BINARY,
35 | KCLUSTER_BINARY,
36 | KLEDSPACE_BINARY,
37 | KPOLICY_BINARY,
38 | FLAG_OPTION,
39 | KLED_UI_ENV_VAR,
40 | API_KEY_ENV_VAR,
41 | API_BASE_URL_ENV_VAR
42 | } from "./constants"
43 | import { TStreamEvent } from "./types"
44 | import { TAURI_SERVER_URL } from "./tauriClient"
45 |
46 | export type TStreamEventListenerFn = (event: TStreamEvent) => void
47 | export type TEventListener = Parameters<
48 | EventEmitter["addListener"]
49 | >[1]
50 | type TStreamOptions = Readonly<{
51 | ignoreStdoutError?: boolean
52 | ignoreStderrError?: boolean
53 | }>
54 | const defaultStreamOptions: TStreamOptions = {
55 | ignoreStdoutError: false,
56 | ignoreStderrError: false,
57 | }
58 |
59 | export type TCommand = {
60 | run(): Promise>
61 | stream(listener: TStreamEventListenerFn): Promise
62 | cancel(): Promise
63 | }
64 |
65 | export class Command implements TCommand> {
66 | private sidecarCommand: ShellCommand
67 | private childProcess?: Child
68 | private args: string[]
69 | private cancelled = false
70 | private cliType: 'kled' | 'kcluster' | 'kledspace' | 'kpolicy'
71 |
72 | public static ADDITIONAL_ENV_VARS: string = ""
73 | public static HTTP_PROXY: string = ""
74 | public static HTTPS_PROXY: string = ""
75 | public static NO_PROXY: string = ""
76 |
77 | constructor(args: string[], cliType: 'kled' | 'kcluster' | 'kledspace' | 'kpolicy' = 'kled') {
78 | debug("commands", `Creating ${cliType} command with args:`, args)
79 | this.cliType = cliType
80 | const extraEnvVars = Command.ADDITIONAL_ENV_VARS.split(",")
81 | .map((envVarStr) => envVarStr.split("="))
82 | .reduce(
83 | (acc, pair) => {
84 | const [key, value] = pair
85 | if (key === undefined || value === undefined) {
86 | return acc
87 | }
88 |
89 | return { ...acc, [key]: value }
90 | },
91 | {} as Record
92 | )
93 |
94 | // set proxy related environment variables
95 | if (Command.HTTP_PROXY) {
96 | extraEnvVars["HTTP_PROXY"] = Command.HTTP_PROXY
97 | }
98 | if (Command.HTTPS_PROXY) {
99 | extraEnvVars["HTTPS_PROXY"] = Command.HTTPS_PROXY
100 | }
101 | if (Command.NO_PROXY) {
102 | extraEnvVars["NO_PROXY"] = Command.NO_PROXY
103 | }
104 |
105 | // allows the CLI to detect if commands have been invoked from the UI
106 | extraEnvVars[KLED_UI_ENV_VAR] = "true"
107 |
108 | try {
109 | const apiKey = typeof process !== 'undefined' && process.env &&
110 | (process.env as unknown as Record)[API_KEY_ENV_VAR]
111 | if (apiKey) {
112 | extraEnvVars[API_KEY_ENV_VAR] = apiKey
113 | }
114 |
115 | const apiBaseUrl = typeof process !== 'undefined' && process.env &&
116 | (process.env as unknown as Record)[API_BASE_URL_ENV_VAR]
117 | if (apiBaseUrl) {
118 | extraEnvVars[API_BASE_URL_ENV_VAR] = apiBaseUrl
119 | }
120 | } catch (e) {
121 | console.warn('Could not access environment variables:', e)
122 | }
123 |
124 | const cliTypeProperty = 'cliType'
125 | extraEnvVars[cliTypeProperty] = cliType
126 |
127 | let binaryPath = KLED_BINARY
128 | if (cliType === 'kcluster') {
129 | binaryPath = KCLUSTER_BINARY
130 | } else if (cliType === 'kledspace') {
131 | binaryPath = KLEDSPACE_BINARY
132 | } else if (cliType === 'kpolicy') {
133 | binaryPath = KPOLICY_BINARY
134 | }
135 |
136 | const isFlatpak = typeof window !== 'undefined' &&
137 | window.hasOwnProperty('__TAURI__') &&
138 | (window as any).__TAURI__?.env?.TAURI_IS_FLATPAK === "true";
139 |
140 | if (isFlatpak) {
141 | this.sidecarCommand = ShellCommand.create("run-path-kled-wrapper", args, {
142 | env: { ...extraEnvVars, ["FLATPAK_ID"]: "sh.spectrumwebco.kled" },
143 | })
144 | } else {
145 | this.sidecarCommand = ShellCommand.sidecar(binaryPath, args, { env: extraEnvVars })
146 | }
147 | this.args = args
148 | }
149 |
150 | public async run(): Promise>> {
151 | try {
152 | const rawResult = await this.sidecarCommand.execute()
153 | debug("commands", `Result for command with args ${this.args}:`, rawResult)
154 |
155 | return Return.Value(rawResult)
156 | } catch (e) {
157 | return Return.Failed(e + "")
158 | }
159 | }
160 |
161 | public async stream(
162 | listener: TStreamEventListenerFn,
163 | streamOptions?: TStreamOptions
164 | ): Promise {
165 | let opts = defaultStreamOptions
166 | if (streamOptions) {
167 | opts = { ...defaultStreamOptions, ...streamOptions }
168 | }
169 |
170 | try {
171 | this.childProcess = await this.sidecarCommand.spawn()
172 | if (this.cancelled) {
173 | await this.childProcess.kill()
174 |
175 | return Return.Failed("Command already cancelled", "", ErrorTypeCancelled)
176 | }
177 |
178 | await new Promise((res, rej) => {
179 | const stdoutListener: TEventListener<"data"> = (message: string) => {
180 | try {
181 | const data = JSON.parse(message)
182 |
183 | // special case: the cli sends us a message where "done" is "true"
184 | // to signal the command is terminated and we should stop listen to it
185 | // This happens for the vscode browser command as it needs to stay open
186 | // for port-forwarding, but we don't care anymore about its output.
187 | if (data?.done === "true") {
188 | res(Return.Ok())
189 | } else {
190 | listener({ type: "data", data })
191 | }
192 | } catch (error) {
193 | if (!opts.ignoreStdoutError) {
194 | console.error("Failed to parse stdout message ", message, error)
195 | }
196 | }
197 | }
198 | const stderrListener: TEventListener<"data"> = (message: string) => {
199 | try {
200 | const error = JSON.parse(message)
201 | listener({ type: "error", error })
202 | } catch (error) {
203 | if (!opts.ignoreStderrError) {
204 | console.error("Failed to parse stderr message ", message, error)
205 | }
206 | }
207 | }
208 |
209 | this.sidecarCommand.stderr.addListener("data", stderrListener)
210 | this.sidecarCommand.stdout.addListener("data", stdoutListener)
211 |
212 | const cleanup = () => {
213 | this.sidecarCommand.stderr.removeListener("data", stderrListener)
214 | this.sidecarCommand.stdout.removeListener("data", stdoutListener)
215 | this.childProcess = undefined
216 | }
217 |
218 | this.sidecarCommand.on("close", ({ code }: { code: number }) => {
219 | cleanup()
220 | if (code !== 0) {
221 | rej(new Error("exit code: " + code))
222 | } else {
223 | res(Return.Ok())
224 | }
225 | })
226 |
227 | this.sidecarCommand.on("error", (arg: Error) => {
228 | cleanup()
229 | rej(arg)
230 | })
231 | })
232 |
233 | return Return.Ok()
234 | } catch (e) {
235 | if (isError(e)) {
236 | if (this.cancelled) {
237 | return Return.Failed(e.message, "", ErrorTypeCancelled)
238 | }
239 |
240 | return Return.Failed(e.message)
241 | }
242 | console.error(e)
243 |
244 | return Return.Failed("streaming failed")
245 | }
246 | }
247 |
248 | /**
249 | * Cancel the command.
250 | * Only works if it has been created with the `stream` method.
251 | */
252 | public async cancel(): Promise> {
253 | try {
254 | this.cancelled = true
255 | if (!this.childProcess) {
256 | // nothing to clean up
257 | return Return.Ok()
258 | }
259 | // Try to send signal first before force killing process
260 | await fetch(TAURI_SERVER_URL + "/child-process/signal", {
261 | method: "POST",
262 | headers: {
263 | "content-type": "application/json",
264 | },
265 | body: JSON.stringify({
266 | processId: this.childProcess.pid,
267 | signal: 2, // SIGINT
268 | }),
269 | })
270 |
271 | await sleep(3_000)
272 | // the actual child process could be gone after sending a SIGINT
273 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
274 | if (this.childProcess) {
275 | await this.childProcess.kill()
276 | }
277 |
278 | return Return.Ok()
279 | } catch (e) {
280 | if (isError(e)) {
281 | return Return.Failed(e.message)
282 | }
283 |
284 | return Return.Failed("failed to cancel command")
285 | }
286 | }
287 | }
288 |
289 | export function isOk(result: ChildProcess): boolean {
290 | return result.code === 0
291 | }
292 |
293 | export function toFlagArg(flag: string, arg: string) {
294 | return [flag, arg].join("=")
295 | }
296 |
297 | export function serializeRawOptions(
298 | rawOptions: Record,
299 | flag: string = FLAG_OPTION
300 | ): string[] {
301 | return Object.entries(rawOptions).map(([key, value]) => flag + `=${key}=${value}`)
302 | }
303 |
--------------------------------------------------------------------------------
/desktop/src/client/constants.ts:
--------------------------------------------------------------------------------
1 | export const KLED_GIT_REPOSITORY = "https://github.com/spectrumwebco/kled.io"
2 |
3 | export const DEFAULT_STATIC_COMMAND_CONFIG = {
4 | streamResponse: false,
5 | debug: false,
6 | } as const
7 | /** placeholder for arbitrary additional flags */
8 | export const WORKSPACE_COMMAND_ADDITIONAL_FLAGS_KEY = "additionalFlags"
9 |
10 | export const KLED_BINARY = "bin/kled"
11 | export const KCLUSTER_BINARY = "bin/kcluster"
12 | export const KLEDSPACE_BINARY = "bin/kledspace"
13 | export const KPOLICY_BINARY = "bin/kpolicy"
14 |
15 | export const COMMAND_LIST = "list"
16 | export const COMMAND_STATUS = "status"
17 | export const COMMAND_UP = "up"
18 | export const COMMAND_STOP = "stop"
19 | export const COMMAND_BUILD = "build"
20 | export const COMMAND_DELETE = "delete"
21 | export const COMMAND_OPTIONS = "options"
22 | export const COMMAND_SET_OPTIONS = "set-options"
23 | export const COMMAND_USE = "use"
24 | export const COMMAND_ADD = "add"
25 | export const COMMAND_UPDATE = "update"
26 | export const COMMAND_LOGIN = "login"
27 |
28 | export const KLED_COMMAND_PROVIDER = "provider"
29 | export const KLED_COMMAND_IDE = "ide"
30 | export const KLED_COMMAND_PRO = "pro"
31 | export const KLED_COMMAND_HELPER = "helper"
32 | export const KLED_COMMAND_CONTEXT = "context"
33 | export const KLED_COMMAND_IMPORT_WORKSPACE = "import-workspace"
34 | export const KLED_COMMAND_GET_WORKSPACE_NAME = "get-workspace-name"
35 | export const KLED_COMMAND_GET_WORKSPACE_UID = "get-workspace-uid"
36 | export const KLED_COMMAND_GET_WORKSPACE_CONFIG = "get-workspace-config"
37 | export const KLED_COMMAND_GET_PROVIDER_NAME = "get-provider-name"
38 | export const KLED_COMMAND_GET_PRO_NAME = "get-pro-name"
39 | export const KLED_COMMAND_CHECK_PROVIDER_UPDATE = "check-provider-update"
40 | export const KLED_COMMAND_TROUBLESHOOT = "troubleshoot"
41 |
42 | export const KCLUSTER_COMMAND_CREATE = "create"
43 | export const KCLUSTER_COMMAND_CONNECT = "connect"
44 | export const KCLUSTER_COMMAND_DISCONNECT = "disconnect"
45 |
46 | export const KLEDSPACE_COMMAND_INIT = "init"
47 | export const KLEDSPACE_COMMAND_DEPLOY = "deploy"
48 | export const KLEDSPACE_COMMAND_PURGE = "purge"
49 |
50 | export const KPOLICY_COMMAND_VALIDATE = "validate"
51 | export const KPOLICY_COMMAND_APPLY = "apply"
52 | export const KPOLICY_COMMAND_REMOVE = "remove"
53 | export const FLAG_JSON_LOG_OUTPUT = "--log-output=json"
54 | export const FLAG_JSON_OUTPUT = "--output=json"
55 | export const FLAG_OPTION = "--option"
56 | export const FLAG_FORCE = "--force"
57 | export const FLAG_DEBUG = "--debug"
58 | export const FLAG_NAME = "--name"
59 | export const FLAG_ID = "--id"
60 | export const FLAG_SOURCE = "--source"
61 | export const FLAG_DRY = "--dry"
62 | export const FLAG_TIMEOUT = "--timeout"
63 | export const FLAG_LOGIN = "--login"
64 | export const FLAG_HOST = "--host"
65 | export const FLAG_PROJECT = "--project"
66 |
67 | export const KLED_FLAG_FORCE_BUILD = "--force-build"
68 | export const KLED_FLAG_RECREATE = "--recreate"
69 | export const KLED_FLAG_RESET = "--reset"
70 | export const KLED_FLAG_IDE = "--ide"
71 | export const KLED_FLAG_PROVIDER = "--provider"
72 | export const KLED_FLAG_PROVIDER_OPTION = "--provider-option"
73 | export const KLED_FLAG_ACCESS_KEY = "--access-key"
74 | export const KLED_FLAG_PREBUILD_REPOSITORY = "--prebuild-repository"
75 | export const KLED_FLAG_USE = "--use"
76 | export const KLED_FLAG_SINGLE_MACHINE = "--single-machine"
77 | export const KLED_FLAG_RECONFIGURE = "--reconfigure"
78 | export const KLED_FLAG_SKIP_REQUIRED = "--skip-required"
79 | export const KLED_FLAG_DEVCONTAINER_PATH = "--devcontainer-path"
80 | export const KLED_FLAG_WORKSPACE_ID = "--workspace-id"
81 | export const KLED_FLAG_WORKSPACE_UID = "--workspace-uid"
82 | export const KLED_FLAG_WORKSPACE_PROJECT = "--workspace-project"
83 | export const KLED_FLAG_INSTANCE = "--instance"
84 | export const KLED_FLAG_SKIP_PRO = "--skip-pro"
85 | export const KLED_FLAG_DOTFILES = "--dotfiles"
86 | export const KLED_FLAG_GIT_SIGNING_KEY = "--git-ssh-signing-key"
87 | export const KLED_FLAG_FORCE_BROWSER = "--force-browser"
88 |
89 | export const KCLUSTER_FLAG_NAMESPACE = "--namespace"
90 | export const KCLUSTER_FLAG_CONTEXT = "--context"
91 | export const KCLUSTER_FLAG_KUBECONFIG = "--kubeconfig"
92 |
93 | export const KLEDSPACE_FLAG_ENV = "--env"
94 | export const KLEDSPACE_FLAG_VALUES = "--values"
95 | export const KLEDSPACE_FLAG_SET = "--set"
96 |
97 | export const KPOLICY_FLAG_POLICY = "--policy"
98 | export const KPOLICY_FLAG_NAMESPACE = "--namespace"
99 | export const KPOLICY_FLAG_ALL_NAMESPACES = "--all-namespaces"
100 |
101 | export const KLED_UI_ENV_VAR = "KLED_UI"
102 | export const API_KEY_ENV_VAR = "KLED_API_KEY"
103 | export const API_BASE_URL_ENV_VAR = "KLED_API_BASE_URL"
104 |
--------------------------------------------------------------------------------
/desktop/src/components/CLIInterfaceNav.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tabs, TabList, Tab, TabPanels, TabPanel, Box, Heading } from '@chakra-ui/react';
3 | import { Command } from '../client/command';
4 |
5 | interface CLIInterfaceNavProps {
6 | onInterfaceChange?: (cliType: 'kled' | 'kcluster' | 'kledspace' | 'kpolicy') => void;
7 | }
8 |
9 | /**
10 | * Navigation component for switching between different CLI interfaces
11 | * Supports all 4 CLI interfaces: kled, kcluster, kledspace, kpolicy
12 | */
13 | export const CLIInterfaceNav: React.FC = ({ onInterfaceChange }) => {
14 | const handleTabChange = (index: number) => {
15 | const cliTypes: Array<'kled' | 'kcluster' | 'kledspace' | 'kpolicy'> = [
16 | 'kled', 'kcluster', 'kledspace', 'kpolicy'
17 | ];
18 |
19 | if (onInterfaceChange && index >= 0 && index < cliTypes.length) {
20 | onInterfaceChange(cliTypes[index]);
21 | }
22 | };
23 |
24 | return (
25 |
26 | CLI Interfaces
27 |
28 |
29 | Kled
30 | Cluster
31 | Space
32 | Policy
33 |
34 |
35 |
36 |
37 | Kled Workspace Management
38 | Create and manage development workspaces
39 |
40 |
41 |
42 |
43 | Kubernetes Cluster Management
44 | Create, connect, and manage Kubernetes clusters
45 |
46 |
47 |
48 |
49 | Kled Space Management
50 | Initialize, deploy, and manage application spaces
51 |
52 |
53 |
54 |
55 | Kubernetes Policy Management
56 | Validate, apply, and manage Kubernetes policies
57 |
58 |
59 |
60 |
61 |
62 | );
63 | };
64 |
65 | export default CLIInterfaceNav;
66 |
--------------------------------------------------------------------------------
/desktop/src/components/KledProWebAppEmbed.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Box, Heading, Text } from '@chakra-ui/react';
3 | import { App as KledProApp } from '../../../../kled-pro/frontend/src/App';
4 |
5 | interface KledProWebAppEmbedProps {
6 | isVisible: boolean;
7 | }
8 |
9 | /**
10 | * Component that embeds the kled-pro web app into the desktop application
11 | * This allows sharing UI components between web and desktop platforms
12 | */
13 | export const KledProWebAppEmbed: React.FC = ({ isVisible }) => {
14 | if (!isVisible) return null;
15 |
16 | return (
17 |
18 | kCluster Management
19 | Integrated kCluster management interface from kled-pro web app
20 |
21 | {/* Embed the kled-pro App component */}
22 |
23 |
24 |
25 |
26 | );
27 | };
28 |
29 | export default KledProWebAppEmbed;
30 |
--------------------------------------------------------------------------------
/desktop/src/components/UnifiedCommandPanel.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Box, Heading, Tabs, TabList, Tab, TabPanels, TabPanel } from '@chakra-ui/react';
3 | import {
4 | SharedCommandInterface,
5 | getKledCommands,
6 | getKclusterCommands,
7 | getKledspaceCommands,
8 | getKpolicyCommands
9 | } from './shared/SharedCommandInterface';
10 | import { invoke } from '@tauri-apps/api/tauri';
11 |
12 | interface UnifiedCommandPanelProps {
13 | currentCLIType: 'kled' | 'kcluster' | 'kledspace' | 'kpolicy';
14 | }
15 |
16 | /**
17 | * Unified command panel component that displays commands for all CLI interfaces
18 | * This component uses the shared command interface to provide a consistent UI
19 | * across desktop and web applications
20 | */
21 | export const UnifiedCommandPanel: React.FC = ({
22 | currentCLIType
23 | }) => {
24 | const [commandOutput, setCommandOutput] = useState('');
25 | const [isExecuting, setIsExecuting] = useState(false);
26 |
27 | const executeCommand = async (commandName: string) => {
28 | setIsExecuting(true);
29 | setCommandOutput(`Executing: ${commandName}...`);
30 |
31 | try {
32 | const result = await invoke('execute_cli_command', { command: commandName });
33 | setCommandOutput(result as string);
34 | } catch (error) {
35 | setCommandOutput(`Error executing command: ${error}`);
36 | } finally {
37 | setIsExecuting(false);
38 | }
39 | };
40 |
41 | const getCommandsForType = () => {
42 | switch (currentCLIType) {
43 | case 'kled':
44 | return getKledCommands();
45 | case 'kcluster':
46 | return getKclusterCommands();
47 | case 'kledspace':
48 | return getKledspaceCommands();
49 | case 'kpolicy':
50 | return getKpolicyCommands();
51 | default:
52 | return getKledCommands();
53 | }
54 | };
55 |
56 | const getTitleForType = () => {
57 | switch (currentCLIType) {
58 | case 'kled':
59 | return 'Kled Workspace Commands';
60 | case 'kcluster':
61 | return 'Kubernetes Cluster Commands';
62 | case 'kledspace':
63 | return 'Application Space Commands';
64 | case 'kpolicy':
65 | return 'Kubernetes Policy Commands';
66 | default:
67 | return 'CLI Commands';
68 | }
69 | };
70 |
71 | return (
72 |
73 |
78 |
79 | {/* Command output display */}
80 |
81 | Command Output
82 |
92 | {commandOutput || 'No command executed yet. Select a command above to execute it.'}
93 |
94 |
95 |
96 | );
97 | };
98 |
99 | export default UnifiedCommandPanel;
100 |
--------------------------------------------------------------------------------
/desktop/src/components/__tests__/CLIInterfaceNav.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen, fireEvent } from '@testing-library/react';
3 | import { CLIInterfaceNav } from '../CLIInterfaceNav';
4 |
5 | describe('CLIInterfaceNav Component', () => {
6 | test('renders all CLI interface tabs', () => {
7 | render();
8 |
9 | expect(screen.getByText('Kled')).toBeInTheDocument();
10 | expect(screen.getByText('Cluster')).toBeInTheDocument();
11 | expect(screen.getByText('Space')).toBeInTheDocument();
12 | expect(screen.getByText('Policy')).toBeInTheDocument();
13 | });
14 |
15 | test('calls onInterfaceChange when tab is clicked', () => {
16 | const mockOnInterfaceChange = jest.fn();
17 | render();
18 |
19 | fireEvent.click(screen.getByText('Cluster'));
20 |
21 | expect(mockOnInterfaceChange).toHaveBeenCalledWith('kcluster');
22 |
23 | fireEvent.click(screen.getByText('Space'));
24 |
25 | expect(mockOnInterfaceChange).toHaveBeenCalledWith('kledspace');
26 |
27 | fireEvent.click(screen.getByText('Policy'));
28 |
29 | expect(mockOnInterfaceChange).toHaveBeenCalledWith('kpolicy');
30 |
31 | fireEvent.click(screen.getByText('Kled'));
32 |
33 | expect(mockOnInterfaceChange).toHaveBeenCalledWith('kled');
34 | });
35 |
36 | test('displays correct descriptions for each tab', () => {
37 | render();
38 |
39 | fireEvent.click(screen.getByText('Kled'));
40 | expect(screen.getByText('Create and manage development workspaces')).toBeInTheDocument();
41 |
42 | fireEvent.click(screen.getByText('Cluster'));
43 | expect(screen.getByText('Create, connect, and manage Kubernetes clusters')).toBeInTheDocument();
44 |
45 | fireEvent.click(screen.getByText('Space'));
46 | expect(screen.getByText('Initialize, deploy, and manage application spaces')).toBeInTheDocument();
47 |
48 | fireEvent.click(screen.getByText('Policy'));
49 | expect(screen.getByText('Validate, apply, and manage Kubernetes policies')).toBeInTheDocument();
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/desktop/src/components/__tests__/KledProWebAppEmbed.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 |
4 | jest.mock('../KledProWebAppEmbed', () => ({
5 | KledProWebAppEmbed: ({ isVisible }: { isVisible: boolean }) =>
6 | isVisible ? (
7 |
8 |
kCluster Management
9 |
Integrated kCluster management interface from kled-pro web app
10 |
11 |
Mocked KledProApp
12 |
13 |
14 | ) : null
15 | }));
16 |
17 | import { KledProWebAppEmbed } from '../KledProWebAppEmbed';
18 |
19 | describe('KledProWebAppEmbed Component', () => {
20 | test('renders nothing when isVisible is false', () => {
21 | const { container } = render();
22 | expect(container.firstChild).toBeNull();
23 | });
24 |
25 | test('renders component when isVisible is true', () => {
26 | render();
27 |
28 | expect(screen.getByText('kCluster Management')).toBeInTheDocument();
29 |
30 | expect(screen.getByText('Integrated kCluster management interface from kled-pro web app')).toBeInTheDocument();
31 |
32 | expect(screen.getByTestId('kled-pro-app')).toBeInTheDocument();
33 | });
34 |
35 | test('has correct structure and CSS classes', () => {
36 | render();
37 |
38 | const mainContainer = screen.getByText('kCluster Management').closest('.kled-pro-embed');
39 | expect(mainContainer).toBeInTheDocument();
40 |
41 | const appContainer = screen.getByTestId('kled-pro-app').closest('.kled-pro-app-container');
42 | expect(appContainer).toBeInTheDocument();
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/desktop/src/components/shared/SharedCommandInterface.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Box, Heading, Text, Flex, Icon, Button } from '@chakra-ui/react';
3 | import { FaTerminal, FaCubes, FaLayerGroup, FaShieldAlt } from 'react-icons/fa';
4 |
5 | interface CommandProps {
6 | name: string;
7 | description: string;
8 | icon: React.ElementType;
9 | onClick?: () => void;
10 | }
11 |
12 | interface SharedCommandInterfaceProps {
13 | title: string;
14 | commands: CommandProps[];
15 | onCommandSelect?: (commandName: string) => void;
16 | }
17 |
18 | /**
19 | * Shared component for displaying command interfaces across desktop and web apps
20 | * This component can be used in both the desktop app and web app to provide
21 | * a consistent interface for interacting with CLI commands
22 | */
23 | export const SharedCommandInterface: React.FC = ({
24 | title,
25 | commands,
26 | onCommandSelect
27 | }) => {
28 | const handleCommandClick = (commandName: string) => {
29 | if (onCommandSelect) {
30 | onCommandSelect(commandName);
31 | }
32 | };
33 |
34 | return (
35 |
36 | {title}
37 |
38 |
39 | {commands.map((command) => (
40 | {
47 | handleCommandClick(command.name);
48 | if (command.onClick) command.onClick();
49 | }}
50 | >
51 |
52 |
53 |
54 | {command.name}
55 | {command.description}
56 |
57 |
58 |
59 | ))}
60 |
61 |
62 | );
63 | };
64 |
65 | /**
66 | * Predefined command sets for each CLI interface
67 | * These can be imported and used in both desktop and web apps
68 | */
69 | export const getKledCommands = (): CommandProps[] => [
70 | {
71 | name: 'kled workspace create',
72 | description: 'Create a new development workspace',
73 | icon: FaTerminal
74 | },
75 | {
76 | name: 'kled workspace list',
77 | description: 'List all available workspaces',
78 | icon: FaTerminal
79 | },
80 | {
81 | name: 'kled workspace delete',
82 | description: 'Delete an existing workspace',
83 | icon: FaTerminal
84 | }
85 | ];
86 |
87 | export const getKclusterCommands = (): CommandProps[] => [
88 | {
89 | name: 'kcluster create',
90 | description: 'Create a new Kubernetes cluster',
91 | icon: FaCubes
92 | },
93 | {
94 | name: 'kcluster connect',
95 | description: 'Connect to an existing cluster',
96 | icon: FaCubes
97 | },
98 | {
99 | name: 'kcluster list',
100 | description: 'List all available clusters',
101 | icon: FaCubes
102 | }
103 | ];
104 |
105 | export const getKledspaceCommands = (): CommandProps[] => [
106 | {
107 | name: 'kledspace init',
108 | description: 'Initialize a new application space',
109 | icon: FaLayerGroup
110 | },
111 | {
112 | name: 'kledspace deploy',
113 | description: 'Deploy an application to a space',
114 | icon: FaLayerGroup
115 | },
116 | {
117 | name: 'kledspace list',
118 | description: 'List all available spaces',
119 | icon: FaLayerGroup
120 | }
121 | ];
122 |
123 | export const getKpolicyCommands = (): CommandProps[] => [
124 | {
125 | name: 'kpolicy validate',
126 | description: 'Validate policies against a cluster',
127 | icon: FaShieldAlt
128 | },
129 | {
130 | name: 'kpolicy apply',
131 | description: 'Apply policies to a cluster',
132 | icon: FaShieldAlt
133 | },
134 | {
135 | name: 'kpolicy list',
136 | description: 'List all available policies',
137 | icon: FaShieldAlt
138 | }
139 | ];
140 |
141 | export default SharedCommandInterface;
142 |
--------------------------------------------------------------------------------
/desktop/src/main.js:
--------------------------------------------------------------------------------
1 | const { app, BrowserWindow } = require('electron');
2 | const path = require('path');
3 | const isDev = require('electron-is-dev');
4 | const log = require('electron-log');
5 | const { autoUpdater } = require('electron-updater');
6 |
7 | log.transports.file.level = 'info';
8 | autoUpdater.logger = log;
9 | autoUpdater.logger.transports.file.level = 'info';
10 |
11 | let mainWindow;
12 |
13 | function createWindow() {
14 | mainWindow = new BrowserWindow({
15 | width: 1200,
16 | height: 800,
17 | webPreferences: {
18 | nodeIntegration: true,
19 | contextIsolation: false,
20 | preload: path.join(__dirname, 'preload.js')
21 | },
22 | icon: path.join(__dirname, '../assets/icon.png')
23 | });
24 |
25 | const startUrl = isDev
26 | ? 'http://localhost:8080' // Dev server URL
27 | : `file://${path.join(__dirname, '../dist/index.html')}`; // Production build
28 |
29 | mainWindow.loadURL(startUrl);
30 |
31 | if (isDev) {
32 | mainWindow.webContents.openDevTools();
33 | }
34 |
35 | mainWindow.on('closed', () => {
36 | mainWindow = null;
37 | });
38 | }
39 |
40 | app.whenReady().then(() => {
41 | createWindow();
42 |
43 | app.on('activate', () => {
44 | if (BrowserWindow.getAllWindows().length === 0) {
45 | createWindow();
46 | }
47 | });
48 | });
49 |
50 | app.on('window-all-closed', () => {
51 | if (process.platform !== 'darwin') {
52 | app.quit();
53 | }
54 | });
55 |
56 | app.on('ready', () => {
57 | if (!isDev) {
58 | autoUpdater.checkForUpdatesAndNotify();
59 | }
60 | });
61 |
62 | autoUpdater.on('checking-for-update', () => {
63 | log.info('Checking for update...');
64 | });
65 |
66 | autoUpdater.on('update-available', (info) => {
67 | log.info('Update available:', info);
68 | });
69 |
70 | autoUpdater.on('update-not-available', (info) => {
71 | log.info('Update not available:', info);
72 | });
73 |
74 | autoUpdater.on('error', (err) => {
75 | log.error('Error in auto-updater:', err);
76 | });
77 |
78 | autoUpdater.on('download-progress', (progressObj) => {
79 | let logMessage = `Download speed: ${progressObj.bytesPerSecond}`;
80 | logMessage = `${logMessage} - Downloaded ${progressObj.percent}%`;
81 | logMessage = `${logMessage} (${progressObj.transferred}/${progressObj.total})`;
82 | log.info(logMessage);
83 | });
84 |
85 | autoUpdater.on('update-downloaded', (info) => {
86 | log.info('Update downloaded:', info);
87 | });
88 |
--------------------------------------------------------------------------------
/desktop/src/preload.js:
--------------------------------------------------------------------------------
1 | window.addEventListener('DOMContentLoaded', () => {
2 | const replaceText = (selector, text) => {
3 | const element = document.getElementById(selector);
4 | if (element) element.innerText = text;
5 | };
6 |
7 | for (const dependency of ['chrome', 'node', 'electron']) {
8 | replaceText(`${dependency}-version`, process.versions[dependency]);
9 | }
10 | });
11 |
--------------------------------------------------------------------------------
/desktop/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom';
2 |
--------------------------------------------------------------------------------
/desktop/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 | "esModuleInterop": true,
9 |
10 | /* Bundler mode */
11 | "moduleResolution": "bundler",
12 | "allowImportingTsExtensions": true,
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "noEmit": true,
16 | "jsx": "react-jsx",
17 |
18 | /* Linting */
19 | "strict": true,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "noFallthroughCasesInSwitch": true,
23 | "baseUrl": ".",
24 | "paths": {
25 | "@/*": ["src/*"]
26 | }
27 | },
28 | "include": ["src"],
29 | "references": [{ "path": "./tsconfig.node.json" }]
30 | }
31 |
--------------------------------------------------------------------------------
/desktop/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/desktop/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 | import { resolve } from 'path';
4 |
5 | export default defineConfig({
6 | plugins: [react()],
7 | resolve: {
8 | alias: {
9 | '@': resolve(__dirname, 'src'),
10 | },
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/docs/RELEASE_PROCESS.md:
--------------------------------------------------------------------------------
1 | # Kled.io Release Process
2 |
3 | This document outlines the release process for the Kled.io platform, including CLI tools and desktop applications.
4 |
5 | ## Overview
6 |
7 | The Kled.io release process includes:
8 |
9 | 1. Building CLI tools for multiple platforms (Windows, macOS, Linux)
10 | 2. Building desktop applications for multiple platforms
11 | 3. Testing installers for functionality
12 | 4. Creating GitHub releases with proper artifacts
13 | 5. Verifying installation on target platforms
14 |
15 | ## Release Workflow
16 |
17 | The release process is automated through GitHub Actions workflows in `.github/workflows/build-unified-cli.yml`. This workflow is triggered:
18 |
19 | - Manually through the GitHub Actions UI
20 | - Automatically when a tag with the format `v*` is pushed
21 |
22 | ## Release Artifacts
23 |
24 | Each release includes the following artifacts:
25 |
26 | ### CLI Tools
27 |
28 | - **Linux (AMD64/ARM64)**
29 | - `kled` - Main CLI tool
30 | - `kcluster` - Kubernetes cluster management
31 | - `kledspace` - Workspace management
32 | - `kpolicy` - Policy management
33 |
34 | - **Windows (AMD64)**
35 | - `kled.exe`
36 | - `kcluster.exe`
37 | - `kledspace.exe`
38 | - `kpolicy.exe`
39 |
40 | - **macOS (AMD64/ARM64)**
41 | - `kled`
42 | - `kcluster`
43 | - `kledspace`
44 | - `kpolicy`
45 |
46 | ### Desktop Applications
47 |
48 | - **Linux**: `.deb` package and `.AppImage`
49 | - **Windows**: `.exe` installer
50 | - **macOS**: `.dmg` installer
51 |
52 | ## Testing Installers
53 |
54 | ### Windows Installer Testing
55 |
56 | The Windows installer is tested during the build process using the `scripts/validate-installer.ps1` script, which:
57 |
58 | 1. Verifies the installer exists
59 | 2. Checks file size and creation time
60 | 3. Validates file signature (if signtool is available)
61 | 4. Tests extraction in silent mode
62 | 5. Verifies critical files are present
63 |
64 | ### Manual Testing
65 |
66 | For thorough testing before release:
67 |
68 | 1. Download the installer from the GitHub release page
69 | 2. Run the installer on a clean Windows machine
70 | 3. Verify the application launches correctly
71 | 4. Test basic functionality (CLI commands, UI navigation)
72 | 5. Verify uninstallation works properly
73 |
74 | ## Creating a Release
75 |
76 | To create a new release:
77 |
78 | 1. Update version numbers in relevant files:
79 | - `cli/unified/version.go`
80 | - `desktop/package.json`
81 |
82 | 2. Commit changes and push to the repository
83 |
84 | 3. Create and push a new tag:
85 | ```bash
86 | git tag v1.0.0
87 | git push origin v1.0.0
88 | ```
89 |
90 | 4. Monitor the GitHub Actions workflow to ensure all builds succeed
91 |
92 | 5. Verify the release on GitHub includes all expected artifacts
93 |
94 | ## Troubleshooting
95 |
96 | If the release process fails:
97 |
98 | 1. Check GitHub Actions logs for specific errors
99 | 2. Verify all dependencies are correctly installed in the build environment
100 | 3. Test builds locally before creating a release tag
101 | 4. Ensure all required secrets are configured in the repository settings
102 |
103 | ## Windows Installation Verification
104 |
105 | To verify Windows installation:
106 |
107 | 1. Download the `.exe` installer from the release page
108 | 2. Run the installer on a Windows machine
109 | 3. Check that the application is installed in the expected location (usually `C:\Program Files\Kled Desktop`)
110 | 4. Verify desktop shortcuts are created
111 | 5. Launch the application and ensure it runs correctly
112 | 6. Test CLI commands from Command Prompt or PowerShell
113 |
--------------------------------------------------------------------------------
/docs/architecture/cli-integration-architecture.md:
--------------------------------------------------------------------------------
1 | # CLI Integration Architecture
2 |
3 | ## Overview
4 |
5 | The Kled.io CLI integration architecture describes how the various command line tools (kled, kcluster, kledspace, kpolicy) are implemented, integrated, and distributed across the repositories. This document outlines the unified CLI approach, command implementation locations, and the build and distribution process.
6 |
7 | ## Unified CLI Approach
8 |
9 | ```mermaid
10 | graph TD
11 | subgraph UnifiedCLI["Unified CLI Binary"]
12 | main[main.go]
13 | router[Command Router]
14 | config[Config Management]
15 | auth[API Key Authentication]
16 | end
17 |
18 | subgraph Commands["Command Implementation"]
19 | kled[kled Commands]
20 | kcluster[kcluster Commands]
21 | kledspace[kledspace Commands]
22 | kpolicy[kpolicy Commands]
23 | end
24 |
25 | subgraph Distribution["Distribution Methods"]
26 | symlinks[Symlinks]
27 | batch[Batch Files]
28 | direct[Direct Execution]
29 | end
30 |
31 | main --> router
32 | router --> kled
33 | router --> kcluster
34 | router --> kledspace
35 | router --> kpolicy
36 |
37 | router --> distribution[Distribution Method Detection]
38 | distribution --> symlinks
39 | distribution --> batch
40 | distribution --> direct
41 |
42 | config --> auth
43 | ```
44 |
45 | ### Command Implementation Locations
46 |
47 | ```mermaid
48 | graph TD
49 | subgraph PrivateRepos["Private Repositories"]
50 | kledRepo[kled Repository]
51 | kledProRepo[kled-pro Repository]
52 | end
53 |
54 | subgraph PublicRepo["Public Repository"]
55 | kledioRepo[kled.io Repository]
56 | end
57 |
58 | subgraph CommandImplementation["Command Implementation"]
59 | kledCmd[kled Commands]
60 | kclusterCmd[kcluster Commands]
61 | kledspaceCmd[kledspace Commands]
62 | kpolicyCmd[kpolicy Commands]
63 | end
64 |
65 | kledRepo --> kledCmd
66 | kledRepo --> kledspaceCmd
67 | kledProRepo --> kclusterCmd
68 | kledProRepo --> kpolicyCmd
69 |
70 | kledCmd --> kledioRepo
71 | kclusterCmd --> kledioRepo
72 | kledspaceCmd --> kledioRepo
73 | kpolicyCmd --> kledioRepo
74 | ```
75 |
76 | ## Build and Distribution Process
77 |
78 | ```mermaid
79 | flowchart TD
80 | subgraph BuildProcess["Build Process"]
81 | makefile[Makefile]
82 | crossCompile[Cross Compilation]
83 | unifiedBinary[Unified Binary]
84 | end
85 |
86 | subgraph GitHubActions["GitHub Actions"]
87 | buildCLI[build-cli.yml]
88 | release[Release Creation]
89 | publish[Publish Downloads]
90 | end
91 |
92 | subgraph Distribution["Distribution"]
93 | website[Website Downloads]
94 | installScript[Installation Script]
95 | githubReleases[GitHub Releases]
96 | end
97 |
98 | makefile --> crossCompile
99 | crossCompile --> unifiedBinary
100 |
101 | unifiedBinary --> buildCLI
102 | buildCLI --> release
103 | release --> publish
104 |
105 | publish --> website
106 | publish --> installScript
107 | publish --> githubReleases
108 | ```
109 |
110 | ## C4 Model: Component Level
111 |
112 | ```mermaid
113 | C4Component
114 | title Component diagram for Kled.io CLI
115 |
116 | Container_Boundary(cli, "CLI Tools") {
117 | Component(unified, "Unified CLI Binary", "Go", "Single binary with multiple command capabilities")
118 | Component(router, "Command Router", "Go", "Routes commands based on executable name or first argument")
119 | Component(auth, "Authentication", "Go", "Manages API keys and authentication")
120 | Component(config, "Configuration", "Go", "Handles configuration file management")
121 |
122 | Component(kled_cmd, "kled Commands", "Go", "Workspace management commands")
123 | Component(kcluster_cmd, "kcluster Commands", "Go", "Kubernetes cluster management commands")
124 | Component(kledspace_cmd, "kledspace Commands", "Go", "Environment management commands")
125 | Component(kpolicy_cmd, "kpolicy Commands", "Go", "Policy management commands")
126 | }
127 |
128 | Container(api, "Backend API", "Go", "API for workspace management")
129 |
130 | System_Ext(kubernetes, "Kubernetes", "Container orchestration platform")
131 |
132 | Rel(unified, router, "Uses")
133 | Rel(router, kled_cmd, "Routes to")
134 | Rel(router, kcluster_cmd, "Routes to")
135 | Rel(router, kledspace_cmd, "Routes to")
136 | Rel(router, kpolicy_cmd, "Routes to")
137 |
138 | Rel(unified, auth, "Uses")
139 | Rel(unified, config, "Uses")
140 |
141 | Rel(kled_cmd, api, "Makes API calls to")
142 | Rel(kcluster_cmd, api, "Makes API calls to")
143 | Rel(kledspace_cmd, api, "Makes API calls to")
144 | Rel(kpolicy_cmd, api, "Makes API calls to")
145 |
146 | Rel(api, kubernetes, "Manages")
147 | ```
148 |
149 | ## Implementation Details
150 |
151 | ### Unified CLI Binary
152 |
153 | The unified CLI binary is implemented in the kled.io repository and serves as the entry point for all CLI commands. It determines which command to execute based on:
154 |
155 | 1. The executable name (e.g., `kled`, `kcluster`)
156 | 2. The first argument if executed as the unified binary (e.g., `kled-linux-amd64 kled`)
157 |
158 | ```go
159 | func main() {
160 | execName := filepath.Base(os.Args[0])
161 | execName = strings.TrimSuffix(execName, filepath.Ext(execName))
162 |
163 | var command string
164 | if strings.HasPrefix(execName, "kled-") {
165 | if len(os.Args) < 2 {
166 | printUsage()
167 | os.Exit(1)
168 | }
169 | command = os.Args[1]
170 | os.Args = append(os.Args[:1], os.Args[2:]...)
171 | } else {
172 | switch execName {
173 | case "kled":
174 | command = "kled"
175 | case "kcluster":
176 | command = "kcluster"
177 | case "kledspace":
178 | command = "kledspace"
179 | case "kpolicy":
180 | command = "kpolicy"
181 | default:
182 | fmt.Printf("Unknown command: %s\n", execName)
183 | printUsage()
184 | os.Exit(1)
185 | }
186 | }
187 |
188 | // Execute the appropriate command
189 | switch command {
190 | case "kled":
191 | executeKled()
192 | case "kcluster":
193 | executeKCluster()
194 | case "kledspace":
195 | executeKledSpace()
196 | case "kpolicy":
197 | executeKPolicy()
198 | default:
199 | fmt.Printf("Unknown command: %s\n", command)
200 | printUsage()
201 | os.Exit(1)
202 | }
203 | }
204 | ```
205 |
206 | ### Authentication and Configuration
207 |
208 | The CLI tools use a shared authentication and configuration system that manages API keys and server URLs:
209 |
210 | ```go
211 | type Auth struct {
212 | APIKey string `json:"apiKey"`
213 | APIBaseURL string `json:"apiBaseURL"`
214 | CreatedAt time.Time `json:"createdAt"`
215 | }
216 |
217 | func LoadAuth() (*Auth, error) {
218 | configPath, err := getConfigPath()
219 | if err != nil {
220 | return nil, err
221 | }
222 |
223 | if _, err := os.Stat(configPath); os.IsNotExist(err) {
224 | return nil, errors.New("no authentication configuration found")
225 | }
226 |
227 | data, err := os.ReadFile(configPath)
228 | if err != nil {
229 | return nil, err
230 | }
231 |
232 | var auth Auth
233 | if err := json.Unmarshal(data, &auth); err != nil {
234 | return nil, err
235 | }
236 |
237 | // Environment variables take precedence
238 | if apiKey := os.Getenv("KLED_API_KEY"); apiKey != "" {
239 | auth.APIKey = apiKey
240 | }
241 | if apiBaseURL := os.Getenv("KLED_API_BASE_URL"); apiBaseURL != "" {
242 | auth.APIBaseURL = apiBaseURL
243 | }
244 |
245 | return &auth, nil
246 | }
247 | ```
248 |
249 | ### Distribution Methods
250 |
251 | The CLI tools can be distributed and executed in several ways:
252 |
253 | 1. **Symlinks**: On Unix-like systems, symlinks to the unified binary allow execution as individual commands
254 | 2. **Batch Files**: On Windows, batch files route commands to the unified binary
255 | 3. **Direct Execution**: The unified binary can be executed directly with the command as the first argument
256 |
257 | ## GitHub Actions Workflow
258 |
259 | The GitHub Actions workflow automates the build and distribution process:
260 |
261 | ```yaml
262 | name: Build and Release CLI Tools
263 |
264 | on:
265 | push:
266 | branches: [main]
267 | tags: ['v*']
268 | workflow_dispatch:
269 |
270 | jobs:
271 | build:
272 | name: Build CLI Tools
273 | runs-on: ${{ matrix.os }}
274 | strategy:
275 | fail-fast: false
276 | matrix:
277 | os: [ubuntu-latest, macos-latest, windows-latest]
278 | include:
279 | - os: ubuntu-latest
280 | platform: linux
281 | arch: amd64
282 | - os: macos-latest
283 | platform: darwin
284 | arch: amd64
285 | - os: windows-latest
286 | platform: windows
287 | arch: amd64
288 |
289 | steps:
290 | - name: Checkout code
291 | uses: actions/checkout@v3
292 |
293 | - name: Set up Go
294 | uses: actions/setup-go@v4
295 | with:
296 | go-version: '1.21'
297 |
298 | - name: Build unified CLI binary
299 | run: |
300 | mkdir -p bin
301 | GOOS=${{ matrix.platform }} GOARCH=${{ matrix.arch }} go build -o bin/${{ matrix.artifact_name }} ./cli/unified
302 | ```
303 |
304 | ## Installation Script
305 |
306 | The installation script simplifies the installation process for end users:
307 |
308 | ```bash
309 | #!/bin/bash
310 | set -e
311 |
312 | TOOL="unified"
313 | INSTALL_DIR="/usr/local/bin"
314 | VERSION="latest"
315 |
316 | # Download the appropriate binary
317 | curl -fsSL "https://kled.io/download/${FILENAME}" -o "/tmp/$FILENAME"
318 |
319 | # Install the binary
320 | chmod +x "/tmp/$FILENAME"
321 | sudo mv "/tmp/$FILENAME" "$INSTALL_DIR/$TOOL$EXTENSION"
322 |
323 | # Create symlinks for individual commands
324 | for SYMLINK in kled kcluster kledspace kpolicy; do
325 | sudo ln -sf "$INSTALL_DIR/$TOOL$EXTENSION" "$INSTALL_DIR/$SYMLINK$EXTENSION"
326 | done
327 | ```
328 |
329 | ## Conclusion
330 |
331 | The CLI integration architecture provides a unified approach to command line tools while maintaining separation between public and private repositories. The implementation details are kept in private repositories, while the public repository handles distribution and user-facing components.
332 |
--------------------------------------------------------------------------------
/docs/architecture/diagrams/build-system-flow.svg:
--------------------------------------------------------------------------------
1 |
87 |
--------------------------------------------------------------------------------
/docs/architecture/diagrams/cli-build-distribution.svg:
--------------------------------------------------------------------------------
1 |
95 |
--------------------------------------------------------------------------------
/docs/architecture/diagrams/cli-command-flow.svg:
--------------------------------------------------------------------------------
1 |
92 |
--------------------------------------------------------------------------------
/docs/architecture/diagrams/database-integration.svg:
--------------------------------------------------------------------------------
1 |
98 |
--------------------------------------------------------------------------------
/docs/architecture/diagrams/desktop-mobile-architecture.svg:
--------------------------------------------------------------------------------
1 |
101 |
--------------------------------------------------------------------------------
/docs/architecture/diagrams/monorepo-architecture.svg:
--------------------------------------------------------------------------------
1 |
125 |
--------------------------------------------------------------------------------
/docs/architecture/diagrams/monorepo-structure.svg:
--------------------------------------------------------------------------------
1 |
79 |
--------------------------------------------------------------------------------
/docs/architecture/diagrams/repository-relationships.svg:
--------------------------------------------------------------------------------
1 |
70 |
--------------------------------------------------------------------------------
/docs/architecture/diagrams/tauri-spacetime-integration.svg:
--------------------------------------------------------------------------------
1 |
119 |
--------------------------------------------------------------------------------
/docs/architecture/monorepo-architecture.md:
--------------------------------------------------------------------------------
1 | # Kled.io Monorepo Architecture
2 |
3 | ## Overview
4 |
5 | The Kled.io monorepo is structured to support a unified development environment for multiple related projects while maintaining separation of concerns. This document outlines the high-level architecture of the monorepo, the relationships between its components, and the build system flow.
6 |
7 | ## Monorepo Structure
8 |
9 | ```mermaid
10 | graph TD
11 | subgraph monorepo["/repos/monorepo"]
12 | turbo[Turborepo Configuration]
13 | pnpm[pnpm-workspace.yaml]
14 |
15 | subgraph apps["apps/ directory"]
16 | kled[kled - Core CLI & Desktop]
17 | kledpro[kled-pro - Enterprise Features]
18 | kledio[kled.io - Public Repository]
19 | cisco[cisco-meraki - Network Integration]
20 | end
21 |
22 | subgraph packages["packages/ directory"]
23 | shared[Shared Components]
24 | ui[UI Library]
25 | config[Configuration]
26 | end
27 |
28 | subgraph mcp["mcp-servers/ directory"]
29 | mcpservers[MCP Server Components]
30 | end
31 | end
32 |
33 | turbo --> apps
34 | turbo --> packages
35 | pnpm --> apps
36 | pnpm --> packages
37 | ```
38 |
39 | ### Key Components
40 |
41 | 1. **apps/** - Contains the main application repositories:
42 | - **kled/** - Core CLI and desktop application (private repository)
43 | - **kled-pro/** - Enterprise features and extensions (private repository)
44 | - **kled.io/** - Public-facing repository for distribution
45 | - **cisco-meraki/** - Network integration components
46 |
47 | 2. **packages/** - Contains shared libraries and components:
48 | - Shared UI components
49 | - Configuration utilities
50 | - Common types and interfaces
51 |
52 | 3. **mcp-servers/** - Contains server components placed at the monorepo root
53 |
54 | ## Repository Relationships
55 |
56 | ```mermaid
57 | graph TD
58 | subgraph Public["Public Repository"]
59 | kledio[kled.io]
60 | end
61 |
62 | subgraph Private["Private Repositories"]
63 | kled[kled]
64 | kledpro[kled-pro]
65 | end
66 |
67 | kled -->|CLI Implementation| kledio
68 | kled -->|Desktop App Source| kledio
69 | kledpro -->|kcluster Implementation| kledio
70 | kledpro -->|Enterprise Features| kled
71 | kledio -->|Distribution| users[End Users]
72 |
73 | kled -->|Imports API| kledpro
74 | ```
75 |
76 | ### Integration Points
77 |
78 | 1. **CLI Commands**:
79 | - `kled` commands implemented in the private kled repository
80 | - `kcluster` commands implemented in the private kled-pro repository
81 | - Unified CLI distributed through the public kled.io repository
82 |
83 | 2. **API Integration**:
84 | - kled-pro/platform provides API implementation
85 | - kled imports API from kled-pro/platform instead of github.com/loft-sh/api/v4
86 |
87 | 3. **Desktop Application**:
88 | - Source code in private kled repository
89 | - Built applications distributed through public kled.io repository
90 |
91 | ## Build System Flow
92 |
93 | ```mermaid
94 | flowchart TD
95 | subgraph Turborepo["Turborepo Build System"]
96 | direction LR
97 | turbo[turbo.json] --> pipeline[Build Pipeline]
98 | pipeline --> tasks[Task Dependencies]
99 | end
100 |
101 | subgraph BuildProcess["Build Process"]
102 | direction TB
103 | deps[Install Dependencies] --> build[Build Packages]
104 | build --> test[Run Tests]
105 | test --> bundle[Bundle Applications]
106 | end
107 |
108 | subgraph Artifacts["Build Artifacts"]
109 | cli[CLI Binaries]
110 | desktop[Desktop Applications]
111 | mobile[Mobile Applications]
112 | web[Web Application]
113 | end
114 |
115 | Turborepo --> BuildProcess
116 | BuildProcess --> Artifacts
117 | ```
118 |
119 | ### Build Order
120 |
121 | 1. Install dependencies (yarn install)
122 | 2. Build shared packages
123 | 3. Build applications in dependency order:
124 | - SpacetimeDB server component first
125 | - Then desktop application components
126 | - CLI components
127 | - Web and mobile components
128 |
129 | ## C4 Model: Context Level
130 |
131 | ```mermaid
132 | C4Context
133 | title System Context diagram for Kled.io
134 |
135 | Person(user, "End User", "A user of the Kled.io platform")
136 |
137 | System(kledSystem, "Kled.io System", "Provides workspace management capabilities")
138 |
139 | System_Ext(kubernetes, "Kubernetes", "Container orchestration platform")
140 | System_Ext(cloud, "Cloud Providers", "Infrastructure providers (AWS, GCP, Azure)")
141 | System_Ext(slack, "Slack", "Authentication and communication")
142 |
143 | Rel(user, kledSystem, "Uses")
144 | Rel(kledSystem, kubernetes, "Manages workspaces on")
145 | Rel(kledSystem, cloud, "Provisions resources on")
146 | Rel(kledSystem, slack, "Authenticates through")
147 | ```
148 |
149 | ## C4 Model: Container Level
150 |
151 | ```mermaid
152 | C4Container
153 | title Container diagram for Kled.io System
154 |
155 | Person(user, "End User", "A user of the Kled.io platform")
156 |
157 | System_Boundary(kledSystem, "Kled.io System") {
158 | Container(cli, "CLI Tools", "Go", "Command-line interface for workspace management")
159 | Container(desktop, "Desktop App", "Tauri/React", "Desktop application for workspace management")
160 | Container(mobile, "Mobile App", "React Native", "Mobile application for workspace management")
161 | Container(web, "Web App", "React", "Web interface for workspace management")
162 | Container(api, "API", "Go", "Backend API for workspace management")
163 | Container(spacetime, "SpacetimeDB", "Rust", "State management database")
164 | Container(supabase, "Supabase", "PostgreSQL", "User authentication and API key storage")
165 | }
166 |
167 | System_Ext(kubernetes, "Kubernetes", "Container orchestration platform")
168 | System_Ext(cloud, "Cloud Providers", "Infrastructure providers")
169 | System_Ext(slack, "Slack", "Authentication and communication")
170 |
171 | Rel(user, cli, "Uses")
172 | Rel(user, desktop, "Uses")
173 | Rel(user, mobile, "Uses")
174 | Rel(user, web, "Uses")
175 |
176 | Rel(cli, api, "Makes API calls to")
177 | Rel(desktop, api, "Makes API calls to")
178 | Rel(mobile, api, "Makes API calls to")
179 | Rel(web, api, "Makes API calls to")
180 |
181 | Rel(api, spacetime, "Stores state in")
182 | Rel(api, supabase, "Authenticates users with")
183 | Rel(api, kubernetes, "Manages workspaces on")
184 | Rel(api, cloud, "Provisions resources on")
185 | Rel(api, slack, "Integrates with")
186 | ```
187 |
188 | ## Conclusion
189 |
190 | The Kled.io monorepo architecture is designed to support a multi-platform application ecosystem with shared components and a unified build system. The separation between public and private repositories allows for open-source distribution while protecting proprietary code.
191 |
--------------------------------------------------------------------------------
/docs/architecture/triple-cd-pipeline.md:
--------------------------------------------------------------------------------
1 | # Triple CD Pipeline Architecture
2 |
3 | ## Overview
4 |
5 | The Kled.io Triple CD Pipeline provides a comprehensive approach to continuous delivery across different aspects of the system:
6 |
7 | 1. **FluxCD** - Manages code from private repositories
8 | 2. **ArgoCD** - Manages infrastructure components
9 | 3. **Hydra CD** - Manages Go CLI code and integrations
10 |
11 | This document outlines the architecture, components, and workflows of the triple CD pipeline.
12 |
13 | ## Pipeline Components
14 |
15 | ```mermaid
16 | graph TD
17 | subgraph TripleCD["Triple CD Pipeline"]
18 | flux[FluxCD]
19 | argo[ArgoCD]
20 | hydra[Hydra CD]
21 | vault[Hashicorp Vault]
22 | end
23 |
24 | subgraph Repositories["Source Repositories"]
25 | kled[kled]
26 | kledpro[kled-pro]
27 | kledspace[kledspace]
28 | kpolicy[kpolicy]
29 | kledmarketing[kled-marketing]
30 | end
31 |
32 | subgraph Infrastructure["Infrastructure"]
33 | spacetime[SpacetimeDB]
34 | supabase[Supabase]
35 | sonarqube[SonarQube]
36 | end
37 |
38 | subgraph CLI["CLI Tools"]
39 | kledcli[kled CLI]
40 | sonarqubecli[SonarQube CLI]
41 | end
42 |
43 | flux --> Repositories
44 | argo --> Infrastructure
45 | hydra --> CLI
46 |
47 | vault --> flux
48 | vault --> argo
49 | vault --> hydra
50 | ```
51 |
52 | ## FluxCD Configuration
53 |
54 | FluxCD is responsible for syncing code from private repositories to ensure all components are up-to-date.
55 |
56 | ### Key Components
57 |
58 | - **GitRepository**: Defines the source repositories to monitor
59 | - **Kustomization**: Defines how to apply the Kubernetes manifests
60 | - **Vault Integration**: Securely manages access to private repositories
61 |
62 | ## ArgoCD Configuration
63 |
64 | ArgoCD manages the deployment and configuration of infrastructure components.
65 |
66 | ### Key Components
67 |
68 | - **Application**: Defines the applications to deploy
69 | - **ApplicationSet**: Manages multiple related applications
70 | - **Vault Plugin**: Securely manages secrets for deployments
71 |
72 | ## Hydra CD Configuration
73 |
74 | Hydra CD handles the building and distribution of Go CLI code.
75 |
76 | ### Key Components
77 |
78 | - **Jobsets**: Defines the build jobs for CLI tools
79 | - **Nix Expressions**: Defines the build process for Go applications
80 | - **Vault Integration**: Securely manages build secrets
81 |
82 | ## Secret Management
83 |
84 | All secrets are managed by Hashicorp Vault, ensuring secure access across the pipeline.
85 |
86 | ### Key Features
87 |
88 | - **Kubernetes Authentication**: Authenticates services using Kubernetes service accounts
89 | - **Dynamic Secrets**: Generates dynamic credentials for accessing resources
90 | - **Secret Rotation**: Automatically rotates secrets based on policies
91 |
92 | ## Workflow Examples
93 |
94 | ### Updating a Private Repository
95 |
96 | 1. Developer commits changes to a private repository
97 | 2. FluxCD detects the changes and syncs the code
98 | 3. ArgoCD deploys updated infrastructure components
99 | 4. Hydra CD builds updated CLI tools
100 | 5. End users can access the updated functionality
101 |
102 | ### Adding a New Integration
103 |
104 | 1. Developer adds a new integration (e.g., SonarQube)
105 | 2. ArgoCD deploys the new infrastructure component
106 | 3. Hydra CD builds the CLI tools with the new integration
107 | 4. FluxCD ensures all repositories are updated with the integration
108 | 5. End users can access the new integration through the CLI
109 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/spectrumwebco/kled.io
2 |
3 | go 1.20
4 |
--------------------------------------------------------------------------------
/k8s/argo/argo-system.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: argo-cd
5 | namespace: argocd
6 | spec:
7 | project: default
8 | source:
9 | repoURL: https://github.com/spectrumwebco/kled.io.git
10 | targetRevision: HEAD
11 | path: k8s/argo/chart
12 | destination:
13 | server: https://kubernetes.default.svc
14 | namespace: argocd
15 | syncPolicy:
16 | automated:
17 | prune: true
18 | selfHeal: true
19 |
--------------------------------------------------------------------------------
/k8s/argo/components/sonarqube/sonarqube.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: sonarqube
5 | namespace: sonarqube
6 | spec:
7 | replicas: 1
8 | selector:
9 | matchLabels:
10 | app: sonarqube
11 | template:
12 | metadata:
13 | labels:
14 | app: sonarqube
15 | spec:
16 | containers:
17 | - name: sonarqube
18 | image: sonarqube:latest
19 | ports:
20 | - containerPort: 9000
21 | env:
22 | - name: SONARQUBE_JDBC_URL
23 | valueFrom:
24 | secretKeyRef:
25 | name: sonarqube-db-credentials
26 | key: url
27 | - name: SONARQUBE_JDBC_USERNAME
28 | valueFrom:
29 | secretKeyRef:
30 | name: sonarqube-db-credentials
31 | key: username
32 | - name: SONARQUBE_JDBC_PASSWORD
33 | valueFrom:
34 | secretKeyRef:
35 | name: sonarqube-db-credentials
36 | key: password
37 | ---
38 | apiVersion: v1
39 | kind: Service
40 | metadata:
41 | name: sonarqube
42 | namespace: sonarqube
43 | spec:
44 | selector:
45 | app: sonarqube
46 | ports:
47 | - port: 9000
48 | targetPort: 9000
49 | type: ClusterIP
50 |
--------------------------------------------------------------------------------
/k8s/argo/infra-components.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: spacetime-db
5 | namespace: argocd
6 | spec:
7 | project: default
8 | source:
9 | repoURL: https://github.com/spectrumwebco/kled.io.git
10 | targetRevision: HEAD
11 | path: k8s/argo/components/spacetime-db
12 | destination:
13 | server: https://kubernetes.default.svc
14 | namespace: spacetime
15 | syncPolicy:
16 | automated:
17 | prune: true
18 | selfHeal: true
19 | ---
20 | apiVersion: argoproj.io/v1alpha1
21 | kind: Application
22 | metadata:
23 | name: supabase
24 | namespace: argocd
25 | spec:
26 | project: default
27 | source:
28 | repoURL: https://github.com/spectrumwebco/kled.io.git
29 | targetRevision: HEAD
30 | path: k8s/argo/components/supabase
31 | destination:
32 | server: https://kubernetes.default.svc
33 | namespace: supabase
34 | syncPolicy:
35 | automated:
36 | prune: true
37 | selfHeal: true
38 | ---
39 | apiVersion: argoproj.io/v1alpha1
40 | kind: Application
41 | metadata:
42 | name: sonarqube
43 | namespace: argocd
44 | spec:
45 | project: default
46 | source:
47 | repoURL: https://github.com/spectrumwebco/kled.io.git
48 | targetRevision: HEAD
49 | path: k8s/argo/components/sonarqube
50 | destination:
51 | server: https://kubernetes.default.svc
52 | namespace: sonarqube
53 | syncPolicy:
54 | automated:
55 | prune: true
56 | selfHeal: true
57 |
--------------------------------------------------------------------------------
/k8s/argo/vault-integration.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: argocd-vault-plugin
5 | namespace: argocd
6 | spec:
7 | project: default
8 | source:
9 | repoURL: https://github.com/spectrumwebco/kled.io.git
10 | targetRevision: HEAD
11 | path: k8s/argo/vault-plugin
12 | destination:
13 | server: https://kubernetes.default.svc
14 | namespace: argocd
15 | syncPolicy:
16 | automated:
17 | prune: true
18 | selfHeal: true
19 |
--------------------------------------------------------------------------------
/k8s/flux/flux-system.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: source.toolkit.fluxcd.io/v1beta2
2 | kind: GitRepository
3 | metadata:
4 | name: kled-io
5 | namespace: flux-system
6 | spec:
7 | interval: 1m
8 | url: https://github.com/spectrumwebco/kled.io.git
9 | ref:
10 | branch: main
11 | secretRef:
12 | name: github-credentials
13 | ---
14 | apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
15 | kind: Kustomization
16 | metadata:
17 | name: kled-io
18 | namespace: flux-system
19 | spec:
20 | interval: 10m
21 | path: "./k8s/flux"
22 | prune: true
23 | sourceRef:
24 | kind: GitRepository
25 | name: kled-io
26 |
--------------------------------------------------------------------------------
/k8s/flux/private-repos.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: source.toolkit.fluxcd.io/v1beta2
2 | kind: GitRepository
3 | metadata:
4 | name: kled
5 | namespace: flux-system
6 | spec:
7 | interval: 1m
8 | url: https://github.com/spectrumwebco/kled.git
9 | ref:
10 | branch: main
11 | secretRef:
12 | name: github-credentials
13 | ---
14 | apiVersion: source.toolkit.fluxcd.io/v1beta2
15 | kind: GitRepository
16 | metadata:
17 | name: kled-pro
18 | namespace: flux-system
19 | spec:
20 | interval: 1m
21 | url: https://github.com/spectrumwebco/kled-pro.git
22 | ref:
23 | branch: main
24 | secretRef:
25 | name: github-credentials
26 | ---
27 | apiVersion: source.toolkit.fluxcd.io/v1beta2
28 | kind: GitRepository
29 | metadata:
30 | name: kledspace
31 | namespace: flux-system
32 | spec:
33 | interval: 1m
34 | url: https://github.com/spectrumwebco/kledspace.git
35 | ref:
36 | branch: main
37 | secretRef:
38 | name: github-credentials
39 | ---
40 | apiVersion: source.toolkit.fluxcd.io/v1beta2
41 | kind: GitRepository
42 | metadata:
43 | name: kpolicy
44 | namespace: flux-system
45 | spec:
46 | interval: 1m
47 | url: https://github.com/spectrumwebco/kpolicy.git
48 | ref:
49 | branch: main
50 | secretRef:
51 | name: github-credentials
52 | ---
53 | apiVersion: source.toolkit.fluxcd.io/v1beta2
54 | kind: GitRepository
55 | metadata:
56 | name: kled-marketing
57 | namespace: flux-system
58 | spec:
59 | interval: 1m
60 | url: https://github.com/spectrumwebco/kled-marketing.git
61 | ref:
62 | branch: main
63 | secretRef:
64 | name: github-credentials
65 |
--------------------------------------------------------------------------------
/k8s/flux/vault-integration.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: external-secrets.io/v1beta1
2 | kind: ExternalSecret
3 | metadata:
4 | name: github-credentials
5 | namespace: flux-system
6 | spec:
7 | refreshInterval: "1h"
8 | secretStoreRef:
9 | name: vault-backend
10 | kind: ClusterSecretStore
11 | target:
12 | name: github-credentials
13 | data:
14 | - secretKey: identity
15 | remoteRef:
16 | key: github/flux-identity
17 | property: identity
18 | - secretKey: known_hosts
19 | remoteRef:
20 | key: github/flux-identity
21 | property: known_hosts
22 |
--------------------------------------------------------------------------------
/k8s/hydra/default.nix:
--------------------------------------------------------------------------------
1 | { config, pkgs, ... }:
2 |
3 | {
4 | services.hydra = {
5 | enable = true;
6 | hydraURL = "https://hydra.kled.io";
7 | notificationSender = "hydra@kled.io";
8 | buildMachinesFiles = [];
9 | useSubstitutes = true;
10 | };
11 |
12 | services.postgresql = {
13 | enable = true;
14 | package = pkgs.postgresql_11;
15 | extraConfig = ''
16 | max_connections = 250
17 | work_mem = 32MB
18 | shared_buffers = 512MB
19 | '';
20 | };
21 |
22 | networking.firewall.allowedTCPPorts = [ 80 443 3000 ];
23 |
24 | security.acme = {
25 | email = "admin@kled.io";
26 | acceptTerms = true;
27 | };
28 |
29 | services.nginx = {
30 | enable = true;
31 | virtualHosts."hydra.kled.io" = {
32 | forceSSL = true;
33 | enableACME = true;
34 | locations."/".proxyPass = "http://127.0.0.1:3000";
35 | };
36 | };
37 | }
38 |
--------------------------------------------------------------------------------
/k8s/hydra/jobsets.nix:
--------------------------------------------------------------------------------
1 | { nixpkgs ? }:
2 |
3 | let
4 | pkgs = import nixpkgs {};
5 | in {
6 | jobsets = pkgs.runCommand "jobsets.json" {} ''
7 | cat > $out < }:
2 |
3 | let
4 | pkgs = import nixpkgs {};
5 |
6 | buildKledCLI = { name, src, goPackagePath }: pkgs.buildGoModule {
7 | pname = name;
8 | version = "0.1.0";
9 | inherit src;
10 |
11 | vendorSha256 = null; # Set this to the actual hash after initial build
12 |
13 | nativeBuildInputs = [ pkgs.installShellFiles ];
14 |
15 | postInstall = ''
16 | installShellCompletion --cmd ${name} \
17 | --bash <($out/bin/${name} completion bash) \
18 | --zsh <($out/bin/${name} completion zsh)
19 | '';
20 |
21 | meta = with pkgs.lib; {
22 | description = "Kled CLI for Kubernetes workspace management";
23 | homepage = "https://kled.io";
24 | license = licenses.mit;
25 | maintainers = [ "Spectrum Web Co" ];
26 | };
27 | };
28 | in {
29 | kled = buildKledCLI {
30 | name = "kled";
31 | src = ./.;
32 | goPackagePath = "github.com/spectrumwebco/kled";
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/k8s/hydra/vault-integration.nix:
--------------------------------------------------------------------------------
1 | { config, pkgs, ... }:
2 |
3 | {
4 | environment.systemPackages = with pkgs; [
5 | vault
6 | ];
7 |
8 | systemd.services.hydra-vault-auth = {
9 | description = "Hydra Vault Authentication";
10 | wantedBy = [ "multi-user.target" ];
11 | after = [ "network.target" "vault.service" ];
12 | path = [ pkgs.vault ];
13 | script = ''
14 | vault login -method=kubernetes \
15 | role=hydra \
16 | jwt_path=/var/run/secrets/kubernetes.io/serviceaccount/token
17 | '';
18 | serviceConfig = {
19 | Type = "oneshot";
20 | RemainAfterExit = true;
21 | User = "hydra";
22 | Group = "hydra";
23 | };
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/k8s/vault/chart/values.yaml:
--------------------------------------------------------------------------------
1 | server:
2 | dev:
3 | enabled: false
4 | ha:
5 | enabled: true
6 | replicas: 3
7 | service:
8 | type: ClusterIP
9 | ui:
10 | enabled: true
11 |
--------------------------------------------------------------------------------
/k8s/vault/vault.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: argoproj.io/v1alpha1
2 | kind: Application
3 | metadata:
4 | name: vault
5 | namespace: argocd
6 | spec:
7 | project: default
8 | source:
9 | repoURL: https://github.com/spectrumwebco/kled.io.git
10 | targetRevision: HEAD
11 | path: k8s/vault/chart
12 | destination:
13 | server: https://kubernetes.default.svc
14 | namespace: vault
15 | syncPolicy:
16 | automated:
17 | prune: true
18 | selfHeal: true
19 |
--------------------------------------------------------------------------------
/mobile/README.md:
--------------------------------------------------------------------------------
1 |
2 | This is a placeholder for the Kled.io mobile applications. The actual implementation is maintained in private repositories.
3 |
4 |
5 | - iOS and Android applications
6 | - Built with the Lynx Family framework
7 | - Provides a unified interface for managing Kubernetes workspaces
8 |
9 |
10 | See the installation instructions in the main README.md file.
11 |
--------------------------------------------------------------------------------
/scripts/test-windows-installer.ps1:
--------------------------------------------------------------------------------
1 | # Windows Installer Testing Script for Kled Desktop
2 | # This script downloads and tests the latest Kled Desktop installer for Windows
3 |
4 | param (
5 | [Parameter(Mandatory=$false)]
6 | [string]$ReleaseTag = "latest",
7 |
8 | [Parameter(Mandatory=$false)]
9 | [string]$DownloadPath = "$env:TEMP\kled-installer"
10 | )
11 |
12 | # Create download directory if it doesn't exist
13 | if (-not (Test-Path -Path $DownloadPath)) {
14 | New-Item -ItemType Directory -Path $DownloadPath | Out-Null
15 | Write-Host "Created download directory: $DownloadPath" -ForegroundColor Green
16 | }
17 |
18 | # Function to get the latest release or specific tag
19 | function Get-KledRelease {
20 | param (
21 | [string]$Tag
22 | )
23 |
24 | Write-Host "Fetching release information for tag: $Tag" -ForegroundColor Cyan
25 |
26 | $headers = @{
27 | "Accept" = "application/vnd.github.v3+json"
28 | }
29 |
30 | $apiUrl = if ($Tag -eq "latest") {
31 | "https://api.github.com/repos/spectrumwebco/kled.io/releases/latest"
32 | } else {
33 | "https://api.github.com/repos/spectrumwebco/kled.io/releases/tags/$Tag"
34 | }
35 |
36 | try {
37 | $release = Invoke-RestMethod -Uri $apiUrl -Headers $headers
38 | return $release
39 | } catch {
40 | Write-Host "Error fetching release information: $_" -ForegroundColor Red
41 | exit 1
42 | }
43 | }
44 |
45 | # Function to download the Windows installer
46 | function Download-KledInstaller {
47 | param (
48 | [object]$Release
49 | )
50 |
51 | Write-Host "Looking for Windows installer in release assets..." -ForegroundColor Cyan
52 |
53 | $windowsAsset = $Release.assets | Where-Object { $_.name -like "*windows*.exe" } | Select-Object -First 1
54 |
55 | if (-not $windowsAsset) {
56 | Write-Host "No Windows installer found in release assets!" -ForegroundColor Red
57 | exit 1
58 | }
59 |
60 | $installerPath = Join-Path -Path $DownloadPath -ChildPath $windowsAsset.name
61 |
62 | Write-Host "Downloading Windows installer: $($windowsAsset.name)" -ForegroundColor Cyan
63 | Write-Host "Download URL: $($windowsAsset.browser_download_url)" -ForegroundColor Cyan
64 |
65 | try {
66 | Invoke-WebRequest -Uri $windowsAsset.browser_download_url -OutFile $installerPath
67 | Write-Host "Download complete: $installerPath" -ForegroundColor Green
68 | return $installerPath
69 | } catch {
70 | Write-Host "Error downloading installer: $_" -ForegroundColor Red
71 | exit 1
72 | }
73 | }
74 |
75 | # Function to test the installer
76 | function Test-KledInstaller {
77 | param (
78 | [string]$InstallerPath
79 | )
80 |
81 | Write-Host "Testing installer: $InstallerPath" -ForegroundColor Cyan
82 |
83 | # Verify the installer exists
84 | if (-not (Test-Path -Path $InstallerPath)) {
85 | Write-Host "Installer file not found at $InstallerPath" -ForegroundColor Red
86 | exit 1
87 | }
88 |
89 | # Get file information
90 | $fileInfo = Get-Item $InstallerPath
91 | Write-Host "Installer file size: $($fileInfo.Length) bytes" -ForegroundColor Green
92 | Write-Host "Installer created: $($fileInfo.CreationTime)" -ForegroundColor Green
93 |
94 | # Test silent installation
95 | $installDir = Join-Path -Path $env:TEMP -ChildPath "KledDesktopTest"
96 | Write-Host "Testing silent installation to: $installDir" -ForegroundColor Cyan
97 |
98 | try {
99 | # Create installation directory
100 | if (Test-Path -Path $installDir) {
101 | Remove-Item -Path $installDir -Recurse -Force
102 | }
103 | New-Item -ItemType Directory -Path $installDir | Out-Null
104 |
105 | # Run installer silently
106 | $installArgs = @("/SILENT", "/NOCANCEL", "/NORESTART", "/SUPPRESSMSGBOXES", "/DIR=`"$installDir`"")
107 | Start-Process -FilePath $InstallerPath -ArgumentList $installArgs -Wait
108 |
109 | # Verify installation
110 | if (Test-Path -Path "$installDir\Kled Desktop.exe") {
111 | Write-Host "Installation successful!" -ForegroundColor Green
112 |
113 | # Check for critical files
114 | $criticalFiles = @(
115 | "Kled Desktop.exe",
116 | "resources.pak",
117 | "LICENSE"
118 | )
119 |
120 | foreach ($file in $criticalFiles) {
121 | if (Test-Path -Path "$installDir\$file") {
122 | Write-Host "Found critical file: $file" -ForegroundColor Green
123 | } else {
124 | Write-Host "Missing critical file: $file" -ForegroundColor Yellow
125 | }
126 | }
127 |
128 | # Test application launch (optional)
129 | Write-Host "Testing application launch..." -ForegroundColor Cyan
130 | try {
131 | $process = Start-Process -FilePath "$installDir\Kled Desktop.exe" -PassThru
132 | Start-Sleep -Seconds 5
133 |
134 | if (-not $process.HasExited) {
135 | Write-Host "Application launched successfully!" -ForegroundColor Green
136 | Stop-Process -Id $process.Id -Force
137 | } else {
138 | Write-Host "Application launched but exited immediately with code: $($process.ExitCode)" -ForegroundColor Yellow
139 | }
140 | } catch {
141 | Write-Host "Error launching application: $_" -ForegroundColor Red
142 | }
143 | } else {
144 | Write-Host "Installation failed - executable not found" -ForegroundColor Red
145 | }
146 | } catch {
147 | Write-Host "Error during installation test: $_" -ForegroundColor Red
148 | } finally {
149 | # Clean up
150 | if (Test-Path -Path $installDir) {
151 | Write-Host "Cleaning up test installation..." -ForegroundColor Cyan
152 | Remove-Item -Path $installDir -Recurse -Force
153 | }
154 | }
155 | }
156 |
157 | # Main execution
158 | Write-Host "=== Kled Desktop Windows Installer Test ===" -ForegroundColor Cyan
159 | Write-Host "Release Tag: $ReleaseTag" -ForegroundColor Cyan
160 | Write-Host "Download Path: $DownloadPath" -ForegroundColor Cyan
161 |
162 | $release = Get-KledRelease -Tag $ReleaseTag
163 | Write-Host "Testing release: $($release.name) ($($release.tag_name))" -ForegroundColor Cyan
164 |
165 | $installerPath = Download-KledInstaller -Release $release
166 | Test-KledInstaller -InstallerPath $installerPath
167 |
168 | Write-Host "=== Test Complete ===" -ForegroundColor Cyan
169 |
--------------------------------------------------------------------------------
/scripts/validate-installer.ps1:
--------------------------------------------------------------------------------
1 | # Windows Installer Validation Script for Kled Desktop
2 | # This script validates the Windows installer for Kled Desktop
3 |
4 | param (
5 | [Parameter(Mandatory=$true)]
6 | [string]$InstallerPath
7 | )
8 |
9 | Write-Host "Validating Kled Desktop Windows installer at: $InstallerPath"
10 |
11 | # Check if installer file exists
12 | if (-not (Test-Path -Path $InstallerPath)) {
13 | Write-Host "Error: Installer file not found at $InstallerPath" -ForegroundColor Red
14 | exit 1
15 | }
16 |
17 | # Get file information
18 | $fileInfo = Get-Item $InstallerPath
19 | Write-Host "Installer file size: $($fileInfo.Length) bytes" -ForegroundColor Green
20 | Write-Host "Installer created: $($fileInfo.CreationTime)" -ForegroundColor Green
21 |
22 | # Check file signature (optional, requires signtool.exe)
23 | try {
24 | $signtoolPath = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe"
25 | if (Test-Path $signtoolPath) {
26 | $signResult = & $signtoolPath verify /pa $InstallerPath
27 | if ($LASTEXITCODE -eq 0) {
28 | Write-Host "Installer signature is valid" -ForegroundColor Green
29 | } else {
30 | Write-Host "Installer signature validation failed or not signed" -ForegroundColor Yellow
31 | }
32 | } else {
33 | Write-Host "Signtool not found, skipping signature validation" -ForegroundColor Yellow
34 | }
35 | } catch {
36 | Write-Host "Error checking signature: $_" -ForegroundColor Yellow
37 | }
38 |
39 | # Test installer extraction (silent mode)
40 | Write-Host "Testing installer extraction..."
41 | $tempDir = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.Guid]::NewGuid().ToString())
42 | New-Item -ItemType Directory -Path $tempDir | Out-Null
43 |
44 | try {
45 | # Extract installer (this is for NSIS-based installers, adjust if using different format)
46 | $extractArgs = @("/SILENT", "/NOCANCEL", "/NORESTART", "/SUPPRESSMSGBOXES", "/DIR=`"$tempDir`"")
47 | Start-Process -FilePath $InstallerPath -ArgumentList $extractArgs -Wait
48 |
49 | # Check if extraction succeeded
50 | if (Test-Path -Path "$tempDir\Kled Desktop.exe") {
51 | Write-Host "Installer extraction successful" -ForegroundColor Green
52 | } else {
53 | Write-Host "Installer extraction failed - executable not found" -ForegroundColor Red
54 | }
55 |
56 | # Check for critical files
57 | $criticalFiles = @(
58 | "Kled Desktop.exe",
59 | "resources.pak",
60 | "LICENSE"
61 | )
62 |
63 | foreach ($file in $criticalFiles) {
64 | if (Test-Path -Path "$tempDir\$file") {
65 | Write-Host "Found critical file: $file" -ForegroundColor Green
66 | } else {
67 | Write-Host "Missing critical file: $file" -ForegroundColor Yellow
68 | }
69 | }
70 | } catch {
71 | Write-Host "Error during extraction test: $_" -ForegroundColor Red
72 | } finally {
73 | # Clean up
74 | if (Test-Path -Path $tempDir) {
75 | Remove-Item -Path $tempDir -Recurse -Force
76 | }
77 | }
78 |
79 | Write-Host "Validation complete" -ForegroundColor Green
80 |
--------------------------------------------------------------------------------
/web/README.md:
--------------------------------------------------------------------------------
1 | # Kled.io Web App
2 |
3 | This directory contains the web application for Kled.io, built using React.
4 |
5 | ## Getting Started
6 |
7 | ### Prerequisites
8 |
9 | - Node.js 16 or later
10 | - Yarn
11 |
12 | ### Installation
13 |
14 | 1. Install dependencies:
15 |
16 | ```bash
17 | yarn install
18 | ```
19 |
20 | 2. Run the app in development mode:
21 |
22 | ```bash
23 | yarn dev
24 | ```
25 |
26 | ## Building for Production
27 |
28 | ```bash
29 | yarn build
30 | ```
31 |
32 | This will create a production build in the `dist` directory.
33 |
34 | ## Features
35 |
36 | - Workspace management
37 | - Cluster management
38 | - Policy management
39 | - Real-time monitoring
40 | - Responsive design for all devices
41 |
--------------------------------------------------------------------------------
/web/download/files/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spectrumwebco/autobots.ai/0509db4f322d0941126fdbc01a8dff4104d231e0/web/download/files/.gitkeep
--------------------------------------------------------------------------------
/web/download/files/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Kled.io CLI Installer
4 | # This script detects the OS and architecture and downloads the appropriate Kled CLI binary
5 |
6 | set -e
7 |
8 | # Colors
9 | RED='\033[0;31m'
10 | GREEN='\033[0;32m'
11 | YELLOW='\033[0;33m'
12 | BLUE='\033[0;34m'
13 | NC='\033[0m' # No Color
14 |
15 | # Base URL for downloads
16 | BASE_URL="https://www.kled.io/download/files"
17 |
18 | # Detect OS
19 | detect_os() {
20 | case "$(uname -s)" in
21 | Linux*) echo "linux";;
22 | Darwin*) echo "darwin";;
23 | CYGWIN*) echo "windows";;
24 | MINGW*) echo "windows";;
25 | MSYS*) echo "windows";;
26 | *) echo "unknown";;
27 | esac
28 | }
29 |
30 | # Detect architecture
31 | detect_arch() {
32 | case "$(uname -m)" in
33 | x86_64*) echo "amd64";;
34 | amd64*) echo "amd64";;
35 | arm64*) echo "arm64";;
36 | aarch64*) echo "arm64";;
37 | *) echo "unknown";;
38 | esac
39 | }
40 |
41 | # Main installation function
42 | install_kled() {
43 | echo -e "${BLUE}Kled.io CLI Installer${NC}"
44 | echo "Detecting your system..."
45 |
46 | OS=$(detect_os)
47 | ARCH=$(detect_arch)
48 |
49 | if [ "$OS" = "unknown" ] || [ "$ARCH" = "unknown" ]; then
50 | echo -e "${RED}Error: Could not detect your operating system or architecture.${NC}"
51 | echo "Please download the appropriate binary manually from https://www.kled.io/download"
52 | exit 1
53 | fi
54 |
55 | echo -e "Detected: ${GREEN}$OS/$ARCH${NC}"
56 |
57 | # Handle Windows separately
58 | if [ "$OS" = "windows" ]; then
59 | FILENAME="kled-windows-amd64.exe"
60 | echo -e "${YELLOW}Note: For Windows, please download the installer from https://www.kled.io/download${NC}"
61 | exit 0
62 | else
63 | FILENAME="kled-$OS-$ARCH"
64 | fi
65 |
66 | DOWNLOAD_URL="$BASE_URL/$FILENAME"
67 |
68 | echo "Downloading Kled CLI from $DOWNLOAD_URL..."
69 | curl -L -o kled "$DOWNLOAD_URL"
70 |
71 | echo "Making binary executable..."
72 | chmod +x kled
73 |
74 | echo "Installing to /usr/local/bin/kled..."
75 | sudo mv kled /usr/local/bin/
76 |
77 | echo -e "${GREEN}Installation complete!${NC}"
78 | echo "Run 'kled --version' to verify the installation."
79 | echo -e "${YELLOW}Note: You may need to restart your terminal for changes to take effect.${NC}"
80 | }
81 |
82 | # Run the installer
83 | install_kled
84 |
--------------------------------------------------------------------------------
/web/download/install.sh:
--------------------------------------------------------------------------------
1 |
2 | set -e
3 |
4 | TOOL="unified"
5 | INSTALL_DIR="/usr/local/bin"
6 | VERSION="latest"
7 |
8 | while [[ $# -gt 0 ]]; do
9 | case $1 in
10 | -t|--tool)
11 | TOOL="$2"
12 | shift 2
13 | ;;
14 | -d|--dir)
15 | INSTALL_DIR="$2"
16 | shift 2
17 | ;;
18 | -v|--version)
19 | VERSION="$2"
20 | shift 2
21 | ;;
22 | *)
23 | echo "Unknown option: $1"
24 | exit 1
25 | ;;
26 | esac
27 | done
28 |
29 | if [[ "$TOOL" != "unified" && "$TOOL" != "kled" && "$TOOL" != "kcluster" && "$TOOL" != "kledspace" && "$TOOL" != "kpolicy" ]]; then
30 | echo "Invalid tool: $TOOL"
31 | echo "Valid options: unified, kled, kcluster, kledspace, kpolicy"
32 | exit 1
33 | fi
34 |
35 | OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
36 | ARCH="$(uname -m)"
37 |
38 | case "$ARCH" in
39 | x86_64)
40 | GOARCH="amd64"
41 | ;;
42 | aarch64|arm64)
43 | GOARCH="arm64"
44 | ;;
45 | *)
46 | echo "Unsupported architecture: $ARCH"
47 | exit 1
48 | ;;
49 | esac
50 |
51 | case "$OS" in
52 | linux)
53 | GOOS="linux"
54 | ;;
55 | darwin)
56 | GOOS="darwin"
57 | ;;
58 | msys*|mingw64|windows*)
59 | GOOS="windows"
60 | ;;
61 | *)
62 | echo "Unsupported OS: $OS"
63 | exit 1
64 | ;;
65 | esac
66 |
67 | if [[ "$GOOS" == "windows" ]]; then
68 | EXTENSION=".exe"
69 | else
70 | EXTENSION=""
71 | fi
72 |
73 | if [[ "$TOOL" == "unified" ]]; then
74 | FILENAME="kled-${GOOS}-${GOARCH}${EXTENSION}"
75 | else
76 | FILENAME="${TOOL}-${GOOS}-${GOARCH}${EXTENSION}"
77 | fi
78 |
79 | if [[ "$VERSION" == "latest" ]]; then
80 | DOWNLOAD_URL="https://kled.io/download/${FILENAME}"
81 | else
82 | DOWNLOAD_URL="https://kled.io/download/${VERSION}/${FILENAME}"
83 | fi
84 |
85 | echo "Downloading $TOOL for $GOOS/$GOARCH..."
86 | curl -fsSL "$DOWNLOAD_URL" -o "/tmp/$FILENAME"
87 |
88 | echo "Installing to $INSTALL_DIR..."
89 | chmod +x "/tmp/$FILENAME"
90 |
91 | if [[ $EUID -ne 0 && ! -w "$INSTALL_DIR" ]]; then
92 | sudo mv "/tmp/$FILENAME" "$INSTALL_DIR/$TOOL$EXTENSION"
93 | else
94 | mv "/tmp/$FILENAME" "$INSTALL_DIR/$TOOL$EXTENSION"
95 | fi
96 |
97 | if [[ "$TOOL" == "unified" ]]; then
98 | echo "Creating symlinks for CLI tools..."
99 | for SYMLINK in kled kcluster kledspace kpolicy; do
100 | if [[ $EUID -ne 0 && ! -w "$INSTALL_DIR" ]]; then
101 | sudo ln -sf "$INSTALL_DIR/$TOOL$EXTENSION" "$INSTALL_DIR/$SYMLINK$EXTENSION"
102 | else
103 | ln -sf "$INSTALL_DIR/$TOOL$EXTENSION" "$INSTALL_DIR/$SYMLINK$EXTENSION"
104 | fi
105 | done
106 | fi
107 |
108 | echo "Installation complete!"
109 | echo "You can now use the $TOOL command."
110 | if [[ "$TOOL" == "unified" ]]; then
111 | echo "The following commands are also available: kled, kcluster, kledspace, kpolicy"
112 | fi
113 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Kled.io - Kubernetes Workspace Management
7 |
8 |
27 |
28 |
29 |
53 |
54 |
55 |
56 |
Kubernetes Workspace Management Made Simple
57 |
Kled.io provides a unified interface for managing Kubernetes workspaces, clusters, and policies.
58 |
62 |
63 |
64 |
65 |
66 |
67 |
Features
68 |
69 |
70 |
71 |
🖥️
72 |
Desktop App
73 |
Cross-platform desktop application for managing Kubernetes workspaces.
74 |
75 |
76 |
77 |
78 |
🌐
79 |
Web App
80 |
Browser-based interface with the same functionality as the desktop app.
81 |
82 |
83 |
84 |
85 |
📱
86 |
Mobile Apps
87 |
iOS and Android applications for on-the-go management.
88 |
89 |
90 |
91 |
92 |
⌨️
93 |
CLI Tools
94 |
Command-line tools for workspace, cluster, and policy management.
95 |
96 |
97 |
98 |
99 |
🔒
100 |
Security
101 |
Built-in security features for managing access and policies.
102 |
103 |
104 |
105 |
106 |
🔄
107 |
Automation
108 |
Automate common tasks and workflows with built-in automation tools.
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
Download
118 |
119 |
120 |
121 |
122 |
Desktop App
123 |
Available for Windows, macOS, and Linux.
124 |
Download
125 |
126 |
127 |
128 |
129 |
130 |
131 |
CLI Tools
132 |
Command-line tools for all platforms.
133 |
Download
134 |
135 |
136 |
137 |
138 |
139 |
140 |
Mobile Apps
141 |
Available on iOS and Android.
142 |
Download
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
Documentation
153 |
154 |
155 |
156 |
157 |
Getting Started
158 |
Learn how to install and set up Kled.io for your environment.
159 |
Read More
160 |
161 |
162 |
163 |
164 |
165 |
166 |
API Reference
167 |
Detailed documentation for the Kled.io API.
168 |
Read More
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
Ready to Get Started?
179 |
Join thousands of developers who are already using Kled.io to manage their Kubernetes workspaces.
180 |
Download Now
181 |
182 |
183 |
184 |
195 |
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/web/install.sh:
--------------------------------------------------------------------------------
1 |
2 |
3 | set -e
4 |
5 | TOOL="unified"
6 | INSTALL_DIR="/usr/local/bin"
7 | VERSION="latest"
8 |
9 | while [[ $# -gt 0 ]]; do
10 | case $1 in
11 | -t|--tool)
12 | TOOL="$2"
13 | shift 2
14 | ;;
15 | -d|--dir)
16 | INSTALL_DIR="$2"
17 | shift 2
18 | ;;
19 | -v|--version)
20 | VERSION="$2"
21 | shift 2
22 | ;;
23 | *)
24 | echo "Unknown option: $1"
25 | exit 1
26 | ;;
27 | esac
28 | done
29 |
30 | if [[ "$TOOL" != "unified" && "$TOOL" != "kled" && "$TOOL" != "kcluster" && "$TOOL" != "kledspace" && "$TOOL" != "kpolicy" ]]; then
31 | echo "Invalid tool: $TOOL"
32 | echo "Valid options: unified, kled, kcluster, kledspace, kpolicy"
33 | exit 1
34 | fi
35 |
36 | OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
37 | ARCH="$(uname -m)"
38 |
39 | case "$ARCH" in
40 | x86_64)
41 | GOARCH="amd64"
42 | ;;
43 | aarch64|arm64)
44 | GOARCH="arm64"
45 | ;;
46 | *)
47 | echo "Unsupported architecture: $ARCH"
48 | exit 1
49 | ;;
50 | esac
51 |
52 | case "$OS" in
53 | linux)
54 | GOOS="linux"
55 | ;;
56 | darwin)
57 | GOOS="darwin"
58 | ;;
59 | *)
60 | echo "Unsupported OS: $OS"
61 | exit 1
62 | ;;
63 | esac
64 |
65 | if [[ "$TOOL" == "unified" ]]; then
66 | FILENAME="kled-${GOOS}-${GOARCH}"
67 | if [[ "$GOOS" == "windows" ]]; then
68 | FILENAME="${FILENAME}.exe"
69 | fi
70 | DOWNLOAD_URL="https://kled.io/download/${FILENAME}"
71 | else
72 | FILENAME="${TOOL}"
73 | if [[ "$GOOS" == "windows" ]]; then
74 | FILENAME="${FILENAME}.exe"
75 | fi
76 | DOWNLOAD_URL="https://kled.io/download/${TOOL}-${GOOS}-${GOARCH}"
77 | fi
78 |
79 | echo "Downloading $TOOL for $GOOS/$GOARCH..."
80 | curl -fsSL "$DOWNLOAD_URL" -o "/tmp/$FILENAME"
81 |
82 | echo "Installing to $INSTALL_DIR..."
83 | chmod +x "/tmp/$FILENAME"
84 | sudo mv "/tmp/$FILENAME" "$INSTALL_DIR/$TOOL"
85 |
86 | if [[ "$TOOL" == "unified" ]]; then
87 | echo "Creating symlinks for CLI tools..."
88 | sudo ln -sf "$INSTALL_DIR/unified" "$INSTALL_DIR/kled"
89 | sudo ln -sf "$INSTALL_DIR/unified" "$INSTALL_DIR/kcluster"
90 | sudo ln -sf "$INSTALL_DIR/unified" "$INSTALL_DIR/kledspace"
91 | sudo ln -sf "$INSTALL_DIR/unified" "$INSTALL_DIR/kpolicy"
92 | fi
93 |
94 | echo "Installation complete!"
95 | echo "You can now use the $TOOL command."
96 | if [[ "$TOOL" == "unified" ]]; then
97 | echo "The following commands are also available: kled, kcluster, kledspace, kpolicy"
98 | fi
99 |
--------------------------------------------------------------------------------
/web/src/components/shared/SharedCommandInterface.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Box, Heading, Text, Flex, Icon, Button } from '@chakra-ui/react';
3 | import { FaTerminal, FaCubes, FaLayerGroup, FaShieldAlt } from 'react-icons/fa';
4 |
5 | interface CommandProps {
6 | name: string;
7 | description: string;
8 | icon: React.ElementType;
9 | onClick?: () => void;
10 | }
11 |
12 | interface SharedCommandInterfaceProps {
13 | title: string;
14 | commands: CommandProps[];
15 | onCommandSelect?: (commandName: string) => void;
16 | }
17 |
18 | /**
19 | * Shared component for displaying command interfaces across desktop and web apps
20 | * This component can be used in both the desktop app and web app to provide
21 | * a consistent interface for interacting with CLI commands
22 | */
23 | export const SharedCommandInterface: React.FC = ({
24 | title,
25 | commands,
26 | onCommandSelect
27 | }) => {
28 | const handleCommandClick = (commandName: string) => {
29 | if (onCommandSelect) {
30 | onCommandSelect(commandName);
31 | }
32 | };
33 |
34 | return (
35 |
36 | {title}
37 |
38 |
39 | {commands.map((command) => (
40 | {
47 | handleCommandClick(command.name);
48 | if (command.onClick) command.onClick();
49 | }}
50 | >
51 |
52 |
53 |
54 | {command.name}
55 | {command.description}
56 |
57 |
58 |
59 | ))}
60 |
61 |
62 | );
63 | };
64 |
65 | /**
66 | * Predefined command sets for each CLI interface
67 | * These can be imported and used in both desktop and web apps
68 | */
69 | export const getKledCommands = (): CommandProps[] => [
70 | {
71 | name: 'kled workspace create',
72 | description: 'Create a new development workspace',
73 | icon: FaTerminal
74 | },
75 | {
76 | name: 'kled workspace list',
77 | description: 'List all available workspaces',
78 | icon: FaTerminal
79 | },
80 | {
81 | name: 'kled workspace delete',
82 | description: 'Delete an existing workspace',
83 | icon: FaTerminal
84 | }
85 | ];
86 |
87 | export const getKclusterCommands = (): CommandProps[] => [
88 | {
89 | name: 'kcluster create',
90 | description: 'Create a new Kubernetes cluster',
91 | icon: FaCubes
92 | },
93 | {
94 | name: 'kcluster connect',
95 | description: 'Connect to an existing cluster',
96 | icon: FaCubes
97 | },
98 | {
99 | name: 'kcluster list',
100 | description: 'List all available clusters',
101 | icon: FaCubes
102 | }
103 | ];
104 |
105 | export const getKledspaceCommands = (): CommandProps[] => [
106 | {
107 | name: 'kledspace init',
108 | description: 'Initialize a new application space',
109 | icon: FaLayerGroup
110 | },
111 | {
112 | name: 'kledspace deploy',
113 | description: 'Deploy an application to a space',
114 | icon: FaLayerGroup
115 | },
116 | {
117 | name: 'kledspace list',
118 | description: 'List all available spaces',
119 | icon: FaLayerGroup
120 | }
121 | ];
122 |
123 | export const getKpolicyCommands = (): CommandProps[] => [
124 | {
125 | name: 'kpolicy validate',
126 | description: 'Validate policies against a cluster',
127 | icon: FaShieldAlt
128 | },
129 | {
130 | name: 'kpolicy apply',
131 | description: 'Apply policies to a cluster',
132 | icon: FaShieldAlt
133 | },
134 | {
135 | name: 'kpolicy list',
136 | description: 'List all available policies',
137 | icon: FaShieldAlt
138 | }
139 | ];
140 |
141 | export default SharedCommandInterface;
142 |
--------------------------------------------------------------------------------