mlclaw 0.4.6 → 0.4.8
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 +3 -3
- package/README.md +18 -14
- package/dist/mlclaw-space-runtime.js +9907 -6677
- package/dist/mlclaw.mjs +186 -325
- package/package.json +11 -7
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 os8 from "node:os";
|
|
8958
8958
|
import process4 from "node:process";
|
|
@@ -10124,10 +10124,10 @@ function parseGatewayLocation(value) {
|
|
|
10124
10124
|
|
|
10125
10125
|
// src/mlclaw/git.ts
|
|
10126
10126
|
import { execFile as execFile2 } from "node:child_process";
|
|
10127
|
-
import
|
|
10127
|
+
import fs12 from "node:fs/promises";
|
|
10128
10128
|
import os5 from "node:os";
|
|
10129
|
-
import
|
|
10130
|
-
import { fileURLToPath as
|
|
10129
|
+
import path13 from "node:path";
|
|
10130
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10131
10131
|
import { promisify as promisify2 } from "node:util";
|
|
10132
10132
|
|
|
10133
10133
|
// src/vendor/hfjs-xet/error.ts
|
|
@@ -13950,10 +13950,10 @@ var CurrentXorbInfo = class {
|
|
|
13950
13950
|
hash: computeXorbHash(xorbChunksCleaned),
|
|
13951
13951
|
chunks: xorbChunksCleaned,
|
|
13952
13952
|
id: this.id,
|
|
13953
|
-
files: Object.entries(this.fileProcessedBytes).map(([
|
|
13954
|
-
path:
|
|
13955
|
-
progress: processedBytes / this.fileSize[
|
|
13956
|
-
lastSentProgress: ((this.fileUploadedBytes[
|
|
13953
|
+
files: Object.entries(this.fileProcessedBytes).map(([path16, processedBytes]) => ({
|
|
13954
|
+
path: path16,
|
|
13955
|
+
progress: processedBytes / this.fileSize[path16],
|
|
13956
|
+
lastSentProgress: ((this.fileUploadedBytes[path16] ?? 0) + (processedBytes - (this.fileUploadedBytes[path16] ?? 0)) * PROCESSING_PROGRESS_RATIO) / this.fileSize[path16]
|
|
13957
13957
|
}))
|
|
13958
13958
|
};
|
|
13959
13959
|
}
|
|
@@ -14826,7 +14826,7 @@ var BucketClient = class {
|
|
|
14826
14826
|
if (paths.length === 0) {
|
|
14827
14827
|
return;
|
|
14828
14828
|
}
|
|
14829
|
-
await this.batch(paths.map((
|
|
14829
|
+
await this.batch(paths.map((path16) => ({ type: "deleteFile", path: path16 })));
|
|
14830
14830
|
}
|
|
14831
14831
|
async batch(operations) {
|
|
14832
14832
|
const body = `${operations.map((op) => JSON.stringify(op)).join("\n")}
|
|
@@ -14842,8 +14842,8 @@ var BucketClient = class {
|
|
|
14842
14842
|
* any other failure (including bucket/auth errors), so a missing object is
|
|
14843
14843
|
* never conflated with an unreachable bucket.
|
|
14844
14844
|
*/
|
|
14845
|
-
async downloadFile(
|
|
14846
|
-
const url = `${this.hubUrl}/buckets/${this.bucket}/resolve/${encodeURIComponent(
|
|
14845
|
+
async downloadFile(path16) {
|
|
14846
|
+
const url = `${this.hubUrl}/buckets/${this.bucket}/resolve/${encodeURIComponent(path16)}`;
|
|
14847
14847
|
const response = await this.fetchWithRetry(url);
|
|
14848
14848
|
if (response.status === 404) {
|
|
14849
14849
|
await this.assertBucketAccessible();
|
|
@@ -14945,19 +14945,19 @@ var HubApi = class {
|
|
|
14945
14945
|
async deploymentControlStore(owner, deploymentId) {
|
|
14946
14946
|
const repoId = `${owner}/mlclaw-control-${deploymentId.replaceAll("-", "")}`;
|
|
14947
14947
|
await this.ensurePrivateModelRepo(repoId);
|
|
14948
|
-
const
|
|
14948
|
+
const path16 = "control-lease.json";
|
|
14949
14949
|
return {
|
|
14950
|
-
read: async () => await this.readModelDocument(repoId,
|
|
14951
|
-
compareAndSwap: async (expectedRevision, value) => await this.commitModelDocument(repoId,
|
|
14950
|
+
read: async () => await this.readModelDocument(repoId, path16),
|
|
14951
|
+
compareAndSwap: async (expectedRevision, value) => await this.commitModelDocument(repoId, path16, expectedRevision, value)
|
|
14952
14952
|
};
|
|
14953
14953
|
}
|
|
14954
14954
|
async deploymentClaimStore(owner) {
|
|
14955
14955
|
const repoId = `${owner}/mlclaw-control-claims`;
|
|
14956
14956
|
await this.ensurePrivateModelRepo(repoId);
|
|
14957
|
-
const
|
|
14957
|
+
const path16 = "control-lease.json";
|
|
14958
14958
|
return {
|
|
14959
|
-
read: async () => await this.readModelDocument(repoId,
|
|
14960
|
-
compareAndSwap: async (expectedRevision, value) => await this.commitModelDocument(repoId,
|
|
14959
|
+
read: async () => await this.readModelDocument(repoId, path16),
|
|
14960
|
+
compareAndSwap: async (expectedRevision, value) => await this.commitModelDocument(repoId, path16, expectedRevision, value)
|
|
14961
14961
|
};
|
|
14962
14962
|
}
|
|
14963
14963
|
async createDockerSpace(repoId, options) {
|
|
@@ -14968,7 +14968,7 @@ var HubApi = class {
|
|
|
14968
14968
|
organization: owner === me2.name ? null : owner,
|
|
14969
14969
|
type: "space",
|
|
14970
14970
|
sdk: "docker",
|
|
14971
|
-
|
|
14971
|
+
visibility: options?.visibility ?? "protected"
|
|
14972
14972
|
};
|
|
14973
14973
|
if (options?.hardware) {
|
|
14974
14974
|
payload.hardware = options.hardware;
|
|
@@ -15001,8 +15001,19 @@ var HubApi = class {
|
|
|
15001
15001
|
}
|
|
15002
15002
|
}
|
|
15003
15003
|
async getSpaceVisibility(repoId) {
|
|
15004
|
-
const
|
|
15005
|
-
|
|
15004
|
+
const [owner] = splitRepoId(repoId);
|
|
15005
|
+
const me2 = await this.whoami();
|
|
15006
|
+
let url = owner === me2.name ? `${this.hubUrl}/api/settings/repositories` : `${this.hubUrl}/api/organizations/${encodeURIComponent(owner)}/settings/repositories`;
|
|
15007
|
+
while (url) {
|
|
15008
|
+
const response = await this.request(url);
|
|
15009
|
+
const repositories = await response.json();
|
|
15010
|
+
const visibility = repositories.find((repo) => repo.id === repoId && repo.type === "space")?.visibility;
|
|
15011
|
+
if (visibility === "private" || visibility === "protected" || visibility === "public") {
|
|
15012
|
+
return visibility;
|
|
15013
|
+
}
|
|
15014
|
+
url = nextLink(response.headers.get("link"));
|
|
15015
|
+
}
|
|
15016
|
+
throw new Error(`Hub repository settings omitted Space visibility for ${repoId}`);
|
|
15006
15017
|
}
|
|
15007
15018
|
async updateSpaceVisibility(repoId, visibility) {
|
|
15008
15019
|
await this.requestJson(`/api/spaces/${repoId}/settings`, {
|
|
@@ -15174,9 +15185,9 @@ var HubApi = class {
|
|
|
15174
15185
|
encoding: "base64"
|
|
15175
15186
|
}
|
|
15176
15187
|
})),
|
|
15177
|
-
...(params.deletePaths ?? []).map((
|
|
15188
|
+
...(params.deletePaths ?? []).map((path16) => ({
|
|
15178
15189
|
key: "deletedFile",
|
|
15179
|
-
value: { path:
|
|
15190
|
+
value: { path: path16 }
|
|
15180
15191
|
}))
|
|
15181
15192
|
].map((entry) => JSON.stringify(entry)).join("\n");
|
|
15182
15193
|
await this.request(`/api/spaces/${repoId}/commit/${encodeURIComponent(params.branch ?? "main")}`, {
|
|
@@ -15206,10 +15217,10 @@ var HubApi = class {
|
|
|
15206
15217
|
if (info.sha) return;
|
|
15207
15218
|
await this.commitModelDocument(repoId, "README.md", "", "# ML Claw deployment control\n");
|
|
15208
15219
|
}
|
|
15209
|
-
async readModelDocument(repoId,
|
|
15220
|
+
async readModelDocument(repoId, path16) {
|
|
15210
15221
|
const info = await this.requestJson(`/api/models/${repoId}`);
|
|
15211
15222
|
if (!info.sha) throw new Error(`control repository ${repoId} has no revision`);
|
|
15212
|
-
const url = `${this.hubUrl}/${repoId}/resolve/${info.sha}/${
|
|
15223
|
+
const url = `${this.hubUrl}/${repoId}/resolve/${info.sha}/${path16.split("/").map(encodeURIComponent).join("/")}`;
|
|
15213
15224
|
const response = await this.fetchImpl(url, {
|
|
15214
15225
|
headers: { Authorization: `Bearer ${this.token}` }
|
|
15215
15226
|
});
|
|
@@ -15217,16 +15228,16 @@ var HubApi = class {
|
|
|
15217
15228
|
if (!response.ok) throw new HubApiError2(response.status, url, await response.text());
|
|
15218
15229
|
return { value: JSON.parse(await response.text()), revision: info.sha };
|
|
15219
15230
|
}
|
|
15220
|
-
async commitModelDocument(repoId,
|
|
15231
|
+
async commitModelDocument(repoId, path16, parentCommit, value) {
|
|
15221
15232
|
const header = {
|
|
15222
15233
|
summary: value === null ? "Release deployment control" : "Update deployment control",
|
|
15223
15234
|
description: "ML Claw deployment reconciliation state"
|
|
15224
15235
|
};
|
|
15225
15236
|
if (parentCommit) header.parentCommit = parentCommit;
|
|
15226
|
-
const operation = value === null ? { key: "deletedFile", value: { path:
|
|
15237
|
+
const operation = value === null ? { key: "deletedFile", value: { path: path16 } } : {
|
|
15227
15238
|
key: "file",
|
|
15228
15239
|
value: {
|
|
15229
|
-
path:
|
|
15240
|
+
path: path16,
|
|
15230
15241
|
content: Buffer.from(typeof value === "string" ? value : `${JSON.stringify(value, null, 2)}
|
|
15231
15242
|
`).toString(
|
|
15232
15243
|
"base64"
|
|
@@ -15370,21 +15381,22 @@ function nextLink(header) {
|
|
|
15370
15381
|
return null;
|
|
15371
15382
|
}
|
|
15372
15383
|
|
|
15384
|
+
// src/mlclaw/release-config.generated.ts
|
|
15385
|
+
var RELEASE_CONFIG = {
|
|
15386
|
+
"packageVersion": "0.4.8",
|
|
15387
|
+
"openclawVersion": "2026.7.1",
|
|
15388
|
+
"brokerkitVersion": "hf-broker/v0.4.2",
|
|
15389
|
+
"brokerkitPluginVersion": "0.3.4",
|
|
15390
|
+
"runtimeImageRepository": "ghcr.io/huggingface/mlclaw"
|
|
15391
|
+
};
|
|
15392
|
+
|
|
15373
15393
|
// src/mlclaw/runtime-image.ts
|
|
15374
|
-
|
|
15375
|
-
|
|
15376
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
15377
|
-
var DEFAULT_OPENCLAW_VERSION = "2026.7.1";
|
|
15378
|
-
var DEFAULT_BROKERKIT_PLUGIN_VERSION = "0.3.2";
|
|
15379
|
-
var DEFAULT_BROKERKIT_VERSION = "hf-broker/v0.4.0";
|
|
15380
|
-
var DEFAULT_RUNTIME_IMAGE_REPOSITORY = "ghcr.io/huggingface/mlclaw";
|
|
15381
|
-
var PACKAGE_METADATA = readPackageMetadata();
|
|
15382
|
-
var PACKAGE_VERSION = packageString("version", "unknown");
|
|
15383
|
-
var OPENCLAW_VERSION = packageConfigString("openclawVersion", DEFAULT_OPENCLAW_VERSION);
|
|
15394
|
+
var PACKAGE_VERSION = RELEASE_CONFIG.packageVersion;
|
|
15395
|
+
var OPENCLAW_VERSION = RELEASE_CONFIG.openclawVersion;
|
|
15384
15396
|
var OPENCLAW_BASE_IMAGE = `ghcr.io/openclaw/openclaw:${OPENCLAW_VERSION}`;
|
|
15385
|
-
var BROKERKIT_PLUGIN_VERSION =
|
|
15386
|
-
var BROKERKIT_VERSION =
|
|
15387
|
-
var RUNTIME_IMAGE_REPOSITORY =
|
|
15397
|
+
var BROKERKIT_PLUGIN_VERSION = RELEASE_CONFIG.brokerkitPluginVersion;
|
|
15398
|
+
var BROKERKIT_VERSION = RELEASE_CONFIG.brokerkitVersion;
|
|
15399
|
+
var RUNTIME_IMAGE_REPOSITORY = RELEASE_CONFIG.runtimeImageRepository;
|
|
15388
15400
|
var DEFAULT_RUNTIME_IMAGE_TAG = `${PACKAGE_VERSION}-openclaw-${OPENCLAW_VERSION}`;
|
|
15389
15401
|
var DEFAULT_RUNTIME_IMAGE = `${RUNTIME_IMAGE_REPOSITORY}:${DEFAULT_RUNTIME_IMAGE_TAG}`;
|
|
15390
15402
|
function resolveRuntimeImage(value, env = process.env) {
|
|
@@ -15402,45 +15414,16 @@ function resolveSpaceRuntimeImage(opts, env = process.env) {
|
|
|
15402
15414
|
function bundledSpaceRuntimeRef(templateRev) {
|
|
15403
15415
|
return `bundled:${templateRev}`;
|
|
15404
15416
|
}
|
|
15405
|
-
function packageString(key, fallback) {
|
|
15406
|
-
const value = PACKAGE_METADATA[key];
|
|
15407
|
-
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
15408
|
-
}
|
|
15409
|
-
function packageConfigString(key, fallback) {
|
|
15410
|
-
const value = PACKAGE_METADATA.config?.[key];
|
|
15411
|
-
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
15412
|
-
}
|
|
15413
|
-
function readPackageMetadata() {
|
|
15414
|
-
let dir = path13.dirname(fileURLToPath2(import.meta.url));
|
|
15415
|
-
while (true) {
|
|
15416
|
-
const candidate = path13.join(dir, "package.json");
|
|
15417
|
-
try {
|
|
15418
|
-
return JSON.parse(fs12.readFileSync(candidate, "utf8"));
|
|
15419
|
-
} catch (err) {
|
|
15420
|
-
if (!isMissingFileError(err)) {
|
|
15421
|
-
throw err;
|
|
15422
|
-
}
|
|
15423
|
-
}
|
|
15424
|
-
const parent = path13.dirname(dir);
|
|
15425
|
-
if (parent === dir) {
|
|
15426
|
-
throw new Error("could not find package.json while resolving default runtime image");
|
|
15427
|
-
}
|
|
15428
|
-
dir = parent;
|
|
15429
|
-
}
|
|
15430
|
-
}
|
|
15431
|
-
function isMissingFileError(err) {
|
|
15432
|
-
return err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
15433
|
-
}
|
|
15434
15417
|
|
|
15435
15418
|
// src/mlclaw/git.ts
|
|
15436
15419
|
var execFileAsync2 = promisify2(execFile2);
|
|
15437
15420
|
async function pushTemplateToSpace(params) {
|
|
15438
|
-
const tempRoot = await
|
|
15421
|
+
const tempRoot = await fs12.mkdtemp(path13.join(os5.tmpdir(), "mlclaw-space-"));
|
|
15439
15422
|
try {
|
|
15440
15423
|
const sourceDir = params.sourceDir ?? process.env.MLCLAW_SOURCE_DIR ?? await findPackagedSourceRoot();
|
|
15441
15424
|
const templateRev = await currentTemplateRev(sourceDir);
|
|
15442
|
-
const outDir =
|
|
15443
|
-
await
|
|
15425
|
+
const outDir = path13.join(tempRoot, "space");
|
|
15426
|
+
await fs12.mkdir(outDir, { recursive: true });
|
|
15444
15427
|
await generateSpaceRepo(sourceDir, outDir, {
|
|
15445
15428
|
...params.runtimeImage ? { runtimeImage: params.runtimeImage } : {}
|
|
15446
15429
|
});
|
|
@@ -15458,7 +15441,7 @@ async function pushTemplateToSpace(params) {
|
|
|
15458
15441
|
});
|
|
15459
15442
|
return { templateRev };
|
|
15460
15443
|
} finally {
|
|
15461
|
-
await
|
|
15444
|
+
await fs12.rm(tempRoot, { recursive: true, force: true });
|
|
15462
15445
|
}
|
|
15463
15446
|
}
|
|
15464
15447
|
async function currentTemplateRev(sourceDir) {
|
|
@@ -15471,7 +15454,7 @@ async function currentTemplateRev(sourceDir) {
|
|
|
15471
15454
|
}
|
|
15472
15455
|
} catch {
|
|
15473
15456
|
}
|
|
15474
|
-
const pkg = JSON.parse(await
|
|
15457
|
+
const pkg = JSON.parse(await fs12.readFile(path13.join(sourceDir, "package.json"), "utf8"));
|
|
15475
15458
|
return `npm:${pkg.name ?? "mlclaw"}@${pkg.version ?? "unknown"}`;
|
|
15476
15459
|
}
|
|
15477
15460
|
async function generateSpaceRepo(sourceDir, outDir, options = {}) {
|
|
@@ -15497,13 +15480,13 @@ async function generateSpaceRepo(sourceDir, outDir, options = {}) {
|
|
|
15497
15480
|
);
|
|
15498
15481
|
}
|
|
15499
15482
|
for (const [from, to] of copies) {
|
|
15500
|
-
await copyExisting(
|
|
15483
|
+
await copyExisting(path13.join(sourceDir, from), path13.join(outDir, to));
|
|
15501
15484
|
}
|
|
15502
|
-
const hfLogoPng = await
|
|
15503
|
-
await
|
|
15485
|
+
const hfLogoPng = await fs12.readFile(path13.join(sourceDir, "assets/hf-logo.png"));
|
|
15486
|
+
await fs12.writeFile(path13.join(outDir, "assets/hf-logo.png.base64"), `${hfLogoPng.toString("base64")}
|
|
15504
15487
|
`, "utf8");
|
|
15505
|
-
await
|
|
15506
|
-
|
|
15488
|
+
await fs12.writeFile(
|
|
15489
|
+
path13.join(outDir, "Dockerfile"),
|
|
15507
15490
|
options.runtimeImage ? imageDockerfile(options.runtimeImage) : bundledDockerfile(),
|
|
15508
15491
|
"utf8"
|
|
15509
15492
|
);
|
|
@@ -15591,13 +15574,13 @@ CMD ["/app/entrypoint.sh"]
|
|
|
15591
15574
|
`;
|
|
15592
15575
|
}
|
|
15593
15576
|
async function findPackagedSourceRoot() {
|
|
15594
|
-
const start =
|
|
15577
|
+
const start = path13.dirname(fileURLToPath2(import.meta.url));
|
|
15595
15578
|
let dir = start;
|
|
15596
15579
|
while (true) {
|
|
15597
15580
|
if (await hasPackagedSourceFiles(dir)) {
|
|
15598
15581
|
return dir;
|
|
15599
15582
|
}
|
|
15600
|
-
const parent =
|
|
15583
|
+
const parent = path13.dirname(dir);
|
|
15601
15584
|
if (parent === dir) {
|
|
15602
15585
|
throw new Error("Could not find packaged ML Claw source files. Reinstall the mlclaw npm package.");
|
|
15603
15586
|
}
|
|
@@ -15614,20 +15597,20 @@ async function hasPackagedSourceFiles(dir) {
|
|
|
15614
15597
|
"src/hf-bucket-client/client.ts"
|
|
15615
15598
|
];
|
|
15616
15599
|
try {
|
|
15617
|
-
await Promise.all(required.map((file) =>
|
|
15600
|
+
await Promise.all(required.map((file) => fs12.access(path13.join(dir, file))));
|
|
15618
15601
|
return true;
|
|
15619
15602
|
} catch {
|
|
15620
15603
|
return false;
|
|
15621
15604
|
}
|
|
15622
15605
|
}
|
|
15623
15606
|
async function copyExisting(from, to) {
|
|
15624
|
-
const stat = await
|
|
15625
|
-
await
|
|
15607
|
+
const stat = await fs12.stat(from);
|
|
15608
|
+
await fs12.mkdir(path13.dirname(to), { recursive: true });
|
|
15626
15609
|
if (stat.isDirectory()) {
|
|
15627
|
-
await
|
|
15610
|
+
await fs12.cp(from, to, { recursive: true });
|
|
15628
15611
|
} else {
|
|
15629
|
-
await
|
|
15630
|
-
await
|
|
15612
|
+
await fs12.copyFile(from, to);
|
|
15613
|
+
await fs12.chmod(to, stat.mode);
|
|
15631
15614
|
}
|
|
15632
15615
|
}
|
|
15633
15616
|
async function readFilesForCommit(root) {
|
|
@@ -15635,24 +15618,24 @@ async function readFilesForCommit(root) {
|
|
|
15635
15618
|
for (const relativePath of await listFiles(root)) {
|
|
15636
15619
|
files.push({
|
|
15637
15620
|
path: relativePath,
|
|
15638
|
-
content: await
|
|
15621
|
+
content: await fs12.readFile(path13.join(root, relativePath))
|
|
15639
15622
|
});
|
|
15640
15623
|
}
|
|
15641
15624
|
return files;
|
|
15642
15625
|
}
|
|
15643
15626
|
async function listFiles(root, dir = "") {
|
|
15644
|
-
const absoluteDir =
|
|
15645
|
-
const entries = await
|
|
15627
|
+
const absoluteDir = path13.join(root, dir);
|
|
15628
|
+
const entries = await fs12.readdir(absoluteDir, { withFileTypes: true });
|
|
15646
15629
|
const files = [];
|
|
15647
15630
|
for (const entry of entries) {
|
|
15648
|
-
const relativePath =
|
|
15649
|
-
const absolutePath =
|
|
15631
|
+
const relativePath = path13.posix.join(dir.split(path13.sep).join(path13.posix.sep), entry.name);
|
|
15632
|
+
const absolutePath = path13.join(root, relativePath);
|
|
15650
15633
|
if (entry.isDirectory()) {
|
|
15651
15634
|
files.push(...await listFiles(root, relativePath));
|
|
15652
15635
|
} else if (entry.isFile()) {
|
|
15653
15636
|
files.push(relativePath);
|
|
15654
15637
|
} else {
|
|
15655
|
-
const stat = await
|
|
15638
|
+
const stat = await fs12.stat(absolutePath);
|
|
15656
15639
|
if (stat.isFile()) {
|
|
15657
15640
|
files.push(relativePath);
|
|
15658
15641
|
}
|
|
@@ -15731,9 +15714,9 @@ function deriveLocalAccessToken(sessionSecret) {
|
|
|
15731
15714
|
}
|
|
15732
15715
|
|
|
15733
15716
|
// src/mlclaw/local-config.ts
|
|
15734
|
-
import
|
|
15717
|
+
import fs13 from "node:fs/promises";
|
|
15735
15718
|
import os6 from "node:os";
|
|
15736
|
-
import
|
|
15719
|
+
import path14 from "node:path";
|
|
15737
15720
|
import { createHash as createHash2 } from "node:crypto";
|
|
15738
15721
|
|
|
15739
15722
|
// node_modules/zod/v3/external.js
|
|
@@ -16214,8 +16197,8 @@ function getErrorMap() {
|
|
|
16214
16197
|
|
|
16215
16198
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
16216
16199
|
var makeIssue = (params) => {
|
|
16217
|
-
const { data, path:
|
|
16218
|
-
const fullPath = [...
|
|
16200
|
+
const { data, path: path16, errorMaps, issueData } = params;
|
|
16201
|
+
const fullPath = [...path16, ...issueData.path || []];
|
|
16219
16202
|
const fullIssue = {
|
|
16220
16203
|
...issueData,
|
|
16221
16204
|
path: fullPath
|
|
@@ -16331,11 +16314,11 @@ var errorUtil;
|
|
|
16331
16314
|
|
|
16332
16315
|
// node_modules/zod/v3/types.js
|
|
16333
16316
|
var ParseInputLazyPath = class {
|
|
16334
|
-
constructor(parent, value,
|
|
16317
|
+
constructor(parent, value, path16, key) {
|
|
16335
16318
|
this._cachedPath = [];
|
|
16336
16319
|
this.parent = parent;
|
|
16337
16320
|
this.data = value;
|
|
16338
|
-
this._path =
|
|
16321
|
+
this._path = path16;
|
|
16339
16322
|
this._key = key;
|
|
16340
16323
|
}
|
|
16341
16324
|
get path() {
|
|
@@ -19841,14 +19824,14 @@ var manifestFields = {
|
|
|
19841
19824
|
model: external_exports.string().min(1).max(512),
|
|
19842
19825
|
runtimeImage: external_exports.string().min(1).max(1024),
|
|
19843
19826
|
brokerCredential: external_exports.object({
|
|
19844
|
-
|
|
19827
|
+
credentialKind: external_exports.literal("fine_grained_user_token"),
|
|
19845
19828
|
account: external_exports.string().min(1).max(128),
|
|
19846
19829
|
fingerprintSha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
19847
19830
|
verifiedAt: external_exports.string().datetime()
|
|
19848
19831
|
}).strict().optional(),
|
|
19849
19832
|
credentialKeySha256: external_exports.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
19850
19833
|
tailscaleMode: external_exports.enum(["off", "direct", "serve"]).optional(),
|
|
19851
|
-
spaceVisibility: external_exports.enum(["private", "public"]).optional(),
|
|
19834
|
+
spaceVisibility: external_exports.enum(["private", "protected", "public"]).optional(),
|
|
19852
19835
|
spaceHardware: external_exports.string().min(1).max(128).optional(),
|
|
19853
19836
|
spaceSleepTime: external_exports.number().int().min(-1).optional(),
|
|
19854
19837
|
recoveredWithoutCredentialKey: external_exports.boolean().optional(),
|
|
@@ -19873,24 +19856,24 @@ function defaultConfigRoot(env = process.env) {
|
|
|
19873
19856
|
}
|
|
19874
19857
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
19875
19858
|
if (xdg) {
|
|
19876
|
-
return
|
|
19859
|
+
return path14.join(xdg, "mlclaw");
|
|
19877
19860
|
}
|
|
19878
|
-
return
|
|
19861
|
+
return path14.join(os6.homedir(), ".config", "mlclaw");
|
|
19879
19862
|
}
|
|
19880
19863
|
function localConfigPaths(root) {
|
|
19881
19864
|
return {
|
|
19882
19865
|
root,
|
|
19883
|
-
deploymentsDir:
|
|
19884
|
-
secretsDir:
|
|
19885
|
-
operationsDir:
|
|
19886
|
-
locksDir:
|
|
19866
|
+
deploymentsDir: path14.join(root, "deployments"),
|
|
19867
|
+
secretsDir: path14.join(root, "secrets"),
|
|
19868
|
+
operationsDir: path14.join(root, "operations"),
|
|
19869
|
+
locksDir: path14.join(root, "locks")
|
|
19887
19870
|
};
|
|
19888
19871
|
}
|
|
19889
19872
|
function manifestPath(root, agent) {
|
|
19890
|
-
return
|
|
19873
|
+
return path14.join(localConfigPaths(root).deploymentsDir, `${assertAgentName(agent)}.json`);
|
|
19891
19874
|
}
|
|
19892
19875
|
function secretEnvPath(root, agent) {
|
|
19893
|
-
return
|
|
19876
|
+
return path14.join(localConfigPaths(root).secretsDir, `${assertAgentName(agent)}.env`);
|
|
19894
19877
|
}
|
|
19895
19878
|
async function writeManifest(root, input) {
|
|
19896
19879
|
const manifest = input.version === 1 ? importLegacyManifest(legacyManifestSchema.parse(input)) : manifestSchema.parse(input);
|
|
@@ -19900,7 +19883,7 @@ async function writeManifest(root, input) {
|
|
|
19900
19883
|
}
|
|
19901
19884
|
async function readManifest(root, agent) {
|
|
19902
19885
|
const file = manifestPath(root, agent);
|
|
19903
|
-
const raw = JSON.parse(await
|
|
19886
|
+
const raw = JSON.parse(await fs13.readFile(file, "utf8"));
|
|
19904
19887
|
const version = raw && typeof raw === "object" && "version" in raw ? raw.version : void 0;
|
|
19905
19888
|
const parsed = version === 1 ? legacyManifestSchema.parse(raw) : manifestSchema.parse(raw);
|
|
19906
19889
|
if (parsed.version === 1) {
|
|
@@ -19913,7 +19896,7 @@ async function readManifest(root, agent) {
|
|
|
19913
19896
|
}
|
|
19914
19897
|
async function listManifests(root) {
|
|
19915
19898
|
const dir = localConfigPaths(root).deploymentsDir;
|
|
19916
|
-
const entries = await
|
|
19899
|
+
const entries = await fs13.readdir(dir, { withFileTypes: true }).catch((error) => {
|
|
19917
19900
|
if (error.code === "ENOENT") return [];
|
|
19918
19901
|
throw error;
|
|
19919
19902
|
});
|
|
@@ -19924,7 +19907,7 @@ async function listManifests(root) {
|
|
|
19924
19907
|
}
|
|
19925
19908
|
async function manifestExists(root, agent) {
|
|
19926
19909
|
try {
|
|
19927
|
-
await
|
|
19910
|
+
await fs13.access(manifestPath(root, agent));
|
|
19928
19911
|
return true;
|
|
19929
19912
|
} catch {
|
|
19930
19913
|
return false;
|
|
@@ -19939,7 +19922,7 @@ async function writeSecretEnv(root, agent, values) {
|
|
|
19939
19922
|
await writePrivateFile(file, renderSecretEnv(values));
|
|
19940
19923
|
}
|
|
19941
19924
|
async function readSecretEnv(root, agent) {
|
|
19942
|
-
return parseSecretEnv(await
|
|
19925
|
+
return parseSecretEnv(await fs13.readFile(secretEnvPath(root, agent), "utf8"));
|
|
19943
19926
|
}
|
|
19944
19927
|
function parseSecretEnv(raw) {
|
|
19945
19928
|
const out = {};
|
|
@@ -19971,17 +19954,17 @@ function importLegacyManifest(manifest) {
|
|
|
19971
19954
|
return { ...manifest, version: 2, deploymentId, desiredGeneration: 0 };
|
|
19972
19955
|
}
|
|
19973
19956
|
async function writePrivateFile(file, content) {
|
|
19974
|
-
await
|
|
19957
|
+
await fs13.mkdir(path14.dirname(file), { recursive: true, mode: 448 });
|
|
19975
19958
|
const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
19976
|
-
await
|
|
19977
|
-
await
|
|
19978
|
-
await
|
|
19959
|
+
await fs13.writeFile(temporary, content, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
19960
|
+
await fs13.rename(temporary, file);
|
|
19961
|
+
await fs13.chmod(file, 384);
|
|
19979
19962
|
}
|
|
19980
19963
|
|
|
19981
19964
|
// src/mlclaw/deployment-state.ts
|
|
19982
|
-
import
|
|
19965
|
+
import fs14 from "node:fs/promises";
|
|
19983
19966
|
import os7 from "node:os";
|
|
19984
|
-
import
|
|
19967
|
+
import path15 from "node:path";
|
|
19985
19968
|
import { randomUUID } from "node:crypto";
|
|
19986
19969
|
var DEPLOYMENT_PATH = ".mlclaw/deployment.json";
|
|
19987
19970
|
var DESIRED_STATE_PATH = ".mlclaw/desired-state.json";
|
|
@@ -20013,7 +19996,7 @@ var desiredStateSchema = external_exports.object({
|
|
|
20013
19996
|
runtimeImage: external_exports.string().min(1).max(1024),
|
|
20014
19997
|
space: external_exports.object({
|
|
20015
19998
|
repo: external_exports.string().min(3).max(256),
|
|
20016
|
-
visibility: external_exports.enum(["private", "public"]),
|
|
19999
|
+
visibility: external_exports.enum(["private", "protected", "public"]),
|
|
20017
20000
|
hardware: external_exports.string().min(1).max(128).optional(),
|
|
20018
20001
|
sleepTime: external_exports.number().int().min(-1).optional()
|
|
20019
20002
|
}).strict()
|
|
@@ -20067,7 +20050,7 @@ function deploymentIdentity(manifest, statePrefix = "openclaw-state") {
|
|
|
20067
20050
|
createdAt: manifest.createdAt
|
|
20068
20051
|
});
|
|
20069
20052
|
}
|
|
20070
|
-
function deploymentDesiredState(manifest, visibility = manifest.spaceVisibility ?? "
|
|
20053
|
+
function deploymentDesiredState(manifest, visibility = manifest.spaceVisibility ?? "protected") {
|
|
20071
20054
|
return desiredStateSchema.parse({
|
|
20072
20055
|
schemaVersion: 1,
|
|
20073
20056
|
deploymentId: manifest.deploymentId,
|
|
@@ -20128,7 +20111,7 @@ function newOperation(manifest, now) {
|
|
|
20128
20111
|
}
|
|
20129
20112
|
async function writeOperation(root, client, operation) {
|
|
20130
20113
|
const parsed = operationSchema.parse(operation);
|
|
20131
|
-
const local =
|
|
20114
|
+
const local = path15.join(localConfigPaths(root).operationsDir, `${parsed.operationId}.json`);
|
|
20132
20115
|
await atomicPrivateWrite(local, stringify(parsed));
|
|
20133
20116
|
await client.uploadFiles([jsonBlob(`.mlclaw/operations/${parsed.operationId}.json`, parsed)]);
|
|
20134
20117
|
}
|
|
@@ -20144,13 +20127,13 @@ async function updateOperation(root, client, operation, state, now, detail) {
|
|
|
20144
20127
|
}
|
|
20145
20128
|
async function readResumableOperation(root, deploymentId, targetGeneration) {
|
|
20146
20129
|
const directory = localConfigPaths(root).operationsDir;
|
|
20147
|
-
const entries = await
|
|
20130
|
+
const entries = await fs14.readdir(directory, { withFileTypes: true }).catch((error) => {
|
|
20148
20131
|
if (error.code === "ENOENT") return [];
|
|
20149
20132
|
throw error;
|
|
20150
20133
|
});
|
|
20151
20134
|
const operations = await Promise.all(
|
|
20152
20135
|
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => {
|
|
20153
|
-
const raw = await
|
|
20136
|
+
const raw = await fs14.readFile(path15.join(directory, entry.name), "utf8");
|
|
20154
20137
|
return operationSchema.parse(JSON.parse(raw));
|
|
20155
20138
|
})
|
|
20156
20139
|
);
|
|
@@ -20159,12 +20142,12 @@ async function readResumableOperation(root, deploymentId, targetGeneration) {
|
|
|
20159
20142
|
).sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt))[0] ?? null;
|
|
20160
20143
|
}
|
|
20161
20144
|
async function withDeploymentLock(root, deploymentId, task) {
|
|
20162
|
-
const file =
|
|
20163
|
-
await
|
|
20145
|
+
const file = path15.join(localConfigPaths(root).locksDir, `${deploymentId}.lock`);
|
|
20146
|
+
await fs14.mkdir(path15.dirname(file), { recursive: true, mode: 448 });
|
|
20164
20147
|
const token = randomUUID();
|
|
20165
20148
|
const lock = stringify({ pid: process.pid, host: os7.hostname(), token, createdAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
20166
20149
|
try {
|
|
20167
|
-
await
|
|
20150
|
+
await fs14.writeFile(file, lock, { flag: "wx", mode: 384 });
|
|
20168
20151
|
} catch (error) {
|
|
20169
20152
|
if (error.code !== "EEXIST") throw error;
|
|
20170
20153
|
if (!await replaceStaleLocalLock(file, lock)) {
|
|
@@ -20187,38 +20170,38 @@ async function withDeploymentLock(root, deploymentId, task) {
|
|
|
20187
20170
|
async function replaceStaleLocalLock(file, replacement) {
|
|
20188
20171
|
const guard = `${file}.reclaim`;
|
|
20189
20172
|
try {
|
|
20190
|
-
await
|
|
20173
|
+
await fs14.mkdir(guard);
|
|
20191
20174
|
} catch (error) {
|
|
20192
20175
|
if (error.code === "EEXIST") return false;
|
|
20193
20176
|
throw error;
|
|
20194
20177
|
}
|
|
20195
20178
|
try {
|
|
20196
|
-
const [raw, stat] = await Promise.all([
|
|
20179
|
+
const [raw, stat] = await Promise.all([fs14.readFile(file, "utf8"), fs14.stat(file)]);
|
|
20197
20180
|
const value = JSON.parse(raw);
|
|
20198
20181
|
if (value.host !== os7.hostname() || typeof value.pid !== "number") return false;
|
|
20199
20182
|
const createdAt = typeof value.createdAt === "string" ? Date.parse(value.createdAt) : Number.NaN;
|
|
20200
20183
|
const lastRefresh = Number.isFinite(createdAt) ? Math.max(createdAt, stat.mtimeMs) : stat.mtimeMs;
|
|
20201
20184
|
if (processIsAlive(value.pid) && Date.now() - lastRefresh <= LOCAL_LOCK_STALE_MS) return false;
|
|
20202
|
-
await
|
|
20203
|
-
await
|
|
20185
|
+
await fs14.rm(file);
|
|
20186
|
+
await fs14.writeFile(file, replacement, { flag: "wx", mode: 384 });
|
|
20204
20187
|
return true;
|
|
20205
20188
|
} catch {
|
|
20206
20189
|
return false;
|
|
20207
20190
|
} finally {
|
|
20208
|
-
await
|
|
20191
|
+
await fs14.rm(guard, { recursive: true, force: true });
|
|
20209
20192
|
}
|
|
20210
20193
|
}
|
|
20211
20194
|
async function refreshOwnedLocalLock(file, token) {
|
|
20212
20195
|
if (!await localLockHasToken(file, token)) return;
|
|
20213
20196
|
const now = /* @__PURE__ */ new Date();
|
|
20214
|
-
await
|
|
20197
|
+
await fs14.utimes(file, now, now);
|
|
20215
20198
|
}
|
|
20216
20199
|
async function removeOwnedLocalLock(file, token) {
|
|
20217
|
-
if (await localLockHasToken(file, token)) await
|
|
20200
|
+
if (await localLockHasToken(file, token)) await fs14.rm(file, { force: true });
|
|
20218
20201
|
}
|
|
20219
20202
|
async function localLockHasToken(file, token) {
|
|
20220
20203
|
try {
|
|
20221
|
-
const value = JSON.parse(await
|
|
20204
|
+
const value = JSON.parse(await fs14.readFile(file, "utf8"));
|
|
20222
20205
|
return value.token === token;
|
|
20223
20206
|
} catch {
|
|
20224
20207
|
return false;
|
|
@@ -20284,19 +20267,19 @@ async function readDocument(client, file, schema) {
|
|
|
20284
20267
|
if (blob.size > MAX_CONTROL_BYTES) throw new Error(`${file} exceeds ${MAX_CONTROL_BYTES} bytes`);
|
|
20285
20268
|
return schema.parse(JSON.parse(await blob.text()));
|
|
20286
20269
|
}
|
|
20287
|
-
function jsonBlob(
|
|
20288
|
-
return { path:
|
|
20270
|
+
function jsonBlob(path16, value) {
|
|
20271
|
+
return { path: path16, content: new Blob([stringify(value)], { type: "application/json" }) };
|
|
20289
20272
|
}
|
|
20290
20273
|
function stringify(value) {
|
|
20291
20274
|
return `${JSON.stringify(value, null, 2)}
|
|
20292
20275
|
`;
|
|
20293
20276
|
}
|
|
20294
20277
|
async function atomicPrivateWrite(file, content) {
|
|
20295
|
-
await
|
|
20278
|
+
await fs14.mkdir(path15.dirname(file), { recursive: true, mode: 448 });
|
|
20296
20279
|
const temporary = `${file}.${process.pid}.tmp`;
|
|
20297
|
-
await
|
|
20298
|
-
await
|
|
20299
|
-
await
|
|
20280
|
+
await fs14.writeFile(temporary, content, { mode: 384, flag: "wx" });
|
|
20281
|
+
await fs14.rename(temporary, file);
|
|
20282
|
+
await fs14.chmod(file, 384);
|
|
20300
20283
|
}
|
|
20301
20284
|
|
|
20302
20285
|
// src/mlclaw/telegram.ts
|
|
@@ -20336,116 +20319,13 @@ function delay(ms) {
|
|
|
20336
20319
|
|
|
20337
20320
|
// src/mlclaw/hf-broker-credential.ts
|
|
20338
20321
|
import { createHash as createHash3 } from "node:crypto";
|
|
20339
|
-
|
|
20340
|
-
|
|
20341
|
-
|
|
20342
|
-
|
|
20343
|
-
|
|
20344
|
-
token_form_url: "https://huggingface.co/settings/tokens/new",
|
|
20345
|
-
token_type: "fineGrained",
|
|
20346
|
-
requires_gated_repositories: true,
|
|
20347
|
-
personal_permissions: [
|
|
20348
|
-
"collection.read",
|
|
20349
|
-
"collection.write",
|
|
20350
|
-
"discussion.write",
|
|
20351
|
-
"inference.endpoints.infer.write",
|
|
20352
|
-
"inference.endpoints.write",
|
|
20353
|
-
"inference.serverless.write",
|
|
20354
|
-
"job.write",
|
|
20355
|
-
"repo.access.read",
|
|
20356
|
-
"repo.content.read",
|
|
20357
|
-
"repo.write",
|
|
20358
|
-
"sql-console.embed.write",
|
|
20359
|
-
"user.billing.read",
|
|
20360
|
-
"user.mcp.read",
|
|
20361
|
-
"user.notifications.read",
|
|
20362
|
-
"user.notifications.write",
|
|
20363
|
-
"user.papers.write",
|
|
20364
|
-
"user.preferences.write",
|
|
20365
|
-
"user.settings.notifications.write",
|
|
20366
|
-
"user.social.likes.write",
|
|
20367
|
-
"user.webhooks.read",
|
|
20368
|
-
"user.webhooks.write"
|
|
20369
|
-
],
|
|
20370
|
-
global_permissions: [
|
|
20371
|
-
"discussion.write",
|
|
20372
|
-
"post.write"
|
|
20373
|
-
],
|
|
20374
|
-
organization_permissions: [
|
|
20375
|
-
"collection.read",
|
|
20376
|
-
"collection.write",
|
|
20377
|
-
"discussion.write",
|
|
20378
|
-
"inference.endpoints.infer.write",
|
|
20379
|
-
"inference.endpoints.write",
|
|
20380
|
-
"inference.serverless.write",
|
|
20381
|
-
"job.write",
|
|
20382
|
-
"org.auditLog.write",
|
|
20383
|
-
"org.billing.read",
|
|
20384
|
-
"org.members.read",
|
|
20385
|
-
"org.members.write",
|
|
20386
|
-
"org.networkSecurity.read",
|
|
20387
|
-
"org.networkSecurity.write",
|
|
20388
|
-
"org.read",
|
|
20389
|
-
"org.repos.read",
|
|
20390
|
-
"org.serviceAccounts.read",
|
|
20391
|
-
"org.serviceAccounts.write",
|
|
20392
|
-
"org.write",
|
|
20393
|
-
"repo.access.read",
|
|
20394
|
-
"repo.content.read",
|
|
20395
|
-
"repo.write",
|
|
20396
|
-
"resourceGroup.write",
|
|
20397
|
-
"sql-console.embed.write"
|
|
20398
|
-
]
|
|
20399
|
-
};
|
|
20400
|
-
|
|
20401
|
-
// src/mlclaw/hf-broker-credential.ts
|
|
20402
|
-
var permissionListSchema = external_exports.array(external_exports.string().min(1)).min(1).superRefine((permissions, context) => {
|
|
20403
|
-
if (permissions.some((permission) => permission !== permission.trim())) {
|
|
20404
|
-
context.addIssue({ code: external_exports.ZodIssueCode.custom, message: "permissions must not contain outer whitespace" });
|
|
20405
|
-
}
|
|
20406
|
-
if (new Set(permissions).size !== permissions.length) {
|
|
20407
|
-
context.addIssue({ code: external_exports.ZodIssueCode.custom, message: "permissions must be unique" });
|
|
20408
|
-
}
|
|
20409
|
-
if (permissions.some((permission, index) => index > 0 && permission < permissions[index - 1])) {
|
|
20410
|
-
context.addIssue({ code: external_exports.ZodIssueCode.custom, message: "permissions must be sorted" });
|
|
20411
|
-
}
|
|
20412
|
-
});
|
|
20413
|
-
var profileSchema = external_exports.object({
|
|
20414
|
-
version: external_exports.literal(1),
|
|
20415
|
-
profile_id: external_exports.literal("hf-broker-complete-v1"),
|
|
20416
|
-
token_form_url: external_exports.literal("https://huggingface.co/settings/tokens/new"),
|
|
20417
|
-
token_type: external_exports.literal("fineGrained"),
|
|
20418
|
-
requires_gated_repositories: external_exports.literal(true),
|
|
20419
|
-
personal_permissions: permissionListSchema,
|
|
20420
|
-
global_permissions: permissionListSchema,
|
|
20421
|
-
organization_permissions: permissionListSchema
|
|
20422
|
-
}).strict();
|
|
20423
|
-
var BROKER_CREDENTIAL_PROFILE = Object.freeze(profileSchema.parse(hf_broker_credential_requirements_default));
|
|
20424
|
-
var HF_TOKEN_CREATE_URL = BROKER_CREDENTIAL_PROFILE.token_form_url;
|
|
20425
|
-
var BROKER_PERSONAL_PERMISSIONS = BROKER_CREDENTIAL_PROFILE.personal_permissions;
|
|
20426
|
-
var BROKER_GLOBAL_PERMISSIONS = BROKER_CREDENTIAL_PROFILE.global_permissions;
|
|
20427
|
-
var BROKER_ORGANIZATION_PERMISSIONS = BROKER_CREDENTIAL_PROFILE.organization_permissions;
|
|
20428
|
-
function buildBrokerTokenUrl(owner, accountName) {
|
|
20429
|
-
const url = new URL(HF_TOKEN_CREATE_URL);
|
|
20430
|
-
url.searchParams.set("tokenType", BROKER_CREDENTIAL_PROFILE.token_type);
|
|
20431
|
-
for (const permission of BROKER_PERSONAL_PERMISSIONS) {
|
|
20432
|
-
url.searchParams.append("ownUserPermissions", permission);
|
|
20433
|
-
}
|
|
20434
|
-
for (const permission of BROKER_GLOBAL_PERMISSIONS) {
|
|
20435
|
-
url.searchParams.append("globalPermissions", permission);
|
|
20436
|
-
}
|
|
20437
|
-
url.searchParams.set("canReadGatedRepos", String(BROKER_CREDENTIAL_PROFILE.requires_gated_repositories));
|
|
20438
|
-
if (owner !== accountName) {
|
|
20439
|
-
url.searchParams.append("orgs", owner);
|
|
20440
|
-
for (const permission of BROKER_ORGANIZATION_PERMISSIONS) {
|
|
20441
|
-
url.searchParams.append("orgPermissions", permission);
|
|
20442
|
-
}
|
|
20443
|
-
}
|
|
20444
|
-
return url.toString();
|
|
20445
|
-
}
|
|
20446
|
-
function assessBrokerCredential(identity, owner) {
|
|
20322
|
+
var HF_TOKEN_CREATE_URL = "https://huggingface.co/settings/tokens/new?tokenType=fineGrained";
|
|
20323
|
+
function buildBrokerTokenUrl() {
|
|
20324
|
+
return HF_TOKEN_CREATE_URL;
|
|
20325
|
+
}
|
|
20326
|
+
function assessBrokerCredential(identity) {
|
|
20447
20327
|
const accessToken = identity.auth?.accessToken;
|
|
20448
|
-
if (accessToken?.role !==
|
|
20328
|
+
if (accessToken?.role !== "fineGrained") {
|
|
20449
20329
|
return {
|
|
20450
20330
|
status: "unsupported",
|
|
20451
20331
|
reason: "HF Broker requires a dedicated fine-grained Hugging Face token"
|
|
@@ -20457,40 +20337,16 @@ function assessBrokerCredential(identity, owner) {
|
|
|
20457
20337
|
reason: "Hugging Face omitted this fine-grained token's permission details"
|
|
20458
20338
|
};
|
|
20459
20339
|
}
|
|
20460
|
-
|
|
20461
|
-
const globalAvailable = new Set(accessToken.fineGrained.global);
|
|
20462
|
-
const missing = BROKER_PERSONAL_PERMISSIONS.filter((permission) => !personalAvailable.has(permission));
|
|
20463
|
-
missing.push(
|
|
20464
|
-
...BROKER_GLOBAL_PERMISSIONS.filter((permission) => !globalAvailable.has(permission)).map(
|
|
20465
|
-
(permission) => `global:${permission}`
|
|
20466
|
-
)
|
|
20467
|
-
);
|
|
20468
|
-
if (!accessToken.fineGrained.canReadGatedRepos) {
|
|
20469
|
-
missing.push("canReadGatedRepos");
|
|
20470
|
-
}
|
|
20471
|
-
if (owner !== identity.name) {
|
|
20472
|
-
const organizationAvailable = new Set(scopedPermissions(accessToken.fineGrained.scoped, "org", owner));
|
|
20473
|
-
missing.push(
|
|
20474
|
-
...BROKER_ORGANIZATION_PERMISSIONS.filter((permission) => !organizationAvailable.has(permission)).map(
|
|
20475
|
-
(permission) => `org:${permission}`
|
|
20476
|
-
)
|
|
20477
|
-
);
|
|
20478
|
-
}
|
|
20479
|
-
missing.sort();
|
|
20480
|
-
return missing.length === 0 ? { status: "sufficient" } : { status: "insufficient", missing };
|
|
20340
|
+
return { status: "sufficient" };
|
|
20481
20341
|
}
|
|
20482
20342
|
function brokerCredentialMetadata(token, identity, verifiedAt) {
|
|
20483
20343
|
return {
|
|
20484
|
-
|
|
20344
|
+
credentialKind: "fine_grained_user_token",
|
|
20485
20345
|
account: identity.name,
|
|
20486
20346
|
fingerprintSha256: createHash3("sha256").update(token).digest("hex"),
|
|
20487
20347
|
verifiedAt: verifiedAt.toISOString()
|
|
20488
20348
|
};
|
|
20489
20349
|
}
|
|
20490
|
-
function scopedPermissions(scopes, type, name) {
|
|
20491
|
-
if (!Array.isArray(scopes)) return [];
|
|
20492
|
-
return scopes.filter((scope) => scope.entity.type === type && (!scope.entity.name || scope.entity.name === name)).flatMap((scope) => scope.permissions);
|
|
20493
|
-
}
|
|
20494
20350
|
|
|
20495
20351
|
// src/mlclaw/tailscale.ts
|
|
20496
20352
|
import { execFile as execFile3 } from "node:child_process";
|
|
@@ -20751,7 +20607,7 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
20751
20607
|
program2.name("mlclaw").description("Deploy OpenClaw to a Hugging Face Space and private bucket").showHelpAfterError().exitOverride((err) => {
|
|
20752
20608
|
throw err;
|
|
20753
20609
|
});
|
|
20754
|
-
program2.command("bootstrap", { isDefault: true }).alias("configure").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").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", "
|
|
20610
|
+
program2.command("bootstrap", { isDefault: true }).alias("configure").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").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", "Expose the Space source as well as the authenticated app", false).addOption(new Option("--gateway-token <token>").hideHelp()).option("--router-token <token>", "Hugging Face Router inference token for Space gateway model calls").option(
|
|
20755
20611
|
"--router-token-file <path>",
|
|
20756
20612
|
"File containing MLCLAW_ROUTER_TOKEN=..., HF_ROUTER_TOKEN=..., or a raw token"
|
|
20757
20613
|
).option("--broker-hf-token-file <path>", "File containing MLCLAW_BROKER_HF_TOKEN=... or a raw Hugging Face token").option("--docker-context <name>", "Docker context for local gateway mode").option("--container-runtime <auto|docker|podman>", "Local container runtime", "auto").option("--local-port <port>", "Loopback port for a local gateway", parseLocalPort).option("--tailscale <off|direct|serve>", "Tailnet access mode", parseTailscaleMode).option("--tailscale-port <port>", "Tailnet listener or Serve HTTPS port", parseLocalPort).option(
|
|
@@ -20802,7 +20658,7 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
20802
20658
|
gateway.command("logs").argument("<agent>", "Agent name").option("--tail <lines>", "Number of log lines", parseInteger, 200).action(async (agent, opts) => {
|
|
20803
20659
|
await gatewayLogs(agent, opts, runtime);
|
|
20804
20660
|
});
|
|
20805
|
-
gateway.command("migrate").argument("<agent>", "Agent name").requiredOption("--to <local|space>", "Target gateway location").option("--hardware <flavor>", "Hugging Face Space hardware flavor").option("--sleep-time <seconds>", "Space sleep timeout in seconds; -1 means never sleep", parseInteger).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", "
|
|
20661
|
+
gateway.command("migrate").argument("<agent>", "Agent name").requiredOption("--to <local|space>", "Target gateway location").option("--hardware <flavor>", "Hugging Face Space hardware flavor").option("--sleep-time <seconds>", "Space sleep timeout in seconds; -1 means never sleep", parseInteger).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", "Expose the Space source as well as the authenticated app", false).option("--router-token <token>", "Hugging Face Router inference token for Space gateway model calls").option(
|
|
20806
20662
|
"--router-token-file <path>",
|
|
20807
20663
|
"File containing MLCLAW_ROUTER_TOKEN=..., HF_ROUTER_TOKEN=..., or a raw token"
|
|
20808
20664
|
).option("--docker-context <name>", "Docker context for local gateway startup when migrating to local").option("--container-runtime <auto|docker|podman>", "Local container runtime", "auto").option("--local-port <port>", "Loopback port for the local gateway", parseLocalPort).option("--tailscale <off|direct|serve>", "Tailnet access mode", parseTailscaleMode).option("--tailscale-port <port>", "Tailnet listener or Serve HTTPS port", parseLocalPort).option("--no-pull", "Do not docker pull before starting a local gateway").option("--takeover", "Start even if another live runtime lease is present", false).option("--yes", "Confirm paid hardware prompts for automation", false).action(async (agent, opts) => {
|
|
@@ -21353,7 +21209,7 @@ async function reconcileManifest(params) {
|
|
|
21353
21209
|
if (currentDesired && currentDesired.deploymentId !== requestedManifest.deploymentId) {
|
|
21354
21210
|
throw new Error(`bucket ${requestedManifest.bucket} desired state belongs to another deployment`);
|
|
21355
21211
|
}
|
|
21356
|
-
const visibility = params.visibility ?? requestedManifest.spaceVisibility ?? "
|
|
21212
|
+
const visibility = params.visibility ?? requestedManifest.spaceVisibility ?? "protected";
|
|
21357
21213
|
const candidate = deploymentDesiredState(requestedManifest, visibility);
|
|
21358
21214
|
const sameDesired = currentDesired && JSON.stringify({ ...currentDesired, generation: 0, updatedAt: "" }) === JSON.stringify({ ...candidate, generation: 0, updatedAt: "" });
|
|
21359
21215
|
if (currentDesired && currentDesired.generation > requestedManifest.desiredGeneration && !sameDesired) {
|
|
@@ -21481,7 +21337,6 @@ async function resolveBootstrapPlan(params) {
|
|
|
21481
21337
|
const existingSecrets = await readSecretEnv(runtime.configRoot, agentName).catch(() => ({}));
|
|
21482
21338
|
const brokerCredential = await resolveBrokerHfToken({
|
|
21483
21339
|
opts,
|
|
21484
|
-
owner,
|
|
21485
21340
|
hfIdentity,
|
|
21486
21341
|
...providedBrokerHfToken ? { preferredToken: providedBrokerHfToken } : {},
|
|
21487
21342
|
existingSecrets,
|
|
@@ -21543,7 +21398,7 @@ async function resolveBootstrapPlan(params) {
|
|
|
21543
21398
|
spacePlan = {
|
|
21544
21399
|
space: names.space,
|
|
21545
21400
|
exists,
|
|
21546
|
-
visibility: opts.publicSpace ? "public" :
|
|
21401
|
+
visibility: opts.publicSpace ? "public" : "protected",
|
|
21547
21402
|
...currentVisibility ? { currentVisibility } : {}
|
|
21548
21403
|
};
|
|
21549
21404
|
}
|
|
@@ -21824,7 +21679,7 @@ async function createOrAdoptSpace(params) {
|
|
|
21824
21679
|
}
|
|
21825
21680
|
params.runtime.stdout.log(`Creating ${params.spacePlan.visibility} Space ${params.spacePlan.space}`);
|
|
21826
21681
|
await params.hub.createDockerSpace(params.spacePlan.space, {
|
|
21827
|
-
|
|
21682
|
+
visibility: params.spacePlan.visibility,
|
|
21828
21683
|
...params.hardware ? { hardware: params.hardware } : {},
|
|
21829
21684
|
...typeof params.sleepTime === "number" ? { sleepTimeSeconds: params.sleepTime } : {}
|
|
21830
21685
|
});
|
|
@@ -22225,7 +22080,7 @@ async function deployLocalBootstrap(plan, opts, runtime, desiredChanged = true,
|
|
|
22225
22080
|
await writeSecretEnv(runtime.configRoot, plan.agentName, previousSecrets);
|
|
22226
22081
|
} else {
|
|
22227
22082
|
await assertLease();
|
|
22228
|
-
await
|
|
22083
|
+
await fs15.rm(secretEnvPath(runtime.configRoot, plan.agentName), { force: true });
|
|
22229
22084
|
}
|
|
22230
22085
|
if (previousContainer?.running && previousManifest) {
|
|
22231
22086
|
await startLocalGateway({ manifest: previousManifest, runtime, pull: false, refresh: true, assertLease });
|
|
@@ -22254,7 +22109,7 @@ async function deployLocalBootstrap(plan, opts, runtime, desiredChanged = true,
|
|
|
22254
22109
|
}
|
|
22255
22110
|
if (!previousManifest) {
|
|
22256
22111
|
await assertLease();
|
|
22257
|
-
await
|
|
22112
|
+
await fs15.rm(manifestPath(runtime.configRoot, plan.agentName), { force: true });
|
|
22258
22113
|
}
|
|
22259
22114
|
} catch (rollbackError) {
|
|
22260
22115
|
throw new AggregateError([error, rollbackError], "local bootstrap and rollback both failed");
|
|
@@ -22301,11 +22156,11 @@ async function deploySpaceGateway(params) {
|
|
|
22301
22156
|
const assertLease = params.assertLease ?? (async () => void 0);
|
|
22302
22157
|
if (!params.spacePrepared) {
|
|
22303
22158
|
runtime.stdout.log(
|
|
22304
|
-
params.spaceExists ? `Updating existing Space ${manifest.space}` : `Creating ${params.
|
|
22159
|
+
params.spaceExists ? `Updating existing Space ${manifest.space}` : `Creating ${params.visibility ?? "protected"} Space ${manifest.space}`
|
|
22305
22160
|
);
|
|
22306
22161
|
await assertLease();
|
|
22307
22162
|
await hub.createDockerSpace(manifest.space, {
|
|
22308
|
-
|
|
22163
|
+
visibility: params.visibility ?? "protected",
|
|
22309
22164
|
...params.hardware && !params.spaceExists ? { hardware: params.hardware } : {},
|
|
22310
22165
|
...typeof params.sleepTime === "number" ? { sleepTimeSeconds: params.sleepTime } : {}
|
|
22311
22166
|
});
|
|
@@ -22739,7 +22594,7 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
22739
22594
|
});
|
|
22740
22595
|
updated = {
|
|
22741
22596
|
...updated,
|
|
22742
|
-
spaceVisibility: opts.publicSpace ? "public" :
|
|
22597
|
+
spaceVisibility: opts.publicSpace ? "public" : "protected",
|
|
22743
22598
|
...paidHardware.kind === "explicit" ? { spaceHardware: paidHardware.hardware } : {},
|
|
22744
22599
|
...typeof paidHardware.sleepTime === "number" ? { spaceSleepTime: paidHardware.sleepTime } : {}
|
|
22745
22600
|
};
|
|
@@ -22770,7 +22625,7 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
22770
22625
|
manifest: updated,
|
|
22771
22626
|
secrets: deploymentSecrets2,
|
|
22772
22627
|
allowedUsers: me2.name,
|
|
22773
|
-
|
|
22628
|
+
visibility: updated.spaceVisibility === "public" ? "public" : "protected",
|
|
22774
22629
|
spaceExists,
|
|
22775
22630
|
assertLease,
|
|
22776
22631
|
...paidHardware.kind === "explicit" ? { hardware: paidHardware.hardware } : {},
|
|
@@ -23609,6 +23464,15 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
23609
23464
|
}
|
|
23610
23465
|
return;
|
|
23611
23466
|
}
|
|
23467
|
+
const visibility = await hub.getSpaceVisibility(repoId);
|
|
23468
|
+
if (visibility !== "protected") {
|
|
23469
|
+
if (fix) {
|
|
23470
|
+
await hub.updateSpaceVisibility(repoId, "protected");
|
|
23471
|
+
fixed.push("set protected Space visibility");
|
|
23472
|
+
} else {
|
|
23473
|
+
issues.push(`Space visibility is ${visibility}; hosted gateways require protected visibility`);
|
|
23474
|
+
}
|
|
23475
|
+
}
|
|
23612
23476
|
const bucket = variables.get("OPENCLAW_HF_STATE_BUCKET")?.value ?? opts.bucket;
|
|
23613
23477
|
let signedInUser;
|
|
23614
23478
|
const currentUsername = async () => {
|
|
@@ -23995,7 +23859,7 @@ async function readOptionalTelegramToken(opts, runtime) {
|
|
|
23995
23859
|
return direct;
|
|
23996
23860
|
}
|
|
23997
23861
|
if (opts.telegramTokenFile) {
|
|
23998
|
-
const raw = await
|
|
23862
|
+
const raw = await fs15.readFile(opts.telegramTokenFile, "utf8");
|
|
23999
23863
|
const match = raw.match(/(?:^|\n)\s*TELEGRAM_BOT_TOKEN\s*=\s*['"]?([^'"\n]+)['"]?/);
|
|
24000
23864
|
return (match?.[1] ?? raw.trim()).trim();
|
|
24001
23865
|
}
|
|
@@ -24017,7 +23881,7 @@ async function readOptionalRouterTokenFile(file) {
|
|
|
24017
23881
|
if (!file) {
|
|
24018
23882
|
return void 0;
|
|
24019
23883
|
}
|
|
24020
|
-
const raw = await
|
|
23884
|
+
const raw = await fs15.readFile(file, "utf8");
|
|
24021
23885
|
const parsed = parseSecretEnv(raw);
|
|
24022
23886
|
return nonEmpty(parsed.MLCLAW_ROUTER_TOKEN) ?? nonEmpty(parsed.HF_ROUTER_TOKEN) ?? nonEmpty(raw);
|
|
24023
23887
|
}
|
|
@@ -24028,7 +23892,7 @@ async function credentialsStatus(requestedAgent, runtime) {
|
|
|
24028
23892
|
if (!metadata) throw new Error("verified HF Broker credential metadata is missing");
|
|
24029
23893
|
runtime.stdout.log(`Agent: ${manifest.agent}`);
|
|
24030
23894
|
runtime.stdout.log(`Status: healthy`);
|
|
24031
|
-
runtime.stdout.log(`
|
|
23895
|
+
runtime.stdout.log(`Credential kind: ${metadata.credentialKind}`);
|
|
24032
23896
|
runtime.stdout.log(`Account: ${metadata.account}`);
|
|
24033
23897
|
runtime.stdout.log(`Fingerprint: ${metadata.fingerprintSha256.slice(0, 12)}`);
|
|
24034
23898
|
runtime.stdout.log(`Verified: ${metadata.verifiedAt}`);
|
|
@@ -24042,7 +23906,7 @@ async function verifiedStoredBrokerCredential(manifest, runtime) {
|
|
|
24042
23906
|
`HF Broker credential metadata is missing; run \`mlclaw credentials repair ${manifest.agent}\` to complete the cutover`
|
|
24043
23907
|
);
|
|
24044
23908
|
}
|
|
24045
|
-
const verified = await verifyBrokerHfToken(token, manifest.
|
|
23909
|
+
const verified = await verifyBrokerHfToken(token, manifest.brokerCredential.account, runtime);
|
|
24046
23910
|
const observed = brokerCredentialMetadata(token, verified.identity, runtime.now());
|
|
24047
23911
|
if (observed.fingerprintSha256 !== manifest.brokerCredential.fingerprintSha256) {
|
|
24048
23912
|
throw new Error(
|
|
@@ -24062,14 +23926,14 @@ async function credentialsRepair(requestedAgent, opts, runtime) {
|
|
|
24062
23926
|
const suppliedToken = fileToken ?? nonEmpty(runtime.env.MLCLAW_BROKER_HF_TOKEN);
|
|
24063
23927
|
let replacement;
|
|
24064
23928
|
if (suppliedToken) {
|
|
24065
|
-
replacement = await verifyBrokerHfToken(suppliedToken,
|
|
23929
|
+
replacement = await verifyBrokerHfToken(suppliedToken, account, runtime);
|
|
24066
23930
|
} else {
|
|
24067
23931
|
if (!runtime.prompt.isInteractive()) {
|
|
24068
23932
|
throw new Error(
|
|
24069
23933
|
"credential repair requires --broker-hf-token-file, MLCLAW_BROKER_HF_TOKEN, or an interactive terminal"
|
|
24070
23934
|
);
|
|
24071
23935
|
}
|
|
24072
|
-
replacement = await promptForBrokerHfToken(
|
|
23936
|
+
replacement = await promptForBrokerHfToken(account, runtime);
|
|
24073
23937
|
}
|
|
24074
23938
|
const updatedManifest = {
|
|
24075
23939
|
...manifest,
|
|
@@ -24240,7 +24104,7 @@ async function resolveBrokerHfToken(params) {
|
|
|
24240
24104
|
const configuredToken = fileToken ?? environmentToken ?? nonEmpty(params.preferredToken) ?? nonEmpty(params.existingSecrets.MLCLAW_BROKER_HF_TOKEN);
|
|
24241
24105
|
if (configuredToken) {
|
|
24242
24106
|
try {
|
|
24243
|
-
return await verifyBrokerHfToken(configuredToken, params.
|
|
24107
|
+
return await verifyBrokerHfToken(configuredToken, params.hfIdentity.name, params.runtime);
|
|
24244
24108
|
} catch (error) {
|
|
24245
24109
|
if (fileToken || environmentToken || params.preferredToken) throw error;
|
|
24246
24110
|
if (params.runtime.prompt.isInteractive()) {
|
|
@@ -24260,19 +24124,19 @@ async function resolveBrokerHfToken(params) {
|
|
|
24260
24124
|
"a dedicated HF Broker credential is required; set MLCLAW_BROKER_HF_TOKEN, pass --broker-hf-token-file, or run bootstrap interactively"
|
|
24261
24125
|
);
|
|
24262
24126
|
}
|
|
24263
|
-
return await promptForBrokerHfToken(params.
|
|
24127
|
+
return await promptForBrokerHfToken(params.hfIdentity.name, params.runtime);
|
|
24264
24128
|
}
|
|
24265
|
-
async function promptForBrokerHfToken(
|
|
24129
|
+
async function promptForBrokerHfToken(account, runtime) {
|
|
24266
24130
|
runtime.prompt.note(
|
|
24267
|
-
"ML Claw will open a Hugging Face token form
|
|
24131
|
+
"ML Claw will open a Hugging Face fine-grained token form. Choose the permissions and resources this broker may use, create a dedicated token, and paste it here. Your current HF CLI login will not be changed.",
|
|
24268
24132
|
"HF Broker credential"
|
|
24269
24133
|
);
|
|
24270
|
-
const url = buildBrokerTokenUrl(
|
|
24134
|
+
const url = buildBrokerTokenUrl();
|
|
24271
24135
|
const opened = await runtime.hfCli.openUrl(url);
|
|
24272
24136
|
runtime.prompt.note(
|
|
24273
24137
|
`${opened ? "The token form was opened in your browser." : "Open this token form in your browser."}
|
|
24274
24138
|
|
|
24275
|
-
|
|
24139
|
+
Choose the permissions and resources, name and create the token, then copy it. The URL contains no credential. The exact URL is printed below.`,
|
|
24276
24140
|
"Create the broker token"
|
|
24277
24141
|
);
|
|
24278
24142
|
runtime.stdout.log(url);
|
|
@@ -24282,7 +24146,7 @@ Name and create the token, then copy it. The URL contains permission names only;
|
|
|
24282
24146
|
"Hugging Face broker token"
|
|
24283
24147
|
);
|
|
24284
24148
|
try {
|
|
24285
|
-
const verified = await verifyBrokerHfToken(replacement,
|
|
24149
|
+
const verified = await verifyBrokerHfToken(replacement, account, runtime);
|
|
24286
24150
|
runtime.prompt.note(
|
|
24287
24151
|
"The dedicated broker token was verified. It will be stored only in ML Claw's trusted broker configuration.",
|
|
24288
24152
|
"HF Broker credential ready"
|
|
@@ -24296,12 +24160,12 @@ Name and create the token, then copy it. The URL contains permission names only;
|
|
|
24296
24160
|
}
|
|
24297
24161
|
}
|
|
24298
24162
|
}
|
|
24299
|
-
async function verifyBrokerHfToken(token,
|
|
24163
|
+
async function verifyBrokerHfToken(token, expectedAccount, runtime) {
|
|
24300
24164
|
const identity = await runtime.hubFactory(token).whoami();
|
|
24301
24165
|
if (identity.name !== expectedAccount) {
|
|
24302
24166
|
throw new Error(`broker token belongs to ${identity.name}, not ${expectedAccount}`);
|
|
24303
24167
|
}
|
|
24304
|
-
const assessment = assessBrokerCredential(identity
|
|
24168
|
+
const assessment = assessBrokerCredential(identity);
|
|
24305
24169
|
if (assessment.status !== "sufficient") {
|
|
24306
24170
|
throw new Error(brokerCredentialAssessmentDetail(assessment));
|
|
24307
24171
|
}
|
|
@@ -24309,17 +24173,14 @@ async function verifyBrokerHfToken(token, owner, expectedAccount, runtime) {
|
|
|
24309
24173
|
}
|
|
24310
24174
|
async function readOptionalBrokerHfTokenFile(file) {
|
|
24311
24175
|
if (!file) return void 0;
|
|
24312
|
-
const raw = await
|
|
24176
|
+
const raw = await fs15.readFile(file, "utf8");
|
|
24313
24177
|
const parsed = parseSecretEnv(raw);
|
|
24314
24178
|
const token = nonEmpty(parsed.MLCLAW_BROKER_HF_TOKEN) ?? nonEmpty(raw);
|
|
24315
24179
|
if (!token) throw new Error("HF Broker token file is empty");
|
|
24316
24180
|
return token;
|
|
24317
24181
|
}
|
|
24318
24182
|
function brokerCredentialAssessmentDetail(assessment) {
|
|
24319
|
-
|
|
24320
|
-
const shown = assessment.missing.slice(0, 8);
|
|
24321
|
-
const remaining = assessment.missing.length - shown.length;
|
|
24322
|
-
return `The HF Broker credential is missing ${assessment.missing.length} required permission${assessment.missing.length === 1 ? "" : "s"}: ${shown.join(", ")}${remaining > 0 ? `, and ${remaining} more` : ""}`;
|
|
24183
|
+
return assessment.reason;
|
|
24323
24184
|
}
|
|
24324
24185
|
function errorMessage(error) {
|
|
24325
24186
|
return error instanceof Error ? error.message : String(error);
|