mlclaw 0.3.5 → 0.3.7
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/Dockerfile +1 -1
- package/README.md +16 -11
- package/dist/mlclaw-space-runtime.js +8 -5
- package/dist/mlclaw.mjs +255 -91
- package/package.json +3 -3
package/Dockerfile
CHANGED
|
@@ -2,7 +2,7 @@ ARG OPENCLAW_VERSION=2026.7.1
|
|
|
2
2
|
ARG OPENCLAW_BASE_IMAGE=ghcr.io/openclaw/openclaw:${OPENCLAW_VERSION}
|
|
3
3
|
ARG BROKERKIT_PLUGIN_VERSION=0.2.1
|
|
4
4
|
ARG BROKERKIT_VERSION=hf-broker/v0.1.0
|
|
5
|
-
ARG MLCLAW_RUNTIME_IMAGE=ghcr.io/osolmaz/mlclaw:0.3.
|
|
5
|
+
ARG MLCLAW_RUNTIME_IMAGE=ghcr.io/osolmaz/mlclaw:0.3.7-openclaw-2026.7.1
|
|
6
6
|
|
|
7
7
|
FROM golang:1.26.5-bookworm AS hf-broker-build
|
|
8
8
|
ARG BROKERKIT_VERSION
|
package/README.md
CHANGED
|
@@ -40,8 +40,12 @@ irm https://raw.githubusercontent.com/osolmaz/mlclaw/main/mlclaw.ps1 | iex
|
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
You need a Hugging Face account and a token available through `HF_TOKEN`,
|
|
43
|
-
`HF_TOKEN_PATH`, `$HF_HOME/token`, or `hf auth login`.
|
|
44
|
-
token
|
|
43
|
+
`HF_TOKEN_PATH`, `$HF_HOME/token`, or `hf auth login`. If an interactive
|
|
44
|
+
bootstrap cannot find a token, it offers to install the official Hugging Face
|
|
45
|
+
CLI, asks whether you need to create an account, opens the appropriate browser
|
|
46
|
+
flow, and resumes after sign-in. Non-interactive runs never install software and
|
|
47
|
+
still require a token up front. You never paste that token into someone else's
|
|
48
|
+
app; the bootstrapper runs locally.
|
|
45
49
|
|
|
46
50
|
ML Claw places the active Hugging Face token behind an in-container HF Broker.
|
|
47
51
|
The broker owns the real token; OpenClaw receives only a separate agent
|
|
@@ -86,12 +90,13 @@ Choose a Hugging Face Router model with `--model`:
|
|
|
86
90
|
```bash
|
|
87
91
|
npx mlclaw@latest bootstrap \
|
|
88
92
|
--name mlclaw \
|
|
89
|
-
--model huggingface/
|
|
93
|
+
--model huggingface/zai-org/GLM-5.2:fireworks-ai
|
|
90
94
|
```
|
|
91
95
|
|
|
92
96
|
Recommended router-compatible options:
|
|
93
97
|
|
|
94
|
-
- `huggingface/
|
|
98
|
+
- `huggingface/zai-org/GLM-5.2:fireworks-ai`: default long-context model with tool support.
|
|
99
|
+
- `huggingface/google/gemma-4-26B-A4B-it:deepinfra`: lower-cost Gemma option with tool and structured-output support.
|
|
95
100
|
- `huggingface/Qwen/Qwen3.6-35B-A3B:deepinfra`: strong Qwen 3.6 option with tool and structured-output support.
|
|
96
101
|
- `huggingface/Qwen/Qwen3.6-27B:deepinfra`: live Qwen 3.6 option with tool and structured-output support.
|
|
97
102
|
- `huggingface/zai-org/GLM-5.2:deepinfra`: long-context GLM option with tool and structured-output support.
|
|
@@ -102,10 +107,10 @@ Recommended router-compatible options:
|
|
|
102
107
|
- `huggingface/deepseek-ai/DeepSeek-V4-Pro:deepinfra`: higher-quality long-context DeepSeek V4 option.
|
|
103
108
|
- `huggingface/MiniMaxAI/MiniMax-M3:together`: long-context MiniMax option with tool and structured-output support.
|
|
104
109
|
|
|
105
|
-
Fireworks
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
Fireworks options are also included for Kimi K2.7 Code, GPT-OSS 120B and 20B,
|
|
111
|
+
DeepSeek V4 Flash and Pro, and MiniMax M3. Use the provider suffix
|
|
112
|
+
`:fireworks-ai`. The current Router catalog does not expose the Gemma 4 or Qwen
|
|
113
|
+
3.6 presets through Fireworks.
|
|
109
114
|
|
|
110
115
|
## Optional Telegram
|
|
111
116
|
|
|
@@ -210,9 +215,9 @@ merges every BrokerKit-compatible backend configured in
|
|
|
210
215
|
cancel, or revoke requests. The plugin registers the Gateway tab, while ML Claw
|
|
211
216
|
serves the immutable packaged UI from its trusted HTTP boundary and gives it a
|
|
212
217
|
small popover inside the Gateway. Administrators decide requests directly in
|
|
213
|
-
the sandboxed popover
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
the sandboxed popover by default. Set
|
|
219
|
+
`MLCLAW_BROKERKIT_POPOVER_DECISIONS=false` only to make the popover read-only.
|
|
220
|
+
The popover uses a renewable, short-lived, admin-bound browser token.
|
|
216
221
|
Broker operator tokens remain in backend-only files and are never sent to the
|
|
217
222
|
browser or OpenClaw. See
|
|
218
223
|
[Operator Broker Configuration](docs/operator-brokers-config.md).
|
|
@@ -129,16 +129,19 @@ function normalizeAssetRef(value, fallback) {
|
|
|
129
129
|
return normalized;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
// src/mlclaw-space-runtime/model-default.ts
|
|
133
|
+
var DEFAULT_MODEL_ID = "zai-org/GLM-5.2";
|
|
134
|
+
var DEFAULT_MODEL_PROVIDER = "fireworks-ai";
|
|
135
|
+
var DEFAULT_MODEL = `huggingface/${DEFAULT_MODEL_ID}:${DEFAULT_MODEL_PROVIDER}`;
|
|
136
|
+
|
|
132
137
|
// src/mlclaw-space-runtime/model-choices.ts
|
|
133
138
|
var DEFAULT_ROUTER_PROVIDER = "deepinfra";
|
|
134
|
-
var DEFAULT_ROUTER_MODEL_ID = "google/gemma-4-26B-A4B-it";
|
|
135
|
-
var DEFAULT_MODEL = formatOpenClawModelRef(DEFAULT_ROUTER_MODEL_ID, DEFAULT_ROUTER_PROVIDER);
|
|
136
139
|
var PRESET_MODEL_CHOICES = [
|
|
137
140
|
freezeChoice({
|
|
138
141
|
modelId: "google/gemma-4-26B-A4B-it",
|
|
139
142
|
provider: "deepinfra",
|
|
140
143
|
label: "Gemma 4 26B A4B",
|
|
141
|
-
note: "
|
|
144
|
+
note: "Low-cost Gemma preset on DeepInfra",
|
|
142
145
|
contextLength: 262144,
|
|
143
146
|
pricing: { input: 0.07, output: 0.34 },
|
|
144
147
|
supportsTools: true,
|
|
@@ -298,7 +301,7 @@ var PRESET_MODEL_CHOICES = [
|
|
|
298
301
|
modelId: "zai-org/GLM-5.2",
|
|
299
302
|
provider: "fireworks-ai",
|
|
300
303
|
label: "GLM 5.2",
|
|
301
|
-
note: "
|
|
304
|
+
note: "Default long-context GLM preset on Fireworks",
|
|
302
305
|
contextLength: 1048576,
|
|
303
306
|
pricing: { input: 1.4, output: 4.4 },
|
|
304
307
|
supportsTools: true,
|
|
@@ -5106,7 +5109,7 @@ function loadConfig(env = process.env) {
|
|
|
5106
5109
|
brokerAgentSecret: readOptionalSecret(trim(env.MLCLAW_HF_BROKER_AGENT_SECRET_FILE)),
|
|
5107
5110
|
brokerAgentSecretFile: trim(env.MLCLAW_HF_BROKER_AGENT_SECRET_FILE),
|
|
5108
5111
|
operatorBrokers: loadOperatorBrokers(trim(env.MLCLAW_OPERATOR_BROKERS_FILE)),
|
|
5109
|
-
brokerKitPopoverDecisions: env.MLCLAW_BROKERKIT_POPOVER_DECISIONS
|
|
5112
|
+
brokerKitPopoverDecisions: env.MLCLAW_BROKERKIT_POPOVER_DECISIONS !== "0" && env.MLCLAW_BROKERKIT_POPOVER_DECISIONS !== "false",
|
|
5110
5113
|
hubUrl: trim(env.HF_ENDPOINT) ?? "https://huggingface.co",
|
|
5111
5114
|
openaiCredentialFile: trim(env.MLCLAW_OPENAI_CREDENTIAL_FILE) ?? "/tmp/mlclaw-secrets/openai.env",
|
|
5112
5115
|
openaiCredentialStoreFile,
|
package/dist/mlclaw.mjs
CHANGED
|
@@ -1202,8 +1202,8 @@ var require_command = __commonJS({
|
|
|
1202
1202
|
"node_modules/commander/lib/command.js"(exports) {
|
|
1203
1203
|
var EventEmitter = __require("node:events").EventEmitter;
|
|
1204
1204
|
var childProcess = __require("node:child_process");
|
|
1205
|
-
var
|
|
1206
|
-
var
|
|
1205
|
+
var path16 = __require("node:path");
|
|
1206
|
+
var fs16 = __require("node:fs");
|
|
1207
1207
|
var process5 = __require("node:process");
|
|
1208
1208
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1209
1209
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -2197,7 +2197,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2197
2197
|
* @param {string} subcommandName
|
|
2198
2198
|
*/
|
|
2199
2199
|
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2200
|
-
if (
|
|
2200
|
+
if (fs16.existsSync(executableFile)) return;
|
|
2201
2201
|
const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
|
|
2202
2202
|
const executableMissing = `'${executableFile}' does not exist
|
|
2203
2203
|
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
@@ -2215,11 +2215,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2215
2215
|
let launchWithNode = false;
|
|
2216
2216
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2217
2217
|
function findFile(baseDir, baseName) {
|
|
2218
|
-
const localBin =
|
|
2219
|
-
if (
|
|
2220
|
-
if (sourceExt.includes(
|
|
2218
|
+
const localBin = path16.resolve(baseDir, baseName);
|
|
2219
|
+
if (fs16.existsSync(localBin)) return localBin;
|
|
2220
|
+
if (sourceExt.includes(path16.extname(baseName))) return void 0;
|
|
2221
2221
|
const foundExt = sourceExt.find(
|
|
2222
|
-
(ext) =>
|
|
2222
|
+
(ext) => fs16.existsSync(`${localBin}${ext}`)
|
|
2223
2223
|
);
|
|
2224
2224
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
2225
2225
|
return void 0;
|
|
@@ -2231,21 +2231,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2231
2231
|
if (this._scriptPath) {
|
|
2232
2232
|
let resolvedScriptPath;
|
|
2233
2233
|
try {
|
|
2234
|
-
resolvedScriptPath =
|
|
2234
|
+
resolvedScriptPath = fs16.realpathSync(this._scriptPath);
|
|
2235
2235
|
} catch {
|
|
2236
2236
|
resolvedScriptPath = this._scriptPath;
|
|
2237
2237
|
}
|
|
2238
|
-
executableDir =
|
|
2239
|
-
|
|
2238
|
+
executableDir = path16.resolve(
|
|
2239
|
+
path16.dirname(resolvedScriptPath),
|
|
2240
2240
|
executableDir
|
|
2241
2241
|
);
|
|
2242
2242
|
}
|
|
2243
2243
|
if (executableDir) {
|
|
2244
2244
|
let localFile = findFile(executableDir, executableFile);
|
|
2245
2245
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2246
|
-
const legacyName =
|
|
2246
|
+
const legacyName = path16.basename(
|
|
2247
2247
|
this._scriptPath,
|
|
2248
|
-
|
|
2248
|
+
path16.extname(this._scriptPath)
|
|
2249
2249
|
);
|
|
2250
2250
|
if (legacyName !== this._name) {
|
|
2251
2251
|
localFile = findFile(
|
|
@@ -2256,7 +2256,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2256
2256
|
}
|
|
2257
2257
|
executableFile = localFile || executableFile;
|
|
2258
2258
|
}
|
|
2259
|
-
launchWithNode = sourceExt.includes(
|
|
2259
|
+
launchWithNode = sourceExt.includes(path16.extname(executableFile));
|
|
2260
2260
|
let proc;
|
|
2261
2261
|
if (process5.platform !== "win32") {
|
|
2262
2262
|
if (launchWithNode) {
|
|
@@ -3171,7 +3171,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3171
3171
|
* @return {Command}
|
|
3172
3172
|
*/
|
|
3173
3173
|
nameFromFilename(filename) {
|
|
3174
|
-
this._name =
|
|
3174
|
+
this._name = path16.basename(filename, path16.extname(filename));
|
|
3175
3175
|
return this;
|
|
3176
3176
|
}
|
|
3177
3177
|
/**
|
|
@@ -3185,9 +3185,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3185
3185
|
* @param {string} [path]
|
|
3186
3186
|
* @return {(string|null|Command)}
|
|
3187
3187
|
*/
|
|
3188
|
-
executableDir(
|
|
3189
|
-
if (
|
|
3190
|
-
this._executableDir =
|
|
3188
|
+
executableDir(path17) {
|
|
3189
|
+
if (path17 === void 0) return this._executableDir;
|
|
3190
|
+
this._executableDir = path17;
|
|
3191
3191
|
return this;
|
|
3192
3192
|
}
|
|
3193
3193
|
/**
|
|
@@ -8952,7 +8952,7 @@ var init_cli = __esm({
|
|
|
8952
8952
|
});
|
|
8953
8953
|
|
|
8954
8954
|
// src/mlclaw/cli.ts
|
|
8955
|
-
import
|
|
8955
|
+
import fs15 from "node:fs/promises";
|
|
8956
8956
|
import { realpathSync } from "node:fs";
|
|
8957
8957
|
import process4 from "node:process";
|
|
8958
8958
|
import { randomBytes } from "node:crypto";
|
|
@@ -9587,9 +9587,103 @@ async function handleSkillflag(argv, opts) {
|
|
|
9587
9587
|
}
|
|
9588
9588
|
|
|
9589
9589
|
// src/mlclaw/auth.ts
|
|
9590
|
-
import
|
|
9590
|
+
import fs11 from "node:fs/promises";
|
|
9591
|
+
import os4 from "node:os";
|
|
9592
|
+
import path12 from "node:path";
|
|
9593
|
+
|
|
9594
|
+
// src/mlclaw/hf-cli.ts
|
|
9595
|
+
import { spawn } from "node:child_process";
|
|
9596
|
+
import fs10, { constants as fsConstants } from "node:fs/promises";
|
|
9591
9597
|
import os3 from "node:os";
|
|
9592
9598
|
import path11 from "node:path";
|
|
9599
|
+
var HF_CLI_INSTALL_URL = "https://hf.co/cli/install.sh";
|
|
9600
|
+
var HF_ACCOUNT_CREATE_URL = "https://huggingface.co/join";
|
|
9601
|
+
var HF_CLI_INSTALL_COMMAND = `curl -LsSf ${HF_CLI_INSTALL_URL} | bash`;
|
|
9602
|
+
function createSystemHfCli(env = process.env) {
|
|
9603
|
+
return {
|
|
9604
|
+
findExecutable: async () => await findHfExecutable(env),
|
|
9605
|
+
install: async () => await installHfCli(env),
|
|
9606
|
+
login: async (executable) => await runInherited(executable, ["auth", "login"], env),
|
|
9607
|
+
openUrl: async (url) => await openUrl(url, env)
|
|
9608
|
+
};
|
|
9609
|
+
}
|
|
9610
|
+
async function findHfExecutable(env) {
|
|
9611
|
+
const fromPath = await hfCommandPath(env);
|
|
9612
|
+
if (fromPath) {
|
|
9613
|
+
return fromPath;
|
|
9614
|
+
}
|
|
9615
|
+
const home = env.HOME || os3.homedir();
|
|
9616
|
+
const candidates = [
|
|
9617
|
+
env.HF_CLI_BIN_DIR && path11.join(env.HF_CLI_BIN_DIR, "hf"),
|
|
9618
|
+
path11.join(home, ".local", "bin", "hf")
|
|
9619
|
+
].filter((candidate) => Boolean(candidate));
|
|
9620
|
+
for (const candidate of candidates) {
|
|
9621
|
+
try {
|
|
9622
|
+
await fs10.access(candidate, fsConstants.X_OK);
|
|
9623
|
+
return candidate;
|
|
9624
|
+
} catch {
|
|
9625
|
+
}
|
|
9626
|
+
}
|
|
9627
|
+
return void 0;
|
|
9628
|
+
}
|
|
9629
|
+
async function hfCommandPath(env) {
|
|
9630
|
+
return await new Promise((resolve) => {
|
|
9631
|
+
const child = spawn("sh", ["-c", "command -v hf"], {
|
|
9632
|
+
env,
|
|
9633
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
9634
|
+
});
|
|
9635
|
+
let output = "";
|
|
9636
|
+
child.stdout?.setEncoding("utf8");
|
|
9637
|
+
child.stdout?.on("data", (chunk) => {
|
|
9638
|
+
output += chunk;
|
|
9639
|
+
});
|
|
9640
|
+
child.once("error", () => resolve(void 0));
|
|
9641
|
+
child.once("close", (code) => resolve(code === 0 ? output.trim() || void 0 : void 0));
|
|
9642
|
+
});
|
|
9643
|
+
}
|
|
9644
|
+
async function installHfCli(env) {
|
|
9645
|
+
if (process.platform !== "darwin" && process.platform !== "linux") {
|
|
9646
|
+
throw new Error(`automatic Hugging Face CLI installation is not supported on ${process.platform}`);
|
|
9647
|
+
}
|
|
9648
|
+
const response = await fetch(HF_CLI_INSTALL_URL, { redirect: "follow" });
|
|
9649
|
+
if (!response.ok) {
|
|
9650
|
+
throw new Error(`failed to download the Hugging Face CLI installer: HTTP ${response.status}`);
|
|
9651
|
+
}
|
|
9652
|
+
const temporaryDirectory = await fs10.mkdtemp(path11.join(os3.tmpdir(), "mlclaw-hf-cli-"));
|
|
9653
|
+
const installerPath = path11.join(temporaryDirectory, "install.sh");
|
|
9654
|
+
try {
|
|
9655
|
+
await fs10.writeFile(installerPath, await response.text(), { mode: 448 });
|
|
9656
|
+
await runInherited("bash", [installerPath], env);
|
|
9657
|
+
} finally {
|
|
9658
|
+
await fs10.rm(temporaryDirectory, { recursive: true, force: true });
|
|
9659
|
+
}
|
|
9660
|
+
}
|
|
9661
|
+
async function runInherited(command, args, env) {
|
|
9662
|
+
await new Promise((resolve, reject) => {
|
|
9663
|
+
const child = spawn(command, args, { env, stdio: "inherit" });
|
|
9664
|
+
child.once("error", reject);
|
|
9665
|
+
child.once("close", (code, signal) => {
|
|
9666
|
+
if (code === 0) {
|
|
9667
|
+
resolve();
|
|
9668
|
+
return;
|
|
9669
|
+
}
|
|
9670
|
+
reject(new Error(`${command} exited ${signal ? `after signal ${signal}` : `with status ${code ?? "unknown"}`}`));
|
|
9671
|
+
});
|
|
9672
|
+
});
|
|
9673
|
+
}
|
|
9674
|
+
async function openUrl(url, env) {
|
|
9675
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "linux" ? "xdg-open" : void 0;
|
|
9676
|
+
if (!command) {
|
|
9677
|
+
return false;
|
|
9678
|
+
}
|
|
9679
|
+
return await new Promise((resolve) => {
|
|
9680
|
+
const child = spawn(command, [url], { env, stdio: "ignore" });
|
|
9681
|
+
child.once("error", () => resolve(false));
|
|
9682
|
+
child.once("close", (code) => resolve(code === 0));
|
|
9683
|
+
});
|
|
9684
|
+
}
|
|
9685
|
+
|
|
9686
|
+
// src/mlclaw/auth.ts
|
|
9593
9687
|
async function readToken(env = process.env) {
|
|
9594
9688
|
const fromEnv = env.HF_TOKEN?.trim();
|
|
9595
9689
|
if (fromEnv) {
|
|
@@ -9597,13 +9691,13 @@ async function readToken(env = process.env) {
|
|
|
9597
9691
|
}
|
|
9598
9692
|
const candidates = [
|
|
9599
9693
|
env.HF_TOKEN_PATH,
|
|
9600
|
-
env.HF_HOME &&
|
|
9601
|
-
|
|
9602
|
-
|
|
9694
|
+
env.HF_HOME && path12.join(env.HF_HOME, "token"),
|
|
9695
|
+
path12.join(os4.homedir(), ".cache", "huggingface", "token"),
|
|
9696
|
+
path12.join(os4.homedir(), ".huggingface", "token")
|
|
9603
9697
|
].filter((value) => Boolean(value));
|
|
9604
9698
|
for (const candidate of candidates) {
|
|
9605
9699
|
try {
|
|
9606
|
-
const token = (await
|
|
9700
|
+
const token = (await fs11.readFile(candidate, "utf8")).trim();
|
|
9607
9701
|
if (token) {
|
|
9608
9702
|
return token;
|
|
9609
9703
|
}
|
|
@@ -9612,6 +9706,62 @@ async function readToken(env = process.env) {
|
|
|
9612
9706
|
}
|
|
9613
9707
|
throw new Error("HF token not found. Set HF_TOKEN or run `hf auth login` once.");
|
|
9614
9708
|
}
|
|
9709
|
+
async function ensureHfToken(params) {
|
|
9710
|
+
let missingTokenError;
|
|
9711
|
+
try {
|
|
9712
|
+
return await params.readToken();
|
|
9713
|
+
} catch (error) {
|
|
9714
|
+
missingTokenError = error;
|
|
9715
|
+
}
|
|
9716
|
+
if (!params.prompt.isInteractive()) {
|
|
9717
|
+
throw missingTokenError;
|
|
9718
|
+
}
|
|
9719
|
+
let executable = await params.hfCli.findExecutable();
|
|
9720
|
+
if (!executable) {
|
|
9721
|
+
params.prompt.note(
|
|
9722
|
+
`ML Claw uses the official Hugging Face CLI to sign you in.
|
|
9723
|
+
|
|
9724
|
+
Manual install command:
|
|
9725
|
+
${HF_CLI_INSTALL_COMMAND}`,
|
|
9726
|
+
"Hugging Face CLI required"
|
|
9727
|
+
);
|
|
9728
|
+
const install = await params.prompt.confirm("Install the Hugging Face CLI now?", true);
|
|
9729
|
+
if (!install) {
|
|
9730
|
+
throw new Error(`Hugging Face CLI installation was declined. Install it with: ${HF_CLI_INSTALL_COMMAND}`);
|
|
9731
|
+
}
|
|
9732
|
+
await params.hfCli.install();
|
|
9733
|
+
executable = await params.hfCli.findExecutable();
|
|
9734
|
+
if (!executable) {
|
|
9735
|
+
throw new Error(
|
|
9736
|
+
`Hugging Face CLI was installed but could not be found. Open a new terminal or run: ${HF_CLI_INSTALL_COMMAND}`
|
|
9737
|
+
);
|
|
9738
|
+
}
|
|
9739
|
+
}
|
|
9740
|
+
const hasAccount = await params.prompt.confirm("Do you already have a Hugging Face account?", true);
|
|
9741
|
+
if (!hasAccount) {
|
|
9742
|
+
const opened = await params.hfCli.openUrl(HF_ACCOUNT_CREATE_URL);
|
|
9743
|
+
params.prompt.note(
|
|
9744
|
+
`${opened ? "A browser was opened for account creation." : "Create your account in a browser."}
|
|
9745
|
+
|
|
9746
|
+
${HF_ACCOUNT_CREATE_URL}`,
|
|
9747
|
+
"Create a Hugging Face account"
|
|
9748
|
+
);
|
|
9749
|
+
const accountCreated = await params.prompt.confirm("Have you created your Hugging Face account?", false);
|
|
9750
|
+
if (!accountCreated) {
|
|
9751
|
+
throw new Error(`Create a Hugging Face account at ${HF_ACCOUNT_CREATE_URL}, then run ML Claw again`);
|
|
9752
|
+
}
|
|
9753
|
+
}
|
|
9754
|
+
params.prompt.note(
|
|
9755
|
+
"Complete Hugging Face sign-in in the browser. ML Claw will resume automatically afterward.",
|
|
9756
|
+
"Hugging Face sign-in"
|
|
9757
|
+
);
|
|
9758
|
+
await params.hfCli.login(executable);
|
|
9759
|
+
try {
|
|
9760
|
+
return await params.readToken();
|
|
9761
|
+
} catch {
|
|
9762
|
+
throw new Error("Hugging Face sign-in completed, but no local token was found. Run `hf auth login` and try again.");
|
|
9763
|
+
}
|
|
9764
|
+
}
|
|
9615
9765
|
|
|
9616
9766
|
// src/mlclaw/docker.ts
|
|
9617
9767
|
import { execFile } from "node:child_process";
|
|
@@ -9756,9 +9906,9 @@ function parseGatewayLocation(value) {
|
|
|
9756
9906
|
|
|
9757
9907
|
// src/mlclaw/git.ts
|
|
9758
9908
|
import { execFile as execFile2 } from "node:child_process";
|
|
9759
|
-
import
|
|
9760
|
-
import
|
|
9761
|
-
import
|
|
9909
|
+
import fs13 from "node:fs/promises";
|
|
9910
|
+
import os5 from "node:os";
|
|
9911
|
+
import path14 from "node:path";
|
|
9762
9912
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
9763
9913
|
import { promisify as promisify2 } from "node:util";
|
|
9764
9914
|
|
|
@@ -13582,10 +13732,10 @@ var CurrentXorbInfo = class {
|
|
|
13582
13732
|
hash: computeXorbHash(xorbChunksCleaned),
|
|
13583
13733
|
chunks: xorbChunksCleaned,
|
|
13584
13734
|
id: this.id,
|
|
13585
|
-
files: Object.entries(this.fileProcessedBytes).map(([
|
|
13586
|
-
path:
|
|
13587
|
-
progress: processedBytes / this.fileSize[
|
|
13588
|
-
lastSentProgress: ((this.fileUploadedBytes[
|
|
13735
|
+
files: Object.entries(this.fileProcessedBytes).map(([path16, processedBytes]) => ({
|
|
13736
|
+
path: path16,
|
|
13737
|
+
progress: processedBytes / this.fileSize[path16],
|
|
13738
|
+
lastSentProgress: ((this.fileUploadedBytes[path16] ?? 0) + (processedBytes - (this.fileUploadedBytes[path16] ?? 0)) * PROCESSING_PROGRESS_RATIO) / this.fileSize[path16]
|
|
13589
13739
|
}))
|
|
13590
13740
|
};
|
|
13591
13741
|
}
|
|
@@ -14458,7 +14608,7 @@ var BucketClient = class {
|
|
|
14458
14608
|
if (paths.length === 0) {
|
|
14459
14609
|
return;
|
|
14460
14610
|
}
|
|
14461
|
-
await this.batch(paths.map((
|
|
14611
|
+
await this.batch(paths.map((path16) => ({ type: "deleteFile", path: path16 })));
|
|
14462
14612
|
}
|
|
14463
14613
|
async batch(operations) {
|
|
14464
14614
|
const body = `${operations.map((op) => JSON.stringify(op)).join("\n")}
|
|
@@ -14474,8 +14624,8 @@ var BucketClient = class {
|
|
|
14474
14624
|
* any other failure (including bucket/auth errors), so a missing object is
|
|
14475
14625
|
* never conflated with an unreachable bucket.
|
|
14476
14626
|
*/
|
|
14477
|
-
async downloadFile(
|
|
14478
|
-
const url = `${this.hubUrl}/buckets/${this.bucket}/resolve/${encodeURIComponent(
|
|
14627
|
+
async downloadFile(path16) {
|
|
14628
|
+
const url = `${this.hubUrl}/buckets/${this.bucket}/resolve/${encodeURIComponent(path16)}`;
|
|
14479
14629
|
const response = await this.fetchWithRetry(url);
|
|
14480
14630
|
if (response.status === 404) {
|
|
14481
14631
|
await this.assertBucketAccessible();
|
|
@@ -14759,9 +14909,9 @@ var HubApi = class {
|
|
|
14759
14909
|
encoding: "base64"
|
|
14760
14910
|
}
|
|
14761
14911
|
})),
|
|
14762
|
-
...(params.deletePaths ?? []).map((
|
|
14912
|
+
...(params.deletePaths ?? []).map((path16) => ({
|
|
14763
14913
|
key: "deletedFile",
|
|
14764
|
-
value: { path:
|
|
14914
|
+
value: { path: path16 }
|
|
14765
14915
|
}))
|
|
14766
14916
|
].map((entry) => JSON.stringify(entry)).join("\n");
|
|
14767
14917
|
await this.request(`/api/spaces/${repoId}/commit/${encodeURIComponent(params.branch ?? "main")}`, {
|
|
@@ -14833,8 +14983,8 @@ function sseDataToText(raw) {
|
|
|
14833
14983
|
}
|
|
14834
14984
|
|
|
14835
14985
|
// src/mlclaw/runtime-image.ts
|
|
14836
|
-
import
|
|
14837
|
-
import
|
|
14986
|
+
import fs12 from "node:fs";
|
|
14987
|
+
import path13 from "node:path";
|
|
14838
14988
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
14839
14989
|
var DEFAULT_OPENCLAW_VERSION = "2026.7.1";
|
|
14840
14990
|
var DEFAULT_BROKERKIT_PLUGIN_VERSION = "0.2.1";
|
|
@@ -14873,17 +15023,17 @@ function packageConfigString(key, fallback) {
|
|
|
14873
15023
|
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
14874
15024
|
}
|
|
14875
15025
|
function readPackageMetadata() {
|
|
14876
|
-
let dir =
|
|
15026
|
+
let dir = path13.dirname(fileURLToPath2(import.meta.url));
|
|
14877
15027
|
while (true) {
|
|
14878
|
-
const candidate =
|
|
15028
|
+
const candidate = path13.join(dir, "package.json");
|
|
14879
15029
|
try {
|
|
14880
|
-
return JSON.parse(
|
|
15030
|
+
return JSON.parse(fs12.readFileSync(candidate, "utf8"));
|
|
14881
15031
|
} catch (err) {
|
|
14882
15032
|
if (!isMissingFileError(err)) {
|
|
14883
15033
|
throw err;
|
|
14884
15034
|
}
|
|
14885
15035
|
}
|
|
14886
|
-
const parent =
|
|
15036
|
+
const parent = path13.dirname(dir);
|
|
14887
15037
|
if (parent === dir) {
|
|
14888
15038
|
throw new Error("could not find package.json while resolving default runtime image");
|
|
14889
15039
|
}
|
|
@@ -14897,12 +15047,12 @@ function isMissingFileError(err) {
|
|
|
14897
15047
|
// src/mlclaw/git.ts
|
|
14898
15048
|
var execFileAsync2 = promisify2(execFile2);
|
|
14899
15049
|
async function pushTemplateToSpace(params) {
|
|
14900
|
-
const tempRoot = await
|
|
15050
|
+
const tempRoot = await fs13.mkdtemp(path14.join(os5.tmpdir(), "mlclaw-space-"));
|
|
14901
15051
|
try {
|
|
14902
15052
|
const sourceDir = params.sourceDir ?? process.env.MLCLAW_SOURCE_DIR ?? await findPackagedSourceRoot();
|
|
14903
15053
|
const templateRev = await currentTemplateRev(sourceDir);
|
|
14904
|
-
const outDir =
|
|
14905
|
-
await
|
|
15054
|
+
const outDir = path14.join(tempRoot, "space");
|
|
15055
|
+
await fs13.mkdir(outDir, { recursive: true });
|
|
14906
15056
|
await generateSpaceRepo(sourceDir, outDir, {
|
|
14907
15057
|
...params.runtimeImage ? { runtimeImage: params.runtimeImage } : {}
|
|
14908
15058
|
});
|
|
@@ -14920,7 +15070,7 @@ async function pushTemplateToSpace(params) {
|
|
|
14920
15070
|
});
|
|
14921
15071
|
return { templateRev };
|
|
14922
15072
|
} finally {
|
|
14923
|
-
await
|
|
15073
|
+
await fs13.rm(tempRoot, { recursive: true, force: true });
|
|
14924
15074
|
}
|
|
14925
15075
|
}
|
|
14926
15076
|
async function currentTemplateRev(sourceDir) {
|
|
@@ -14933,7 +15083,7 @@ async function currentTemplateRev(sourceDir) {
|
|
|
14933
15083
|
}
|
|
14934
15084
|
} catch {
|
|
14935
15085
|
}
|
|
14936
|
-
const pkg = JSON.parse(await
|
|
15086
|
+
const pkg = JSON.parse(await fs13.readFile(path14.join(sourceDir, "package.json"), "utf8"));
|
|
14937
15087
|
return `npm:${pkg.name ?? "mlclaw"}@${pkg.version ?? "unknown"}`;
|
|
14938
15088
|
}
|
|
14939
15089
|
async function generateSpaceRepo(sourceDir, outDir, options = {}) {
|
|
@@ -14959,13 +15109,13 @@ async function generateSpaceRepo(sourceDir, outDir, options = {}) {
|
|
|
14959
15109
|
);
|
|
14960
15110
|
}
|
|
14961
15111
|
for (const [from, to] of copies) {
|
|
14962
|
-
await copyExisting(
|
|
15112
|
+
await copyExisting(path14.join(sourceDir, from), path14.join(outDir, to));
|
|
14963
15113
|
}
|
|
14964
|
-
const hfLogoPng = await
|
|
14965
|
-
await
|
|
15114
|
+
const hfLogoPng = await fs13.readFile(path14.join(sourceDir, "assets/hf-logo.png"));
|
|
15115
|
+
await fs13.writeFile(path14.join(outDir, "assets/hf-logo.png.base64"), `${hfLogoPng.toString("base64")}
|
|
14966
15116
|
`, "utf8");
|
|
14967
|
-
await
|
|
14968
|
-
|
|
15117
|
+
await fs13.writeFile(
|
|
15118
|
+
path14.join(outDir, "Dockerfile"),
|
|
14969
15119
|
options.runtimeImage ? imageDockerfile(options.runtimeImage) : bundledDockerfile(),
|
|
14970
15120
|
"utf8"
|
|
14971
15121
|
);
|
|
@@ -15053,13 +15203,13 @@ CMD ["/app/entrypoint.sh"]
|
|
|
15053
15203
|
`;
|
|
15054
15204
|
}
|
|
15055
15205
|
async function findPackagedSourceRoot() {
|
|
15056
|
-
const start =
|
|
15206
|
+
const start = path14.dirname(fileURLToPath3(import.meta.url));
|
|
15057
15207
|
let dir = start;
|
|
15058
15208
|
while (true) {
|
|
15059
15209
|
if (await hasPackagedSourceFiles(dir)) {
|
|
15060
15210
|
return dir;
|
|
15061
15211
|
}
|
|
15062
|
-
const parent =
|
|
15212
|
+
const parent = path14.dirname(dir);
|
|
15063
15213
|
if (parent === dir) {
|
|
15064
15214
|
throw new Error("Could not find packaged ML Claw source files. Reinstall the mlclaw npm package.");
|
|
15065
15215
|
}
|
|
@@ -15076,20 +15226,20 @@ async function hasPackagedSourceFiles(dir) {
|
|
|
15076
15226
|
"src/hf-bucket-client/client.ts"
|
|
15077
15227
|
];
|
|
15078
15228
|
try {
|
|
15079
|
-
await Promise.all(required.map((file) =>
|
|
15229
|
+
await Promise.all(required.map((file) => fs13.access(path14.join(dir, file))));
|
|
15080
15230
|
return true;
|
|
15081
15231
|
} catch {
|
|
15082
15232
|
return false;
|
|
15083
15233
|
}
|
|
15084
15234
|
}
|
|
15085
15235
|
async function copyExisting(from, to) {
|
|
15086
|
-
const stat = await
|
|
15087
|
-
await
|
|
15236
|
+
const stat = await fs13.stat(from);
|
|
15237
|
+
await fs13.mkdir(path14.dirname(to), { recursive: true });
|
|
15088
15238
|
if (stat.isDirectory()) {
|
|
15089
|
-
await
|
|
15239
|
+
await fs13.cp(from, to, { recursive: true });
|
|
15090
15240
|
} else {
|
|
15091
|
-
await
|
|
15092
|
-
await
|
|
15241
|
+
await fs13.copyFile(from, to);
|
|
15242
|
+
await fs13.chmod(to, stat.mode);
|
|
15093
15243
|
}
|
|
15094
15244
|
}
|
|
15095
15245
|
async function readFilesForCommit(root) {
|
|
@@ -15097,24 +15247,24 @@ async function readFilesForCommit(root) {
|
|
|
15097
15247
|
for (const relativePath of await listFiles(root)) {
|
|
15098
15248
|
files.push({
|
|
15099
15249
|
path: relativePath,
|
|
15100
|
-
content: await
|
|
15250
|
+
content: await fs13.readFile(path14.join(root, relativePath))
|
|
15101
15251
|
});
|
|
15102
15252
|
}
|
|
15103
15253
|
return files;
|
|
15104
15254
|
}
|
|
15105
15255
|
async function listFiles(root, dir = "") {
|
|
15106
|
-
const absoluteDir =
|
|
15107
|
-
const entries = await
|
|
15256
|
+
const absoluteDir = path14.join(root, dir);
|
|
15257
|
+
const entries = await fs13.readdir(absoluteDir, { withFileTypes: true });
|
|
15108
15258
|
const files = [];
|
|
15109
15259
|
for (const entry of entries) {
|
|
15110
|
-
const relativePath =
|
|
15111
|
-
const absolutePath =
|
|
15260
|
+
const relativePath = path14.posix.join(dir.split(path14.sep).join(path14.posix.sep), entry.name);
|
|
15261
|
+
const absolutePath = path14.join(root, relativePath);
|
|
15112
15262
|
if (entry.isDirectory()) {
|
|
15113
15263
|
files.push(...await listFiles(root, relativePath));
|
|
15114
15264
|
} else if (entry.isFile()) {
|
|
15115
15265
|
files.push(relativePath);
|
|
15116
15266
|
} else {
|
|
15117
|
-
const stat = await
|
|
15267
|
+
const stat = await fs13.stat(absolutePath);
|
|
15118
15268
|
if (stat.isFile()) {
|
|
15119
15269
|
files.push(relativePath);
|
|
15120
15270
|
}
|
|
@@ -15180,10 +15330,15 @@ async function assertNoLiveForeignLease(params) {
|
|
|
15180
15330
|
);
|
|
15181
15331
|
}
|
|
15182
15332
|
|
|
15333
|
+
// src/mlclaw-space-runtime/model-default.ts
|
|
15334
|
+
var DEFAULT_MODEL_ID = "zai-org/GLM-5.2";
|
|
15335
|
+
var DEFAULT_MODEL_PROVIDER = "fireworks-ai";
|
|
15336
|
+
var DEFAULT_MODEL = `huggingface/${DEFAULT_MODEL_ID}:${DEFAULT_MODEL_PROVIDER}`;
|
|
15337
|
+
|
|
15183
15338
|
// src/mlclaw/local-config.ts
|
|
15184
|
-
import
|
|
15185
|
-
import
|
|
15186
|
-
import
|
|
15339
|
+
import fs14 from "node:fs/promises";
|
|
15340
|
+
import os6 from "node:os";
|
|
15341
|
+
import path15 from "node:path";
|
|
15187
15342
|
function defaultConfigRoot(env = process.env) {
|
|
15188
15343
|
const explicit = env.MLCLAW_CONFIG_HOME?.trim();
|
|
15189
15344
|
if (explicit) {
|
|
@@ -15191,32 +15346,32 @@ function defaultConfigRoot(env = process.env) {
|
|
|
15191
15346
|
}
|
|
15192
15347
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
15193
15348
|
if (xdg) {
|
|
15194
|
-
return
|
|
15349
|
+
return path15.join(xdg, "mlclaw");
|
|
15195
15350
|
}
|
|
15196
|
-
return
|
|
15351
|
+
return path15.join(os6.homedir(), ".config", "mlclaw");
|
|
15197
15352
|
}
|
|
15198
15353
|
function localConfigPaths(root) {
|
|
15199
15354
|
return {
|
|
15200
15355
|
root,
|
|
15201
|
-
deploymentsDir:
|
|
15202
|
-
secretsDir:
|
|
15356
|
+
deploymentsDir: path15.join(root, "deployments"),
|
|
15357
|
+
secretsDir: path15.join(root, "secrets")
|
|
15203
15358
|
};
|
|
15204
15359
|
}
|
|
15205
15360
|
function manifestPath(root, agent) {
|
|
15206
|
-
return
|
|
15361
|
+
return path15.join(localConfigPaths(root).deploymentsDir, `${agent}.json`);
|
|
15207
15362
|
}
|
|
15208
15363
|
function secretEnvPath(root, agent) {
|
|
15209
|
-
return
|
|
15364
|
+
return path15.join(localConfigPaths(root).secretsDir, `${agent}.env`);
|
|
15210
15365
|
}
|
|
15211
15366
|
async function writeManifest(root, manifest) {
|
|
15212
15367
|
const file = manifestPath(root, manifest.agent);
|
|
15213
|
-
await
|
|
15214
|
-
await
|
|
15368
|
+
await fs14.mkdir(path15.dirname(file), { recursive: true });
|
|
15369
|
+
await fs14.writeFile(file, `${JSON.stringify(manifest, null, 2)}
|
|
15215
15370
|
`, "utf8");
|
|
15216
15371
|
}
|
|
15217
15372
|
async function readManifest(root, agent) {
|
|
15218
15373
|
const file = manifestPath(root, agent);
|
|
15219
|
-
const parsed = JSON.parse(await
|
|
15374
|
+
const parsed = JSON.parse(await fs14.readFile(file, "utf8"));
|
|
15220
15375
|
if (parsed.version !== 1) {
|
|
15221
15376
|
throw new Error(`unsupported deployment manifest version in ${file}`);
|
|
15222
15377
|
}
|
|
@@ -15224,7 +15379,7 @@ async function readManifest(root, agent) {
|
|
|
15224
15379
|
}
|
|
15225
15380
|
async function manifestExists(root, agent) {
|
|
15226
15381
|
try {
|
|
15227
|
-
await
|
|
15382
|
+
await fs14.access(manifestPath(root, agent));
|
|
15228
15383
|
return true;
|
|
15229
15384
|
} catch {
|
|
15230
15385
|
return false;
|
|
@@ -15236,12 +15391,12 @@ function renderSecretEnv(values) {
|
|
|
15236
15391
|
}
|
|
15237
15392
|
async function writeSecretEnv(root, agent, values) {
|
|
15238
15393
|
const file = secretEnvPath(root, agent);
|
|
15239
|
-
await
|
|
15240
|
-
await
|
|
15241
|
-
await
|
|
15394
|
+
await fs14.mkdir(path15.dirname(file), { recursive: true, mode: 448 });
|
|
15395
|
+
await fs14.writeFile(file, renderSecretEnv(values), { encoding: "utf8", mode: 384 });
|
|
15396
|
+
await fs14.chmod(file, 384);
|
|
15242
15397
|
}
|
|
15243
15398
|
async function readSecretEnv(root, agent) {
|
|
15244
|
-
return parseSecretEnv(await
|
|
15399
|
+
return parseSecretEnv(await fs14.readFile(secretEnvPath(root, agent), "utf8"));
|
|
15245
15400
|
}
|
|
15246
15401
|
function parseSecretEnv(raw) {
|
|
15247
15402
|
const out = {};
|
|
@@ -15319,7 +15474,7 @@ function delay(ms) {
|
|
|
15319
15474
|
}
|
|
15320
15475
|
|
|
15321
15476
|
// src/mlclaw/cli.ts
|
|
15322
|
-
var
|
|
15477
|
+
var DEFAULT_MODEL2 = DEFAULT_MODEL;
|
|
15323
15478
|
var DEFAULT_HARDWARE = "cpu-basic";
|
|
15324
15479
|
var TELEGRAM_HARDWARE = "cpu-upgrade";
|
|
15325
15480
|
var TELEGRAM_SLEEP_TIME = -1;
|
|
@@ -15352,6 +15507,7 @@ function createRuntime(overrides = {}) {
|
|
|
15352
15507
|
stdout: overrides.stdout ?? console,
|
|
15353
15508
|
stderr: overrides.stderr ?? console,
|
|
15354
15509
|
readToken: overrides.readToken ?? readToken,
|
|
15510
|
+
hfCli: overrides.hfCli ?? createSystemHfCli(overrides.env ?? process4.env),
|
|
15355
15511
|
hubFactory: overrides.hubFactory ?? ((token) => new HubApi({ token })),
|
|
15356
15512
|
pushTemplateToSpace: overrides.pushTemplateToSpace ?? pushTemplateToSpace,
|
|
15357
15513
|
getTelegramBot: overrides.getTelegramBot ?? getTelegramBot,
|
|
@@ -15367,7 +15523,7 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15367
15523
|
program2.name("mlclaw").description("Deploy OpenClaw to a Hugging Face Space and private bucket").showHelpAfterError().exitOverride((err) => {
|
|
15368
15524
|
throw err;
|
|
15369
15525
|
});
|
|
15370
|
-
program2.command("bootstrap", { isDefault: true }).description("Create or update a Hugging Face OpenClaw deployment").option("--owner <owner>", "Hugging Face user or organization").option("--name <name>", "Agent and runtime resource base name").option("--bucket <owner/bucket>", "State bucket to create or adopt").option("--gateway <local|space>", "Where the live gateway runs").option("--telegram-token <token>", "Optional Telegram bot token").option("--telegram-token-file <path>", "File containing TELEGRAM_BOT_TOKEN=... or a raw token").option("--telegram-user-id <id>", "Allowed Telegram user ID").option("--telegram-api-root <url>", "Telegram API root override").option("--telegram-proxy <url>", "Telegram proxy URL override").option("--hardware <flavor>", "Hugging Face Space hardware flavor").option("--sleep-time <seconds>", "Space sleep timeout in seconds; -1 means never sleep", parseInteger).option("--model <model>", "OpenClaw model identifier",
|
|
15526
|
+
program2.command("bootstrap", { isDefault: true }).description("Create or update a Hugging Face OpenClaw deployment").option("--owner <owner>", "Hugging Face user or organization").option("--name <name>", "Agent and runtime resource base name").option("--bucket <owner/bucket>", "State bucket to create or adopt").option("--gateway <local|space>", "Where the live gateway runs").option("--telegram-token <token>", "Optional Telegram bot token").option("--telegram-token-file <path>", "File containing TELEGRAM_BOT_TOKEN=... or a raw token").option("--telegram-user-id <id>", "Allowed Telegram user ID").option("--telegram-api-root <url>", "Telegram API root override").option("--telegram-proxy <url>", "Telegram proxy URL override").option("--hardware <flavor>", "Hugging Face Space hardware flavor").option("--sleep-time <seconds>", "Space sleep timeout in seconds; -1 means never sleep", parseInteger).option("--model <model>", "OpenClaw model identifier", DEFAULT_MODEL2).option("--runtime-image <image>", "ML Claw runtime image").option("--bundled-runtime", "Generate a bundled Space runtime instead of using the prebuilt ML Claw image", false).option("--public-space", "Create the Hugging Face Space as public instead of private", false).addOption(new Option("--gateway-token <token>").hideHelp()).option("--router-token <token>", "Hugging Face Router inference token for Space gateway model calls").option("--router-token-file <path>", "File containing MLCLAW_ROUTER_TOKEN=..., HF_ROUTER_TOKEN=..., or a raw token").option("--docker-context <name>", "Docker context for local gateway mode").option("--no-pull", "Do not docker pull before starting a local gateway").option("--takeover", "Start even if a stale runtime lease is present", false).option("--yes", "Confirm paid hardware prompts for automation", false).action(async (opts) => {
|
|
15371
15527
|
await bootstrap(opts, runtime);
|
|
15372
15528
|
});
|
|
15373
15529
|
program2.command("update").description("Regenerate and upload current ML Claw Space files").argument("<owner/space>", "Hugging Face Space repo ID").option("--runtime-image <image>", "Runtime image to write into the generated Space Dockerfile").option("--bundled-runtime", "Generate a bundled Space runtime instead of using the prebuilt ML Claw image", false).option("--router-token <token>", "Dedicated Hugging Face Router inference token").option("--router-token-file <path>", "File containing MLCLAW_ROUTER_TOKEN=..., HF_ROUTER_TOKEN=..., or a raw token").option("--force", "Update even if the Space does not look like ML Claw", false).action(async (repoId, opts) => {
|
|
@@ -15430,7 +15586,15 @@ async function main(argv = process4.argv.slice(2), runtimeOverrides = {}) {
|
|
|
15430
15586
|
async function bootstrap(opts, runtime) {
|
|
15431
15587
|
runtime.prompt.intro("ML Claw bootstrap");
|
|
15432
15588
|
const requestedGatewayLocation = opts.gateway ? parseGatewayLocation(opts.gateway) : void 0;
|
|
15433
|
-
const hfToken = await
|
|
15589
|
+
const hfToken = await ensureHfToken({
|
|
15590
|
+
readToken: async () => await runtime.readToken(runtime.env),
|
|
15591
|
+
hfCli: runtime.hfCli,
|
|
15592
|
+
prompt: {
|
|
15593
|
+
isInteractive: runtime.prompt.isInteractive,
|
|
15594
|
+
note: runtime.prompt.note,
|
|
15595
|
+
confirm: async (message, initialValue) => await promptConfirm(message, initialValue, runtime)
|
|
15596
|
+
}
|
|
15597
|
+
});
|
|
15434
15598
|
const hub = runtime.hubFactory(hfToken);
|
|
15435
15599
|
const me2 = await hub.whoami();
|
|
15436
15600
|
const owner = opts.owner ?? me2.name;
|
|
@@ -15438,7 +15602,7 @@ async function bootstrap(opts, runtime) {
|
|
|
15438
15602
|
const bot = telegramToken ? await runtime.getTelegramBot(telegramToken, opts.telegramApiRoot) : void 0;
|
|
15439
15603
|
let agentName = slugifyAgentName(opts.name ?? bot?.username ?? await promptAgentName(runtime));
|
|
15440
15604
|
const telegramUserId = telegramToken ? opts.telegramUserId ?? runtime.env.TELEGRAM_ALLOWED_USERS ?? await promptRequired("Telegram allowed user ID", runtime) : void 0;
|
|
15441
|
-
const model = opts.model ??
|
|
15605
|
+
const model = opts.model ?? DEFAULT_MODEL2;
|
|
15442
15606
|
const runtimeImage = resolveRuntimeImage(opts.runtimeImage, runtime.env);
|
|
15443
15607
|
const templateRuntimeImage = resolveSpaceRuntimeImage(opts, runtime.env);
|
|
15444
15608
|
let plan;
|
|
@@ -16527,7 +16691,7 @@ async function update(repoId, opts, hub, hfToken, runtime) {
|
|
|
16527
16691
|
await ensureUpdateRouterToken({
|
|
16528
16692
|
repoId,
|
|
16529
16693
|
agentName,
|
|
16530
|
-
model: variables.get("OPENCLAW_MODEL")?.value ??
|
|
16694
|
+
model: variables.get("OPENCLAW_MODEL")?.value ?? DEFAULT_MODEL2,
|
|
16531
16695
|
opts,
|
|
16532
16696
|
hub,
|
|
16533
16697
|
runtime
|
|
@@ -16699,7 +16863,7 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16699
16863
|
}
|
|
16700
16864
|
const staleTokenSecrets = ["HF_TOKEN", "HUGGINGFACE_HUB_TOKEN"].filter((key) => secrets.has(key));
|
|
16701
16865
|
if (staleTokenSecrets.length > 0) {
|
|
16702
|
-
const model = variables.get("OPENCLAW_MODEL")?.value ??
|
|
16866
|
+
const model = variables.get("OPENCLAW_MODEL")?.value ?? DEFAULT_MODEL2;
|
|
16703
16867
|
const canDelete = canDeleteBroadTokenSecrets({
|
|
16704
16868
|
model,
|
|
16705
16869
|
routerTokenPresent: hasBrokerOrRouterTokenSecretMap(secrets)
|
|
@@ -16967,7 +17131,7 @@ async function readOptionalTelegramToken(opts, runtime) {
|
|
|
16967
17131
|
return direct;
|
|
16968
17132
|
}
|
|
16969
17133
|
if (opts.telegramTokenFile) {
|
|
16970
|
-
const raw = await
|
|
17134
|
+
const raw = await fs15.readFile(opts.telegramTokenFile, "utf8");
|
|
16971
17135
|
const match = raw.match(/(?:^|\n)\s*TELEGRAM_BOT_TOKEN\s*=\s*['"]?([^'"\n]+)['"]?/);
|
|
16972
17136
|
return (match?.[1] ?? raw.trim()).trim();
|
|
16973
17137
|
}
|
|
@@ -16989,7 +17153,7 @@ async function readOptionalRouterTokenFile(file) {
|
|
|
16989
17153
|
if (!file) {
|
|
16990
17154
|
return void 0;
|
|
16991
17155
|
}
|
|
16992
|
-
const raw = await
|
|
17156
|
+
const raw = await fs15.readFile(file, "utf8");
|
|
16993
17157
|
const parsed = parseSecretEnv(raw);
|
|
16994
17158
|
return nonEmpty(parsed.MLCLAW_ROUTER_TOKEN) ?? nonEmpty(parsed.HF_ROUTER_TOKEN) ?? nonEmpty(raw);
|
|
16995
17159
|
}
|
|
@@ -17105,7 +17269,7 @@ export {
|
|
|
17105
17269
|
DEFAULT_GATEWAY_LOCATION,
|
|
17106
17270
|
DEFAULT_HARDWARE,
|
|
17107
17271
|
DEFAULT_LOCAL_PORT,
|
|
17108
|
-
DEFAULT_MODEL,
|
|
17272
|
+
DEFAULT_MODEL2 as DEFAULT_MODEL,
|
|
17109
17273
|
DEFAULT_SPACE_OPENCLAW_PORT,
|
|
17110
17274
|
LOCAL_LIVE_DIR,
|
|
17111
17275
|
LOCAL_VOLUME_MOUNT_PATH,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mlclaw",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"config": {
|
|
6
6
|
"openclawVersion": "2026.7.1",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"pack:check": "node scripts/check-package.mjs",
|
|
56
56
|
"prepack": "npm run build",
|
|
57
57
|
"check:secrets": "node scripts/check-secrets.mjs",
|
|
58
|
-
"format": "prettier --check eslint.config.mjs vitest.config.ts vitest.stryker.config.ts package.json README.md src/hf-state-sync/archive.ts src/hf-state-sync/cli.ts src/hf-state-sync/paths.ts src/hf-state-sync/prepare.ts src/hf-state-sync/stage-worker.ts src/hf-state-sync/supervise.ts src/mlclaw-space-runtime/delegated-brokerkit.ts src/mlclaw-space-runtime/delegated-revisions.ts src/mlclaw-space-runtime/operator-brokers.ts src/mlclaw-space-runtime/openclaw-config.ts src/mlclaw-space-runtime/app.ts src/mlclaw-space-runtime/config.ts src/mlclaw-space-runtime/openai-credentials.ts src/mlclaw-space-runtime/server.ts src/mlclaw-space-runtime/shell.ts src/mlclaw-control-ui/src/main.tsx src/mlclaw-control-ui/src/styles.css src/mlclaw-control-ui/src/components/ui/utils.ts src/mlclaw/git.ts test/hf-state-sync.archive.test.ts test/hf-state-sync.roundtrip.test.ts test/hf-state-sync.supervise.test.ts test/mlclaw.brokerkit-agent-tools.test.ts test/mlclaw.bundle.test.ts test/mlclaw.delegated-brokerkit.test.ts test/mlclaw.generate-space.test.ts test/mlclaw.operator-brokers.test.ts test/mlclaw.runtime-image.test.ts test/mlclaw.space-runtime.test.ts docs/operator-brokers-config.md",
|
|
59
|
-
"format:write": "prettier --write eslint.config.mjs vitest.config.ts vitest.stryker.config.ts package.json README.md src/hf-state-sync/archive.ts src/hf-state-sync/cli.ts src/hf-state-sync/paths.ts src/hf-state-sync/prepare.ts src/hf-state-sync/stage-worker.ts src/hf-state-sync/supervise.ts src/mlclaw-space-runtime/delegated-brokerkit.ts src/mlclaw-space-runtime/delegated-revisions.ts src/mlclaw-space-runtime/operator-brokers.ts src/mlclaw-space-runtime/openclaw-config.ts src/mlclaw-space-runtime/app.ts src/mlclaw-space-runtime/config.ts src/mlclaw-space-runtime/openai-credentials.ts src/mlclaw-space-runtime/server.ts src/mlclaw-space-runtime/shell.ts src/mlclaw-control-ui/src/main.tsx src/mlclaw-control-ui/src/styles.css src/mlclaw-control-ui/src/components/ui/utils.ts src/mlclaw/git.ts test/hf-state-sync.archive.test.ts test/hf-state-sync.roundtrip.test.ts test/hf-state-sync.supervise.test.ts test/mlclaw.brokerkit-agent-tools.test.ts test/mlclaw.bundle.test.ts test/mlclaw.delegated-brokerkit.test.ts test/mlclaw.generate-space.test.ts test/mlclaw.operator-brokers.test.ts test/mlclaw.runtime-image.test.ts test/mlclaw.space-runtime.test.ts docs/operator-brokers-config.md",
|
|
58
|
+
"format": "prettier --check eslint.config.mjs vitest.config.ts vitest.stryker.config.ts package.json README.md src/hf-state-sync/archive.ts src/hf-state-sync/cli.ts src/hf-state-sync/paths.ts src/hf-state-sync/prepare.ts src/hf-state-sync/stage-worker.ts src/hf-state-sync/supervise.ts src/mlclaw/auth.ts src/mlclaw/hf-cli.ts src/mlclaw-space-runtime/delegated-brokerkit.ts src/mlclaw-space-runtime/delegated-revisions.ts src/mlclaw-space-runtime/operator-brokers.ts src/mlclaw-space-runtime/openclaw-config.ts src/mlclaw-space-runtime/app.ts src/mlclaw-space-runtime/config.ts src/mlclaw-space-runtime/openai-credentials.ts src/mlclaw-space-runtime/server.ts src/mlclaw-space-runtime/shell.ts src/mlclaw-control-ui/src/main.tsx src/mlclaw-control-ui/src/styles.css src/mlclaw-control-ui/src/components/ui/utils.ts src/mlclaw/git.ts test/hf-state-sync.archive.test.ts test/hf-state-sync.roundtrip.test.ts test/hf-state-sync.supervise.test.ts test/mlclaw.auth.test.ts test/mlclaw.brokerkit-agent-tools.test.ts test/mlclaw.bundle.test.ts test/mlclaw.delegated-brokerkit.test.ts test/mlclaw.generate-space.test.ts test/mlclaw.operator-brokers.test.ts test/mlclaw.runtime-image.test.ts test/mlclaw.space-runtime.test.ts docs/operator-brokers-config.md",
|
|
59
|
+
"format:write": "prettier --write eslint.config.mjs vitest.config.ts vitest.stryker.config.ts package.json README.md src/hf-state-sync/archive.ts src/hf-state-sync/cli.ts src/hf-state-sync/paths.ts src/hf-state-sync/prepare.ts src/hf-state-sync/stage-worker.ts src/hf-state-sync/supervise.ts src/mlclaw/auth.ts src/mlclaw/hf-cli.ts src/mlclaw-space-runtime/delegated-brokerkit.ts src/mlclaw-space-runtime/delegated-revisions.ts src/mlclaw-space-runtime/operator-brokers.ts src/mlclaw-space-runtime/openclaw-config.ts src/mlclaw-space-runtime/app.ts src/mlclaw-space-runtime/config.ts src/mlclaw-space-runtime/openai-credentials.ts src/mlclaw-space-runtime/server.ts src/mlclaw-space-runtime/shell.ts src/mlclaw-control-ui/src/main.tsx src/mlclaw-control-ui/src/styles.css src/mlclaw-control-ui/src/components/ui/utils.ts src/mlclaw/git.ts test/hf-state-sync.archive.test.ts test/hf-state-sync.roundtrip.test.ts test/hf-state-sync.supervise.test.ts test/mlclaw.auth.test.ts test/mlclaw.brokerkit-agent-tools.test.ts test/mlclaw.bundle.test.ts test/mlclaw.delegated-brokerkit.test.ts test/mlclaw.generate-space.test.ts test/mlclaw.operator-brokers.test.ts test/mlclaw.runtime-image.test.ts test/mlclaw.space-runtime.test.ts docs/operator-brokers-config.md",
|
|
60
60
|
"lint": "eslint --max-warnings 0 src/mlclaw-space-runtime/delegated-brokerkit.ts src/mlclaw-space-runtime/delegated-revisions.ts src/mlclaw-space-runtime/operator-brokers.ts src/mlclaw-space-runtime/openclaw-config.ts",
|
|
61
61
|
"coverage": "vitest run --coverage",
|
|
62
62
|
"dry": "slophammer-ts dry .",
|