mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 16:02:29 +00:00
reland(workspace): set project location from the create-worktree host picker (#14965)
* feat(workspace): reland set project location from the create-worktree host picker Relands #14868 (reverted by #14912) with a fix for the regression that caused the revert: setting a project location could change the path before Orca used it. The retarget-after-setup path read the raw store record to find a just-created setup, because the memoized picker options had not refreshed yet: useAppStore.getState().projectHostSetups.find( (candidate) => candidate.id === setupId && candidate.setupState === 'ready' ) That hand-rolls a second selection path that skips every rule the option builder applies — repo eligibility, ephemeral-VM and runtime-owned SSH host exclusion, and the one-setup-per-host dedupe whose own comment notes that resolveWorkspaceCreationTarget takes the first project+host match and ignores the rest. So the composer could be retargeted at a setup other than the canonical one for that host, pointing creation at a different location than the one chosen. Resolves through buildProjectHostSetupOptions against fresh store state instead, so the fallback and the steady-state picker agree by construction. STA-4547 * fix(workspace): sanitize the clone prefill and drop an abandoned set-location Review follow-ups on this PR. The "Clone from URL" prefill seeded the field with the verbatim `git remote` URL, which can embed a PAT (`https://x-access-token:ghp_...@github.com/...`). The clone then runs on the *target* host, writing that token into its .git/config — a credential the user never typed into this flow, now readable by anyone on a shared host. Strip it with the same sanitizer `getProvisionedRootRecipeRepoUrl` already applies to the ephemeral-VM recipe URL. Extracted to resolveProjectCloneUrlPrefill so the rule is directly testable. The dialog also stays dismissable while a submit is in flight, and an SSH clone is unbounded. A clone the user backed out of minutes earlier still called onReady, silently moving the run target and resetting start-from under a form they had since pointed at another host. Drop the result if the dialog went away. * fix(workspace): re-arm the abandoned guard on mount StrictMode runs mount/cleanup/mount, so latching `abandoned` on the first cleanup left it true for the rest of the session and permanently suppressed onReady — the app wraps its root in StrictMode. Reset it on mount.
This commit is contained in:
@@ -93,6 +93,8 @@ export function isProjectRemoteIdentityPending(
|
||||
return repo.gitRemoteIdentity === undefined && !hasProjectRemoteIdentity(repo)
|
||||
}
|
||||
|
||||
const HOST_LOCAL_PROJECT_ID_PREFIX = 'repo:'
|
||||
|
||||
export function getProjectIdentityKey(
|
||||
repo: Pick<Repo, 'id' | 'upstream' | 'repoIcon' | 'gitRemoteIdentity'>
|
||||
): string {
|
||||
@@ -104,7 +106,16 @@ export function getProjectIdentityKey(
|
||||
if (gitRemoteIdentity) {
|
||||
return `git:${gitRemoteIdentity.canonicalKey}`
|
||||
}
|
||||
return `repo:${repo.id}`
|
||||
return `${HOST_LOCAL_PROJECT_ID_PREFIX}${repo.id}`
|
||||
}
|
||||
|
||||
/**
|
||||
* True for the `repo:<id>` fallback above — a folder project, or a git repo with no
|
||||
* remote. The id is a per-host repo id, so the same project on another host derives a
|
||||
* different one and can never be matched there.
|
||||
*/
|
||||
export function isHostLocalProjectId(projectId: string): boolean {
|
||||
return projectId.startsWith(HOST_LOCAL_PROJECT_ID_PREFIX)
|
||||
}
|
||||
|
||||
export function getProjectIdForProviderIdentity(identity: ProjectProviderIdentity): string {
|
||||
|
||||
Reference in New Issue
Block a user