mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 18:37:24 +00:00
fix: align group workspace owners and create result consumers
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { getRepoRoutingConnectionId } from '../../shared/execution-host'
|
||||
import type { ProjectGroup } from '../../shared/project-group-types'
|
||||
import type { Repo } from '../../shared/repo-types'
|
||||
import type { FolderWorkspace } from '../../shared/folder-workspaces/folder-workspace-types'
|
||||
@@ -38,10 +39,11 @@ export function createFolderWorkspaceConnectionResolver(
|
||||
children.set(group.parentGroupId, siblings)
|
||||
}
|
||||
for (const repo of repos) {
|
||||
const connectionId = getRepoRoutingConnectionId(repo)
|
||||
const summary: Connections = {
|
||||
count: 1,
|
||||
local: !repo.connectionId,
|
||||
first: repo.connectionId || undefined,
|
||||
local: !connectionId,
|
||||
first: connectionId ?? undefined,
|
||||
multiple: false
|
||||
}
|
||||
if (repo.projectGroupId) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createFolderWorkspaceConnectionResolver } from './folder-workspace-connection-index'
|
||||
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
||||
|
||||
import { tmpdir } from 'node:os'
|
||||
@@ -41,6 +42,32 @@ function makeRepo(overrides: Partial<Repo> = {}): Repo {
|
||||
}
|
||||
|
||||
describe('folder workspace path status', () => {
|
||||
it.each([undefined, 'stale-target'])(
|
||||
'routes indexed group teardown to the canonical SSH owner with legacy target %s',
|
||||
(connectionId) => {
|
||||
const workspace: FolderWorkspace = {
|
||||
id: 'saved',
|
||||
projectGroupId: 'group-1',
|
||||
name: 'Saved',
|
||||
folderPath: '/workspace/task',
|
||||
linkedTask: null,
|
||||
comment: '',
|
||||
isArchived: false,
|
||||
isUnread: false,
|
||||
isPinned: false,
|
||||
sortOrder: 0,
|
||||
lastActivityAt: 0,
|
||||
createdAt: 0,
|
||||
updatedAt: 0
|
||||
}
|
||||
const resolve = createFolderWorkspaceConnectionResolver(
|
||||
[makeGroup()],
|
||||
[makeRepo({ executionHostId: 'ssh:prod', connectionId })]
|
||||
)
|
||||
expect(resolve(workspace)).toEqual({ kind: 'ssh', connectionId: 'prod' })
|
||||
}
|
||||
)
|
||||
|
||||
it.each([false, true])(
|
||||
'reports canonical owner ambiguity through the persisted folder status route (%s)',
|
||||
async (reverse) => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { selectFolderWorkspaces } from '../workspaces/catalog/workspace-record-fields'
|
||||
import { checkRuntimeGroupHooks } from '@/runtime/repo-hooks/runtime-hooks-client'
|
||||
import { confirmInspectedArchiveHook } from '@/runtime/repo-hooks/ensure-hooks-confirmed'
|
||||
import { projectGroupHookOwner } from '../../../../shared/workspace-hook-owner'
|
||||
@@ -103,7 +104,7 @@ export function createFolderWorkspaceDeleteAction(
|
||||
return false
|
||||
}
|
||||
set((s) => ({
|
||||
folderWorkspaces: s.folderWorkspaces.filter(
|
||||
folderWorkspaces: selectFolderWorkspaces(s).filter(
|
||||
(workspace) =>
|
||||
workspace.id !== folderWorkspaceId ||
|
||||
getFolderWorkspaceHostId(workspace, s.projectGroups) !== ownerHostId
|
||||
@@ -116,7 +117,7 @@ export function createFolderWorkspaceDeleteAction(
|
||||
(owner, scope) =>
|
||||
owner.workspaceId === workspaceKey && launchDraftSource(scope)?.origin === origin
|
||||
)
|
||||
if (!get().folderWorkspaces.some((workspace) => workspace.id === folderWorkspaceId)) {
|
||||
if (!selectFolderWorkspaces(get()).some((workspace) => workspace.id === folderWorkspaceId)) {
|
||||
// Folder/worktree browser registry keys coincide; retire Chromium guests before renderer cleanup.
|
||||
await Promise.resolve(get().shutdownWorktreeBrowsers(workspaceKey))
|
||||
// Why here too: a server-hosted folder's local pages use this desktop's profile, which the server can't reach.
|
||||
@@ -157,7 +158,7 @@ export function createFolderWorkspaceDeleteAction(
|
||||
worktreeId: workspaceKey,
|
||||
executionHostId: ownerHostId,
|
||||
worktreeName:
|
||||
state.folderWorkspaces.find(
|
||||
selectFolderWorkspaces(state).find(
|
||||
(workspace) =>
|
||||
workspace.id === folderWorkspaceId &&
|
||||
getFolderWorkspaceHostId(workspace, state.projectGroups) === ownerHostId
|
||||
|
||||
@@ -114,12 +114,12 @@ export function createProjectGroupMutationActions(
|
||||
))
|
||||
const folders =
|
||||
supported && ownerHostId && target.kind === 'environment'
|
||||
? (
|
||||
? selectFolderWorkspaces(
|
||||
await callRuntimeRpc<{ folderWorkspaces: FolderWorkspace[] }>(
|
||||
target,
|
||||
'folderWorkspace.list'
|
||||
)
|
||||
).folderWorkspaces.map((row) =>
|
||||
).map((row) =>
|
||||
adoptFromEndpoint(target, {
|
||||
kind: 'folderWorkspace',
|
||||
row,
|
||||
|
||||
@@ -47,7 +47,7 @@ for (const selection of ['workspace', 'tab'] as const) {
|
||||
if (!target || !other) {
|
||||
throw new Error('Could not create folder workspaces')
|
||||
}
|
||||
return { target: target.id, other: other.id }
|
||||
return { target: target.folderWorkspace.id, other: other.folderWorkspace.id }
|
||||
}, folderPath)
|
||||
const openPalette = async (): Promise<void> => {
|
||||
// Native menu accelerators need desktop focus; drive the same palette through CDP.
|
||||
|
||||
@@ -164,7 +164,7 @@ async function createHostFolderWorkspace(page: Page, folderPath: string): Promis
|
||||
name: 'Paired skill folder E2E',
|
||||
folderPath: path
|
||||
})
|
||||
return workspace.id
|
||||
return workspace.folderWorkspace.id
|
||||
}, folderPath)
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ test.describe('PDF in a folder workspace', () => {
|
||||
if (!folder) {
|
||||
throw new Error('Missing fixture folder')
|
||||
}
|
||||
const worktreeId = `folder:${folder.id}`
|
||||
const worktreeId = `folder:${folder.folderWorkspace.id}`
|
||||
state.setActiveWorktree(worktreeId)
|
||||
state.openFile({
|
||||
filePath,
|
||||
|
||||
@@ -35,7 +35,7 @@ test('Resource Manager ends a tab-less terminal this run spawned', async ({
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
cwd: folderPath,
|
||||
worktreeId: `folder:${folder.id}`,
|
||||
worktreeId: `folder:${folder.folderWorkspace.id}`,
|
||||
initiallyHidden: true
|
||||
})
|
||||
return spawned.id
|
||||
|
||||
@@ -43,7 +43,7 @@ test('names a folder terminal recovered from the daemon after restart without an
|
||||
cols: 80,
|
||||
rows: 24,
|
||||
cwd: folderPath,
|
||||
worktreeId: `folder:${folder.id}`,
|
||||
worktreeId: `folder:${folder.folderWorkspace.id}`,
|
||||
initiallyHidden: true
|
||||
})
|
||||
}, root)
|
||||
|
||||
@@ -37,13 +37,15 @@ test('cycles visible folder workspaces with shortcuts and focused list arrows',
|
||||
if (!folder || !repo || !first || !last) {
|
||||
throw new Error('Expected two git worktrees and one folder workspace')
|
||||
}
|
||||
await store.getState().updateFolderWorkspace(folder.id, { workspaceStatus: 'in-progress' })
|
||||
await store
|
||||
.getState()
|
||||
.updateFolderWorkspace(folder.folderWorkspace.id, { workspaceStatus: 'in-progress' })
|
||||
// Through the metadata writes, so main's copy of the catalog agrees with the window's.
|
||||
await store.getState().updateWorktreeMeta(first.id, { workspaceStatus: 'todo' })
|
||||
await store.getState().updateWorktreeMeta(last.id, { workspaceStatus: 'completed' })
|
||||
store.getState().setGroupBy('workspace-status')
|
||||
store.setState({ collapsedGroups: new Set() })
|
||||
return [first.id, `folder:${folder.id}`, last.id]
|
||||
return [first.id, `folder:${folder.folderWorkspace.id}`, last.id]
|
||||
}, folderPath)
|
||||
|
||||
expect(
|
||||
|
||||
@@ -79,7 +79,7 @@ test('a restart clears a tier nothing backs, keeps a hook row’s and an SSH hos
|
||||
if (!folder) {
|
||||
throw new Error('Expected a folder workspace')
|
||||
}
|
||||
return folder.id
|
||||
return folder.folderWorkspace.id
|
||||
}, folderPath)
|
||||
folderToRemove = folderPath
|
||||
const goneId = `folder:${goneFolderId}`
|
||||
|
||||
@@ -59,7 +59,7 @@ test('main lists the window’s git and folder workspaces and follows its metada
|
||||
if (!folder || !worktree) {
|
||||
throw new Error('Expected a seeded worktree and a folder workspace')
|
||||
}
|
||||
return { worktreeId: worktree.id, folderKey: `local|folder:${folder.id}` }
|
||||
return { worktreeId: worktree.id, folderKey: `local|folder:${folder.folderWorkspace.id}` }
|
||||
}, folderPath)
|
||||
const rowKey = `local|${worktreeId}`
|
||||
|
||||
|
||||
@@ -39,9 +39,11 @@ for (const theme of ['light', 'dark'] as const) {
|
||||
if (!folder) {
|
||||
throw new Error('Could not create folder workspace')
|
||||
}
|
||||
await store.getState().updateFolderWorkspace(folder.id, { comment: 'Research synthesis' })
|
||||
await store
|
||||
.getState()
|
||||
.updateFolderWorkspace(folder.folderWorkspace.id, { comment: 'Research synthesis' })
|
||||
store.setState({ showSleepingWorkspaces: false })
|
||||
return `folder:${folder.id}`
|
||||
return `folder:${folder.folderWorkspace.id}`
|
||||
},
|
||||
{ folderPath, theme }
|
||||
)
|
||||
|
||||
@@ -235,7 +235,7 @@ test.describe('Toggle Child Workspaces shortcut', () => {
|
||||
if (!folder) {
|
||||
throw new Error('Missing created folder workspace')
|
||||
}
|
||||
return `folder:${folder.id}`
|
||||
return `folder:${folder.folderWorkspace.id}`
|
||||
})
|
||||
const folder = sidebarWorktreeRow(orcaPage, folderKey)
|
||||
await expect(folder).toBeVisible()
|
||||
|
||||
@@ -283,7 +283,7 @@ test.describe('Reveal active workspace button', () => {
|
||||
if (!folder) {
|
||||
throw new Error(`Folder workspace ${index} was not created`)
|
||||
}
|
||||
ids.push(folder.id)
|
||||
ids.push(folder.folderWorkspace.id)
|
||||
}
|
||||
const targetKey = `folder:${ids[0]}` as const
|
||||
store.setState({
|
||||
|
||||
@@ -45,7 +45,7 @@ async function createFolders(page: Page, folders: { name: string; folderPath: st
|
||||
if (!workspace) {
|
||||
throw new Error(`Could not create folder workspace ${entry.name}`)
|
||||
}
|
||||
ids.push(workspace.id)
|
||||
ids.push(workspace.folderWorkspace.id)
|
||||
}
|
||||
return ids
|
||||
}, folders)
|
||||
|
||||
Reference in New Issue
Block a user