// HuggingBot Module: Repo Creation & Multi-Type Ops v1.0.1 // ============================================================================ // Companion to the HuggingFace Hub module (modules_huggingface.js). // Adds what the Hub module lacks: // * hf_create_repo — create Space, Dataset, or Model repos // * hf_upload_repo_file — upload a file to ANY repo type (space|model|dataset) // * hf_delete_repo — delete any repo type // * hf_list_my_datasets — list datasets you own // * hf_list_my_models -- list models you own // Tool names are unique (no clash with the Hub module), so both modules // can be registered side by side in the ModuleSystem. // Token is shared with the Hub module via localStorage key 'chatseed_hf_token'. // v1.0.1: renamed the fetch helper from `api` to `req` to avoid colliding // with the exported module object `api`. // ============================================================================ (function (MS) { 'use strict'; var HF = 'https://huggingface.co'; var TK = 'chatseed_hf_token'; function tok() { try { return localStorage.getItem(TK) || ''; } catch (e) { return ''; } } function hdr() { var t = tok(); if (!t) throw new Error('No HuggingFace token set. Use hf_set_token or hf_login_ui first.'); return { 'Authorization': 'Bearer ' + t, 'Content-Type': 'application/json' }; } async function req(path, opts) { opts = opts || {}; var headers = Object.assign({}, hdr(), opts.headers || {}); var res = await fetch(HF + path, Object.assign({}, opts, { headers: headers })); if (!res.ok) { var m = ''; try { var j = await res.json(); m = j.error || JSON.stringify(j); } catch (e) { try { m = await res.text(); } catch (e2) { m = res.statusText; } } throw new Error('HF API ' + res.status + ': ' + m); } if (res.status === 204) return null; return (res.headers.get('content-type') || '').indexOf('application/json') > -1 ? res.json() : res.text(); } function pathFor(t) { return t === 'model' ? 'models' : t === 'dataset' ? 'datasets' : 'spaces'; } function parseRepo(id) { var p = String(id || '').split('/'); if (p.length !== 2) throw new Error('Invalid repo_id. Use format "namespace/repo-name"'); return { ns: p[0], name: p[1] }; } function parseJson(v) { if (!v) return {}; if (typeof v === 'object') return v; try { return JSON.parse(v); } catch (e) { throw new Error('Invalid JSON argument: ' + v); } } async function commit(type, repo, files, deleted, summary) { var p = parseRepo(repo); var body = { summary: summary || 'Update via HuggingBot', description: '', files: (files || []).map(function (f) { return { path: f.path, content: f.content, encoding: f.encoding || 'utf-8' }; }) }; if (deleted && deleted.length) body.deletedEntries = deleted; return req('/api/' + pathFor(type) + '/' + p.ns + '/' + p.name + '/commit/main', { method: 'POST', body: JSON.stringify(body) }); } async function createRepo(a) { var type = a.type || 'space'; if (['space', 'model', 'dataset'].indexOf(type) < 0) throw new Error('Invalid type "' + type + '". Use space|model|dataset.'); var me = await req('/api/whoami-v2'); var ns = a.org || me.name; var payload = { name: a.name, type: type, private: !!a.private }; if (type === 'space') payload.sdk = a.sdk || 'gradio'; 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 sec = parseJson(a.secrets), vars = parseJson(a.variables); if (Object.keys(sec).length) payload.secrets = sec; if (Object.keys(vars).length) payload.variables = vars; await req('/api/repos/create', { method: 'POST', body: JSON.stringify(payload) }); var repoId = ns + '/' + a.name; var base = pathFor(type); var label = type.charAt(0).toUpperCase() + type.slice(1); var out = ['✅ **' + label + ' created successfully!**', '', '**Repo:** ' + repoId, '**Type:** ' + type, '**Visibility:** ' + (a.private ? '🔒 Private' : '🌍 Public'), '**URL:** https://huggingface.co/' + base + '/' + repoId]; if (type === 'space') { out.push('**SDK:** ' + (a.sdk || 'gradio')); out.push('**App:** https://' + repoId.replace(/\//g, '-') + '.hf.space'); } 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 with `hf_upload_repo_file` (pass `type: "' + type + '"`) or `hf_upload_file` for spaces.'); return out.join('\n'); } async function deleteRepo(a) { var type = a.type || 'space'; var p = parseRepo(a.repo_id); var base = pathFor(type); if (type === 'space') { try { var rt = await req('/api/spaces/' + p.ns + '/' + p.name + '/runtime'); if (rt.stage === 'PAUSED' || rt.stage === 'SLEEPING') { await req('/api/spaces/' + p.ns + '/' + p.name + '/restart', { method: 'POST' }); await new Promise(function (r) { setTimeout(r, 2000); }); } } catch (e) { /* best-effort */ } } try { await req('/api/' + base + '/' + p.ns + '/' + p.name, { method: 'DELETE' }); return '✅ **' + type + ' deleted:** ' + a.repo_id; } catch (e) { try { await req('/api/repos/' + p.ns + '/' + p.name + '?type=' + type, { method: 'DELETE' }); return '✅ **' + type + ' deleted (via repos endpoint):** ' + a.repo_id; } catch (e2) { throw new Error('Could not delete ' + type + ': ' + e.message + (type === 'space' ? '. Try restarting it first with hf_restart_space.' : '')); } } } async function uploadRepoFile(a) { if (!a.path || a.content === undefined) throw new Error('Both `path` and `content` are required.'); var type = a.type || 'space'; await commit(type, a.repo_id, [{ path: a.path, content: a.content }], null, a.summary); return ['✅ **File uploaded:** `' + a.path + '` → `' + a.repo_id + '` (' + type + ')', '📝 ' + (a.summary || 'Update via HuggingBot'), '', type === 'space' ? '💡 The Space will now rebuild. Use `hf_space_status` to check.' : '💡 The ' + type + ' repo was updated on `main`.' ].join('\n'); } async function listMine(kind, a) { var me = await req('/api/whoami-v2'); var name = me.name || me.user; var limit = Math.min(Math.max((a && a.limit) || 10, 1), 100); var base = kind === 'dataset' ? 'datasets' : kind === 'model' ? 'models' : 'spaces'; var res = await req('/api/' + base + '?author=' + encodeURIComponent(name) + '&limit=' + limit + '&sort=lastModified'); var label = kind.charAt(0).toUpperCase() + kind.slice(1); if (!res || !res.length) return '📭 No ' + kind + 's found under **' + name + '**.\nCreate one with `hf_create_repo` (type: "' + kind + '")!'; var lines = ['📂 **Your ' + label + 's (' + name + '):**', '']; res.forEach(function (r) { var id = r.id || (r.namespace + '/' + r.name); var vis = r.private ? '🔒' : '🌍'; var likes = r.likes ? ' ⭐' + r.likes : ''; var dl = r.downloads ? ' 📥' + (r.downloads / 1000).toFixed(0) + 'k' : ''; lines.push(vis + ' **' + id + '**' + likes + dl); }); lines.push('', '💡 Use `hf_repo_info` or `hf_list_files` on any of them.'); return lines.join('\n'); } var TOOLS = [ { name: 'hf_create_repo', description: 'Create a new HuggingFace repo: Space, Dataset, or Model. Pass `type` to choose — this is how you create datasets and models.', parameters: { type: 'object', properties: { name: { type: 'string', description: 'Repo name (e.g. "my-dataset")' }, type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space', description: 'Repo type to create' }, private: { type: 'boolean', default: false }, org: { type: 'string' }, title: { type: 'string' }, license: { type: 'string' }, sdk: { type: 'string', enum: ['gradio', 'streamlit', 'docker', 'static'], description: 'SDK (spaces only)' }, hardware: { type: 'string', description: 'Hardware (spaces only)' }, sleep_time: { type: 'number' }, secrets: { type: 'string', description: 'JSON {"key":"value"} (spaces only)' }, variables: { type: 'string', description: 'JSON (spaces only)' } }, required: ['name'] } }, { name: 'hf_upload_repo_file', description: 'Upload a single file to ANY repo type: Space, Dataset, or Model (e.g. README.md, data files, model config).', 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' }, path: { type: 'string', description: 'Path in the repo (e.g. "ideas/001-x.md")' }, content: { type: 'string' }, summary: { type: 'string', description: 'Commit message' } }, required: ['repo_id', 'path', 'content'] } }, { name: 'hf_delete_repo', description: 'Delete any HuggingFace repo: Space, Dataset, or Model.', parameters: { type: 'object', properties: { repo_id: { type: 'string' }, type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space' } }, required: ['repo_id'] } }, { name: 'hf_list_my_datasets', description: 'List all datasets owned by your account.', parameters: { type: 'object', properties: { limit: { type: 'number', default: 10 } }, required: [] } }, { name: 'hf_list_my_models', description: 'List all models owned by your account.', parameters: { type: 'object', properties: { limit: { type: 'number', default: 10 } }, required: [] } } ]; var REGISTRY = { hf_create_repo: createRepo, hf_upload_repo_file: uploadRepoFile, hf_delete_repo: deleteRepo, hf_list_my_datasets: function (a) { return listMine('datasets', a); }, hf_list_my_models: function (a) { return listMine('models', a); } }; var moduleApi = { name: 'Repo Creation & Multi-Type Ops', description: 'Create HF datasets/models/spaces and upload files to any repo type.', tools: TOOLS.map(function (t) { return { type: 'function', function: t }; }), handleToolCall: async function (tn, args) { if (REGISTRY[tn]) return REGISTRY[tn](args || {}); return null; } }; MS.register('huggingface-repos', moduleApi); })(ModuleSystem);