mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 08:03:22 +00:00
The current manifest stamped package.json's version into itself (9 lines), so every RC/stable version bump made the committed artifact stale on every open branch: lint failed until authors committed content-free regeneration diffs, which also dragged the resources/skills-filtered update-roundtrip matrix onto unrelated PRs. Cutting a release tag whose skills tree changed had the same effect through release-mapping.json. - current-manifest.json is now schema 2 and content-only; the generator no longer reads package.json. Registry and mapping stay schema 1 so the append-only released-history guard keeps its schema gate. - The running build's version enters at the IPC boundary (skills:freshnessInventory passes app.getVersion()) and threads through the inventory to placement observation; current-revision placements are labeled with it while historical revisions keep resolving through the release mapping. The artifact loader and its cache stay content-only. - verify tolerates a committed release mapping that is a byte-exact prefix of the derived one when every missing trailing row's revisions equal the current manifest (a just-cut tag over unchanged-since bytes); such rows are provably redundant until the next real regeneration adds them. Artifacts now change only when skills/ content changes.
103 lines
2.8 KiB
TypeScript
103 lines
2.8 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
|
|
}
|
|
|
|
// Why: schema 2 removed the stamped app version so the committed artifact is a
|
|
// pure function of skills/ content; the running build supplies its own version.
|
|
export type SkillBundleManifest = {
|
|
schemaVersion: 2
|
|
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
|
|
}
|