Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,29 @@ jobs:
working-directory: apps/desktop
run: bun run install:deps

# `bun install --ignore-scripts` skips postinstalls for safety; the
# @vscode/ripgrep package uses its postinstall to download the
# platform-specific ripgrep binary. workspace-fs tests that exercise
# the streaming / multiline paths need that binary, so run the
# postinstall explicitly for just this package.
- name: Download bundled ripgrep binary
# @vscode/ripgrep ships the platform binary in different ways across
# versions: pre-1.18 used a postinstall script (lib/postinstall.js),
# 1.18+ uses optional platform-specific subpackages (e.g. @vscode/ripgrep-linux-x64).
# workspace-fs tests need the binary on disk, so handle both shapes.
- name: Ensure ripgrep binary is present
run: |
rg_pkg=$(ls -d node_modules/.bun/@vscode+ripgrep@*/node_modules/@vscode/ripgrep | head -1)
if [ -z "$rg_pkg" ]; then
echo "::error::@vscode/ripgrep not found in node_modules"
exit 1
fi
node "$rg_pkg/lib/postinstall.js"
if [ -f "$rg_pkg/lib/postinstall.js" ]; then
node "$rg_pkg/lib/postinstall.js"
fi
# 1.18+ optional-package layout: link the platform binary into bin/
if [ ! -x "$rg_pkg/bin/rg" ]; then
platform_pkg=$(ls -d node_modules/.bun/@vscode+ripgrep-linux-x64@*/node_modules/@vscode/ripgrep-linux-x64 2>/dev/null | head -1)
if [ -n "$platform_pkg" ] && [ -x "$platform_pkg/bin/rg" ]; then
mkdir -p "$rg_pkg/bin"
cp "$platform_pkg/bin/rg" "$rg_pkg/bin/rg"
chmod +x "$rg_pkg/bin/rg"
fi
fi
ls -la "$rg_pkg/bin" || true

- name: Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { useHostTargetUrl } from "./useHostTargetUrl";
export { resolveHostUrl } from "./resolveHostUrl";
export { useHostUrl } from "./useHostTargetUrl";
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { buildHostRoutingKey } from "@superset/shared/host-routing";
import { env } from "renderer/env.renderer";

/**
* Pure resolver: hostId + machineId + activeHostUrl + organizationId → URL.
* Hosts other than the local machine are reached via relay; the local
* machine is reached directly via electronTrpc through `activeHostUrl`.
*
* Guaranteed-non-null inputs are typed as required because callers inside
* `_authenticated/` get organizationId from the route guard. A null at call
* time is a programmer error, not a runtime UX state.
*/
export function resolveHostUrl(args: {
hostId: string;
machineId: string | null;
activeHostUrl: string | null;
organizationId: string;
}): string | null {
if (args.hostId === args.machineId) return args.activeHostUrl;
const routingKey = buildHostRoutingKey(args.organizationId, args.hostId);
return `${env.RELAY_URL}/hosts/${routingKey}`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@ import { buildHostRoutingKey } from "@superset/shared/host-routing";
import { useMemo } from "react";
import { env } from "renderer/env.renderer";
import { authClient } from "renderer/lib/auth-client";
import type { WorkspaceHostTarget } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/components/DevicePicker/types";
import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider";

export function useHostTargetUrl(
hostTarget: WorkspaceHostTarget | null | undefined,
): string | null {
const { activeHostUrl } = useLocalHostService();
/**
* Resolves a host machineId to a host-service URL. `null` (or `hostId ===
* machineId`) routes through the local electronTrpc proxy; any other id
* routes through the relay tunnel.
*/
export function useHostUrl(hostId: string | null | undefined): string | null {
const { machineId, activeHostUrl } = useLocalHostService();
const { data: session } = authClient.useSession();
const activeOrganizationId = session?.session?.activeOrganizationId ?? null;

return useMemo(() => {
if (!hostTarget) return null;
if (hostTarget.kind === "local") return activeHostUrl;
if (hostId === undefined) return null;
if (hostId === null || hostId === machineId) return activeHostUrl;
if (!activeOrganizationId) return null;
const routingKey = buildHostRoutingKey(
activeOrganizationId,
hostTarget.hostId,
);
const routingKey = buildHostRoutingKey(activeOrganizationId, hostId);
return `${env.RELAY_URL}/hosts/${routingKey}`;
}, [hostTarget, activeOrganizationId, activeHostUrl]);
}, [hostId, machineId, activeOrganizationId, activeHostUrl]);
}
101 changes: 0 additions & 101 deletions apps/desktop/src/renderer/lib/pending-attachment-store.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useState } from "react";
import { EmojiTextInput } from "renderer/components/EmojiTextInput";
import { MarkdownEditor } from "renderer/components/MarkdownEditor";
import { apiTrpcClient } from "renderer/lib/api-trpc-client";
import type { WorkspaceHostTarget } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/components/DevicePicker/types";
import { useProjectFileSearch } from "../../../hooks/useProjectFileSearch";

