diff --git a/docs/agent-skill-sharing-implementation-checklist.md b/docs/agent-skill-sharing-implementation-checklist.md index 1a52c9a9751..fd01717043c 100644 --- a/docs/agent-skill-sharing-implementation-checklist.md +++ b/docs/agent-skill-sharing-implementation-checklist.md @@ -889,9 +889,8 @@ does not mean the surrounding phase is complete. - [x] Show whether a package came from Orca Cloud and its accessible version history. - [x] Refresh discovery and installation state after local or remote actions. - [x] Keep Cloud deletion, share revocation, and local removal clearly separate. -- [ ] Add installed-bundle update, rollback, install-on-another-machine, inspect, and remove actions. - Grouped inspection, selective update/rollback, and one-surface safe removal are implemented; - install-on-another-machine remains. +- [x] Add installed-bundle update, rollback, install-on-another-machine, inspect, and remove actions. + An active unlisted link opens the existing destination-aware installer for another machine. - [x] Add shared-bundle copy-link, access, revoke, publish-version, and Cloud-delete actions. Exact full-bundle version publishing reuses its package; ambiguous or partial managed-bundle selections create a new package rather than mutating the wrong history. diff --git a/src/renderer/src/components/skills/SkillInstallManagementDialog.test.tsx b/src/renderer/src/components/skills/SkillInstallManagementDialog.test.tsx index e66b20098b8..c9612937551 100644 --- a/src/renderer/src/components/skills/SkillInstallManagementDialog.test.tsx +++ b/src/renderer/src/components/skills/SkillInstallManagementDialog.test.tsx @@ -165,7 +165,8 @@ beforeEach(() => { useAppStore.setState({ runtimeEnvironments: [], sshConnectionStates: new Map(), - sshTargetLabels: new Map() + sshTargetLabels: new Map(), + pendingSkillShareId: null }) }) @@ -344,6 +345,33 @@ describe('SkillInstallManagementDialog', () => { expect(skills.installPackageVersion).not.toHaveBeenCalled() }) + it('opens an active bundle link in the existing machine installer', async () => { + const installed = [bundleInstall('alpha-skill'), bundleInstall('beta-skill')] + const versions = [bundleVersion('ver_1', ['alpha-skill', 'beta-skill'])] + const details = { + ...packageDetails(versions), + management: { + shares: [ + { + id: 'share_bundle', + url: 'https://app.orca.dev/skills/share/share_bundle', + createdAt: '2026-08-12T00:00:00.000Z' + } + ] + } + } + const skills = skillsApi(installed, versions, details) + const onOpenChange = vi.fn() + Object.defineProperty(window, 'api', { configurable: true, value: { skills } }) + render() + + fireEvent.click(await screen.findByRole('button', { name: /2 skill bundle.*ver_1/ })) + fireEvent.click(await screen.findByRole('button', { name: 'Install on another machine' })) + + expect(onOpenChange).toHaveBeenCalledWith(false) + expect(useAppStore.getState().pendingSkillShareId).toBe('share_bundle') + }) + it('removes a bundle with one confirmation and preserves modified skills', async () => { const installed = [bundleInstall('alpha-skill'), bundleInstall('beta-skill', 'modified')] const skills = skillsApi(installed, [bundleVersion('ver_1', ['alpha-skill', 'beta-skill'])]) diff --git a/src/renderer/src/components/skills/SkillInstallManagementDialog.tsx b/src/renderer/src/components/skills/SkillInstallManagementDialog.tsx index e4f2098b474..9217639f5cd 100644 --- a/src/renderer/src/components/skills/SkillInstallManagementDialog.tsx +++ b/src/renderer/src/components/skills/SkillInstallManagementDialog.tsx @@ -310,8 +310,6 @@ export function SkillInstallManagementDialog({ setError(null) setNotice(null) setResult(null) - setBundleResult(null) - setConfirmRemove(false) onOpenChange(false) } @@ -377,6 +375,10 @@ export function SkillInstallManagementDialog({ onVersionChange={setVersionId} onInstall={(discardLocal) => void installVersion(discardLocal)} onCancelInstall={() => void cancelInstall()} + onInstallAnotherMachine={(shareId) => { + close() + useAppStore.getState().openSkillShare(shareId) + }} onRemove={(discardLocal) => void remove(discardLocal)} onCloudChanged={refreshPackageDetails} onPackageDeleted={packageDeleted} diff --git a/src/renderer/src/components/skills/SkillManagedInstallDetails.tsx b/src/renderer/src/components/skills/SkillManagedInstallDetails.tsx index 067ee504fa7..bc660e7ebc7 100644 --- a/src/renderer/src/components/skills/SkillManagedInstallDetails.tsx +++ b/src/renderer/src/components/skills/SkillManagedInstallDetails.tsx @@ -1,4 +1,4 @@ -import { AlertTriangle, Loader2, RotateCcw, Trash2 } from 'lucide-react' +import { AlertTriangle, Loader2, MonitorUp, RotateCcw, Trash2 } from 'lucide-react' import { Button } from '@/components/ui/button' import { Select, @@ -30,6 +30,7 @@ export function SkillManagedInstallDetails({ onVersionChange, onInstall, onCancelInstall, + onInstallAnotherMachine, onRemove, onCloudChanged, onPackageDeleted @@ -47,6 +48,7 @@ export function SkillManagedInstallDetails({ onVersionChange: (versionId: string) => void onInstall: (discardLocal?: boolean) => void onCancelInstall: () => void + onInstallAnotherMachine: (shareId: string) => void onRemove: (discardLocal?: boolean) => void onCloudChanged: () => Promise onPackageDeleted: () => void @@ -56,6 +58,7 @@ export function SkillManagedInstallDetails({ selectedVersion?.manifest && 'skills' in selectedVersion.manifest ? selectedVersion.manifest : null + const activeShare = details.management?.shares[0] return (
@@ -125,6 +128,16 @@ export function SkillManagedInstallDetails({ {copy.cancelInstall} ) : null} + {activeShare ? ( + + ) : null}