// HuggingBot Module: HuggingFace Hub v2.0.0 (FULL + EXPANDED) // ============================================================================ // Full remote control of the HuggingFace Hub from chat: // auth, Spaces CRUD, file ops, secrets/hardware/sleep config, search, // model inference (Inference API), Gradio Space API, repo browsing. // Rebuilt for the HuggingBot ModuleSystem from ChatSeed v12 (all API fixes // applied: flavor/gcTimeout keys, PUT /settings, paused-space delete pre-check, // deep hardware name extraction) and expanded with: // * hf_search_datasets // * modern Gradio API routing (/api/{name} with /api/predict fallback) // * pipeline-aware Inference API calls (task routing) // * binary (image) inference response handling -> inline markdown // * multi-repo-type support for file/repo operations (space|model|dataset) // ============================================================================ (function(MS) { 'use strict'; var HF_API_BASE = 'https://huggingface.co'; var INFER_API_BASE = 'https://api-inference.huggingface.co'; var TOKEN_KEY = 'chatseed_hf_token'; // ─── Token helpers ──────────────────────────────────────────────────── function getToken() { try { return localStorage.getItem(TOKEN_KEY) || ''; } catch (e) { return ''; } } function setToken(t) { try { localStorage.setItem(TOKEN_KEY, t); } catch (e) {} } function clearToken() { try { localStorage.removeItem(TOKEN_KEY); } catch (e) {} } function authHeader() { var t = getToken(); if (!t) throw new Error('No HuggingFace token set. Use hf_set_token or hf_login_ui to set your HF access token first.'); return { 'Authorization': 'Bearer ' + t }; } // ─── Fetch helpers ──────────────────────────────────────────────────── async function hfFetch(path, options) { options = options || {}; var headers = Object.assign({ 'Content-Type': 'application/json' }, authHeader(), options.headers || {}); var res = await fetch(HF_API_BASE + path, Object.assign({}, options, { headers: headers })); if (!res.ok) { var msg = ''; try { var err = await res.json(); msg = err.error || JSON.stringify(err); } catch (e) { try { msg = await res.text(); } catch (e2) { msg = res.statusText; } } throw new Error('HF API ' + res.status + ': ' + msg); } if (res.status === 204) return null; return (res.headers.get('content-type') || '').indexOf('application/json') > -1 ? res.json() : res.text(); } async function hfFetchRaw(path) { var token = getToken(); var headers = {}; if (token) headers['Authorization'] = 'Bearer ' + token; var res = await fetch(HF_API_BASE + path, { headers: headers }); if (!res.ok) { var msg = ''; try { var err = await res.json(); msg = err.error || JSON.stringify(err); } catch (e) { msg = res.statusText; } throw new Error('HF API ' + res.status + ': ' + msg); } return res.text(); } async function hfFetchHead(path) { var token = getToken(); var headers = {}; if (token) headers['Authorization'] = 'Bearer ' + token; var res = await fetch(HF_API_BASE + path, { method: 'HEAD', headers: headers }); return { ok: res.ok, status: res.status }; } // ─── Repo helpers ───────────────────────────────────────────────────── function repoTypePath(type) { switch (type) { case 'model': return 'models'; case 'dataset': return 'datasets'; case 'space': return 'spaces'; default: return 'spaces'; } } function parseRepoId(repoId) { var parts = String(repoId || '').split('/'); if (parts.length !== 2) throw new Error('Invalid repo_id. Use format "namespace/repo-name"'); return { namespace: parts[0], name: parts[1] }; } async function commitFiles(repoType, namespace, repo, files, deletedEntries, summary, description) { var body = { summary: summary || 'Update via HuggingBot', description: description || '', files: (files || []).map(function (f) { return { path: f.path, content: f.content, encoding: f.encoding || 'utf-8' }; }) }; if (deletedEntries && deletedEntries.length) body.deletedEntries = deletedEntries; return hfFetch('/api/' + repoTypePath(repoType) + '/' + namespace + '/' + repo + '/commit/main', { method: 'POST', body: JSON.stringify(body) }); } // Centralized settings helper — routes ALL Space settings through // PUT /api/spaces/{ns}/{name}/settings with the keys HF actually accepts. async function updateSpaceSettings(namespace, repo, settings) { return hfFetch('/api/spaces/' + namespace + '/' + repo + '/settings', { method: 'PUT', body: JSON.stringify(settings) }); } // Deep-extract a human-readable hardware name from varied API response shapes. function extractHardwareName(hw) { if (!hw) return 'cpu-basic'; if (typeof hw === 'string') return hw; if (typeof hw === 'object') { return hw.currentPrettyName || hw.requestedPrettyName || (hw.current ? (typeof hw.current === 'object' ? (hw.current.id || hw.current.name) : hw.current) : null) || (hw.requested ? (typeof hw.requested === 'object' ? (hw.requested.id || hw.requested.name) : hw.requested) : null) || hw.id || hw.name || hw.displayName || JSON.stringify(hw) || 'cpu-basic'; } return String(hw); } function repoAppUrl(repoId) { return 'https://' + repoId.replace(/\//g, '-') + '.hf.space'; } function maskToken(t) { return t.substring(0, 6) + '…' + t.substring(t.length - 4); } function parseJsonArg(v) { if (!v) return {}; if (typeof v === 'object') return v; try { return JSON.parse(v); } catch (e) { throw new Error('Invalid JSON argument: ' + v); } } // ─── Secure Login Popup (token never passes through the model) ──────── function openLoginPopup() { var existing = document.getElementById('huggingbot-hf-login-overlay'); if (existing) existing.remove(); var overlay = document.createElement('div'); overlay.id = 'huggingbot-hf-login-overlay'; overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.7);z-index:10000;display:flex;align-items:center;justify-content:center;padding:16px;'; var box = document.createElement('div'); box.style.cssText = 'background:#111827;border:1px solid #374151;border-radius:16px;padding:24px;width:420px;max-width:100%;box-shadow:0 25px 50px rgba(0,0,0,.5);font-family:system-ui,sans-serif;'; var header = document.createElement('div'); header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;'; header.innerHTML = '
Paste your HuggingFace access token.
' + '' + '' + '' + 'Get a token at huggingface.co/settings/tokens
'; box.appendChild(header); box.appendChild(body); overlay.appendChild(box); document.body.appendChild(overlay); var input = document.getElementById('huggingbot-hf-token'); var statusEl = document.getElementById('huggingbot-hf-status'); var submitBtn = document.getElementById('huggingbot-hf-submit'); function close() { overlay.remove(); } function showStatus(msg, color) { statusEl.style.display = 'block'; statusEl.style.background = color + '18'; statusEl.style.border = '1px solid ' + color + '44'; statusEl.style.color = color; statusEl.innerHTML = msg; } document.getElementById('huggingbot-hf-show').addEventListener('change', function () { input.type = this.checked ? 'text' : 'password'; }); document.getElementById('huggingbot-hf-close').addEventListener('click', close); document.getElementById('huggingbot-hf-cancel').addEventListener('click', close); submitBtn.addEventListener('click', async function () { var token = input.value.trim(); if (!token) return showStatus('⚠️ Please enter a token.', '#f59e0b'); if (token.indexOf('hf_') !== 0) return showStatus('⚠️ Tokens should start withhf_.', '#f59e0b');
setToken(token);
submitBtn.disabled = true;
submitBtn.textContent = '⏳ Verifying...';
try {
var res = await fetch('https://huggingface.co/api/whoami-v2', { headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' } });
if (res.ok) {
var info = await res.json();
showStatus('✅ Authenticated as ' + (info.name || info.user || 'unknown') + ' (' + maskToken(token) + ')', '#059669');
setTimeout(close, 1200);
} else {
showStatus('⚠️ Token stored, but verification failed (' + res.status + ').', '#f59e0b');
submitBtn.disabled = false;
submitBtn.textContent = '🔐 Set Token';
}
} catch (e) {
showStatus('⚠️ Token stored locally, but could not verify: ' + e.message, '#f59e0b');
submitBtn.disabled = false;
submitBtn.textContent = '🔐 Set Token';
}
});
input.addEventListener('keydown', function (e) { if (e.key === 'Enter') submitBtn.click(); if (e.key === 'Escape') close(); });
setTimeout(function () { input.focus(); }, 100);
}
// ─── Tool schemas (exactly as registered in the runtime) ──────────────
var TOOLS = [
{ name: 'hf_set_token', description: 'Store your HuggingFace access token for this session. Get it from https://huggingface.co/settings/tokens', parameters: { type: 'object', properties: { token: { type: 'string', description: 'Your HF access token (hf_...). Required for all write operations.' } }, required: ['token'] } },
{ name: 'hf_login_ui', description: '🔐 Open a secure popup to enter your HF token. The token is stored directly in your browser — it never passes through the AI model.', parameters: { type: 'object', properties: {} } },
{ name: 'hf_logout', description: 'Clear your stored HuggingFace token from localStorage.', parameters: { type: 'object', properties: {} } },
{ name: 'hf_token_status', description: 'Check if a HF token is stored and who it belongs to (without revealing the token).', parameters: { type: 'object', properties: {} } },
{ name: 'hf_whoami', description: 'Check who you are authenticated as on HuggingFace Hub.', parameters: { type: 'object', properties: {} } },
{ name: 'hf_create_space', description: 'Create a new HuggingFace Space. Supports Gradio, Streamlit, Docker, or static SDKs.', parameters: { type: 'object', properties: { name: { type: 'string', description: 'Space name (e.g. "my-cool-demo"). Must be unique under your namespace.' }, sdk: { type: 'string', enum: ['gradio', 'streamlit', 'docker', 'static'], default: 'gradio', description: 'SDK/framework to use' }, type: { type: 'string', enum: ['space'], default: 'space' }, title: { type: 'string', description: 'Optional display title' }, license: { type: 'string', description: 'Optional license (e.g. "mit", "apache-2.0")' }, hardware: { type: 'string', description: 'Hardware upgrade (e.g. "t4-medium", "cpu-upgrade"). See HF docs for options.' }, secrets: { type: 'string', description: 'JSON object of secrets {"key": "value", ...}' }, variables: { type: 'string', description: 'JSON object of env variables {"KEY": "VALUE", ...}' }, sleep_time: { type: 'number', description: 'Sleep timeout in seconds (for GPU spaces)' }, private: { type: 'boolean', default: false, description: 'Create as private space?' }, org: { type: 'string', description: 'Organization namespace (if creating under an org)' } }, required: ['name'] } },
{ name: 'hf_duplicate_space', description: 'Duplicate an existing Space (useful for forking with custom config).', parameters: { type: 'object', properties: { from: { type: 'string', description: 'Source Space ID (e.g. "username/space-name")' }, name: { type: 'string', description: 'New Space name' }, org: { type: 'string', description: 'Optional org to duplicate into' }, hardware: { type: 'string', description: 'Hardware for the new Space' }, secrets: { type: 'string', description: 'JSON of secrets' }, variables: { type: 'string', description: 'JSON of env variables' }, sleep_time: { type: 'number', description: 'Sleep timeout in seconds' }, private: { type: 'boolean', default: false } }, required: ['from', 'name'] } },
{ name: 'hf_delete_space', description: 'Delete a Space repository from HuggingFace Hub.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID to delete (e.g. "username/my-space")' } }, required: ['repo_id'] } },
{ name: 'hf_upload_file', description: 'Upload a single file to a Space (app.py, requirements.txt, README.md, etc.). Triggers a rebuild.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID (e.g. "username/my-space")' }, path: { type: 'string', description: 'Path in the repo (e.g. "app.py" or "src/utils.py")' }, content: { type: 'string', description: 'File content as text' }, summary: { type: 'string', description: 'Commit message (default: "Update via HuggingBot")' }, description: { type: 'string', description: 'Optional commit description' } }, required: ['repo_id', 'path', 'content'] } },
{ name: 'hf_upload_app', description: 'Upload a complete Gradio/Streamlit app with requirements.txt in one go.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID (e.g. "username/my-space")' }, app_code: { type: 'string', description: 'The app.py (or app.js for static) source code' }, requirements: { type: 'string', description: 'Optional requirements.txt content (one package per line)' }, readme: { type: 'string', description: 'Optional README.md content' }, extra_files: { type: 'string', description: 'Optional JSON of extra files {"path": "content", ...}' }, summary: { type: 'string', description: 'Commit message' } }, required: ['repo_id', 'app_code'] } },
{ name: 'hf_delete_file', description: 'Delete a file from a Space repository.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID (e.g. "username/my-space")' }, path: { type: 'string', description: 'File path in the repo to delete' }, summary: { type: 'string', description: 'Commit message' } }, required: ['repo_id', 'path'] } },
{ name: 'hf_set_secret', description: 'Add or update a secret (environment variable) for a Space. Triggers a restart.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID (e.g. "username/my-space")' }, key: { type: 'string', description: 'Secret name (e.g. "HF_TOKEN", "API_KEY")' }, value: { type: 'string', description: 'Secret value' } }, required: ['repo_id', 'key', 'value'] } },
{ name: 'hf_delete_secret', description: 'Delete a secret from a Space.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID' }, key: { type: 'string', description: 'Secret name to delete' } }, required: ['repo_id', 'key'] } },
{ name: 'hf_set_hardware', description: 'Request a hardware upgrade/downgrade for a Space (e.g. CPU → GPU). Triggers a restart.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID' }, hardware: { type: 'string', description: 'Hardware tier: cpu-basic (free), cpu-upgrade ($0.03/hr), t4-small ($0.40), t4-medium ($0.60), l4-1x ($0.80), a10g-small ($1.00), a100-large ($2.50)', enum: ['cpu-basic', 'cpu-upgrade', 't4-small', 't4-medium', 'l4-1x', 'a10g-small', 'a10g-large', 'a100-large'] }, sleep_time: { type: 'number', description: 'Auto-sleep timeout in seconds (for GPU) - e.g. 7200 = 2h' } }, required: ['repo_id', 'hardware'] } },
{ name: 'hf_set_visibility', description: 'Change the visibility of a Space (public / private).', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID' }, visibility: { type: 'string', enum: ['public', 'private'], description: 'New visibility level' } }, required: ['repo_id', 'visibility'] } },
{ name: 'hf_set_sleep', description: 'Set auto-sleep timeout for a Space (useful for GPU spaces to save cost).', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID' }, sleep_time: { type: 'number', description: 'Sleep timeout in seconds (0 to disable, 300 = 5 min, 3600 = 1h, 7200 = 2h)' } }, required: ['repo_id', 'sleep_time'] } },
{ name: 'hf_pause_space', description: 'Pause a Space to stop billing. It can be restarted later with hf_restart_space.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID to pause' } }, required: ['repo_id'] } },
{ name: 'hf_restart_space', description: 'Restart a paused Space.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID to restart' } }, required: ['repo_id'] } },
{ name: 'hf_space_status', description: 'Get the runtime status, hardware, and stage of a Space.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID (e.g. "username/my-space")' } }, required: ['repo_id'] } },
{ name: 'hf_space_logs', description: 'Fetch the latest build/runtime logs from a Space.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Space ID' } }, required: ['repo_id'] } },
{ name: 'hf_search_spaces', description: 'Search for Spaces on the HuggingFace Hub by query.', parameters: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, limit: { type: 'number', default: 5, description: 'Max results (1-25)' } }, required: ['query'] } },
{ name: 'hf_list_my_spaces', description: 'List all Spaces owned by your account.', parameters: { type: 'object', properties: { limit: { type: 'number', default: 10, description: 'Max results' } }, required: [] } },
{ name: 'hf_search_models', description: 'Search for models on the HuggingFace Hub.', parameters: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, limit: { type: 'number', default: 5, description: 'Max results' } }, required: ['query'] } },
{ name: 'hf_call_space', description: 'Call a Gradio Space API endpoint (make predictions/inference via the Space API).', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Gradio Space ID (e.g. "abidlabs/en2fr")' }, api_name: { type: 'string', default: '/predict', description: 'API endpoint name (e.g. "/predict", "/generate")' }, data: { type: 'string', description: 'JSON array of input data (e.g. ["Hello world"])' } }, required: ['repo_id', 'data'] } },
{ name: 'hf_inference', description: 'Run inference on a HuggingFace model using the free Inference API.', parameters: { type: 'object', properties: { model: { type: 'string', description: 'Model ID (e.g. "gpt2", "meta-llama/Llama-3.2-3B-Instruct")' }, inputs: { type: 'string', description: 'Input text or JSON string of inputs' }, parameters: { type: 'string', description: 'Optional JSON string of generation parameters (max_length, temperature, etc.)' }, task: { type: 'string', description: 'Optional task type (e.g. "text-generation", "image-classification", "automatic-speech-recognition")' } }, required: ['model', 'inputs'] } },
{ name: 'hf_repo_info', description: 'Get detailed information about any HF repo (Space, Model, or Dataset).', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Repo ID (e.g. "username/repo-name")' }, type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space', description: 'Repo type' } }, required: ['repo_id'] } },
{ name: 'hf_list_files', description: 'List files in a repository at a given path.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Repo ID' }, type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space', description: 'Repo type' }, path: { type: 'string', default: '', description: 'Subdirectory path (optional)' }, revision: { type: 'string', default: 'main', description: 'Branch/revision' } }, required: ['repo_id'] } },
{ name: 'hf_read_file', description: 'Read the content of a file from ANY HuggingFace repo (Space, Model, or Dataset), including PRIVATE repos.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Repo ID (e.g. "username/my-space")' }, path: { type: 'string', description: 'File path in the repo (e.g. "app.py", "config.json")' }, revision: { type: 'string', default: 'main', description: 'Branch/commit revision' }, type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space', description: 'Repo type' } }, required: ['repo_id', 'path'] } },
{ name: 'hf_file_exists', description: 'Check if a file exists in a HuggingFace repo (including private repos).', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Repo ID (e.g. "username/my-space")' }, path: { type: 'string', description: 'File path to check (e.g. "app.py", "config.json")' }, revision: { type: 'string', default: 'main', description: 'Branch/revision' } }, required: ['repo_id', 'path'] } },
{ name: 'hf_repo_exists', description: 'Check if a HuggingFace repository exists (Space, Model, or Dataset). Works for private repos too.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Repo ID (e.g. "username/my-space")' }, type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space', description: 'Repo type' } }, required: ['repo_id'] } },
{ name: 'hf_list_commits', description: 'View the commit history of a HuggingFace repository.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Repo ID (e.g. "username/my-space")' }, type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space', description: 'Repo type' }, revision: { type: 'string', default: 'main', description: 'Branch/revision to view commits for' }, limit: { type: 'number', default: 10, description: 'Max commits to show (1-50)' } }, required: ['repo_id'] } },
{ name: 'hf_move_file', description: 'Rename or move a file in a HuggingFace repository.', parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Repo ID (e.g. "username/my-space")' }, source_path: { type: 'string', description: 'Current file path (e.g. "old_name.py")' }, dest_path: { type: 'string', description: 'New file path (e.g. "new_name.py" or "subfolder/new_name.py")' }, summary: { type: 'string', description: 'Commit message (default: "Move/Rename file")' }, type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space', description: 'Repo type' } }, required: ['repo_id', 'source_path', 'dest_path'] } },
// ── Expanded (new in v2.0.0) ────────────────────────────────────────
{ name: 'hf_search_datasets', description: 'Search for datasets on the HuggingFace Hub.', parameters: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, limit: { type: 'number', default: 5, description: 'Max results (1-25)' } }, required: ['query'] } }
];
// ─── Tool implementations ─────────────────────────────────────────────
var REGISTRY = {
hf_set_token: async function (a) {
var t = String(a.token || '').trim();
if (!t || t.indexOf('hf_') !== 0) return '⚠️ Invalid token format. Tokens start with `hf_`. Get one at https://huggingface.co/settings/tokens';
setToken(t);
try {
var w = await hfFetch('/api/whoami-v2');
return '✅ **HuggingFace token stored** (' + maskToken(t) + ')\n👤 Authenticated as: **' + (w.name || w.user || 'unknown') + '**\nYou can now use all HF tools.';
} catch (e) {
return '⚠️ Token stored (' + maskToken(t) + ') but verification failed: ' + e.message + '. Check that the token is valid.';
}
},
hf_login_ui: async function () {
openLoginPopup();
return '🔐 **Secure Login Popup Opened**\n\nA popup has been opened where you can paste your HuggingFace token.\n\n> 🛡️ Your token is stored directly in your browser\'s localStorage — it **never passes through this AI model**.\n\n💡 Tip: you can also paste your token directly in chat with `hf_set_token`, but the popup is more secure.\n\n📋 Get a token at: https://huggingface.co/settings/tokens';
},
hf_logout: async function () {
var had = !!getToken();
clearToken();
return had ? '🚪 **Logged out.** HuggingFace token removed from localStorage.' : '👋 No token was stored. Nothing to clear.';
},
hf_token_status: async function () {
var t = getToken();
if (!t) return '🔑 **No token stored.** Use `hf_login_ui` (secure popup) or `hf_set_token` to log in.';
try {
var info = await hfFetch('/api/whoami-v2');
return ['🔑 **Token Status:** Active (' + maskToken(t) + ')', '👤 **Authenticated as:** ' + (info.name || info.user || 'unknown'), '📧 **Email:** ' + (info.email || 'N/A'), '💳 **Can Pay:** ' + (info.canPay ? 'Yes' : 'No'), '', '💡 Use `hf_logout` to clear your token.'].join('\n');
} catch (e) {
return '⚠️ **Token stored** (' + maskToken(t) + ') but verification failed: ' + e.message;
}
},
hf_whoami: async function () {
try {
var info = await hfFetch('/api/whoami-v2');
var lines = ['**👤 HuggingFace User Info**', '**ID:** ' + (info.id || info.name || 'N/A'), '**Name:** ' + (info.name || info.user || 'N/A'), '**Email:** ' + (info.email || 'N/A'), '**Type:** ' + (info.type || info.org || 'user'), '**Can Pay:** ' + (info.canPay ? '✅ Yes' : '❌ No'), '**Plan:** ' + (info.plan ? (info.plan.name || info.plan) : 'Free/Unknown')];
if (info.orgs && info.orgs.length) lines.push('**Organizations:** ' + info.orgs.map(function (o) { return o.name; }).join(', '));
if (info.usage && info.usage.inference) lines.push('**Inference Usage:** ' + JSON.stringify(info.usage.inference));
return lines.join('\n');
} catch (e) { return '❌ Failed to get user info: ' + e.message; }
},
hf_create_space: async function (a) {
var me = await hfFetch('/api/whoami-v2');
var ns = a.org || me.name;
var payload = { name: a.name, type: 'space', sdk: a.sdk || 'gradio', private: !!a.private };
if (a.title) payload.title = a.title;
if (a.license) payload.license = a.license;
if (a.hardware) payload.hardware = a.hardware;
if (a.sleep_time) payload.sleep_time = a.sleep_time;
var secrets = parseJsonArg(a.secrets);
var variables = parseJsonArg(a.variables);
if (Object.keys(secrets).length) payload.secrets = secrets;
if (Object.keys(variables).length) payload.variables = variables;
await hfFetch('/api/repos/create', { method: 'POST', body: JSON.stringify(payload) });
var repoId = ns + '/' + a.name;
var out = ['✅ **Space created successfully!**', '', '**Repo:** ' + repoId, '**SDK:** ' + (a.sdk || 'gradio'), '**Visibility:** ' + (a.private ? '🔒 Private' : '🌍 Public'), '**URL:** https://huggingface.co/spaces/' + repoId, '**App:** ' + repoAppUrl(repoId)];
if (a.hardware) out.push('⚡ Hardware requested: ' + a.hardware);
if (a.sleep_time) out.push('💤 Sleep timeout: ' + a.sleep_time + 's');
out.push('', '💡 **Next steps:** Upload files using `hf_upload_file` or `hf_upload_app`');
return out.join('\n');
},
hf_duplicate_space: async function (a) {
var me = await hfFetch('/api/whoami-v2');
var ns = a.org || me.name;
var body = { from: a.from, name: a.name, namespace: ns, private: !!a.private };
if (a.hardware) body.hardware = a.hardware;
if (a.sleep_time) body.sleep_time = a.sleep_time;
var secrets = parseJsonArg(a.secrets);
var variables = parseJsonArg(a.variables);
if (Object.keys(secrets).length) body.secrets = secrets;
if (Object.keys(variables).length) body.variables = variables;
await hfFetch('/api/spaces/duplicate', { method: 'POST', body: JSON.stringify(body) });
var repoId = ns + '/' + a.name;
var out = ['✅ **Space duplicated!**', '', '**From:** ' + a.from, '**New Repo:** ' + repoId, '**URL:** https://huggingface.co/spaces/' + repoId, '**App:** ' + repoAppUrl(repoId)];
if (a.hardware) out.push('**Hardware:** ' + a.hardware);
if (a.sleep_time) out.push('**Sleep:** ' + a.sleep_time + 's');
return out.join('\n');
},
hf_delete_space: async function (a) {
var p = parseRepoId(a.repo_id);
try {
var rt = await hfFetch('/api/spaces/' + p.namespace + '/' + p.name + '/runtime');
if (rt.stage === 'PAUSED' || rt.stage === 'SLEEPING') {
await hfFetch('/api/spaces/' + p.namespace + '/' + p.name + '/restart', { method: 'POST' });
await new Promise(function (r) { setTimeout(r, 2000); });
}
} catch (e) { /* best-effort */ }
try {
await hfFetch('/api/spaces/' + p.namespace + '/' + p.name, { method: 'DELETE' });
return '✅ **Space deleted:** ' + a.repo_id;
} catch (e) {
try {
await hfFetch('/api/repos/' + p.namespace + '/' + p.name, { method: 'DELETE' });
return '✅ **Space deleted (via repos endpoint):** ' + a.repo_id;
} catch (e2) {
throw new Error('Could not delete space: ' + e.message + '. Try restarting it first with hf_restart_space.');
}
}
},
hf_upload_file: async function (a) {
var p = parseRepoId(a.repo_id);
await commitFiles('space', p.namespace, p.name, [{ path: a.path, content: a.content }], null, a.summary || 'Update via HuggingBot', a.description || '');
return ['✅ **File uploaded:** `' + a.path + '` → `' + a.repo_id + '`', '📝 ' + (a.summary || 'Update via HuggingBot'), '', '💡 The Space will now rebuild automatically. Check status with `hf_space_status`.'].join('\n');
},
hf_upload_app: async function (a) {
var p = parseRepoId(a.repo_id);
var files = [{ path: 'app.py', content: a.app_code }];
if (a.requirements) files.push({ path: 'requirements.txt', content: a.requirements });
if (a.readme) files.push({ path: 'README.md', content: a.readme });
if (a.extra_files) {
var extras = parseJsonArg(a.extra_files);
for (var k in extras) if (Object.prototype.hasOwnProperty.call(extras, k)) files.push({ path: k, content: String(extras[k]) });
}
await commitFiles('space', p.namespace, p.name, files, null, a.summary || 'Deploy app via HuggingBot', 'Uploaded app.py' + (a.requirements ? ' + requirements.txt' : '') + (a.readme ? ' + README.md' : ''));
return ['✅ **App deployed to:** `' + a.repo_id + '`', '📦 **Files uploaded:** ' + files.map(function (f) { return '`' + f.path + '`'; }).join(', '), '', '🌐 **Space:** https://huggingface.co/spaces/' + a.repo_id, '🚀 **App URL:** ' + repoAppUrl(a.repo_id), '', '💡 The Space is now building. Use `hf_space_status ' + a.repo_id + '` to check progress.'].join('\n');
},
hf_delete_file: async function (a) {
var p = parseRepoId(a.repo_id);
await commitFiles('space', p.namespace, p.name, null, [{ path: a.path }], a.summary || ('Delete ' + a.path), '');
return '✅ **File deleted:** `' + a.path + '` from `' + a.repo_id + '`';
},
hf_set_secret: async function (a) {
var p = parseRepoId(a.repo_id);
try {
await hfFetch('/api/spaces/' + p.namespace + '/' + p.name + '/secrets', { method: 'PUT', body: JSON.stringify({ key: a.key, value: a.value }) });
} catch (e) {
if (String(e.message).indexOf('404') > -1 || String(e.message).indexOf('405') > -1) {
await hfFetch('/api/spaces/' + p.namespace + '/' + p.name + '/secrets', { method: 'POST', body: JSON.stringify({ key: a.key, value: a.value }) });
} else { throw e; }
}
return '🔐 **Secret set:** `' + a.key + '` in `' + a.repo_id + '`\n⚠️ The Space will restart.';
},
hf_delete_secret: async function (a) {
var p = parseRepoId(a.repo_id);
await hfFetch('/api/spaces/' + p.namespace + '/' + p.name + '/secrets', { method: 'DELETE', body: JSON.stringify({ key: a.key }) });
return '🗑️ **Secret deleted:** `' + a.key + '` from `' + a.repo_id + '`';
},
hf_set_hardware: async function (a) {
var p = parseRepoId(a.repo_id);
var settings = { flavor: a.hardware };
if (a.sleep_time) settings.gcTimeout = a.sleep_time;
await updateSpaceSettings(p.namespace, p.name, settings);
var out = ['⚡ **Hardware change requested for:** `' + a.repo_id + '`', '**New hardware:** ' + a.hardware];
if (a.sleep_time) out.push('**Sleep timeout:** ' + a.sleep_time + 's');
out.push('', '⏳ The Space is restarting. Check status with `hf_space_status`.');
return out.join('\n');
},
hf_set_visibility: async function (a) {
var p = parseRepoId(a.repo_id);
await updateSpaceSettings(p.namespace, p.name, { private: a.visibility === 'private' });
return (a.visibility === 'private' ? '🔒' : '🌍') + ' **Visibility changed to ' + a.visibility + ':** `' + a.repo_id + '`';
},
hf_set_sleep: async function (a) {
var p = parseRepoId(a.repo_id);
await updateSpaceSettings(p.namespace, p.name, { gcTimeout: a.sleep_time });
var status = a.sleep_time === 0 ? '❌ Disabled (never sleeps)' : '💤 ' + a.sleep_time + 's (' + Math.round(a.sleep_time / 60) + ' min)';
return '✅ **Sleep configured:** `' + a.repo_id + '` → ' + status;
},
hf_pause_space: async function (a) {
var p = parseRepoId(a.repo_id);
await hfFetch('/api/spaces/' + p.namespace + '/' + p.name + '/pause', { method: 'POST' });
return '⏸️ **Space paused:** `' + a.repo_id + '`\n💡 Restart with `hf_restart_space ' + a.repo_id + '`';
},
hf_restart_space: async function (a) {
var p = parseRepoId(a.repo_id);
await hfFetch('/api/spaces/' + p.namespace + '/' + p.name + '/restart', { method: 'POST' });
return '▶️ **Space restarting:** `' + a.repo_id + '`';
},
hf_space_status: async function (a) {
var p = parseRepoId(a.repo_id);
var runtime = await hfFetch('/api/spaces/' + p.namespace + '/' + p.name + '/runtime');
var repoInfo = await hfFetch('/api/spaces/' + p.namespace + '/' + p.name);
var emoji = { 'NO_APP_FILE': '📄', 'BUILDING': '🔨', 'RUNNING': '✅', 'RUNNING_BUILDING': '🔄', 'PAUSED': '⏸️', 'SLEEPING': '💤', 'STOPPED': '⛔', 'ERROR': '❌' };
var stage = runtime.stage || 'UNKNOWN';
var out = ['**📊 Space Status:** `' + a.repo_id + '`', '', '**Stage:** ' + (emoji[stage] || '❓') + ' ' + stage, '**SDK:** ' + (repoInfo.sdk || 'N/A'), '**Hardware:** ' + extractHardwareName(runtime.hardware), '**Requested HW:** ' + extractHardwareName(runtime.requestedHardware), '**Visibility:** ' + (repoInfo.private ? '🔒 Private' : '🌍 Public'), '**Likes:** ⭐ ' + (repoInfo.likes || 0), '**URL:** https://huggingface.co/spaces/' + a.repo_id, '**App:** ' + repoAppUrl(a.repo_id)];
return out.join('\n');
},
hf_space_logs: async function (a) {
var p = parseRepoId(a.repo_id);
var endpoints = [
'/api/spaces/' + p.namespace + '/' + p.name + '/runtime/logs',
'/api/spaces/' + p.namespace + '/' + p.name + '/logs',
'/api/spaces/' + p.namespace + '/' + p.name + '/runtime'
];
var lastError = null;
for (var i = 0; i < endpoints.length; i++) {
try {
var result = await hfFetch(endpoints[i]);
if (result && typeof result === 'object') {
var logsStr = result.logs || result.stdout || result.stderr || result.build || JSON.stringify(result, null, 2);
var truncated = logsStr.length > 5000 ? logsStr.slice(-5000) + '\n... (truncated, full: ' + logsStr.length + ' chars)' : logsStr;
return ['📋 **Build Logs for** `' + a.repo_id + '`', '```', truncated, '```'].join('\n');
}
} catch (e) { lastError = e; }
}
try {
var rt = await hfFetch('/api/spaces/' + p.namespace + '/' + p.name + '/runtime');
if (rt && rt.errorMessage) return '⚠️ **Space has an error:**\n```\n' + rt.errorMessage + '\n```';
return 'ℹ️ **Space is** ' + (rt.stage || 'unknown') + '. No logs available via REST API.\n\n💡 Logs can be viewed in the HF UI: https://huggingface.co/spaces/' + a.repo_id;
} catch (e2) { throw lastError || e2; }
},
hf_search_spaces: async function (a) {
var limit = Math.min(Math.max(a.limit || 5, 1), 25);
var results = await hfFetch('/api/spaces?search=' + encodeURIComponent(a.query) + '&limit=' + limit + '&sort=likes');
if (!results || !results.length) return '🔍 No Spaces found for "' + a.query + '"';
var lines = ['🔍 **Spaces matching "' + a.query + '":**', ''];
results.forEach(function (s, i) {
var id = s.id || ((s.namespace || '?') + '/' + (s.name || '?'));
var sdk = s.sdk ? ' [' + s.sdk + ']' : '';
var likes = s.likes ? ' ⭐' + s.likes : '';
var desc = s.description ? '\n > ' + s.description.substring(0, 150) : '';
lines.push((i + 1) + '. **' + id + '**' + sdk + likes);
if (desc) lines.push(desc);
lines.push(' https://huggingface.co/spaces/' + id);
lines.push('');
});
return lines.join('\n');
},
hf_list_my_spaces: async function () {
var userInfo = await hfFetch('/api/whoami-v2');
var name = userInfo.name || userInfo.user;
var results = await hfFetch('/api/spaces?author=' + encodeURIComponent(name) + '&limit=25&sort=lastModified');
if (!results || !results.length) return '📭 No Spaces found under **' + name + '**.\nCreate one with `hf_create_space`!';
var lines = ['📂 **Your Spaces (' + name + '):**', ''];
results.forEach(function (s) {
var id = s.id || (s.namespace + '/' + s.name);
var sdk = s.sdk ? ' [' + s.sdk + ']' : '';
var vis = s.private ? '🔒' : '🌍';
var likes = s.likes ? ' ⭐' + s.likes : '';
var stage = (s.runtime && s.runtime.stage) || '';
lines.push(vis + ' **' + id + '**' + sdk + likes + (stage ? ' — ' + stage : ''));
});
lines.push('', '💡 Use `hf_space_status