import type { CodingPluginToolDefinition } from '../../shared/coding-plugins'; import type { DevicePackageIndexV1, DevicePackageToolDetailsV1, DevicePackageToolOperation, InstallPreviewV1, } from '../../shared/device-packages'; import type { DevicePackageToolName } from '../../shared/device-packages'; import type { PiProductToolResult } from '../coding-runtime/pi/product-tools'; import type { DevicePackageManager } from './device-package-manager'; export { DEVICE_PACKAGE_TOOL_NAMES } from '../../shared/device-packages'; const EMPTY_OBJECT_SCHEMA = Object.freeze({ type: 'object', additionalProperties: false, properties: {}, }); function tool( name: DevicePackageToolName, label: string, description: string, operation: DevicePackageToolOperation, mutation: 'read' | 'write' | 'destructive', inputSchema: Readonly>, ): CodingPluginToolDefinition { return Object.freeze({ name, label, description, capabilityId: 'makelore.device-packages', operation, roles: ['parent'], mutation, projectWriteLease: false, permissions: ['device-packages'], inputSchema, }); } export const DEVICE_PACKAGE_TOOL_DEFINITIONS = Object.freeze([ tool( 'local_package_prepare', 'Prepare local package', 'Resolve an npm, Git, or absolute local Pi package/Skill and show an exact installation preview. This does not install it.', 'prepare', 'read', { type: 'object', additionalProperties: false, required: ['source'], properties: { source: { type: 'string', minLength: 1, maxLength: 2048 } }, }, ), tool( 'local_package_commit', 'Install local package', 'Install a prepared device package only after the user confirms in a later message.', 'commit', 'write', { type: 'object', additionalProperties: false, required: ['planId', 'confirmed'], properties: { planId: { type: 'string', minLength: 1, maxLength: 128 }, confirmed: { const: true }, }, }, ), tool('local_package_list', 'List local packages', 'List packages installed on this device.', 'list', 'read', EMPTY_OBJECT_SCHEMA), tool( 'local_package_set_enabled', 'Enable or disable local package', 'Enable or disable an installed device package for future parent workers.', 'set_enabled', 'write', { type: 'object', additionalProperties: false, required: ['packageId', 'enabled'], properties: { packageId: { type: 'string', minLength: 1, maxLength: 128 }, enabled: { type: 'boolean' }, }, }, ), tool( 'local_package_uninstall', 'Remove local package', 'Remove an installed package from this device after explicit confirmation.', 'uninstall', 'destructive', { type: 'object', additionalProperties: false, required: ['packageId', 'confirmed'], properties: { packageId: { type: 'string', minLength: 1, maxLength: 128 }, confirmed: { const: true }, }, }, ), ]); function record(value: unknown): Record { if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Local package tool input is invalid'); return value as Record; } function exact(input: Record, keys: readonly string[]): void { const actual = Object.keys(input).sort(); const expected = [...keys].sort(); if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) { throw new Error('Local package tool input has unexpected fields'); } } function boundedString(value: unknown, name: string, maximum: number): string { if (typeof value !== 'string' || !value.trim() || value.length > maximum) throw new Error(`${name} is invalid`); return value.trim(); } function result( operation: DevicePackageToolOperation, value: { preview?: InstallPreviewV1; index?: DevicePackageIndexV1 }, ): PiProductToolResult { const details: DevicePackageToolDetailsV1 = { schema: 'makelore-device-package.v1', operation, success: true, ...value, }; return { content: [{ type: 'text', text: JSON.stringify(details) }], details }; } export class DevicePackageTools { readonly tools = DEVICE_PACKAGE_TOOL_DEFINITIONS; constructor(private readonly manager: DevicePackageManager) {} async invoke(toolName: string, turnId: string, value: unknown): Promise { const input = record(value); switch (toolName) { case 'local_package_prepare': { exact(input, ['source']); return result('prepare', { preview: await this.manager.prepare(boundedString(input.source, 'source', 2048), turnId), }); } case 'local_package_commit': { exact(input, ['planId', 'confirmed']); return result('commit', { index: await this.manager.commit( boundedString(input.planId, 'planId', 128), input.confirmed as true, turnId, ), }); } case 'local_package_list': exact(input, []); return result('list', { index: await this.manager.list() }); case 'local_package_set_enabled': { exact(input, ['packageId', 'enabled']); if (typeof input.enabled !== 'boolean') throw new Error('enabled is invalid'); return result('set_enabled', { index: await this.manager.setEnabled( boundedString(input.packageId, 'packageId', 128), input.enabled, ), }); } case 'local_package_uninstall': { exact(input, ['packageId', 'confirmed']); if (input.confirmed !== true) throw new Error('Literal confirmation is required'); return result('uninstall', { index: await this.manager.uninstall(boundedString(input.packageId, 'packageId', 128)), }); } default: throw new Error('Local package tool is unavailable'); } } }