Files
platform/src/servers/sync-marketplace.ts
T
pastilhasandClaude Opus 4.6 49cd559c8a tool registry, claude tool awareness, and model selector fix
- Add agent-agnostic tool registry (tool-registry.ts) that discovers tools from disk
- Embed tool-loader extension as platform infrastructure (ensure-tool-loader.ts)
- Inject tool context into Claude prompts on first message
- Add marketplace tool sync (sync-marketplace.ts)
- Fix model selector defaulting to claude-code when no model explicitly selected
- Exclude tool-loader-source.ts from tsconfig (Pi-specific deps)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:55:38 +00:00

98 lines
2.8 KiB
TypeScript

import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { DATA_PATH } from './data-path';
import { parseSeedVersion } from './sync-version';
const MARKETPLACE_URL = process.env.MARKETPLACE_URL ?? 'https://marketplace.officer.dev';
const GLOBAL_TOOLS_DIR = join(DATA_PATH, 'tools');
type ToolInput = {
type: string;
description: string;
values?: string;
};
type MarketplaceTool = {
dirName: string;
name: string;
label: string;
description: string;
body: string;
version: number;
language: string;
inputs: Record<string, ToolInput>;
implementation: string;
};
type MarketplaceResponse = {
categories: Array<{ name: string; tools: MarketplaceTool[] }>;
uncategorized: MarketplaceTool[];
};
function buildToolMd(tool: MarketplaceTool): string {
const lines = ['---'];
lines.push(`name: ${tool.name}`);
lines.push(`label: ${tool.label}`);
lines.push(`description: ${tool.description}`);
lines.push(`version: ${tool.version}`);
lines.push(`language: ${tool.language}`);
if (tool.inputs && Object.keys(tool.inputs).length > 0) {
lines.push('inputs:');
for (const [key, input] of Object.entries(tool.inputs)) {
lines.push(` ${key}:`);
lines.push(` type: ${input.type}`);
if (input.values) {
lines.push(` values: ${input.values}`);
}
lines.push(` description: "${input.description}"`);
}
}
lines.push('---');
lines.push('');
lines.push(tool.body);
return lines.join('\n');
}
export async function syncMarketplaceTools(): Promise<void> {
try {
const res = await fetch(`${MARKETPLACE_URL}/api/tools/native`);
if (!res.ok) {
console.error(`[marketplace] Failed to fetch tools: ${res.status} ${res.statusText}`);
return;
}
const data = (await res.json()) as MarketplaceResponse;
mkdirSync(GLOBAL_TOOLS_DIR, { recursive: true });
const allTools: MarketplaceTool[] = [];
for (const category of data.categories) {
allTools.push(...category.tools);
}
if (data.uncategorized) {
allTools.push(...data.uncategorized);
}
for (const tool of allTools) {
const targetDir = join(GLOBAL_TOOLS_DIR, tool.dirName);
const targetToolMd = join(targetDir, 'TOOL.md');
if (existsSync(targetToolMd)) {
const existingVersion = parseSeedVersion(readFileSync(targetToolMd, 'utf-8'));
if (tool.version <= existingVersion) continue;
}
mkdirSync(targetDir, { recursive: true });
writeFileSync(targetToolMd, buildToolMd(tool), 'utf-8');
writeFileSync(join(targetDir, 'index.ts'), tool.implementation, 'utf-8');
console.log(`[marketplace] Synced tool: ${tool.dirName} (v${tool.version})`);
}
} catch (err) {
console.error('[marketplace] Failed to sync tools:', err instanceof Error ? err.message : err);
}
}