broadfield commited on
Commit
9c85fb7
Β·
verified Β·
1 Parent(s): 325a0e2

Add Repo Creation & Multi-Type Ops module v1.0.0 (create datasets/models, type-aware file uploads)

Browse files
Files changed (1) hide show
  1. modules_repos.js +188 -0
modules_repos.js ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // HuggingBot Module: Repo Creation & Multi-Type Ops v1.0.0
2
+ // ============================================================================
3
+ // Companion to the HuggingFace Hub module (modules_huggingface.js).
4
+ // Adds what the Hub module lacks:
5
+ // * hf_create_repo β€” create Space, Dataset, or Model repos
6
+ // * hf_upload_repo_file β€” upload a file to ANY repo type (space|model|dataset)
7
+ // * hf_delete_repo β€” delete any repo type
8
+ // * hf_list_my_datasets β€” list datasets you own
9
+ // * hf_list_my_models β€” list models you own
10
+ // Tool names are unique (no clash with the Hub module), so both modules
11
+ // can be registered side by side in the ModuleSystem.
12
+ // Token is shared with the Hub module via localStorage key 'chatseed_hf_token'.
13
+ // ============================================================================
14
+ (function (MS) {
15
+ 'use strict';
16
+
17
+ var HF = 'https://huggingface.co';
18
+ var TK = 'chatseed_hf_token';
19
+
20
+ function tok() { try { return localStorage.getItem(TK) || ''; } catch (e) { return ''; } }
21
+
22
+ function hdr() {
23
+ var t = tok();
24
+ if (!t) throw new Error('No HuggingFace token set. Use hf_set_token or hf_login_ui first.');
25
+ return { 'Authorization': 'Bearer ' + t, 'Content-Type': 'application/json' };
26
+ }
27
+
28
+ async function api(path, opts) {
29
+ opts = opts || {};
30
+ var headers = Object.assign({}, hdr(), opts.headers || {});
31
+ var res = await fetch(HF + path, Object.assign({}, opts, { headers: headers }));
32
+ if (!res.ok) {
33
+ var m = '';
34
+ try { var j = await res.json(); m = j.error || JSON.stringify(j); }
35
+ catch (e) { try { m = await res.text(); } catch (e2) { m = res.statusText; } }
36
+ throw new Error('HF API ' + res.status + ': ' + m);
37
+ }
38
+ if (res.status === 204) return null;
39
+ return (res.headers.get('content-type') || '').indexOf('application/json') > -1 ? res.json() : res.text();
40
+ }
41
+
42
+ function pathFor(t) { return t === 'model' ? 'models' : t === 'dataset' ? 'datasets' : 'spaces'; }
43
+
44
+ function parseRepo(id) {
45
+ var p = String(id || '').split('/');
46
+ if (p.length !== 2) throw new Error('Invalid repo_id. Use format "namespace/repo-name"');
47
+ return { ns: p[0], name: p[1] };
48
+ }
49
+
50
+ function parseJson(v) {
51
+ if (!v) return {};
52
+ if (typeof v === 'object') return v;
53
+ try { return JSON.parse(v); } catch (e) { throw new Error('Invalid JSON argument: ' + v); }
54
+ }
55
+
56
+ async function commit(type, repo, files, deleted, summary) {
57
+ var p = parseRepo(repo);
58
+ var body = { summary: summary || 'Update via HuggingBot', description: '',
59
+ files: (files || []).map(function (f) { return { path: f.path, content: f.content, encoding: f.encoding || 'utf-8' }; }) };
60
+ if (deleted && deleted.length) body.deletedEntries = deleted;
61
+ return api('/api/' + pathFor(type) + '/' + p.ns + '/' + p.name + '/commit/main', { method: 'POST', body: JSON.stringify(body) });
62
+ }
63
+
64
+ async function createRepo(a) {
65
+ var type = a.type || 'space';
66
+ if (['space', 'model', 'dataset'].indexOf(type) < 0) throw new Error('Invalid type "' + type + '". Use space|model|dataset.');
67
+ var me = await api('/api/whoami-v2');
68
+ var ns = a.org || me.name;
69
+ var payload = { name: a.name, type: type, private: !!a.private };
70
+ if (type === 'space') payload.sdk = a.sdk || 'gradio';
71
+ if (a.title) payload.title = a.title;
72
+ if (a.license) payload.license = a.license;
73
+ if (a.hardware) payload.hardware = a.hardware;
74
+ if (a.sleep_time) payload.sleep_time = a.sleep_time;
75
+ var sec = parseJson(a.secrets), vars = parseJson(a.variables);
76
+ if (Object.keys(sec).length) payload.secrets = sec;
77
+ if (Object.keys(vars).length) payload.variables = vars;
78
+ await api('/api/repos/create', { method: 'POST', body: JSON.stringify(payload) });
79
+ var repoId = ns + '/' + a.name;
80
+ var base = pathFor(type);
81
+ var label = type.charAt(0).toUpperCase() + type.slice(1);
82
+ var out = ['βœ… **' + label + ' created successfully!**', '', '**Repo:** ' + repoId, '**Type:** ' + type,
83
+ '**Visibility:** ' + (a.private ? 'πŸ”’ Private' : '🌍 Public'), '**URL:** https://huggingface.co/' + base + '/' + repoId];
84
+ if (type === 'space') { out.push('**SDK:** ' + (a.sdk || 'gradio')); out.push('**App:** https://' + repoId.replace(/\//g, '-') + '.hf.space'); }
85
+ if (a.hardware) out.push('⚑ Hardware requested: ' + a.hardware);
86
+ if (a.sleep_time) out.push('πŸ’€ Sleep timeout: ' + a.sleep_time + 's');
87
+ out.push('', 'πŸ’‘ **Next steps:** upload files with `hf_upload_repo_file` (pass `type: "' + type + '"`) or `hf_upload_file` for spaces.');
88
+ return out.join('\n');
89
+ }
90
+
91
+ async function deleteRepo(a) {
92
+ var type = a.type || 'space';
93
+ var p = parseRepo(a.repo_id);
94
+ var base = pathFor(type);
95
+ if (type === 'space') {
96
+ try {
97
+ var rt = await api('/api/spaces/' + p.ns + '/' + p.name + '/runtime');
98
+ if (rt.stage === 'PAUSED' || rt.stage === 'SLEEPING') {
99
+ await api('/api/spaces/' + p.ns + '/' + p.name + '/restart', { method: 'POST' });
100
+ await new Promise(function (r) { setTimeout(r, 2000); });
101
+ }
102
+ } catch (e) { /* best-effort */ }
103
+ }
104
+ try {
105
+ await api('/api/' + base + '/' + p.ns + '/' + p.name, { method: 'DELETE' });
106
+ return 'βœ… **' + type + ' deleted:** ' + a.repo_id;
107
+ } catch (e) {
108
+ try {
109
+ await api('/api/repos/' + p.ns + '/' + p.name + '?type=' + type, { method: 'DELETE' });
110
+ return 'βœ… **' + type + ' deleted (via repos endpoint):** ' + a.repo_id;
111
+ } catch (e2) {
112
+ throw new Error('Could not delete ' + type + ': ' + e.message + (type === 'space' ? '. Try restarting it first with hf_restart_space.' : ''));
113
+ }
114
+ }
115
+ }
116
+
117
+ async function uploadRepoFile(a) {
118
+ if (!a.path || a.content === undefined) throw new Error('Both `path` and `content` are required.');
119
+ var type = a.type || 'space';
120
+ await commit(type, a.repo_id, [{ path: a.path, content: a.content }], null, a.summary);
121
+ return ['βœ… **File uploaded:** `' + a.path + '` β†’ `' + a.repo_id + '` (' + type + ')',
122
+ 'πŸ“ ' + (a.summary || 'Update via HuggingBot'), '',
123
+ type === 'space' ? 'πŸ’‘ The Space will now rebuild. Use `hf_space_status` to check.' : 'πŸ’‘ The ' + type + ' repo was updated on `main`.'
124
+ ].join('\n');
125
+ }
126
+
127
+ async function listMine(kind, a) {
128
+ var me = await api('/api/whoami-v2');
129
+ var name = me.name || me.user;
130
+ var limit = Math.min(Math.max((a && a.limit) || 10, 1), 100);
131
+ var base = kind === 'dataset' ? 'datasets' : kind === 'model' ? 'models' : 'spaces';
132
+ var res = await api('/api/' + base + '?author=' + encodeURIComponent(name) + '&limit=' + limit + '&sort=lastModified');
133
+ var label = kind.charAt(0).toUpperCase() + kind.slice(1);
134
+ if (!res || !res.length) return 'πŸ“­ No ' + kind + 's found under **' + name + '**.\nCreate one with `hf_create_repo` (type: "' + kind + '")!';
135
+ var lines = ['πŸ“‚ **Your ' + label + 's (' + name + '):**', ''];
136
+ res.forEach(function (r) {
137
+ var id = r.id || (r.namespace + '/' + r.name);
138
+ var vis = r.private ? 'πŸ”’' : '🌍';
139
+ var likes = r.likes ? ' ⭐' + r.likes : '';
140
+ var dl = r.downloads ? ' πŸ“₯' + (r.downloads / 1000).toFixed(0) + 'k' : '';
141
+ lines.push(vis + ' **' + id + '**' + likes + dl);
142
+ });
143
+ lines.push('', 'πŸ’‘ Use `hf_repo_info` or `hf_list_files` on any of them.');
144
+ return lines.join('\n');
145
+ }
146
+
147
+ var TOOLS = [
148
+ { 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.',
149
+ parameters: { type: 'object', properties: { name: { type: 'string', description: 'Repo name (e.g. "my-dataset")' },
150
+ type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space', description: 'Repo type to create' },
151
+ private: { type: 'boolean', default: false }, org: { type: 'string' }, title: { type: 'string' }, license: { type: 'string' },
152
+ sdk: { type: 'string', enum: ['gradio', 'streamlit', 'docker', 'static'], description: 'SDK (spaces only)' },
153
+ hardware: { type: 'string', description: 'Hardware (spaces only)' }, sleep_time: { type: 'number' },
154
+ secrets: { type: 'string', description: 'JSON {"key":"value"} (spaces only)' }, variables: { type: 'string', description: 'JSON (spaces only)' } },
155
+ required: ['name'] } },
156
+ { 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).',
157
+ parameters: { type: 'object', properties: { repo_id: { type: 'string', description: 'Repo ID (e.g. "username/repo-name")' },
158
+ type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space', description: 'Repo type' },
159
+ path: { type: 'string', description: 'Path in the repo (e.g. "ideas/001-x.md")' }, content: { type: 'string' },
160
+ summary: { type: 'string', description: 'Commit message' } }, required: ['repo_id', 'path', 'content'] } },
161
+ { name: 'hf_delete_repo', description: 'Delete any HuggingFace repo: Space, Dataset, or Model.',
162
+ parameters: { type: 'object', properties: { repo_id: { type: 'string' },
163
+ type: { type: 'string', enum: ['space', 'model', 'dataset'], default: 'space' } }, required: ['repo_id'] } },
164
+ { name: 'hf_list_my_datasets', description: 'List all datasets owned by your account.',
165
+ parameters: { type: 'object', properties: { limit: { type: 'number', default: 10 } }, required: [] } },
166
+ { name: 'hf_list_my_models', description: 'List all models owned by your account.',
167
+ parameters: { type: 'object', properties: { limit: { type: 'number', default: 10 } }, required: [] } }
168
+ ];
169
+
170
+ var REGISTRY = {
171
+ hf_create_repo: createRepo,
172
+ hf_upload_repo_file: uploadRepoFile,
173
+ hf_delete_repo: deleteRepo,
174
+ hf_list_my_datasets: function (a) { return listMine('datasets', a); },
175
+ hf_list_my_models: function (a) { return listMine('models', a); }
176
+ };
177
+
178
+ var api = {
179
+ name: 'Repo Creation & Multi-Type Ops',
180
+ description: 'Create HF datasets/models/spaces and upload files to any repo type.',
181
+ tools: TOOLS.map(function (t) { return { type: 'function', function: t }; }),
182
+ handleToolCall: async function (tn, args) {
183
+ if (REGISTRY[tn]) return REGISTRY[tn](args || {});
184
+ return null;
185
+ }
186
+ };
187
+ MS.register('huggingface-repos', api);
188
+ })(ModuleSystem);