Spaces:
Running
Running
Fix name collision: rename fetch helper to req, export object to moduleApi (v1.0.1)
c763302 verified Download modules_repos.js from broadfield/huggingbot-modules: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/spaces/broadfield/huggingbot-modules/resolve/main/modules_repos.js
- Command line
-
hf download hf://spaces/broadfield/huggingbot-modules/modules_repos.js
-
curl -L -o modules_repos.js https://huggingface.co/spaces/broadfield/huggingbot-modules/resolve/main/modules_repos.js
10.6 kB
| // 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); |