Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
144
cli/src/lib/api.ts
Normal file
144
cli/src/lib/api.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* DSS API Client
|
||||
*
|
||||
* Communicates with the DSS server.
|
||||
*/
|
||||
|
||||
import { getConfig } from './config.js';
|
||||
|
||||
export interface ApiOptions {
|
||||
port?: number;
|
||||
baseUrl?: string;
|
||||
}
|
||||
|
||||
export class DSSApiClient {
|
||||
private baseUrl: string;
|
||||
|
||||
constructor(options: ApiOptions = {}) {
|
||||
const config = getConfig();
|
||||
const port = options.port || config.port || 3456;
|
||||
this.baseUrl = options.baseUrl || `http://localhost:${port}/api`;
|
||||
}
|
||||
|
||||
private async request<T>(
|
||||
endpoint: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<T> {
|
||||
const url = `${this.baseUrl}${endpoint}`;
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({ message: response.statusText })) as { message?: string };
|
||||
throw new Error(errorData.message || `Request failed: ${response.status}`);
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
return text ? JSON.parse(text) as T : {} as T;
|
||||
}
|
||||
|
||||
async health(): Promise<{ status: string; figma_mode: string }> {
|
||||
// Health endpoint is at root, not under /api
|
||||
const url = this.baseUrl.replace('/api', '') + '/health';
|
||||
const response = await fetch(url);
|
||||
return response.json() as Promise<{ status: string; figma_mode: string }>;
|
||||
}
|
||||
|
||||
async getConfig(): Promise<Record<string, unknown>> {
|
||||
return this.request('/config');
|
||||
}
|
||||
|
||||
async setFigmaToken(token: string): Promise<void> {
|
||||
await this.request('/config', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ figma_token: token }),
|
||||
});
|
||||
}
|
||||
|
||||
async testFigmaConnection(): Promise<{ success: boolean; user?: string; error?: string }> {
|
||||
return this.request('/config/figma/test', { method: 'POST' });
|
||||
}
|
||||
|
||||
async extractTokens(fileKey: string, format: string = 'css'): Promise<{
|
||||
success: boolean;
|
||||
tokens_count: number;
|
||||
tokens: Array<{ name: string; value: string; type: string; category: string }>;
|
||||
formatted_output: string;
|
||||
output_path: string;
|
||||
}> {
|
||||
return this.request('/figma/extract-variables', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file_key: fileKey, format }),
|
||||
});
|
||||
}
|
||||
|
||||
async extractComponents(fileKey: string): Promise<{
|
||||
success: boolean;
|
||||
components_count: number;
|
||||
components: Array<{ name: string; key: string; description: string; variants: string[] }>;
|
||||
output_path: string;
|
||||
}> {
|
||||
return this.request('/figma/extract-components', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file_key: fileKey }),
|
||||
});
|
||||
}
|
||||
|
||||
async syncTokens(fileKey: string, targetPath: string, format: string = 'css'): Promise<{
|
||||
success: boolean;
|
||||
tokens_synced: number;
|
||||
output_file: string;
|
||||
}> {
|
||||
return this.request('/figma/sync-tokens', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file_key: fileKey, target_path: targetPath, format }),
|
||||
});
|
||||
}
|
||||
|
||||
async generateCode(fileKey: string, componentName: string, framework: string = 'react'): Promise<{
|
||||
success: boolean;
|
||||
component: string;
|
||||
framework: string;
|
||||
code: string;
|
||||
}> {
|
||||
return this.request('/figma/generate-code', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file_key: fileKey, component_name: componentName, framework }),
|
||||
});
|
||||
}
|
||||
|
||||
async getProjects(): Promise<Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
figma_file_key: string;
|
||||
status: string;
|
||||
}>> {
|
||||
return this.request('/projects');
|
||||
}
|
||||
|
||||
async createProject(data: {
|
||||
name: string;
|
||||
description?: string;
|
||||
figma_file_key?: string;
|
||||
}): Promise<{ id: string; name: string }> {
|
||||
return this.request('/projects', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance
|
||||
let apiClient: DSSApiClient | null = null;
|
||||
|
||||
export function getApiClient(options?: ApiOptions): DSSApiClient {
|
||||
if (!apiClient || options) {
|
||||
apiClient = new DSSApiClient(options);
|
||||
}
|
||||
return apiClient;
|
||||
}
|
||||
Reference in New Issue
Block a user