feat(kube): add Kubernetes management support
Some checks failed
Auto Tag / autotag (push) Successful in 7s
Auto Tag / wiki-sync (push) Successful in 8s
Test / frontend-tests (push) Successful in 1m31s
Test / frontend-typecheck (push) Successful in 1m39s
Auto Tag / changelog (push) Successful in 1m47s
Auto Tag / build-macos-arm64 (push) Successful in 2m52s
Auto Tag / build-linux-amd64 (push) Has been cancelled
Auto Tag / build-linux-arm64 (push) Has been cancelled
Auto Tag / build-windows-amd64 (push) Has been cancelled
Test / rust-fmt-check (push) Has been cancelled
Test / rust-clippy (push) Has been cancelled
Test / rust-tests (push) Has been cancelled

- Bump version to 1.1.0
- Add kube module with ClusterClient, PortForwardSession, RefreshRegistry
- Add Tauri IPC commands: add_cluster, remove_cluster, list_clusters
- Add Tauri IPC commands: start_port_forward, stop_port_forward, list_port_forwards
- Update AppState with clusters, port_forwards, refresh_registry fields
- Update auto-tag.yml to mark releases as draft (pre-release)
- Add Buy Me A Coffee section to README
- Add serde_yaml dependency for kubeconfig parsing
This commit is contained in:
Shaun Arman 2026-06-06 11:41:23 -05:00
parent 170990ec00
commit b96ede35cd
15 changed files with 459 additions and 29 deletions

View File

@ -167,7 +167,7 @@ jobs:
--arg tag "$TAG" \ --arg tag "$TAG" \
--arg name "TFTSR $TAG" \ --arg name "TFTSR $TAG" \
--rawfile body /tmp/release_body.md \ --rawfile body /tmp/release_body.md \
'{tag_name: $tag, name: $name, body: $body, draft: false}' \ '{tag_name: $tag, name: $name, body: $body, draft: true}' \
| curl -sf -X POST "$API/releases" \ | curl -sf -X POST "$API/releases" \
-H "Authorization: token $RELEASE_TOKEN" \ -H "Authorization: token $RELEASE_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \

View File

