mlclaw 0.1.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/mlclaw/SKILL.md +70 -18
- package/Dockerfile +21 -4
- package/README.md +77 -24
- package/assets/hf-logo.svg +8 -0
- package/assets/hf-tooling/manifest.json +50 -0
- package/assets/hf-tooling/skills/hf-cli/SKILL.md +218 -0
- package/assets/hf-tooling/skills/hf-mem/SKILL.md +79 -0
- package/assets/hf-tooling/skills/huggingface-best/SKILL.md +134 -0
- package/assets/hf-tooling/skills/huggingface-datasets/SKILL.md +107 -0
- package/assets/hf-tooling/skills/huggingface-gradio/SKILL.md +298 -0
- package/assets/hf-tooling/skills/huggingface-gradio/examples.md +613 -0
- package/assets/hf-tooling/skills/huggingface-local-models/SKILL.md +113 -0
- package/assets/hf-tooling/skills/huggingface-local-models/references/hardware.md +38 -0
- package/assets/hf-tooling/skills/huggingface-local-models/references/hub-discovery.md +178 -0
- package/assets/hf-tooling/skills/huggingface-local-models/references/quantization.md +256 -0
- package/assets/hf-tooling/skills/huggingface-papers/SKILL.md +239 -0
- package/assets/hf-tooling/skills/huggingface-spaces/SKILL.md +239 -0
- package/assets/hf-tooling/skills/huggingface-spaces/references/buckets.md +89 -0
- package/assets/hf-tooling/skills/huggingface-spaces/references/debugging.md +236 -0
- package/assets/hf-tooling/skills/huggingface-spaces/references/gradio.md +200 -0
- package/assets/hf-tooling/skills/huggingface-spaces/references/grants.md +56 -0
- package/assets/hf-tooling/skills/huggingface-spaces/references/inference-providers.md +85 -0
- package/assets/hf-tooling/skills/huggingface-spaces/references/known-errors.md +232 -0
- package/assets/hf-tooling/skills/huggingface-spaces/references/requirements.md +169 -0
- package/assets/hf-tooling/skills/huggingface-spaces/references/zerogpu.md +349 -0
- package/assets/hf-tooling/skills/huggingface-tool-builder/SKILL.md +120 -0
- package/assets/hf-tooling/skills/huggingface-tool-builder/references/baseline_hf_api.py +57 -0
- package/assets/hf-tooling/skills/huggingface-tool-builder/references/baseline_hf_api.sh +40 -0
- package/assets/hf-tooling/skills/huggingface-tool-builder/references/baseline_hf_api.tsx +57 -0
- package/assets/hf-tooling/skills/huggingface-tool-builder/references/find_models_by_paper.sh +230 -0
- package/assets/hf-tooling/skills/huggingface-tool-builder/references/hf_enrich_models.sh +96 -0
- package/assets/hf-tooling/skills/huggingface-tool-builder/references/hf_model_card_frontmatter.sh +188 -0
- package/assets/hf-tooling/skills/huggingface-tool-builder/references/hf_model_papers_auth.sh +171 -0
- package/assets/hf-tooling/skills/huggingface-zerogpu/SKILL.md +289 -0
- package/assets/hf-tooling/skills/huggingface-zerogpu/references/concurrency.md +79 -0
- package/assets/hf-tooling/skills/huggingface-zerogpu/references/cuda-and-deps.md +66 -0
- package/assets/hf-tooling/skills/huggingface-zerogpu/references/how-quota-works.md +74 -0
- package/assets/hf-tooling/skills/huggingface-zerogpu/references/how-zerogpu-works.md +50 -0
- package/assets/hf-tooling/templates/.agents/mcp/huggingface.json +11 -0
- package/assets/hf-tooling/templates/.env.example +5 -0
- package/assets/hf-tooling/templates/examples/huggingface/README.md +16 -0
- package/assets/hf-tooling/templates/examples/huggingface/bucket-sync.md +19 -0
- package/assets/hf-tooling/templates/examples/huggingface/dataset-upload.py +22 -0
- package/assets/hf-tooling/templates/examples/huggingface/hf-discover.md +13 -0
- package/assets/hf-tooling/templates/examples/huggingface/runtime-inspection.py +9 -0
- package/assets/mlclaw-control-ui/assets/index-D2TFes32.js +9 -0
- package/assets/mlclaw-control-ui/assets/index-DP72PFuv.css +1 -0
- package/assets/mlclaw-control-ui/index.html +13 -0
- package/assets/mlclaw.svg +298 -124
- package/dist/hf-tooling-seed.js +261 -0
- package/dist/mlclaw-space-runtime.js +6746 -3321
- package/dist/mlclaw.mjs +428 -178
- package/entrypoint.sh +4 -0
- package/mlclaw.ps1 +4 -2
- package/mlclaw.sh +5 -3
- package/openclaw.default.json +1 -0
- package/package.json +16 -2
- package/space/README.md +32 -2
- package/tsconfig.json +3 -2
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/hf-tooling/seed.ts
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
var DEFAULT_HF_TOOLING_ASSET_ROOT = "/app/assets/hf-tooling";
|
|
8
|
+
var DEFAULT_OPENCLAW_LIVE_DIR = "/tmp/openclaw-live";
|
|
9
|
+
var MLCLAW_CONTEXT_START = "<!-- MLCLAW:HUGGINGFACE_TOOLING:START -->";
|
|
10
|
+
var MLCLAW_CONTEXT_END = "<!-- MLCLAW:HUGGINGFACE_TOOLING:END -->";
|
|
11
|
+
async function seedHuggingFaceTooling(options = {}) {
|
|
12
|
+
const env = options.env ?? process.env;
|
|
13
|
+
const assetRoot = options.assetRoot ?? env.MLCLAW_HF_TOOLING_DIR ?? DEFAULT_HF_TOOLING_ASSET_ROOT;
|
|
14
|
+
const workspaceDir = options.workspaceDir ?? resolveWorkspaceDir(env);
|
|
15
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16
|
+
const manifest = await readToolingManifest(assetRoot);
|
|
17
|
+
const agentSkills = await copyBaselineSkills({
|
|
18
|
+
assetRoot,
|
|
19
|
+
manifest,
|
|
20
|
+
targetRoot: path.join(workspaceDir, ".agents", "skills")
|
|
21
|
+
});
|
|
22
|
+
const workspaceSkills = await copyBaselineSkills({
|
|
23
|
+
assetRoot,
|
|
24
|
+
manifest,
|
|
25
|
+
targetRoot: path.join(workspaceDir, "skills")
|
|
26
|
+
});
|
|
27
|
+
const templateRoot = path.join(assetRoot, "templates");
|
|
28
|
+
const copiedTemplateFiles = await copyMissingTree(templateRoot, workspaceDir);
|
|
29
|
+
const wroteContextFile = await ensureWorkspaceContextFile({ manifest, workspaceDir });
|
|
30
|
+
const wroteManifest = await writeWorkspaceManifest({
|
|
31
|
+
manifest,
|
|
32
|
+
workspaceDir,
|
|
33
|
+
installedAt: now().toISOString()
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
workspaceDir,
|
|
37
|
+
copiedSkills: agentSkills.copied,
|
|
38
|
+
skippedSkills: agentSkills.skipped,
|
|
39
|
+
copiedWorkspaceSkills: workspaceSkills.copied,
|
|
40
|
+
skippedWorkspaceSkills: workspaceSkills.skipped,
|
|
41
|
+
copiedTemplateFiles,
|
|
42
|
+
wroteContextFile,
|
|
43
|
+
wroteManifest
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function copyBaselineSkills(params) {
|
|
47
|
+
const copied = [];
|
|
48
|
+
const skipped = [];
|
|
49
|
+
await fs.mkdir(params.targetRoot, { recursive: true });
|
|
50
|
+
for (const skill of params.manifest.skills.installed) {
|
|
51
|
+
const source = path.join(params.assetRoot, "skills", skill);
|
|
52
|
+
const target = path.join(params.targetRoot, skill);
|
|
53
|
+
if (await pathExists(target)) {
|
|
54
|
+
skipped.push(skill);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
await assertDirectory(source, `baseline skill ${skill}`);
|
|
58
|
+
await fs.cp(source, target, { recursive: true, preserveTimestamps: true });
|
|
59
|
+
copied.push(skill);
|
|
60
|
+
}
|
|
61
|
+
return { copied, skipped };
|
|
62
|
+
}
|
|
63
|
+
async function ensureWorkspaceContextFile(params) {
|
|
64
|
+
const contextPath = path.join(params.workspaceDir, "AGENTS.md");
|
|
65
|
+
const block = buildWorkspaceContextBlock(params.manifest);
|
|
66
|
+
const existing = await readTextFileIfExists(contextPath);
|
|
67
|
+
const next = mergeManagedBlock(existing, block);
|
|
68
|
+
if (existing === next) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
await fs.mkdir(path.dirname(contextPath), { recursive: true });
|
|
72
|
+
await fs.writeFile(contextPath, next, "utf8");
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
function buildWorkspaceContextBlock(manifest) {
|
|
76
|
+
const skills = manifest.skills.installed.map((skill) => `- \`${skill}\``).join("\n");
|
|
77
|
+
return `${MLCLAW_CONTEXT_START}
|
|
78
|
+
## ML Claw Hugging Face Tooling
|
|
79
|
+
|
|
80
|
+
This workspace has Hugging Face tooling preinstalled. Use the Hugging Face CLI
|
|
81
|
+
\`hf\`, \`hf-discover\`, and \`uv\` for Hub, dataset, model, Space, and bucket
|
|
82
|
+
work.
|
|
83
|
+
|
|
84
|
+
Hugging Face agent skills are available in both \`.agents/skills\` and
|
|
85
|
+
\`skills\`. Prefer these skills when the task involves Hugging Face:
|
|
86
|
+
|
|
87
|
+
${skills}
|
|
88
|
+
|
|
89
|
+
The pinned tooling manifest is \`.agents/.mlclaw-hf-tooling.json\`.
|
|
90
|
+
${MLCLAW_CONTEXT_END}
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
function mergeManagedBlock(existing, block) {
|
|
94
|
+
const normalizedBlock = block.endsWith("\n") ? block : `${block}
|
|
95
|
+
`;
|
|
96
|
+
if (!existing) {
|
|
97
|
+
return `# ML Claw Workspace
|
|
98
|
+
|
|
99
|
+
${normalizedBlock}`;
|
|
100
|
+
}
|
|
101
|
+
const start = existing.indexOf(MLCLAW_CONTEXT_START);
|
|
102
|
+
const end = existing.indexOf(MLCLAW_CONTEXT_END);
|
|
103
|
+
if (start >= 0 && end >= start) {
|
|
104
|
+
const afterEnd = end + MLCLAW_CONTEXT_END.length;
|
|
105
|
+
const suffix = existing.slice(afterEnd).replace(/^\r?\n/u, "");
|
|
106
|
+
const next = `${existing.slice(0, start)}${normalizedBlock}${suffix}`;
|
|
107
|
+
return next.replace(/\n{4,}/g, "\n\n\n");
|
|
108
|
+
}
|
|
109
|
+
const trimmed = existing.replace(/\s+$/u, "");
|
|
110
|
+
return `${trimmed}
|
|
111
|
+
|
|
112
|
+
${normalizedBlock}`;
|
|
113
|
+
}
|
|
114
|
+
async function readTextFileIfExists(filePath) {
|
|
115
|
+
try {
|
|
116
|
+
return await fs.readFile(filePath, "utf8");
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if (isMissingFileError(err)) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function resolveWorkspaceDir(env) {
|
|
125
|
+
const explicit = env.OPENCLAW_WORKSPACE_DIR?.trim();
|
|
126
|
+
if (explicit) {
|
|
127
|
+
return explicit;
|
|
128
|
+
}
|
|
129
|
+
const liveDir = env.OPENCLAW_LIVE_DIR?.trim() || DEFAULT_OPENCLAW_LIVE_DIR;
|
|
130
|
+
return path.join(liveDir, "workspace");
|
|
131
|
+
}
|
|
132
|
+
async function readToolingManifest(assetRoot) {
|
|
133
|
+
const raw = await fs.readFile(path.join(assetRoot, "manifest.json"), "utf8");
|
|
134
|
+
const parsed = JSON.parse(raw);
|
|
135
|
+
assertToolingManifest(parsed);
|
|
136
|
+
return parsed;
|
|
137
|
+
}
|
|
138
|
+
function assertToolingManifest(value) {
|
|
139
|
+
if (typeof value !== "object" || value === null) {
|
|
140
|
+
throw new Error("HF tooling manifest must be an object");
|
|
141
|
+
}
|
|
142
|
+
const record = value;
|
|
143
|
+
if (record.schemaVersion !== 1) {
|
|
144
|
+
throw new Error("HF tooling manifest schemaVersion must be 1");
|
|
145
|
+
}
|
|
146
|
+
const skills = record.skills;
|
|
147
|
+
if (!skills || !Array.isArray(skills.installed) || !skills.installed.every((skill) => typeof skill === "string")) {
|
|
148
|
+
throw new Error("HF tooling manifest must list installed skills");
|
|
149
|
+
}
|
|
150
|
+
const python = record.python;
|
|
151
|
+
if (!python || typeof python.packages !== "object" || python.packages === null) {
|
|
152
|
+
throw new Error("HF tooling manifest must list Python packages");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async function copyMissingTree(sourceRoot, targetRoot) {
|
|
156
|
+
const copied = [];
|
|
157
|
+
await assertDirectory(sourceRoot, "HF tooling templates");
|
|
158
|
+
for (const entry of await fs.readdir(sourceRoot, { withFileTypes: true })) {
|
|
159
|
+
const source = path.join(sourceRoot, entry.name);
|
|
160
|
+
const target = path.join(targetRoot, entry.name);
|
|
161
|
+
if (entry.isDirectory()) {
|
|
162
|
+
copied.push(...await copyMissingTree(source, target));
|
|
163
|
+
} else if (entry.isFile()) {
|
|
164
|
+
if (await pathExists(target)) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
168
|
+
await fs.copyFile(source, target);
|
|
169
|
+
copied.push(path.relative(targetRoot, target).split(path.sep).join(path.posix.sep));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return copied;
|
|
173
|
+
}
|
|
174
|
+
async function writeWorkspaceManifest(params) {
|
|
175
|
+
const manifestPath = path.join(params.workspaceDir, ".agents", ".mlclaw-hf-tooling.json");
|
|
176
|
+
const existing = await readExistingWorkspaceManifest(manifestPath);
|
|
177
|
+
const installedAt = existing?.installedAt ?? params.installedAt;
|
|
178
|
+
const next = {
|
|
179
|
+
...params.manifest,
|
|
180
|
+
installedAt
|
|
181
|
+
};
|
|
182
|
+
if (existing && sameBaseline(existing, next)) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
await fs.mkdir(path.dirname(manifestPath), { recursive: true });
|
|
186
|
+
await fs.writeFile(manifestPath, `${JSON.stringify(next, null, 2)}
|
|
187
|
+
`, "utf8");
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
async function readExistingWorkspaceManifest(manifestPath) {
|
|
191
|
+
try {
|
|
192
|
+
const parsed = JSON.parse(await fs.readFile(manifestPath, "utf8"));
|
|
193
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const record = parsed;
|
|
197
|
+
if (typeof record.installedAt !== "string") {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
assertToolingManifest(record);
|
|
201
|
+
return record;
|
|
202
|
+
} catch (err) {
|
|
203
|
+
if (isMissingFileError(err)) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function sameBaseline(a, b) {
|
|
210
|
+
return JSON.stringify(withoutInstalledAt(a)) === JSON.stringify(withoutInstalledAt(b));
|
|
211
|
+
}
|
|
212
|
+
function withoutInstalledAt(value) {
|
|
213
|
+
const { installedAt: _installedAt, ...rest } = value;
|
|
214
|
+
return rest;
|
|
215
|
+
}
|
|
216
|
+
async function assertDirectory(dir, label) {
|
|
217
|
+
let stat;
|
|
218
|
+
try {
|
|
219
|
+
stat = await fs.stat(dir);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
if (isMissingFileError(err)) {
|
|
222
|
+
throw new Error(`${label} is missing: ${dir}`);
|
|
223
|
+
}
|
|
224
|
+
throw err;
|
|
225
|
+
}
|
|
226
|
+
if (!stat.isDirectory()) {
|
|
227
|
+
throw new Error(`${label} is not a directory: ${dir}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function pathExists(candidate) {
|
|
231
|
+
try {
|
|
232
|
+
await fs.access(candidate);
|
|
233
|
+
return true;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
if (isMissingFileError(err)) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
throw err;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function isMissingFileError(err) {
|
|
242
|
+
return err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/hf-tooling/cli.ts
|
|
246
|
+
try {
|
|
247
|
+
const result = await seedHuggingFaceTooling();
|
|
248
|
+
const copied = result.copiedSkills.length;
|
|
249
|
+
const skipped = result.skippedSkills.length;
|
|
250
|
+
const workspaceCopied = result.copiedWorkspaceSkills.length;
|
|
251
|
+
const workspaceSkipped = result.skippedWorkspaceSkills.length;
|
|
252
|
+
const templates = result.copiedTemplateFiles.length;
|
|
253
|
+
const context = result.wroteContextFile ? "updated" : "current";
|
|
254
|
+
const manifest = result.wroteManifest ? "updated" : "current";
|
|
255
|
+
console.log(
|
|
256
|
+
`[hf-tooling] workspace=${result.workspaceDir} agentsSkills=copied:${copied},skipped:${skipped} workspaceSkills=copied:${workspaceCopied},skipped:${workspaceSkipped} templates:${templates} context:${context} manifest:${manifest}`
|
|
257
|
+
);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
console.error(`[hf-tooling] ${err instanceof Error ? err.message : String(err)}`);
|
|
260
|
+
process.exitCode = 1;
|
|
261
|
+
}
|