mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 00:02:29 +00:00
* fix(artifacts): gate agent artifact publishing behind an off-by-default capability Public artifact sharing was reachable by any agent through `orca artifacts share`: the Artifacts settings toggle only controlled sidebar visibility, and nothing in the main process checked a capability before minting a public URL. Add `artifactSharingEnabled` (default off) and enforce it in ArtifactCloudService.share/update — before auth, network, or the share-record write — so the CLI, relay-forwarded remote CLI, and IPC paths are all denied. The denial carries a stable `artifact_sharing_disabled` code plus next steps through the RPC error allowlist, so the CLI prints actionable guidance. list, unshare, and delete stay ungated: turning publishing off must not strand already-published links. The capability is absent from the `settings.update` RPC schema, so an agent cannot grant it to itself — only the desktop UI can. Co-authored-by: Orca <help@stably.ai> * fix(artifacts): gate agent artifact publishing behind an off-by-default Publishing is blocked until enabled in Settings → Artifacts. CLI preflights the capability before reading files to avoid unnecessary uploads. RPC surface rejects capability grants so callers cannot self-grant. UI shows opt-in workflow and recovery path when publishing is off. Web clients mirror the host's setting read-only. --------- Co-authored-by: Orca <help@stably.ai>
38 lines
1.5 KiB
TypeScript
38 lines
1.5 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { getDefaultSettings } from './constants'
|
|
import {
|
|
ARTIFACT_SHARING_DISABLED_CODE,
|
|
ArtifactSharingDisabledError,
|
|
assertArtifactSharingAllowed,
|
|
isArtifactSharingEnabled
|
|
} from './artifact-sharing-gate'
|
|
|
|
describe('artifact sharing capability gate', () => {
|
|
it('denies by default and for profiles written before the setting existed', () => {
|
|
expect(isArtifactSharingEnabled(getDefaultSettings('/tmp'))).toBe(false)
|
|
expect(isArtifactSharingEnabled({})).toBe(false)
|
|
expect(isArtifactSharingEnabled(null)).toBe(false)
|
|
expect(isArtifactSharingEnabled(undefined)).toBe(false)
|
|
})
|
|
|
|
it('requires an exact true, so a truthy value on disk cannot open the gate', () => {
|
|
expect(isArtifactSharingEnabled({ artifactSharingEnabled: true })).toBe(true)
|
|
expect(isArtifactSharingEnabled({ artifactSharingEnabled: 'yes' as never })).toBe(false)
|
|
expect(isArtifactSharingEnabled({ artifactSharingEnabled: 1 as never })).toBe(false)
|
|
})
|
|
|
|
it('throws a coded, actionable error when the capability is withheld', () => {
|
|
expect(() => assertArtifactSharingAllowed(() => false)).toThrow(ArtifactSharingDisabledError)
|
|
try {
|
|
assertArtifactSharingAllowed(() => false)
|
|
expect.unreachable('gate must throw')
|
|
} catch (error) {
|
|
expect(error).toMatchObject({
|
|
code: ARTIFACT_SHARING_DISABLED_CODE,
|
|
data: { nextSteps: expect.arrayContaining([expect.stringContaining('Settings')]) }
|
|
})
|
|
}
|
|
expect(() => assertArtifactSharingAllowed(() => true)).not.toThrow()
|
|
})
|
|
})
|