@ -327,3 +327,15 @@ Override with the `TRCAA_DATA_DIR` (or legacy `TRCAA_DATA_DIR`) environment vari
| 12 | Release Packaging | ✅ linux/amd64 · linux/arm64 (native) · windows/amd64 | | 12 | Release Packaging | ✅ linux/amd64 · linux/arm64 (native) · windows/amd64 |
--- ---
## Support the Project
If you find this project helpful, consider buying me a coffee:
[![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://buymeacoffee.com/tftsr)
---
## License
MIT License — see [LICENSE](LICENSE) for details.

View File

@ -1,7 +1,7 @@
{ {
"name": "trcaa", "name": "trcaa",
"private": true, "private": true,
"version": "1.0.8", "version": "1.1.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

22
src-tauri/Cargo.lock generated
View File

@ -4733,6 +4733,19 @@ dependencies = [
"syn 2.0.117", "syn 2.0.117",
] ]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap 2.14.0",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]] [[package]]
name = "serialize-to-javascript" name = "serialize-to-javascript"
version = "0.1.2" version = "0.1.2"
@ -6069,7 +6082,7 @@ dependencies = [
[[package]] [[package]]
name = "trcaa" name = "trcaa"
version = "1.0.8" version = "1.1.0"
dependencies = [ dependencies = [
"aes-gcm", "aes-gcm",
"aho-corasick", "aho-corasick",
@ -6096,6 +6109,7 @@ dependencies = [
"rusqlite", "rusqlite",
"serde", "serde",
"serde_json", "serde_json",
"serde_yaml",
"sha2", "sha2",
"tauri", "tauri",
"tauri-build", "tauri-build",
@ -6249,6 +6263,12 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.9.0" version = "0.9.0"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "trcaa" name = "trcaa"
version = "1.0.8" version = "1.1.0"
edition = "2021" edition = "2021"
[lib] [lib]
@ -55,6 +55,9 @@ rmcp = { version = "1.7.0", features = [
] } ] }
http = "1.4" http = "1.4"
flate2 = { version = "1", features = ["rust_backend"] } flate2 = { version = "1", features = ["rust_backend"] }
serde_yaml = "0.9"
[dev-dependencies] [dev-dependencies]
tokio-test = "0.4" tokio-test = "0.4"
@ -63,12 +66,3 @@ mockito = "1.2"
[profile.release] [profile.release]
opt-level = "s" opt-level = "s"
strip = true strip = true

View File

@ -1159,12 +1159,24 @@
"const": "fs:allow-size", "const": "fs:allow-size",
"markdownDescription": "Enables the size command without any pre-configured scope." "markdownDescription": "Enables the size command without any pre-configured scope."
}, },
{
"description": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-start-accessing-security-scoped-resource",
"markdownDescription": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope."
},
{ {
"description": "Enables the stat command without any pre-configured scope.", "description": "Enables the stat command without any pre-configured scope.",
"type": "string", "type": "string",
"const": "fs:allow-stat", "const": "fs:allow-stat",
"markdownDescription": "Enables the stat command without any pre-configured scope." "markdownDescription": "Enables the stat command without any pre-configured scope."
}, },
{
"description": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-stop-accessing-security-scoped-resource",
"markdownDescription": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope."
},
{ {
"description": "Enables the truncate command without any pre-configured scope.", "description": "Enables the truncate command without any pre-configured scope.",
"type": "string", "type": "string",
@ -1315,12 +1327,24 @@
"const": "fs:deny-size", "const": "fs:deny-size",
"markdownDescription": "Denies the size command without any pre-configured scope." "markdownDescription": "Denies the size command without any pre-configured scope."
}, },
{
"description": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-start-accessing-security-scoped-resource",
"markdownDescription": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope."
},
{ {
"description": "Denies the stat command without any pre-configured scope.", "description": "Denies the stat command without any pre-configured scope.",
"type": "string", "type": "string",
"const": "fs:deny-stat", "const": "fs:deny-stat",
"markdownDescription": "Denies the stat command without any pre-configured scope." "markdownDescription": "Denies the stat command without any pre-configured scope."
}, },
{
"description": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-stop-accessing-security-scoped-resource",
"markdownDescription": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope."
},
{ {
"description": "Denies the truncate command without any pre-configured scope.", "description": "Denies the truncate command without any pre-configured scope.",
"type": "string", "type": "string",
@ -2331,10 +2355,10 @@
"markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
}, },
{ {
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`", "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`",
"type": "string", "type": "string",
"const": "core:app:default", "const": "core:app:default",
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`" "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`"
}, },
{ {
"description": "Enables the app_hide command without any pre-configured scope.", "description": "Enables the app_hide command without any pre-configured scope.",
@ -2408,6 +2432,12 @@
"const": "core:app:allow-set-dock-visibility", "const": "core:app:allow-set-dock-visibility",
"markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope." "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
}, },
{
"description": "Enables the supports_multiple_windows command without any pre-configured scope.",
"type": "string",
"const": "core:app:allow-supports-multiple-windows",
"markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope."
},
{ {
"description": "Enables the tauri_version command without any pre-configured scope.", "description": "Enables the tauri_version command without any pre-configured scope.",
"type": "string", "type": "string",
@ -2492,6 +2522,12 @@
"const": "core:app:deny-set-dock-visibility", "const": "core:app:deny-set-dock-visibility",
"markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope." "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
}, },
{
"description": "Denies the supports_multiple_windows command without any pre-configured scope.",
"type": "string",
"const": "core:app:deny-supports-multiple-windows",
"markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope."
},
{ {
"description": "Denies the tauri_version command without any pre-configured scope.", "description": "Denies the tauri_version command without any pre-configured scope.",
"type": "string", "type": "string",
@ -3015,10 +3051,10 @@
"markdownDescription": "Denies the close command without any pre-configured scope." "markdownDescription": "Denies the close command without any pre-configured scope."
}, },
{ {
"description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`", "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`",
"type": "string", "type": "string",
"const": "core:tray:default", "const": "core:tray:default",
"markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`" "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`"
}, },
{ {
"description": "Enables the get_by_id command without any pre-configured scope.", "description": "Enables the get_by_id command without any pre-configured scope.",
@ -3050,6 +3086,12 @@
"const": "core:tray:allow-set-icon-as-template", "const": "core:tray:allow-set-icon-as-template",
"markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope." "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
}, },
{
"description": "Enables the set_icon_with_as_template command without any pre-configured scope.",
"type": "string",
"const": "core:tray:allow-set-icon-with-as-template",
"markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope."
},
{ {
"description": "Enables the set_menu command without any pre-configured scope.", "description": "Enables the set_menu command without any pre-configured scope.",
"type": "string", "type": "string",
@ -3116,6 +3158,12 @@
"const": "core:tray:deny-set-icon-as-template", "const": "core:tray:deny-set-icon-as-template",
"markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope." "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
}, },
{
"description": "Denies the set_icon_with_as_template command without any pre-configured scope.",
"type": "string",
"const": "core:tray:deny-set-icon-with-as-template",
"markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope."
},
{ {
"description": "Denies the set_menu command without any pre-configured scope.", "description": "Denies the set_menu command without any pre-configured scope.",
"type": "string", "type": "string",
@ -3375,10 +3423,16 @@
"markdownDescription": "Denies the webview_size command without any pre-configured scope." "markdownDescription": "Denies the webview_size command without any pre-configured scope."
}, },
{ {
"description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`", "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`",
"type": "string", "type": "string",
"const": "core:window:default", "const": "core:window:default",
"markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`" "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`"
},
{
"description": "Enables the activity_name command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-activity-name",
"markdownDescription": "Enables the activity_name command without any pre-configured scope."
}, },
{ {
"description": "Enables the available_monitors command without any pre-configured scope.", "description": "Enables the available_monitors command without any pre-configured scope.",
@ -3572,6 +3626,12 @@
"const": "core:window:allow-scale-factor", "const": "core:window:allow-scale-factor",
"markdownDescription": "Enables the scale_factor command without any pre-configured scope." "markdownDescription": "Enables the scale_factor command without any pre-configured scope."
}, },
{
"description": "Enables the scene_identifier command without any pre-configured scope.",
"type": "string",
"const": "core:window:allow-scene-identifier",
"markdownDescription": "Enables the scene_identifier command without any pre-configured scope."
},
{ {
"description": "Enables the set_always_on_bottom command without any pre-configured scope.", "description": "Enables the set_always_on_bottom command without any pre-configured scope.",
"type": "string", "type": "string",
@ -3836,6 +3896,12 @@
"const": "core:window:allow-unminimize", "const": "core:window:allow-unminimize",
"markdownDescription": "Enables the unminimize command without any pre-configured scope." "markdownDescription": "Enables the unminimize command without any pre-configured scope."
}, },
{
"description": "Denies the activity_name command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-activity-name",
"markdownDescription": "Denies the activity_name command without any pre-configured scope."
},
{ {
"description": "Denies the available_monitors command without any pre-configured scope.", "description": "Denies the available_monitors command without any pre-configured scope.",
"type": "string", "type": "string",
@ -4028,6 +4094,12 @@
"const": "core:window:deny-scale-factor", "const": "core:window:deny-scale-factor",
"markdownDescription": "Denies the scale_factor command without any pre-configured scope." "markdownDescription": "Denies the scale_factor command without any pre-configured scope."
}, },
{
"description": "Denies the scene_identifier command without any pre-configured scope.",
"type": "string",
"const": "core:window:deny-scene-identifier",
"markdownDescription": "Denies the scene_identifier command without any pre-configured scope."
},
{ {
"description": "Denies the set_always_on_bottom command without any pre-configured scope.", "description": "Denies the set_always_on_bottom command without any pre-configured scope.",
"type": "string", "type": "string",
@ -4293,22 +4365,22 @@
"markdownDescription": "Denies the unminimize command without any pre-configured scope." "markdownDescription": "Denies the unminimize command without any pre-configured scope."
}, },
{ {
"description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`", "description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`",
"type": "string", "type": "string",
"const": "dialog:default", "const": "dialog:default",
"markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`" "markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-message`\n- `allow-save`\n- `allow-open`"
}, },
{ {
"description": "Enables the ask command without any pre-configured scope.", "description": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)",
"type": "string", "type": "string",
"const": "dialog:allow-ask", "const": "dialog:allow-ask",
"markdownDescription": "Enables the ask command without any pre-configured scope." "markdownDescription": "Enables the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)"
}, },
{ {
"description": "Enables the confirm command without any pre-configured scope.", "description": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)",
"type": "string", "type": "string",
"const": "dialog:allow-confirm", "const": "dialog:allow-confirm",
"markdownDescription": "Enables the confirm command without any pre-configured scope." "markdownDescription": "Enables the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `allow-message` and will be removed in v3)"
}, },
{ {
"description": "Enables the message command without any pre-configured scope.", "description": "Enables the message command without any pre-configured scope.",
@ -4329,16 +4401,16 @@
"markdownDescription": "Enables the save command without any pre-configured scope." "markdownDescription": "Enables the save command without any pre-configured scope."
}, },
{ {
"description": "Denies the ask command without any pre-configured scope.", "description": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)",
"type": "string", "type": "string",
"const": "dialog:deny-ask", "const": "dialog:deny-ask",
"markdownDescription": "Denies the ask command without any pre-configured scope." "markdownDescription": "Denies the ask command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)"
}, },
{ {
"description": "Denies the confirm command without any pre-configured scope.", "description": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)",
"type": "string", "type": "string",
"const": "dialog:deny-confirm", "const": "dialog:deny-confirm",
"markdownDescription": "Denies the confirm command without any pre-configured scope." "markdownDescription": "Denies the confirm command without any pre-configured scope. (**DEPRECATED**: This is now an alias to `deny-message` and will be removed in v3)"
}, },
{ {
"description": "Denies the message command without any pre-configured scope.", "description": "Denies the message command without any pre-configured scope.",
@ -5378,12 +5450,24 @@
"const": "fs:allow-size", "const": "fs:allow-size",
"markdownDescription": "Enables the size command without any pre-configured scope." "markdownDescription": "Enables the size command without any pre-configured scope."
}, },
{
"description": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-start-accessing-security-scoped-resource",
"markdownDescription": "Enables the start_accessing_security_scoped_resource command without any pre-configured scope."
},
{ {
"description": "Enables the stat command without any pre-configured scope.", "description": "Enables the stat command without any pre-configured scope.",
"type": "string", "type": "string",
"const": "fs:allow-stat", "const": "fs:allow-stat",
"markdownDescription": "Enables the stat command without any pre-configured scope." "markdownDescription": "Enables the stat command without any pre-configured scope."
}, },
{
"description": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope.",
"type": "string",
"const": "fs:allow-stop-accessing-security-scoped-resource",
"markdownDescription": "Enables the stop_accessing_security_scoped_resource command without any pre-configured scope."
},
{ {
"description": "Enables the truncate command without any pre-configured scope.", "description": "Enables the truncate command without any pre-configured scope.",
"type": "string", "type": "string",
@ -5534,12 +5618,24 @@
"const": "fs:deny-size", "const": "fs:deny-size",
"markdownDescription": "Denies the size command without any pre-configured scope." "markdownDescription": "Denies the size command without any pre-configured scope."
}, },
{
"description": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-start-accessing-security-scoped-resource",
"markdownDescription": "Denies the start_accessing_security_scoped_resource command without any pre-configured scope."
},
{ {
"description": "Denies the stat command without any pre-configured scope.", "description": "Denies the stat command without any pre-configured scope.",
"type": "string", "type": "string",
"const": "fs:deny-stat", "const": "fs:deny-stat",
"markdownDescription": "Denies the stat command without any pre-configured scope." "markdownDescription": "Denies the stat command without any pre-configured scope."
}, },
{
"description": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope.",
"type": "string",
"const": "fs:deny-stop-accessing-security-scoped-resource",
"markdownDescription": "Denies the stop_accessing_security_scoped_resource command without any pre-configured scope."
},
{ {
"description": "Denies the truncate command without any pre-configured scope.", "description": "Denies the truncate command without any pre-configured scope.",
"type": "string", "type": "string",

View File

@ -335,6 +335,9 @@ pub async fn initiate_oauth(
integration_webviews, integration_webviews,
mcp_connections, mcp_connections,
pending_approvals, pending_approvals,
clusters: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())),
port_forwards: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())),
refresh_registry: Arc::new(tokio::sync::Mutex::new(crate::kube::RefreshRegistry::new())),
}; };
while let Some(callback) = callback_rx.recv().await { while let Some(callback) = callback_rx.recv().await {
tracing::info!("Received OAuth callback for state: {}", callback.state); tracing::info!("Received OAuth callback for state: {}", callback.state);

View File

@ -0,0 +1,176 @@
use crate::kube::ClusterClient;
use crate::state::AppState;
use serde::{Deserialize, Serialize};
use tauri::State;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterInfo {
pub id: String,
pub name: String,
pub context: String,
pub cluster_url: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PortForwardRequest {
pub cluster_id: String,
pub namespace: String,
pub pod: String,
pub container_port: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PortForwardResponse {
pub id: String,
pub cluster_id: String,
pub namespace: String,
pub pod: String,
pub container_port: u16,
pub local_port: u16,
pub status: String,
}
#[tauri::command]
pub async fn add_cluster(
id: String,
name: String,
kubeconfig_content: String,
state: State<'_, AppState>,
) -> Result<ClusterInfo, String> {
let context = extract_context(&kubeconfig_content)?;
let server_url = extract_server_url(&kubeconfig_content)?;
let client = ClusterClient::new(
id.clone(),
name.clone(),
context.clone(),
server_url.clone(),
);
{
let mut clusters = state.clusters.lock().await;
clusters.insert(id.clone(), client);
}
Ok(ClusterInfo {
id,
name,
context,
cluster_url: server_url,
})
}
#[tauri::command]
pub async fn remove_cluster(
id: String,
state: State<'_, AppState>,
) -> Result<(), String> {
let mut clusters = state.clusters.lock().await;
if clusters.remove(&id).is_none() {
return Err(format!("Cluster {id} not found"));
}
Ok(())
}
#[tauri::command]
pub async fn list_clusters(
state: State<'_, AppState>,
) -> Result<Vec<ClusterInfo>, String> {
let clusters = state.clusters.lock().await;
let cluster_list: Vec<ClusterInfo> = clusters
.values()
.map(|c| ClusterInfo {
id: c.id.clone(),
name: c.name.clone(),
context: c.context.clone(),
cluster_url: c.server_url.clone(),
})
.collect();
Ok(cluster_list)
}
#[tauri::command]
pub async fn start_port_forward(
request: PortForwardRequest,
state: State<'_, AppState>,
) -> Result<PortForwardResponse, String> {
let session_id = uuid::Uuid::now_v7().to_string();
let session = crate::kube::PortForwardSession::new(
session_id.clone(),
request.cluster_id.clone(),
request.namespace.clone(),
request.pod.clone(),
None,
vec![request.container_port],
vec![0],
);
{
let mut port_forwards = state.port_forwards.lock().await;
port_forwards.insert(session_id.clone(), session);
}
Ok(PortForwardResponse {
id: session_id,
cluster_id: request.cluster_id,
namespace: request.namespace,
pod: request.pod,
container_port: request.container_port,
local_port: 0,
status: "Active".to_string(),
})
}
#[tauri::command]
pub async fn stop_port_forward(
id: String,
state: State<'_, AppState>,
) -> Result<(), String> {
let mut port_forwards = state.port_forwards.lock().await;
if let Some(session) = port_forwards.get_mut(&id) {
session.stop();
Ok(())
} else {
Err(format!("Port forward session {id} not found"))
}
}
#[tauri::command]
pub async fn list_port_forwards(
state: State<'_, AppState>,
) -> Result<Vec<PortForwardResponse>, String> {
let port_forwards = state.port_forwards.lock().await;
let forwards: Vec<PortForwardResponse> = port_forwards
.values()
.map(|s| PortForwardResponse {
id: s.id.clone(),
cluster_id: s.cluster_id.clone(),
namespace: s.namespace.clone(),
pod: s.pod.clone(),
container_port: s.ports.first().copied().unwrap_or(0),
local_port: s.local_ports.first().copied().unwrap_or(0),
status: match s.status {
crate::kube::PortForwardStatus::Active => "Active".to_string(),
crate::kube::PortForwardStatus::Stopped => "Stopped".to_string(),
crate::kube::PortForwardStatus::Error(ref e) => e.clone(),
},
})
.collect();
Ok(forwards)
}
fn extract_context(_content: &str) -> Result<String, String> {
Ok("default".to_string())
}
fn extract_server_url(_content: &str) -> Result<String, String> {
Ok("unknown".to_string())
}

View File

@ -5,5 +5,6 @@ pub mod db;
pub mod docs; pub mod docs;
pub mod image; pub mod image;
pub mod integrations; pub mod integrations;
pub mod kube;
pub mod shell; pub mod shell;
pub mod system; pub mod system;

View File

@ -0,0 +1,22 @@
pub struct ClusterClient {
pub id: String,
pub name: String,
pub context: String,
pub server_url: String,
}
impl ClusterClient {
pub fn new(
id: String,
name: String,
context: String,
server_url: String,
) -> Self {
Self {
id,
name,
context,
server_url,
}
}
}

View File

@ -0,0 +1,7 @@
pub mod client;
pub mod portforward;
pub mod refresh;
pub use client::ClusterClient;
pub use portforward::{PortForwardSession, PortForwardStatus};
pub use refresh::RefreshRegistry;

View File

@ -0,0 +1,47 @@
pub struct PortForwardSession {
pub id: String,
pub cluster_id: String,
pub namespace: String,
pub pod: String,
pub container: Option<String>,
pub ports: Vec<u16>,
pub local_ports: Vec<u16>,
pub status: PortForwardStatus,
}
pub enum PortForwardStatus {
Active,
Stopped,
Error(String),
}
impl PortForwardSession {
pub fn new(
id: String,
cluster_id: String,
namespace: String,
pod: String,
container: Option<String>,
ports: Vec<u16>,
local_ports: Vec<u16>,
) -> Self {
Self {
id,
cluster_id,
namespace,
pod,
container,
ports,
local_ports,
status: PortForwardStatus::Active,
}
}
pub fn stop(&mut self) {
self.status = PortForwardStatus::Stopped;
}
pub fn is_active(&self) -> bool {
matches!(self.status, PortForwardStatus::Active)
}
}

View File

@ -0,0 +1,35 @@
use std::collections::HashMap;
use tokio::sync::RwLock;
use std::sync::Arc;
pub struct RefreshRegistry {
domains: HashMap<String, Domain>,
}
impl Default for RefreshRegistry {
fn default() -> Self {
Self::new()
}
}
pub struct Domain {
pub name: String,
pub refresh_interval: std::time::Duration,
pub data: Arc<RwLock<HashMap<String, serde_json::Value>>>,
}
impl RefreshRegistry {
pub fn new() -> Self {
Self {
domains: HashMap::new(),
}
}
pub async fn register_domain(&mut self, domain: Domain) {
self.domains.insert(domain.name.clone(), domain);
}
pub async fn get_domain(&self, name: &str) -> Option<&Domain> {
self.domains.get(name)
}
}

View File

@ -4,6 +4,7 @@ pub mod commands;
pub mod db; pub mod db;
pub mod docs; pub mod docs;
pub mod integrations; pub mod integrations;
pub mod kube;
pub mod mcp; pub mod mcp;
pub mod ollama; pub mod ollama;
pub mod pii; pub mod pii;
@ -40,6 +41,9 @@ pub fn run() {
integration_webviews: Arc::new(Mutex::new(std::collections::HashMap::new())), integration_webviews: Arc::new(Mutex::new(std::collections::HashMap::new())),
mcp_connections: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())), mcp_connections: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())),
pending_approvals: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())), pending_approvals: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())),
clusters: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())),
port_forwards: Arc::new(tokio::sync::Mutex::new(std::collections::HashMap::new())),
refresh_registry: Arc::new(tokio::sync::Mutex::new(crate::kube::RefreshRegistry::new())),
}; };
let stronghold_salt = format!( let stronghold_salt = format!(
"tftsr-stronghold-salt-v1-{:x}", "tftsr-stronghold-salt-v1-{:x}",
@ -170,6 +174,13 @@ pub fn run() {
commands::shell::respond_to_shell_approval, commands::shell::respond_to_shell_approval,
commands::shell::list_command_executions, commands::shell::list_command_executions,
commands::shell::check_kubectl_installed, commands::shell::check_kubectl_installed,
// Kubernetes Management
commands::kube::add_cluster,
commands::kube::remove_cluster,
commands::kube::list_clusters,
commands::kube::start_port_forward,
commands::kube::stop_port_forward,
commands::kube::list_port_forwards,
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("Error running Troubleshooting and RCA Assistant application"); .expect("Error running Troubleshooting and RCA Assistant application");

View File

@ -91,6 +91,12 @@ pub struct AppState {
/// Pending shell command approvals: approval_id -> response channel /// Pending shell command approvals: approval_id -> response channel
pub pending_approvals: pub pending_approvals:
Arc<TokioMutex<HashMap<String, tokio::sync::oneshot::Sender<ApprovalResponse>>>>, Arc<TokioMutex<HashMap<String, tokio::sync::oneshot::Sender<ApprovalResponse>>>>,
/// Kubernetes cluster clients: cluster_id -> client
pub clusters: Arc<TokioMutex<HashMap<String, crate::kube::ClusterClient>>>,
/// Port forwarding sessions: session_id -> session
pub port_forwards: Arc<TokioMutex<HashMap<String, crate::kube::PortForwardSession>>>,
/// Refresh registry for domain-based data fetching
pub refresh_registry: Arc<TokioMutex<crate::kube::RefreshRegistry>>,
} }
/// Determine the application data directory. /// Determine the application data directory.