mirror of
https://github.com/stablyai/orca.git
synced 2026-10-27 00:02:48 +00:00
* Add safe skill freshness detection * Accept observed copy-mode rail outcomes * chore(skills): regenerate snapshot artifacts for the merged guide content The rebase onto main picked up the reviewed guide fixes (#8624), so the current manifest hashes and a new appended snapshot generation must match those bytes; the registry keeps all prior snapshots so existing installs classify as outdated rather than unrecognized. * fix(skills): canonicalize snapshot file order and guard released history Historical snapshots kept git ls-tree byte-order while the working-tree walk and runtime observation use the sorted depth-first order, so any future multi-file skill would misclassify older installs as unrecognized and churn spurious registry revisions; all producers now share one canonical order (no digest changes for today's single-file packages). Also rejects executable files from shipped skills (Windows observation cannot see execute bits, which would misclassify pristine Windows installs) and adds an explicit append-only invariant for released snapshots so a generation-logic change cannot rewrite them silently. * fix(skills): throttle focus rescans and correct self-blocked placement copy Every window focus re-read and re-hashed all installed packages, and the nudge and panel each forced their own trailing rescan for one event; a 15s cooldown plus a shared invalidation latch keep one bounded scan per event while install-change events stay immediate. Bundle artifacts are now loaded once per run instead of re-parsed on every scan. A read-only or otherwise unsupported outdated placement now explains that it blocks itself instead of blaming a phantom sibling placement; the supported topology set moved to shared so eligibility and copy cannot drift. * feat(skills): move freshness surfacing to a lingering toast and update modal The Skills page has been unreachable since its toolbox menu entry was removed (#4535), so surfacing freshness there buried the feature behind its own nudge. The nudge now lingers until acted on (ignoring it records nothing; only the explicit close persists dismissal keys) and opens an update modal hosting the pre-filled editable terminal, an honest current/blocked summary, and the per-placement rows in a collapsed Details section. A compact 'Check for skill updates' row in CLI settings is the manual re-entry point. Skills page restored to main; design-doc surfacing section records the venue decision. * fix(skills): mount update dialog inside the link-routing provider and fold freshness into the setup rails The dialog hosts a live terminal pane that requires the link-routing preference context; mounted outside the provider it crashed the renderer the moment an eligible update existed (caught by live QA — unit tests mock the terminal). It now mounts inside the provider behind its own recoverable boundary. The separate 'Check for skill updates' settings button is gone: the setup rails' own pill now carries freshness (Update available / Up to date, falling back to Installed for blocked or unrecognized copies and for non-local runtimes the local-only scan cannot vouch for), and Re-check refreshes both installation detection and the freshness inventory. Wired for the CLI, Orchestration, Computer Use, and Per-Workspace Environments rails. * fix(skills): use the sleek scrollbar style in the update dialog * chore(skills): regenerate manifest for merged main (v1.4.142-rc.1) Main advanced to 1.4.142-rc.1 with a v1.4.141 release, so the embedded appVersion and release mapping were stale on the PR's merged tree. Only appVersion and the new release entry change; no snapshot digests move (released history preserved). * fix(skills): bound and batch freshness work * fix(skills): harden freshness integrity checks * fix(skills): accept observed copy topology outcomes * chore(skills): regenerate manifest for current main * fix(skills): preserve update terminal lifecycle * chore(skills): regenerate manifest for current main * fix(skills): fail closed on stale freshness scans * chore(skills): regenerate manifest for current main * fix(skills): preserve freshness safety under focus churn * feat(skills): group the update modal by skill with plain-language status The Update skills modal now lists only skills that will update or that can't (with why), grouped by skill with their install locations nested underneath — no more one row per placement. - Statuses collapse to "Update available" / "Can't update" at the skill level. - A location's problem is a chip (Duplicate, Unrecognized, Inaccessible, Read only, In a repo, External/Broken link, Plugin cache) with a hover tooltip that explains what it means for the user and what to do. - Up-to-date, unrecognized-only, and unreadable-only skills are hidden; a current/unrecognized/etc. location only appears when it explains a shown skill. - Copy is de-jargoned (drops "copy"/"placement"/"snapshot"/"official copy") and names the mechanism as the npx skills update command, not "Orca's update". - Rename the section to "Update details"; drop the unreachable newer-known state. Renderer-only: derivation is a pure module (groupSkillFreshness) with unit tests; no IPC or main-process change. Locales updated for all five languages. * chore(skills): regenerate manifest for current main (v1.4.143-rc.0) * feat(skills): don't let a duplicate block the update; clearer skipped copy - Eligibility: a clean standalone duplicate no longer poisons the whole name — the canonical copy still updates and the duplicate is flagged; a duplicate-only skill stays unoffered. - Update modal: "Can't update" -> "Skipped" with a reason-specific sentence (edited/read-only/in-a-repo/plugin/link); chips describe only the location state; footer "Check now" -> "Re-check". - Settings sidebar nav pills go amber "Update available" when a skill is updatable, matching the setup cards. - Localized new strings across en/es/ja/ko/zh. * chore(skills): regenerate manifest for merged main (v1.4.144-rc.1)
103 lines
2.7 KiB
TypeScript
103 lines
2.7 KiB
TypeScript
import type { SkillProvider, SkillSourceKind } from './skills'
|
|
|
|
export type SkillBundleFileIdentity = {
|
|
path: string
|
|
size: number
|
|
executable: boolean
|
|
classification: 'text' | 'binary'
|
|
exactSha256: string
|
|
textNormalizedSha256: string | null
|
|
identitySha256: string
|
|
}
|
|
|
|
export type SkillKnownSnapshot = {
|
|
releaseRevision: number
|
|
packageDigest: string
|
|
gitTreeSha: string
|
|
files: SkillBundleFileIdentity[]
|
|
}
|
|
|
|
export type SkillCurrentBundleEntry = SkillKnownSnapshot & {
|
|
name: string
|
|
sourcePath: string
|
|
appVersion: string
|
|
}
|
|
|
|
export type SkillBundleManifest = {
|
|
schemaVersion: 1
|
|
appVersion: string
|
|
skills: SkillCurrentBundleEntry[]
|
|
}
|
|
|
|
export type SkillSnapshotRegistry = {
|
|
schemaVersion: 1
|
|
skills: Record<string, SkillKnownSnapshot[]>
|
|
}
|
|
|
|
export type SkillReleaseMapping = {
|
|
schemaVersion: 1
|
|
releases: { appVersion: string; skills: Record<string, number> }[]
|
|
}
|
|
|
|
export type SkillFreshnessStatus =
|
|
| 'current'
|
|
| 'outdated'
|
|
| 'newer-known'
|
|
| 'unrecognized'
|
|
| 'inaccessible'
|
|
|
|
export type SkillInstallationTopology =
|
|
| 'canonical-copy'
|
|
| 'provider-alias'
|
|
| 'independent-copy'
|
|
| 'external-link'
|
|
| 'broken-link'
|
|
| 'read-only'
|
|
| 'repo-scope'
|
|
| 'plugin-cache'
|
|
|
|
// Why: eligibility and the explanation copy must agree on which placements the
|
|
// validated npx rail can converge; a drifted copy would blame a phantom sibling.
|
|
export const SUPPORTED_GLOBAL_SKILL_TOPOLOGIES: ReadonlySet<SkillInstallationTopology> = new Set([
|
|
'canonical-copy',
|
|
'provider-alias'
|
|
])
|
|
|
|
export type SkillFreshnessInstallation = {
|
|
id: string
|
|
name: string
|
|
rootId: string
|
|
providers: SkillProvider[]
|
|
sourceKind: SkillSourceKind
|
|
sourceLabel: string
|
|
unresolvedPath: string
|
|
resolvedPath: string | null
|
|
physicalIdentity: string | null
|
|
topology: SkillInstallationTopology
|
|
status: SkillFreshnessStatus
|
|
installedReleaseRevision: number | null
|
|
installedAppVersion: string | null
|
|
currentReleaseRevision: number
|
|
currentPackageDigest: string
|
|
currentAppVersion: string
|
|
observedPackageDigest: string | null
|
|
errorCategory: string | null
|
|
}
|
|
|
|
export type SkillFreshnessInventory = {
|
|
schemaVersion: 1
|
|
installations: SkillFreshnessInstallation[]
|
|
eligibleUpdateNames: string[]
|
|
scannedAt: number
|
|
}
|
|
|
|
export function buildTargetedSkillUpdateCommand(names: readonly string[]): string | null {
|
|
const canonicalNames = [...new Set(names)].sort((left, right) => left.localeCompare(right, 'en'))
|
|
// Why: names become editable shell input. Official manifests use this
|
|
// restricted package-name grammar so no entry can introduce shell syntax.
|
|
if (canonicalNames.some((name) => !/^[a-z0-9][a-z0-9._-]*$/.test(name))) {
|
|
return null
|
|
}
|
|
return canonicalNames.length > 0 ? `npx skills update ${canonicalNames.join(' ')} --global` : null
|
|
}
|