Spaces:
Runtime error
Runtime error
| export type FileEntry = { | |
| name: string; | |
| type: 'file' | 'directory'; | |
| size?: number; | |
| modified?: number; | |
| }; | |
| export type ListeningPort = { | |
| port: number; | |
| pid: number | null; | |
| process: string | null; | |
| }; | |
| export type TerminalFeatures = { | |
| terminal?: boolean; | |
| }; | |
| import { REXPRO_API_BASE_URL } from '$lib/constants'; | |
| export type TerminalServer = { | |
| id: string; | |
| url: string; | |
| name: string; | |
| }; | |
| export const getTerminalServers = async (token: string): Promise<TerminalServer[]> => { | |
| const res = await fetch(`${REXPRO_API_BASE_URL}/terminals/`, { | |
| headers: { | |
| Authorization: `Bearer ${token}` | |
| } | |
| }).catch(() => null); | |
| if (!res || !res.ok) return []; | |
| return res.json().catch(() => []); | |
| }; | |
| export const getTerminalConfig = async ( | |
| baseUrl: string, | |
| apiKey: string | |
| ): Promise<{ features: TerminalFeatures } | null> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/api/config`; | |
| const res = await fetch(url, { | |
| headers: { Authorization: `Bearer ${apiKey}` } | |
| }).catch(() => null); | |
| if (!res || !res.ok) return null; | |
| return res.json().catch(() => null); | |
| }; | |
| export const getCwd = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| sessionId?: string | |
| ): Promise<string | null> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/cwd`; | |
| const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { headers }).catch(() => null); | |
| if (!res || !res.ok) return null; | |
| const json = await res.json().catch(() => null); | |
| return json?.cwd ?? null; | |
| }; | |
| export const listFiles = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| path: string = '/', | |
| sessionId?: string | |
| ): Promise<FileEntry[] | null> => { | |
| // The endpoint uses `directory` as the query param name | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/list?directory=${encodeURIComponent(path)}`; | |
| const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { headers }) | |
| .then(async (res) => { | |
| if (!res.ok) throw await res.json(); | |
| return res.json(); | |
| }) | |
| .catch((err) => { | |
| console.error('open-terminal listFiles error:', err); | |
| return null; | |
| }); | |
| return res?.entries ?? null; | |
| }; | |
| export const readFile = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| path: string, | |
| sessionId?: string | |
| ): Promise<string | null> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/read?path=${encodeURIComponent(path)}`; | |
| const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { headers }).catch((err) => { | |
| console.error('open-terminal readFile error:', err); | |
| return null; | |
| }); | |
| if (!res || !res.ok) return null; | |
| const contentType = res.headers.get('content-type') ?? ''; | |
| if (contentType.startsWith('image/') || contentType.startsWith('application/octet')) { | |
| // Binary — return a placeholder | |
| return `[Binary file: ${contentType}]`; | |
| } | |
| // Text files: endpoint returns JSON { path, total_lines, content } | |
| // Binary image files: endpoint returns raw bytes (handled above) | |
| const json = await res.json().catch(() => null); | |
| return json?.content ?? null; | |
| }; | |
| export const downloadFileBlob = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| path: string, | |
| sessionId?: string | |
| ): Promise<{ blob: Blob; filename: string } | null> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/view?path=${encodeURIComponent(path)}`; | |
| const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { headers }).catch(() => null); | |
| if (!res || !res.ok) return null; | |
| const filename = path.split('/').pop() ?? 'file'; | |
| const blob = await res.blob(); | |
| return { blob, filename }; | |
| }; | |
| export const archiveFromTerminal = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| paths: string[], | |
| sessionId?: string | |
| ): Promise<{ blob: Blob; filename: string } | null> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/archive`; | |
| const headers: Record<string, string> = { | |
| Authorization: `Bearer ${apiKey}`, | |
| 'Content-Type': 'application/json' | |
| }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { | |
| method: 'POST', | |
| headers, | |
| body: JSON.stringify({ paths }) | |
| }).catch(() => null); | |
| if (!res || !res.ok) return null; | |
| const disposition = res.headers.get('content-disposition') ?? ''; | |
| const match = disposition.match(/filename="?([^"]+)"?/); | |
| const filename = match?.[1] ?? 'download.zip'; | |
| const blob = await res.blob(); | |
| return { blob, filename }; | |
| }; | |
| export const uploadToTerminal = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| directory: string, | |
| file: File, | |
| sessionId?: string | |
| ): Promise<{ path: string; size: number } | null> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/upload?directory=${encodeURIComponent(directory)}`; | |
| const body = new FormData(); | |
| body.append('file', file); | |
| const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { | |
| method: 'POST', | |
| headers, | |
| body | |
| }) | |
| .then(async (res) => { | |
| if (!res.ok) throw await res.json(); | |
| return res.json(); | |
| }) | |
| .catch((err) => { | |
| console.error('open-terminal uploadToTerminal error:', err); | |
| return null; | |
| }); | |
| return res; | |
| }; | |
| export const createDirectory = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| path: string, | |
| sessionId?: string | |
| ): Promise<{ path: string } | null> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/mkdir`; | |
| const headers: Record<string, string> = { | |
| Authorization: `Bearer ${apiKey}`, | |
| 'Content-Type': 'application/json' | |
| }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { | |
| method: 'POST', | |
| headers, | |
| body: JSON.stringify({ path }) | |
| }) | |
| .then(async (res) => { | |
| if (!res.ok) throw await res.json(); | |
| return res.json(); | |
| }) | |
| .catch((err) => { | |
| console.error('open-terminal createDirectory error:', err); | |
| return null; | |
| }); | |
| return res; | |
| }; | |
| export const deleteEntry = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| path: string, | |
| sessionId?: string | |
| ): Promise<{ path: string; type: string } | null> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/delete?path=${encodeURIComponent(path)}`; | |
| const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { | |
| method: 'DELETE', | |
| headers | |
| }) | |
| .then(async (res) => { | |
| if (!res.ok) throw await res.json(); | |
| return res.json(); | |
| }) | |
| .catch((err) => { | |
| console.error('open-terminal deleteEntry error:', err); | |
| return null; | |
| }); | |
| return res; | |
| }; | |
| export const setCwd = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| path: string, | |
| sessionId?: string | |
| ): Promise<{ cwd: string } | null> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/cwd`; | |
| const headers: Record<string, string> = { | |
| Authorization: `Bearer ${apiKey}`, | |
| 'Content-Type': 'application/json' | |
| }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { | |
| method: 'POST', | |
| headers, | |
| body: JSON.stringify({ path }) | |
| }) | |
| .then(async (res) => { | |
| if (!res.ok) throw await res.json(); | |
| return res.json(); | |
| }) | |
| .catch((err) => { | |
| console.error('open-terminal setCwd error:', err); | |
| return null; | |
| }); | |
| return res; | |
| }; | |
| export const moveEntry = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| source: string, | |
| destination: string, | |
| sessionId?: string | |
| ): Promise<{ source: string; destination: string } | { error: string }> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/files/move`; | |
| const headers: Record<string, string> = { | |
| Authorization: `Bearer ${apiKey}`, | |
| 'Content-Type': 'application/json' | |
| }; | |
| if (sessionId) headers['X-Session-Id'] = sessionId; | |
| const res = await fetch(url, { | |
| method: 'POST', | |
| headers, | |
| body: JSON.stringify({ source, destination }) | |
| }) | |
| .then(async (res) => { | |
| if (!res.ok) throw await res.json(); | |
| return res.json(); | |
| }) | |
| .catch((err) => { | |
| console.error('open-terminal moveEntry error:', err); | |
| return { error: err?.detail ?? 'Move failed' }; | |
| }); | |
| return res; | |
| }; | |
| export const getListeningPorts = async ( | |
| baseUrl: string, | |
| apiKey: string | |
| ): Promise<ListeningPort[]> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/ports`; | |
| const res = await fetch(url, { | |
| headers: { Authorization: `Bearer ${apiKey}` } | |
| }).catch(() => null); | |
| if (!res || !res.ok) return []; | |
| const json = await res.json().catch(() => null); | |
| return json?.ports ?? []; | |
| }; | |
| export const getPortProxyUrl = (baseUrl: string, port: number, path: string = ''): string => { | |
| return `${baseUrl.replace(/\/$/, '')}/proxy/${port}/${path}`; | |
| }; | |
| // --------------------------------------------------------------------------- | |
| // Notebook execution | |
| // --------------------------------------------------------------------------- | |
| export const createNotebookSession = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| path: string | |
| ): Promise<{ id: string; kernel: string; status: string } | { error: string }> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/notebooks`; | |
| const res = await fetch(url, { | |
| method: 'POST', | |
| headers: { | |
| Authorization: `Bearer ${apiKey}`, | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ path }) | |
| }) | |
| .then(async (res) => { | |
| if (!res.ok) { | |
| const body = await res.json().catch(() => ({})); | |
| return { error: body?.detail ?? `HTTP ${res.status}` }; | |
| } | |
| return res.json(); | |
| }) | |
| .catch((err) => { | |
| console.error('open-terminal createNotebookSession error:', err); | |
| return { error: 'Connection failed' }; | |
| }); | |
| return res; | |
| }; | |
| export const executeNotebookCell = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| sessionId: string, | |
| cellIndex: number, | |
| source?: string | |
| ): Promise<{ status: string; execution_count?: number; outputs: any[] } | { error: string }> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/notebooks/${sessionId}/execute`; | |
| const body: Record<string, any> = { cell_index: cellIndex }; | |
| if (source !== undefined) body.source = source; | |
| const res = await fetch(url, { | |
| method: 'POST', | |
| headers: { | |
| Authorization: `Bearer ${apiKey}`, | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify(body) | |
| }) | |
| .then(async (res) => { | |
| if (!res.ok) { | |
| const body = await res.json().catch(() => ({})); | |
| return { error: body?.detail ?? `HTTP ${res.status}` }; | |
| } | |
| return res.json(); | |
| }) | |
| .catch((err) => { | |
| console.error('open-terminal executeNotebookCell error:', err); | |
| return { error: 'Connection failed' }; | |
| }); | |
| return res; | |
| }; | |
| export const stopNotebookSession = async ( | |
| baseUrl: string, | |
| apiKey: string, | |
| sessionId: string | |
| ): Promise<boolean> => { | |
| const url = `${baseUrl.replace(/\/$/, '')}/notebooks/${sessionId}`; | |
| const res = await fetch(url, { | |
| method: 'DELETE', | |
| headers: { Authorization: `Bearer ${apiKey}` } | |
| }).catch(() => null); | |
| return res?.ok ?? false; | |
| }; | |