feat(plugins): move game resources behind marketplace

This commit is contained in:
2026-08-31 10:45:20 +08:00
parent 62304dc85b
commit fe656dd865
33 changed files with 1413 additions and 1018 deletions

View File

@@ -320,7 +320,7 @@ function parseIndexDocument(value: unknown): IndexDocument {
if (seen.has(key)) fail('plugin_store_index_invalid', `duplicate release ${releaseId}`);
seen.add(key);
const runtimeKind = record.runtime_kind;
if (runtimeKind !== 'skill_only') {
if (runtimeKind !== 'skill_only' && runtimeKind !== 'platform_hosted') {
fail('plugin_store_index_invalid', `invalid release ${index}.runtime_kind`);
}
const installedAt = boundedText(record.installed_at, `release ${index}.installed_at`, 80);
@@ -706,6 +706,16 @@ export class PluginPackageStore {
return this.getInstalledFromIndex(index, validated, undefined, current);
}
async getInstalledRelease(pluginId: string, releaseId: string): Promise<InstalledRelease | null> {
const validatedPluginId = validPluginId(pluginId);
const validatedReleaseId = validReleaseId(releaseId);
return this.getInstalledFromIndex(
await this.readIndex(),
validatedPluginId,
validatedReleaseId,
);
}
async removeUnused(pluginId: string): Promise<InstallationSnapshot> {
const validated = validPluginId(pluginId);
return this.withOperation(() => this.removeUnusedLocked(validated, undefined, 'background'));
@@ -912,7 +922,7 @@ export class PluginPackageStore {
await writeFile(path.join(extractedPath, ORPHAN_ARCHIVE_FILE), toBuffer(artifact), { flag: 'wx' });
await rename(extractedPath, packageRoot);
moved = true;
const record = this.installedRecord(grant, channel);
const record = this.installedRecord(grant, channel, definition.runtimeKind);
const records = index.releases.filter((candidate) => !(candidate.pluginId === pluginId && candidate.releaseId === grant.releaseId));
this.assertBinding(binding);
try {
@@ -968,7 +978,7 @@ export class PluginPackageStore {
const descriptor = this.verifyArtifact(artifact, input.grant);
const definition = await this.loadDefinition(input.packageRoot, input.grant, descriptor);
this.assertBinding(input.binding);
const record = this.installedRecord(input.grant, input.channel);
const record = this.installedRecord(input.grant, input.channel, definition.runtimeKind);
this.assertBinding(input.binding);
try {
await this.writeIndex(this.indexPath, serializeIndex({
@@ -1019,14 +1029,18 @@ export class PluginPackageStore {
return descriptor;
}
private installedRecord(grant: DownloadGrant, channel: 'stable' | 'beta'): InstalledReleaseRecord {
private installedRecord(
grant: DownloadGrant,
channel: 'stable' | 'beta',
runtimeKind: 'skill_only' | 'platform_hosted',
): InstalledReleaseRecord {
return Object.freeze({
pluginId: grant.pluginId,
releaseId: grant.releaseId,
version: grant.version,
packageSchemaVersion: grant.packageSchemaVersion,
contractVersion: grant.contractVersion,
runtimeKind: 'skill_only',
runtimeKind,
sha256: grant.sha256,
sizeBytes: grant.sizeBytes,
installedAt: new Date(this.now()).toISOString(),
@@ -1193,7 +1207,6 @@ export class PluginPackageStore {
let definition: CodingPluginDefinition;
try {
definition = await loadCodingPluginDefinition(packageRoot, {
runtimeKind: 'skill_only',
acquisitionMode: 'user_acquired',
releaseId: grant.releaseId,
provenance: { source: 'marketplace', packageRoot },
@@ -1204,7 +1217,8 @@ export class PluginPackageStore {
}
if (definition.id !== descriptor.pluginId || definition.version !== descriptor.version
|| definition.contractVersion !== descriptor.contractVersion || definition.releaseId !== grant.releaseId
|| definition.runtimeKind !== 'skill_only' || definition.acquisitionMode !== 'user_acquired') {
|| (definition.runtimeKind !== 'skill_only' && definition.runtimeKind !== 'platform_hosted')
|| definition.acquisitionMode !== 'user_acquired') {
fail('plugin_manifest_invalid', 'package definition does not match the signed Release');
}
return definition;