diff --git a/packages/app/src/components/dialog-select-directory-v2.tsx b/packages/app/src/components/dialog-select-directory-v2.tsx index e3bf4c19af..c5aaae20d7 100644 --- a/packages/app/src/components/dialog-select-directory-v2.tsx +++ b/packages/app/src/components/dialog-select-directory-v2.tsx @@ -77,7 +77,7 @@ export function DialogSelectDirectoryV2(props: DialogSelectDirectoryV2Props) { config: "", worktree: location.project.directory, directory: location.directory, - home: "", + home: location.home, }), ) .catch(() => undefined), diff --git a/packages/app/src/components/dialog-select-directory.tsx b/packages/app/src/components/dialog-select-directory.tsx index 33c8a73d41..a98d221189 100644 --- a/packages/app/src/components/dialog-select-directory.tsx +++ b/packages/app/src/components/dialog-select-directory.tsx @@ -68,7 +68,7 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) { config: "", worktree: location.project.directory, directory: location.directory, - home: "", + home: location.home, }), ) .catch(() => undefined), diff --git a/packages/app/src/context/global-sync/bootstrap.ts b/packages/app/src/context/global-sync/bootstrap.ts index d562c1cb0b..d0349d68b4 100644 --- a/packages/app/src/context/global-sync/bootstrap.ts +++ b/packages/app/src/context/global-sync/bootstrap.ts @@ -276,7 +276,7 @@ export const loadPathQuery = ( config: "", worktree: location.project.directory, directory: location.directory, - home: "", + home: location.home, })), }) diff --git a/packages/client/src/effect/api/api.ts b/packages/client/src/effect/api/api.ts index 80df5a87e4..7b051a7779 100644 --- a/packages/client/src/effect/api/api.ts +++ b/packages/client/src/effect/api/api.ts @@ -61,7 +61,7 @@ export interface ServerApi { export type Endpoint2_0Input = { readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined } -export type Endpoint2_0Output = Location.Info +export type Endpoint2_0Output = Location.Details export type LocationGetOperation = (input?: Endpoint2_0Input) => Effect.Effect export interface LocationApi { diff --git a/packages/client/src/promise/generated/types.ts b/packages/client/src/promise/generated/types.ts index 8c8b6295a6..80c8404316 100644 --- a/packages/client/src/promise/generated/types.ts +++ b/packages/client/src/promise/generated/types.ts @@ -2519,6 +2519,7 @@ export type LocationGetOutput = { directory: string workspaceID?: string project: { id: string; directory: string; canonical: string } + home: string } export type AgentListInput = { diff --git a/packages/core/src/location.ts b/packages/core/src/location.ts index ac27cb6ca9..5786fa9f7b 100644 --- a/packages/core/src/location.ts +++ b/packages/core/src/location.ts @@ -1,12 +1,12 @@ import { Context, Effect, Layer } from "effect" -import { Info, Ref, response } from "@opencode-ai/schema/location" +import { Details, Info, Ref, response } from "@opencode-ai/schema/location" import { Project } from "./project" import { LayerNode } from "@opencode-ai/util/effect/layer-node" import { makeLocationNode, tags } from "@opencode-ai/util/effect/app-node" export * as Location from "./location" -export { Info, Ref, response } +export { Details, Info, Ref, response } export interface Interface extends Info { readonly vcs?: Project.Vcs diff --git a/packages/protocol/src/groups/location.ts b/packages/protocol/src/groups/location.ts index 4723b8a46a..5f1c18d7d8 100644 --- a/packages/protocol/src/groups/location.ts +++ b/packages/protocol/src/groups/location.ts @@ -30,7 +30,7 @@ export const LocationGroup = HttpApiGroup.make("server.location") .add( HttpApiEndpoint.get("location.get", "/api/location", { query: LocationQuery, - success: Location.Info, + success: Location.Details, }) .annotateMerge(locationQueryOpenApi) .annotateMerge( diff --git a/packages/schema/src/location.ts b/packages/schema/src/location.ts index d9104435c6..d7c4a20284 100644 --- a/packages/schema/src/location.ts +++ b/packages/schema/src/location.ts @@ -21,6 +21,11 @@ export class Info extends Schema.Class("Location.Info")({ }), }) {} +export class Details extends Schema.Class
("Location.Details")({ + ...Info.fields, + home: AbsolutePath, +}) {} + export function response(data: S) { return Schema.Struct({ location: Info, data }) } diff --git a/packages/schema/test/location.test.ts b/packages/schema/test/location.test.ts new file mode 100644 index 0000000000..e2e0b44302 --- /dev/null +++ b/packages/schema/test/location.test.ts @@ -0,0 +1,14 @@ +import { expect, test } from "bun:test" +import { Schema } from "effect" +import { Location } from "../src/location.js" + +const details = { + directory: "/project", + project: { id: "project", directory: "/project", canonical: "/project" }, + home: "/home/user", +} + +test("Location.Details includes the server home directory", () => { + expect(Schema.decodeUnknownSync(Location.Details)(details).home).toBe("/home/user") + expect(() => Schema.decodeUnknownSync(Location.Details)({ ...details, home: undefined })).toThrow() +}) diff --git a/packages/server/src/handlers/location.ts b/packages/server/src/handlers/location.ts index ded8c8c2e0..c9ca2fe557 100644 --- a/packages/server/src/handlers/location.ts +++ b/packages/server/src/handlers/location.ts @@ -1,6 +1,8 @@ import { Location } from "@opencode-ai/core/location" +import { AbsolutePath } from "@opencode-ai/schema/schema" import { Effect } from "effect" import { HttpApiBuilder } from "effect/unstable/httpapi" +import os from "node:os" import { Api } from "../api" export const LocationHandler = HttpApiBuilder.group(Api, "server.location", (handlers) => @@ -8,10 +10,11 @@ export const LocationHandler = HttpApiBuilder.group(Api, "server.location", (han "location.get", Effect.fn(function* () { const location = yield* Location.Service - return new Location.Info({ + return new Location.Details({ directory: location.directory, workspaceID: location.workspaceID, project: location.project, + home: AbsolutePath.make(os.homedir()), }) }), ),