Skip to content

Commit

Permalink
feat: Harbor app runs on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
av committed Jan 19, 2025
1 parent ee76426 commit 598c46e
Show file tree
Hide file tree
Showing 14 changed files with 152 additions and 20 deletions.
Binary file modified app/bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@tauri-apps/api": "^2.1.1",
"@tauri-apps/plugin-autostart": "2.2.0",
"@tauri-apps/plugin-fs": "2.2.0",
"@tauri-apps/plugin-os": "~2",
"@tauri-apps/plugin-shell": "2.2.0",
"@tauri-apps/plugin-store": "2.2.0",
"@tauri-apps/plugin-window-state": "2.2.0",
Expand Down
53 changes: 51 additions & 2 deletions app/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ edition = "2021"

[lib]
name = "harbor_lib"
crate-type = ["staticlib","cdylib","rlib"]
crate-type = ["staticlib", "cdylib", "rlib"]

[build-dependencies.tauri-build]
version = "2.0.3"
Expand All @@ -19,6 +19,7 @@ tauri-plugin-shell = "2.2.0"
serde_json = "1"
tauri-plugin-store = "2.2.0"
tauri-plugin-fs = "2.2.0"
tauri-plugin-os = "2"

[dependencies.tauri]
version = "2.1.1"
Expand Down
29 changes: 25 additions & 4 deletions app/src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
"shell:allow-open",
"shell:default",
"shell:allow-spawn",
"os:default",
"store:default",
"fs:default",
"autostart:default",
{
"identifier": "shell:allow-execute",
"allow": [
Expand All @@ -20,6 +24,16 @@
}
]
},
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "wsl.exe",
"cmd": "wsl.exe",
"args": true
}
]
},
{
"identifier": "shell:allow-execute",
"allow": [
Expand All @@ -30,8 +44,16 @@
}
]
},
"store:default",
"fs:default",
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "cmd",
"cmd": "cmd",
"args": true
}
]
},
{
"identifier": "fs:scope",
"allow": [
Expand Down Expand Up @@ -65,7 +87,6 @@
"path": "**/.env"
}
]
},
"autostart:default"
}
]
}
1 change: 1 addition & 0 deletions app/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub fn run() {
MacosLauncher::LaunchAgent,
None,
))
.plugin(tauri_plugin_os::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Expand Down
3 changes: 2 additions & 1 deletion app/src/config/HarborConfigEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { ConfirmModal } from "../ConfirmModal";
import { SearchInput } from "../SearchInput";
import { useSearch } from "../useSearch";
import { ChangeEvent } from "react";
import { runOpen } from "../useOpen";

export const HarborConfigEditor = (
{ config }: { config: HarborConfig },
Expand All @@ -33,7 +34,7 @@ export const HarborConfigEditor = (

const maybeExtra = EXTRA[config.profile.name];
const handleFileOpen = async () => {
await Command.create("open", [config.profile.file]).execute();
await runOpen([config.profile.file]);
};

const handleSave = async () => {
Expand Down
13 changes: 8 additions & 5 deletions app/src/home/ServiceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import { HST, ServiceTags } from "../ServiceTags";
import { HSTColors } from "../ServiceTags";
import { HSTColorOpts } from "../ServiceTags";
import { runHarbor } from "../useHarbor";
import { toasted } from "../utils";
import { resolveResultLines, toasted } from "../utils";
import { runOpen } from "../useOpen";

export const ServiceCard = (
{ service, onUpdate }: { service: HarborService; onUpdate: () => void },
) => {
const [loading, setLoading] = useState(false);

const openService = () => {
runHarbor(["open", service.handle]);
const openService = async () => {
const urlResult = await runHarbor(["url", service.handle]);
const url = resolveResultLines(urlResult).join("");
await runOpen([url]);
};

const toggleService = () => {
Expand Down Expand Up @@ -71,11 +74,11 @@ export const ServiceCard = (
{canLaunch && (
<>
{service.isRunning && (
<span className="inline-block bg-success w-2 h-2 rounded-full">
<span className="inline-block bg-success shrink-0 w-2 h-2 rounded-full">
</span>
)}
{!service.isRunning && (
<span className="inline-block bg-base-content/20 w-2 h-2 rounded-full">
<span className="inline-block bg-base-content/20 shrink-0 w-2 h-2 rounded-full">
</span>
)}
<IconButton
Expand Down
4 changes: 3 additions & 1 deletion app/src/home/Version.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Loader } from "../Loading";
import { Section } from "../Section";
import { useHarbor } from "../useHarbor";
import { resolveResultLines } from "../utils";

export const Version = () => {
const { result, loading, error } = useHarbor(["--version"]);
const output = resolveResultLines(result);

return (
<Section
Expand All @@ -13,7 +15,7 @@ export const Version = () => {
<>
<Loader loading={loading} />
{error && <span>{error.message}</span>}
<span>{result?.stdout}</span>
<span>{output}</span>
</>
}
/>
Expand Down
7 changes: 4 additions & 3 deletions app/src/home/useServiceList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useMemo } from "react";

import { useHarbor } from "../useHarbor";
import { HarborService, serviceMetadata } from "../serviceMetadata";
import { resolveResultLines } from "../utils";

export const isCoreService = (handle: string) => {
return !handle.includes('-');
Expand All @@ -19,10 +20,10 @@ export const useServiceList = () => {
]

const services: HarborService[] = useMemo(() => {
const runningResult = running?.result?.stdout ?? '';
const defaultsResult = defaults?.result?.stdout ?? '';
const runningResult = resolveResultLines(running.result)
const defaultsResult = resolveResultLines(defaults.result)

return all?.result?.stdout.split('\n').filter(s => s.trim()).sort().map(line => {
return resolveResultLines(all.result).filter(s => s.trim()).sort().map(line => {
const handle = line.trim();
const maybeMetadata = serviceMetadata[handle] ?? {};

Expand Down
4 changes: 4 additions & 0 deletions app/src/serviceMetadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,5 +277,9 @@ export const serviceMetadata: Record<string, Partial<HarborService>> = {
kobold: {
tags: [HST.satellite, HST.frontend, HST.backend],
wikiUrl: 'https://github.com/av/harbor/wiki/2.2.16-Backend:-KoboldCpp',
},
agent: {
tags: [HST.builtIn, HST.cli],
wikiUrl: ''
}
};
18 changes: 15 additions & 3 deletions app/src/useHarbor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ import { useCallback, useEffect, useState } from "react";
import { ChildProcess, Command } from "@tauri-apps/plugin-shell";
import { join } from "@tauri-apps/api/path";

import { once } from "./utils";
import { isWindows, once, resolveResultLines } from "./utils";
import { PROFILES_DIR } from "./configMetadata";

export const resolveHarborHome = once(async function __resolveHarborHome() {
const result = await runHarbor(["home"]);
return result?.stdout?.trim();
const path = resolveResultLines(result).join('\n');

if (await isWindows()) {
// On windows, we need to resolve the path first via WSL
const wslResult = await Command.create("wsl.exe", ["-e", "wslpath", "-w", path]).execute();
return resolveResultLines(wslResult).join('\n');
}

return path;
});

export const resolveProfilesDir = once(async function __resolveProfilesDir() {
Expand All @@ -16,7 +24,11 @@ export const resolveProfilesDir = once(async function __resolveProfilesDir() {
});

export async function runHarbor(args: string[]) {
return await Command.create("harbor", args).execute();
if (await isWindows()) {
return await Command.create("wsl.exe", ["-e", "bash", "-lic", `harbor ${args.join(' ')}`]).execute();
} else {
return await Command.create("harbor", args).execute();
}
}

export const useHarborTrigger = (args: string[]) => {
Expand Down
14 changes: 14 additions & 0 deletions app/src/useOpen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Command } from "@tauri-apps/plugin-shell";
import { isWindows } from "./utils";

export async function runOpen(args: string[]) {
try {
if (await isWindows()) {
await Command.create("cmd", ['/c', 'start', ...args]).execute();
} else {
await Command.create("open", args).execute();
}
} catch (e) {
console.error(e);
}
}
22 changes: 22 additions & 0 deletions app/src/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { platform } from '@tauri-apps/plugin-os';
import { ChildProcess } from "@tauri-apps/plugin-shell";

import toast from "react-hot-toast";
import { IconCheck, IconOctagonAlert } from "./Icons";

Expand Down Expand Up @@ -144,4 +147,23 @@ export const debounce = <T extends (...args: any) => ReturnType<T>>(
};

return debounced;
}

export const getOSPlatform = once(async () => {
return platform();
});

export const isWindows = once(async () => {
const platform = await getOSPlatform();
return platform === "windows";
});

const outputBlock = [
'To run a command as administrator (user "root"), use "sudo <command>".',
'See "man sudo_root" for details.',
]

export function resolveResultLines(process: ChildProcess<string> | undefined | null) {
const stdout = process?.stdout ?? '';
return stdout.split('\n').filter(Boolean).filter(line => !outputBlock.includes(line));
}

0 comments on commit 598c46e

Please sign in to comment.