export function AutomationBody({
Expand All @@ -20,11 +19,8 @@ export function AutomationBody({
apiTrpcClient.automation.update.mutate({ id: automation.id, ...patch }),
});

const hostTarget: WorkspaceHostTarget = automation.targetHostId
? { kind: "host", hostId: automation.targetHostId }
: { kind: "local" };
const searchFiles = useProjectFileSearch({
hostTarget,
hostId: automation.targetHostId ?? null,
projectId: automation.v2ProjectId,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { useEnabledAgents } from "renderer/hooks/useEnabledAgents";
import { apiTrpcClient } from "renderer/lib/api-trpc-client";
import { DevicePicker } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/components/DevicePicker";
import { useWorkspaceHostOptions } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/components/DevicePicker/hooks/useWorkspaceHostOptions/useWorkspaceHostOptions";
import type { WorkspaceHostTarget } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/components/DevicePicker/types";
import { AgentPicker } from "../../../components/AgentPicker";
import { ProjectPicker } from "../../../components/ProjectPicker";
import { SchedulePicker } from "../../../components/SchedulePicker";
Expand Down Expand Up @@ -37,10 +36,7 @@ export function AutomationDetailSidebar({
(p) => p.id === automation.v2ProjectId,
);

const hostTarget: WorkspaceHostTarget =
automation.targetHostId && automation.targetHostId !== localHostId
? { kind: "host", hostId: automation.targetHostId }
: { kind: "local" };
const hostId = automation.targetHostId ?? localHostId ?? null;

const updateMutation = useMutation({
mutationFn: (
Expand Down Expand Up @@ -104,12 +100,8 @@ export function AutomationDetailSidebar({
value={
<DevicePicker
className="-mr-4"
hostTarget={hostTarget}
onSelectHostTarget={(target) => {
const nextHostId =
target.kind === "host"
? target.hostId
: (localHostId ?? null);
hostId={hostId}
onSelectHostId={(nextHostId) => {
updateMutation.mutate({ targetHostId: nextHostId });
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { useEnabledAgents } from "renderer/hooks/useEnabledAgents";
import { apiTrpcClient } from "renderer/lib/api-trpc-client";
import { DevicePicker } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/components/DevicePicker";
import { useWorkspaceHostOptions } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/components/DevicePicker/hooks/useWorkspaceHostOptions/useWorkspaceHostOptions";
import type { WorkspaceHostTarget } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/components/DevicePicker/types";
import { hideAll as hideAllTippy } from "tippy.js";
import { useProjectFileSearch } from "../../hooks/useProjectFileSearch";
import { useRecentProjects } from "../../hooks/useRecentProjects";
Expand Down Expand Up @@ -51,9 +50,7 @@ export function CreateAutomationDialog({
const [view, setView] = useState<"compose" | "gallery">("compose");
const [name, setName] = useState("");
const [prompt, setPrompt] = useState("");
const [hostTarget, setHostTarget] = useState<WorkspaceHostTarget>({
kind: "local",
});
const [hostId, setHostId] = useState<string | null>(null);
const [selectedProjectId, setSelectedProjectId] = useState<string | null>(
null,
);
Expand All @@ -65,7 +62,7 @@ export function CreateAutomationDialog({
const recentProjects = useRecentProjects();
const { agents: enabledAgents } = useEnabledAgents();
const searchFiles = useProjectFileSearch({
hostTarget,
hostId,
projectId: selectedProjectId,
});
const selectedProject = recentProjects.find(
Expand Down Expand Up @@ -102,16 +99,15 @@ export function CreateAutomationDialog({
setView("compose");
setName("");
setPrompt("");
setHostTarget({ kind: "local" });
setHostId(null);
setSelectedProjectId(null);
setAgentType("claude");
setRrule(DEFAULT_RRULE);
setV2WorkspaceId(null);
}
}, [open]);

const targetHostId =
hostTarget.kind === "host" ? hostTarget.hostId : localHostId;
const targetHostId = hostId ?? localHostId;

const createMutation = useMutation({
mutationFn: () => {
Expand Down Expand Up @@ -233,9 +229,9 @@ export function CreateAutomationDialog({
<div className="flex items-center gap-2">
<DevicePicker
className="w-[160px]"
hostTarget={hostTarget}
onSelectHostTarget={(next) => {
setHostTarget(next);
hostId={hostId}
onSelectHostId={(next) => {
setHostId(next);
setV2WorkspaceId(null);
}}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { useCallback } from "react";
import type { FileMentionSearchFn } from "renderer/components/MarkdownEditor/components/FileMention";
import { useHostTargetUrl } from "renderer/hooks/host-service/useHostTargetUrl";
import { useHostUrl } from "renderer/hooks/host-service/useHostTargetUrl";
import { getHostServiceClientByUrl } from "renderer/lib/host-service-client";
import type { WorkspaceHostTarget } from "renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/components/DevicePicker/types";

const SEARCH_LIMIT = 15;

export function useProjectFileSearch({
hostTarget,
hostId,
projectId,
}: {
hostTarget: WorkspaceHostTarget;
hostId: string | null;
projectId: string | null;
}): FileMentionSearchFn | undefined {
const hostUrl = useHostTargetUrl(hostTarget);
const hostUrl = useHostUrl(hostId);

return useCallback<FileMentionSearchFn>(
async (query) => {
Expand Down
Loading
Loading