86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
import {
|
|
loadGameAssetCandidates,
|
|
type GameAssetCandidate,
|
|
} from '../../../coding-projects/game-asset-browser';
|
|
import {
|
|
loadGameAssetReview,
|
|
type GameAssetReviewSnapshot,
|
|
} from '../../../coding-projects/game-asset-review';
|
|
import type { GameAssetsDetailsV1 } from '../../contracts';
|
|
|
|
export interface GameAssetToolResult {
|
|
content: Array<{ type: 'text'; text: string }>;
|
|
details: GameAssetsDetailsV1;
|
|
}
|
|
|
|
function record(value: unknown): Record<string, unknown> {
|
|
return value && typeof value === 'object' && !Array.isArray(value)
|
|
? value as Record<string, unknown>
|
|
: {};
|
|
}
|
|
|
|
function ids(value: unknown): string[] {
|
|
if (value === undefined) return [];
|
|
if (!Array.isArray(value) || value.length > 200 || value.some((item) => typeof item !== 'string')) {
|
|
throw new Error('Game asset candidate ids are invalid');
|
|
}
|
|
return [...new Set(value.map((item) => item.trim()).filter(Boolean))];
|
|
}
|
|
|
|
function details(review: GameAssetReviewSnapshot): GameAssetsDetailsV1 {
|
|
return {
|
|
schema: 'game-assets.v1',
|
|
invocationId: review.invocationId,
|
|
candidateIds: review.candidateIds,
|
|
status: review.status,
|
|
pendingAssetIds: review.pendingAssetIds,
|
|
approvedAssetIds: review.approvedAssetIds,
|
|
discardedAssetIds: review.discardedAssetIds,
|
|
};
|
|
}
|
|
|
|
function safeCandidate(candidate: GameAssetCandidate) {
|
|
return {
|
|
id: candidate.id,
|
|
name: candidate.name,
|
|
category: candidate.category,
|
|
status: candidate.status,
|
|
purpose: candidate.purpose,
|
|
source: candidate.source,
|
|
license: candidate.license,
|
|
mediaKind: candidate.mediaKind,
|
|
manifest: candidate.manifest,
|
|
...(candidate.unavailableReason ? { unavailableReason: candidate.unavailableReason } : {}),
|
|
};
|
|
}
|
|
|
|
export class PiGameAssetTools {
|
|
async browse(projectPath: string, input: unknown, fallbackInvocationId: string): Promise<GameAssetToolResult> {
|
|
const body = record(input);
|
|
const invocationId = typeof body.invocationId === 'string' && body.invocationId.trim()
|
|
? body.invocationId.trim()
|
|
: fallbackInvocationId;
|
|
const candidates = await loadGameAssetCandidates(projectPath);
|
|
const review = await loadGameAssetReview(projectPath, invocationId, candidates.map(({ id }) => id));
|
|
return {
|
|
content: [{
|
|
type: 'text',
|
|
text: JSON.stringify({ candidates: candidates.map(safeCandidate), review: details(review) }),
|
|
}],
|
|
details: details(review),
|
|
};
|
|
}
|
|
|
|
async review(projectPath: string, input: unknown, fallbackInvocationId: string): Promise<GameAssetToolResult> {
|
|
const body = record(input);
|
|
const invocationId = typeof body.invocationId === 'string' && body.invocationId.trim()
|
|
? body.invocationId.trim()
|
|
: fallbackInvocationId;
|
|
const review = await loadGameAssetReview(projectPath, invocationId, ids(body.candidateIds));
|
|
return {
|
|
content: [{ type: 'text', text: JSON.stringify(details(review)) }],
|
|
details: details(review),
|
|
};
|
|
}
|
|
}
|