mlclaw 0.4.7 → 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 +13 -10
- package/dist/mlclaw-space-runtime.js +9906 -6675
- package/dist/mlclaw.mjs +151 -310
- 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) {
|
|
@@ -15185,9 +15185,9 @@ var HubApi = class {
|
|
|
15185
15185
|
encoding: "base64"
|
|
15186
15186
|
}
|
|
15187
15187
|
})),
|
|
15188
|
-
...(params.deletePaths ?? []).map((
|
|
15188
|
+
...(params.deletePaths ?? []).map((path16) => ({
|
|
15189
15189
|
key: "deletedFile",
|
|
15190
|
-
value: { path:
|
|
15190
|
+
value: { path: path16 }
|
|
15191
15191
|
}))
|
|
15192
15192
|
].map((entry) => JSON.stringify(entry)).join("\n");
|
|
15193
15193
|
await this.request(`/api/spaces/${repoId}/commit/${encodeURIComponent(params.branch ?? "main")}`, {
|
|
@@ -15217,10 +15217,10 @@ var HubApi = class {
|
|
|
15217
15217
|
if (info.sha) return;
|
|
15218
15218
|
await this.commitModelDocument(repoId, "README.md", "", "# ML Claw deployment control\n");
|
|
15219
15219
|
}
|
|
15220
|
-
async readModelDocument(repoId,
|
|
15220
|
+
async readModelDocument(repoId, path16) {
|
|
15221
15221
|
const info = await this.requestJson(`/api/models/${repoId}`);
|
|
15222
15222
|
if (!info.sha) throw new Error(`control repository ${repoId} has no revision`);
|
|
15223
|
-
const url = `${this.hubUrl}/${repoId}/resolve/${info.sha}/${
|
|
15223
|
+
const url = `${this.hubUrl}/${repoId}/resolve/${info.sha}/${path16.split("/").map(encodeURIComponent).join("/")}`;
|
|
15224
15224
|
const response = await this.fetchImpl(url, {
|
|
15225
15225
|
headers: { Authorization: `Bearer ${this.token}` }
|
|
15226
15226
|
});
|
|
@@ -15228,16 +15228,16 @@ var HubApi = class {
|
|
|
15228
15228
|
if (!response.ok) throw new HubApiError2(response.status, url, await response.text());
|
|
15229
15229
|
return { value: JSON.parse(await response.text()), revision: info.sha };
|
|
15230
15230
|
}
|
|
15231
|
-
async commitModelDocument(repoId,
|
|
15231
|
+
async commitModelDocument(repoId, path16, parentCommit, value) {
|
|
15232
15232
|
const header = {
|
|
15233
15233
|
summary: value === null ? "Release deployment control" : "Update deployment control",
|
|
15234
15234
|
description: "ML Claw deployment reconciliation state"
|
|
15235
15235
|
};
|
|
15236
15236
|
if (parentCommit) header.parentCommit = parentCommit;
|
|
15237
|
-
const operation = value === null ? { key: "deletedFile", value: { path:
|
|
15237
|
+
const operation = value === null ? { key: "deletedFile", value: { path: path16 } } : {
|
|
15238
15238
|
key: "file",
|
|
15239
15239
|
value: {
|
|
15240
|
-
path:
|
|
15240
|
+
path: path16,
|
|
15241
15241
|
content: Buffer.from(typeof value === "string" ? value : `${JSON.stringify(value, null, 2)}
|
|
15242
15242
|
`).toString(
|
|
15243
15243
|
"base64"
|
|
@@ -15381,21 +15381,22 @@ function nextLink(header) {
|
|
|
15381
15381
|
return null;
|
|
15382
15382
|
}
|
|
15383
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
|
+
|
|
15384
15393
|
// src/mlclaw/runtime-image.ts
|
|
15385
|
-
|
|
15386
|
-
|
|
15387
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
15388
|
-
var DEFAULT_OPENCLAW_VERSION = "2026.7.1";
|
|
15389
|
-
var DEFAULT_BROKERKIT_PLUGIN_VERSION = "0.3.3";
|
|
15390
|
-
var DEFAULT_BROKERKIT_VERSION = "hf-broker/v0.4.0";
|
|
15391
|
-
var DEFAULT_RUNTIME_IMAGE_REPOSITORY = "ghcr.io/huggingface/mlclaw";
|
|
15392
|
-
var PACKAGE_METADATA = readPackageMetadata();
|
|
15393
|
-
var PACKAGE_VERSION = packageString("version", "unknown");
|
|
15394
|
-
var OPENCLAW_VERSION = packageConfigString("openclawVersion", DEFAULT_OPENCLAW_VERSION);
|
|
15394
|
+
var PACKAGE_VERSION = RELEASE_CONFIG.packageVersion;
|
|
15395
|
+
var OPENCLAW_VERSION = RELEASE_CONFIG.openclawVersion;
|
|
15395
15396
|
var OPENCLAW_BASE_IMAGE = `ghcr.io/openclaw/openclaw:${OPENCLAW_VERSION}`;
|
|
15396
|
-
var BROKERKIT_PLUGIN_VERSION =
|
|
15397
|
-
var BROKERKIT_VERSION =
|
|
15398
|
-
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;
|
|
15399
15400
|
var DEFAULT_RUNTIME_IMAGE_TAG = `${PACKAGE_VERSION}-openclaw-${OPENCLAW_VERSION}`;
|
|
15400
15401
|
var DEFAULT_RUNTIME_IMAGE = `${RUNTIME_IMAGE_REPOSITORY}:${DEFAULT_RUNTIME_IMAGE_TAG}`;
|
|
15401
15402
|
function resolveRuntimeImage(value, env = process.env) {
|
|
@@ -15413,45 +15414,16 @@ function resolveSpaceRuntimeImage(opts, env = process.env) {
|
|
|
15413
15414
|
function bundledSpaceRuntimeRef(templateRev) {
|
|
15414
15415
|
return `bundled:${templateRev}`;
|
|
15415
15416
|
}
|
|
15416
|
-
function packageString(key, fallback) {
|
|
15417
|
-
const value = PACKAGE_METADATA[key];
|
|
15418
|
-
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
15419
|
-
}
|
|
15420
|
-
function packageConfigString(key, fallback) {
|
|
15421
|
-
const value = PACKAGE_METADATA.config?.[key];
|
|
15422
|
-
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
15423
|
-
}
|
|
15424
|
-
function readPackageMetadata() {
|
|
15425
|
-
let dir = path13.dirname(fileURLToPath2(import.meta.url));
|
|
15426
|
-
while (true) {
|
|
15427
|
-
const candidate = path13.join(dir, "package.json");
|
|
15428
|
-
try {
|
|
15429
|
-
return JSON.parse(fs12.readFileSync(candidate, "utf8"));
|
|
15430
|
-
} catch (err) {
|
|
15431
|
-
if (!isMissingFileError(err)) {
|
|
15432
|
-
throw err;
|
|
15433
|
-
}
|
|
15434
|
-
}
|
|
15435
|
-
const parent = path13.dirname(dir);
|
|
15436
|
-
if (parent === dir) {
|
|
15437
|
-
throw new Error("could not find package.json while resolving default runtime image");
|
|
15438
|
-
}
|
|
15439
|
-
dir = parent;
|
|
15440
|
-
}
|
|
15441
|
-
}
|
|
15442
|
-
function isMissingFileError(err) {
|
|
15443
|
-
return err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
15444
|
-
}
|
|
15445
15417
|
|
|
15446
15418
|
// src/mlclaw/git.ts
|
|
15447
15419
|
var execFileAsync2 = promisify2(execFile2);
|
|
15448
15420
|
async function pushTemplateToSpace(params) {
|
|
15449
|
-
const tempRoot = await
|
|
15421
|
+
const tempRoot = await fs12.mkdtemp(path13.join(os5.tmpdir(), "mlclaw-space-"));
|
|
15450
15422
|
try {
|
|
15451
15423
|
const sourceDir = params.sourceDir ?? process.env.MLCLAW_SOURCE_DIR ?? await findPackagedSourceRoot();
|
|
15452
15424
|
const templateRev = await currentTemplateRev(sourceDir);
|
|
15453
|
-
const outDir =
|
|
15454
|
-
await
|
|
15425
|
+
const outDir = path13.join(tempRoot, "space");
|
|
15426
|
+
await fs12.mkdir(outDir, { recursive: true });
|
|
15455
15427
|
await generateSpaceRepo(sourceDir, outDir, {
|
|
15456
15428
|
...params.runtimeImage ? { runtimeImage: params.runtimeImage } : {}
|
|
15457
15429
|
});
|
|
@@ -15469,7 +15441,7 @@ async function pushTemplateToSpace(params) {
|
|
|
15469
15441
|
});
|
|
15470
15442
|
return { templateRev };
|
|
15471
15443
|
} finally {
|
|
15472
|
-
await
|
|
15444
|
+
await fs12.rm(tempRoot, { recursive: true, force: true });
|
|
15473
15445
|
}
|
|
15474
15446
|
}
|
|
15475
15447
|
async function currentTemplateRev(sourceDir) {
|
|
@@ -15482,7 +15454,7 @@ async function currentTemplateRev(sourceDir) {
|
|
|
15482
15454
|
}
|
|
15483
15455
|
} catch {
|
|
15484
15456
|
}
|
|
15485
|
-
const pkg = JSON.parse(await
|
|
15457
|
+
const pkg = JSON.parse(await fs12.readFile(path13.join(sourceDir, "package.json"), "utf8"));
|
|
15486
15458
|
return `npm:${pkg.name ?? "mlclaw"}@${pkg.version ?? "unknown"}`;
|
|
15487
15459
|
}
|
|
15488
15460
|
async function generateSpaceRepo(sourceDir, outDir, options = {}) {
|
|
@@ -15508,13 +15480,13 @@ async function generateSpaceRepo(sourceDir, outDir, options = {}) {
|
|
|
15508
15480
|
);
|
|
15509
15481
|
}
|
|
15510
15482
|
for (const [from, to] of copies) {
|
|
15511
|
-
await copyExisting(
|
|
15483
|
+
await copyExisting(path13.join(sourceDir, from), path13.join(outDir, to));
|
|
15512
15484
|
}
|
|
15513
|
-
const hfLogoPng = await
|
|
15514
|
-
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")}
|
|
15515
15487
|
`, "utf8");
|
|
15516
|
-
await
|
|
15517
|
-
|
|
15488
|
+
await fs12.writeFile(
|
|
15489
|
+
path13.join(outDir, "Dockerfile"),
|
|
15518
15490
|
options.runtimeImage ? imageDockerfile(options.runtimeImage) : bundledDockerfile(),
|
|
15519
15491
|
"utf8"
|
|
15520
15492
|
);
|
|
@@ -15602,13 +15574,13 @@ CMD ["/app/entrypoint.sh"]
|
|
|
15602
15574
|
`;
|
|
15603
15575
|
}
|
|
15604
15576
|
async function findPackagedSourceRoot() {
|
|
15605
|
-
const start =
|
|
15577
|
+
const start = path13.dirname(fileURLToPath2(import.meta.url));
|
|
15606
15578
|
let dir = start;
|
|
15607
15579
|
while (true) {
|
|
15608
15580
|
if (await hasPackagedSourceFiles(dir)) {
|
|
15609
15581
|
return dir;
|
|
15610
15582
|
}
|
|
15611
|
-
const parent =
|
|
15583
|
+
const parent = path13.dirname(dir);
|
|
15612
15584
|
if (parent === dir) {
|
|
15613
15585
|
throw new Error("Could not find packaged ML Claw source files. Reinstall the mlclaw npm package.");
|
|
15614
15586
|
}
|
|
@@ -15625,20 +15597,20 @@ async function hasPackagedSourceFiles(dir) {
|
|
|
15625
15597
|
"src/hf-bucket-client/client.ts"
|
|
15626
15598
|
];
|
|
15627
15599
|
try {
|
|
15628
|
-
await Promise.all(required.map((file) =>
|
|
15600
|
+
await Promise.all(required.map((file) => fs12.access(path13.join(dir, file))));
|
|
15629
15601
|
return true;
|
|
15630
15602
|
} catch {
|
|
15631
15603
|
return false;
|
|
15632
15604
|
}
|
|
15633
15605
|
}
|
|
15634
15606
|
async function copyExisting(from, to) {
|
|
15635
|
-
const stat = await
|
|
15636
|
-
await
|
|
15607
|
+
const stat = await fs12.stat(from);
|
|
15608
|
+
await fs12.mkdir(path13.dirname(to), { recursive: true });
|
|
15637
15609
|
if (stat.isDirectory()) {
|
|
15638
|
-
await
|
|
15610
|
+
await fs12.cp(from, to, { recursive: true });
|
|
15639
15611
|
} else {
|
|
15640
|
-
await
|
|
15641
|
-
await
|
|
15612
|
+
await fs12.copyFile(from, to);
|
|
15613
|
+
await fs12.chmod(to, stat.mode);
|
|
15642
15614
|
}
|
|
15643
15615
|
}
|
|
15644
15616
|
async function readFilesForCommit(root) {
|
|
@@ -15646,24 +15618,24 @@ async function readFilesForCommit(root) {
|
|
|
15646
15618
|
for (const relativePath of await listFiles(root)) {
|
|
15647
15619
|
files.push({
|
|
15648
15620
|
path: relativePath,
|
|
15649
|
-
content: await
|
|
15621
|
+
content: await fs12.readFile(path13.join(root, relativePath))
|
|
15650
15622
|
});
|
|
15651
15623
|
}
|
|
15652
15624
|
return files;
|
|
15653
15625
|
}
|
|
15654
15626
|
async function listFiles(root, dir = "") {
|
|
15655
|
-
const absoluteDir =
|
|
15656
|
-
const entries = await
|
|
15627
|
+
const absoluteDir = path13.join(root, dir);
|
|
15628
|
+
const entries = await fs12.readdir(absoluteDir, { withFileTypes: true });
|
|
15657
15629
|
const files = [];
|
|
15658
15630
|
for (const entry of entries) {
|
|
15659
|
-
const relativePath =
|
|
15660
|
-
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);
|
|
15661
15633
|
if (entry.isDirectory()) {
|
|
15662
15634
|
files.push(...await listFiles(root, relativePath));
|
|
15663
15635
|
} else if (entry.isFile()) {
|
|
15664
15636
|
files.push(relativePath);
|
|
15665
15637
|
} else {
|
|
15666
|
-
const stat = await
|
|
15638
|
+
const stat = await fs12.stat(absolutePath);
|
|
15667
15639
|
if (stat.isFile()) {
|
|
15668
15640
|
files.push(relativePath);
|
|
15669
15641
|
}
|
|
@@ -15742,9 +15714,9 @@ function deriveLocalAccessToken(sessionSecret) {
|
|
|
15742
15714
|
}
|
|
15743
15715
|
|
|
15744
15716
|
// src/mlclaw/local-config.ts
|
|
15745
|
-
import
|
|
15717
|
+
import fs13 from "node:fs/promises";
|
|
15746
15718
|
import os6 from "node:os";
|
|
15747
|
-
import
|
|
15719
|
+
import path14 from "node:path";
|
|
15748
15720
|
import { createHash as createHash2 } from "node:crypto";
|
|
15749
15721
|
|
|
15750
15722
|
// node_modules/zod/v3/external.js
|
|
@@ -16225,8 +16197,8 @@ function getErrorMap() {
|
|
|
16225
16197
|
|
|
16226
16198
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
16227
16199
|
var makeIssue = (params) => {
|
|
16228
|
-
const { data, path:
|
|
16229
|
-
const fullPath = [...
|
|
16200
|
+
const { data, path: path16, errorMaps, issueData } = params;
|
|
16201
|
+
const fullPath = [...path16, ...issueData.path || []];
|
|
16230
16202
|
const fullIssue = {
|
|
16231
16203
|
...issueData,
|
|
16232
16204
|
path: fullPath
|
|
@@ -16342,11 +16314,11 @@ var errorUtil;
|
|
|
16342
16314
|
|
|
16343
16315
|
// node_modules/zod/v3/types.js
|
|
16344
16316
|
var ParseInputLazyPath = class {
|
|
16345
|
-
constructor(parent, value,
|
|
16317
|
+
constructor(parent, value, path16, key) {
|
|
16346
16318
|
this._cachedPath = [];
|
|
16347
16319
|
this.parent = parent;
|
|
16348
16320
|
this.data = value;
|
|
16349
|
-
this._path =
|
|
16321
|
+
this._path = path16;
|
|
16350
16322
|
this._key = key;
|
|
16351
16323
|
}
|
|
16352
16324
|
get path() {
|
|
@@ -19852,7 +19824,7 @@ var manifestFields = {
|
|
|
19852
19824
|
model: external_exports.string().min(1).max(512),
|
|
19853
19825
|
runtimeImage: external_exports.string().min(1).max(1024),
|
|
19854
19826
|
brokerCredential: external_exports.object({
|
|
19855
|
-
|
|
19827
|
+
credentialKind: external_exports.literal("fine_grained_user_token"),
|
|
19856
19828
|
account: external_exports.string().min(1).max(128),
|
|
19857
19829
|
fingerprintSha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
19858
19830
|
verifiedAt: external_exports.string().datetime()
|
|
@@ -19884,24 +19856,24 @@ function defaultConfigRoot(env = process.env) {
|
|
|
19884
19856
|
}
|
|
19885
19857
|
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
19886
19858
|
if (xdg) {
|
|
19887
|
-
return
|
|
19859
|
+
return path14.join(xdg, "mlclaw");
|
|
19888
19860
|
}
|
|
19889
|
-
return
|
|
19861
|
+
return path14.join(os6.homedir(), ".config", "mlclaw");
|
|
19890
19862
|
}
|
|
19891
19863
|
function localConfigPaths(root) {
|
|
19892
19864
|
return {
|
|
19893
19865
|
root,
|
|
19894
|
-
deploymentsDir:
|
|
19895
|
-
secretsDir:
|
|
19896
|
-
operationsDir:
|
|
19897
|
-
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")
|
|
19898
19870
|
};
|
|
19899
19871
|
}
|
|
19900
19872
|
function manifestPath(root, agent) {
|
|
19901
|
-
return
|
|
19873
|
+
return path14.join(localConfigPaths(root).deploymentsDir, `${assertAgentName(agent)}.json`);
|
|
19902
19874
|
}
|
|
19903
19875
|
function secretEnvPath(root, agent) {
|
|
19904
|
-
return
|
|
19876
|
+
return path14.join(localConfigPaths(root).secretsDir, `${assertAgentName(agent)}.env`);
|
|
19905
19877
|
}
|
|
19906
19878
|
async function writeManifest(root, input) {
|
|
19907
19879
|
const manifest = input.version === 1 ? importLegacyManifest(legacyManifestSchema.parse(input)) : manifestSchema.parse(input);
|
|
@@ -19911,7 +19883,7 @@ async function writeManifest(root, input) {
|
|
|
19911
19883
|
}
|
|
19912
19884
|
async function readManifest(root, agent) {
|
|
19913
19885
|
const file = manifestPath(root, agent);
|
|
19914
|
-
const raw = JSON.parse(await
|
|
19886
|
+
const raw = JSON.parse(await fs13.readFile(file, "utf8"));
|
|
19915
19887
|
const version = raw && typeof raw === "object" && "version" in raw ? raw.version : void 0;
|
|
19916
19888
|
const parsed = version === 1 ? legacyManifestSchema.parse(raw) : manifestSchema.parse(raw);
|
|
19917
19889
|
if (parsed.version === 1) {
|
|
@@ -19924,7 +19896,7 @@ async function readManifest(root, agent) {
|
|
|
19924
19896
|
}
|
|
19925
19897
|
async function listManifests(root) {
|
|
19926
19898
|
const dir = localConfigPaths(root).deploymentsDir;
|
|
19927
|
-
const entries = await
|
|
19899
|
+
const entries = await fs13.readdir(dir, { withFileTypes: true }).catch((error) => {
|
|
19928
19900
|
if (error.code === "ENOENT") return [];
|
|
19929
19901
|
throw error;
|
|
19930
19902
|
});
|
|
@@ -19935,7 +19907,7 @@ async function listManifests(root) {
|
|
|
19935
19907
|
}
|
|
19936
19908
|
async function manifestExists(root, agent) {
|
|
19937
19909
|
try {
|
|
19938
|
-
await
|
|
19910
|
+
await fs13.access(manifestPath(root, agent));
|
|
19939
19911
|
return true;
|
|
19940
19912
|
} catch {
|
|
19941
19913
|
return false;
|
|
@@ -19950,7 +19922,7 @@ async function writeSecretEnv(root, agent, values) {
|
|
|
19950
19922
|
await writePrivateFile(file, renderSecretEnv(values));
|
|
19951
19923
|
}
|
|
19952
19924
|
async function readSecretEnv(root, agent) {
|
|
19953
|
-
return parseSecretEnv(await
|
|
19925
|
+
return parseSecretEnv(await fs13.readFile(secretEnvPath(root, agent), "utf8"));
|
|
19954
19926
|
}
|
|
19955
19927
|
function parseSecretEnv(raw) {
|
|
19956
19928
|
const out = {};
|
|
@@ -19982,17 +19954,17 @@ function importLegacyManifest(manifest) {
|
|
|
19982
19954
|
return { ...manifest, version: 2, deploymentId, desiredGeneration: 0 };
|
|
19983
19955
|
}
|
|
19984
19956
|
async function writePrivateFile(file, content) {
|
|
19985
|
-
await
|
|
19957
|
+
await fs13.mkdir(path14.dirname(file), { recursive: true, mode: 448 });
|
|
19986
19958
|
const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
19987
|
-
await
|
|
19988
|
-
await
|
|
19989
|
-
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);
|
|
19990
19962
|
}
|
|
19991
19963
|
|
|
19992
19964
|
// src/mlclaw/deployment-state.ts
|
|
19993
|
-
import
|
|
19965
|
+
import fs14 from "node:fs/promises";
|
|
19994
19966
|
import os7 from "node:os";
|
|
19995
|
-
import
|
|
19967
|
+
import path15 from "node:path";
|
|
19996
19968
|
import { randomUUID } from "node:crypto";
|
|
19997
19969
|
var DEPLOYMENT_PATH = ".mlclaw/deployment.json";
|
|
19998
19970
|
var DESIRED_STATE_PATH = ".mlclaw/desired-state.json";
|
|
@@ -20139,7 +20111,7 @@ function newOperation(manifest, now) {
|
|
|
20139
20111
|
}
|
|
20140
20112
|
async function writeOperation(root, client, operation) {
|
|
20141
20113
|
const parsed = operationSchema.parse(operation);
|
|
20142
|
-
const local =
|
|
20114
|
+
const local = path15.join(localConfigPaths(root).operationsDir, `${parsed.operationId}.json`);
|
|
20143
20115
|
await atomicPrivateWrite(local, stringify(parsed));
|
|
20144
20116
|
await client.uploadFiles([jsonBlob(`.mlclaw/operations/${parsed.operationId}.json`, parsed)]);
|
|
20145
20117
|
}
|
|
@@ -20155,13 +20127,13 @@ async function updateOperation(root, client, operation, state, now, detail) {
|
|
|
20155
20127
|
}
|
|
20156
20128
|
async function readResumableOperation(root, deploymentId, targetGeneration) {
|
|
20157
20129
|
const directory = localConfigPaths(root).operationsDir;
|
|
20158
|
-
const entries = await
|
|
20130
|
+
const entries = await fs14.readdir(directory, { withFileTypes: true }).catch((error) => {
|
|
20159
20131
|
if (error.code === "ENOENT") return [];
|
|
20160
20132
|
throw error;
|
|
20161
20133
|
});
|
|
20162
20134
|
const operations = await Promise.all(
|
|
20163
20135
|
entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => {
|
|
20164
|
-
const raw = await
|
|
20136
|
+
const raw = await fs14.readFile(path15.join(directory, entry.name), "utf8");
|
|
20165
20137
|
return operationSchema.parse(JSON.parse(raw));
|
|
20166
20138
|
})
|
|
20167
20139
|
);
|
|
@@ -20170,12 +20142,12 @@ async function readResumableOperation(root, deploymentId, targetGeneration) {
|
|
|
20170
20142
|
).sort((a, b2) => b2.updatedAt.localeCompare(a.updatedAt))[0] ?? null;
|
|
20171
20143
|
}
|
|
20172
20144
|
async function withDeploymentLock(root, deploymentId, task) {
|
|
20173
|
-
const file =
|
|
20174
|
-
await
|
|
20145
|
+
const file = path15.join(localConfigPaths(root).locksDir, `${deploymentId}.lock`);
|
|
20146
|
+
await fs14.mkdir(path15.dirname(file), { recursive: true, mode: 448 });
|
|
20175
20147
|
const token = randomUUID();
|
|
20176
20148
|
const lock = stringify({ pid: process.pid, host: os7.hostname(), token, createdAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
20177
20149
|
try {
|
|
20178
|
-
await
|
|
20150
|
+
await fs14.writeFile(file, lock, { flag: "wx", mode: 384 });
|
|
20179
20151
|
} catch (error) {
|
|
20180
20152
|
if (error.code !== "EEXIST") throw error;
|
|
20181
20153
|
if (!await replaceStaleLocalLock(file, lock)) {
|
|
@@ -20198,38 +20170,38 @@ async function withDeploymentLock(root, deploymentId, task) {
|
|
|
20198
20170
|
async function replaceStaleLocalLock(file, replacement) {
|
|
20199
20171
|
const guard = `${file}.reclaim`;
|
|
20200
20172
|
try {
|
|
20201
|
-
await
|
|
20173
|
+
await fs14.mkdir(guard);
|
|
20202
20174
|
} catch (error) {
|
|
20203
20175
|
if (error.code === "EEXIST") return false;
|
|
20204
20176
|
throw error;
|
|
20205
20177
|
}
|
|
20206
20178
|
try {
|
|
20207
|
-
const [raw, stat] = await Promise.all([
|
|
20179
|
+
const [raw, stat] = await Promise.all([fs14.readFile(file, "utf8"), fs14.stat(file)]);
|
|
20208
20180
|
const value = JSON.parse(raw);
|
|
20209
20181
|
if (value.host !== os7.hostname() || typeof value.pid !== "number") return false;
|
|
20210
20182
|
const createdAt = typeof value.createdAt === "string" ? Date.parse(value.createdAt) : Number.NaN;
|
|
20211
20183
|
const lastRefresh = Number.isFinite(createdAt) ? Math.max(createdAt, stat.mtimeMs) : stat.mtimeMs;
|
|
20212
20184
|
if (processIsAlive(value.pid) && Date.now() - lastRefresh <= LOCAL_LOCK_STALE_MS) return false;
|
|
20213
|
-
await
|
|
20214
|
-
await
|
|
20185
|
+
await fs14.rm(file);
|
|
20186
|
+
await fs14.writeFile(file, replacement, { flag: "wx", mode: 384 });
|
|
20215
20187
|
return true;
|
|
20216
20188
|
} catch {
|
|
20217
20189
|
return false;
|
|
20218
20190
|
} finally {
|
|
20219
|
-
await
|
|
20191
|
+
await fs14.rm(guard, { recursive: true, force: true });
|
|
20220
20192
|
}
|
|
20221
20193
|
}
|
|
20222
20194
|
async function refreshOwnedLocalLock(file, token) {
|
|
20223
20195
|
if (!await localLockHasToken(file, token)) return;
|
|
20224
20196
|
const now = /* @__PURE__ */ new Date();
|
|
20225
|
-
await
|
|
20197
|
+
await fs14.utimes(file, now, now);
|
|
20226
20198
|
}
|
|
20227
20199
|
async function removeOwnedLocalLock(file, token) {
|
|
20228
|
-
if (await localLockHasToken(file, token)) await
|
|
20200
|
+
if (await localLockHasToken(file, token)) await fs14.rm(file, { force: true });
|
|
20229
20201
|
}
|
|
20230
20202
|
async function localLockHasToken(file, token) {
|
|
20231
20203
|
try {
|
|
20232
|
-
const value = JSON.parse(await
|
|
20204
|
+
const value = JSON.parse(await fs14.readFile(file, "utf8"));
|
|
20233
20205
|
return value.token === token;
|
|
20234
20206
|
} catch {
|
|
20235
20207
|
return false;
|
|
@@ -20295,19 +20267,19 @@ async function readDocument(client, file, schema) {
|
|
|
20295
20267
|
if (blob.size > MAX_CONTROL_BYTES) throw new Error(`${file} exceeds ${MAX_CONTROL_BYTES} bytes`);
|
|
20296
20268
|
return schema.parse(JSON.parse(await blob.text()));
|
|
20297
20269
|
}
|
|
20298
|
-
function jsonBlob(
|
|
20299
|
-
return { path:
|
|
20270
|
+
function jsonBlob(path16, value) {
|
|
20271
|
+
return { path: path16, content: new Blob([stringify(value)], { type: "application/json" }) };
|
|
20300
20272
|
}
|
|
20301
20273
|
function stringify(value) {
|
|
20302
20274
|
return `${JSON.stringify(value, null, 2)}
|
|
20303
20275
|
`;
|
|
20304
20276
|
}
|
|
20305
20277
|
async function atomicPrivateWrite(file, content) {
|
|
20306
|
-
await
|
|
20278
|
+
await fs14.mkdir(path15.dirname(file), { recursive: true, mode: 448 });
|
|
20307
20279
|
const temporary = `${file}.${process.pid}.tmp`;
|
|
20308
|
-
await
|
|
20309
|
-
await
|
|
20310
|
-
await
|
|
20280
|
+
await fs14.writeFile(temporary, content, { mode: 384, flag: "wx" });
|
|
20281
|
+
await fs14.rename(temporary, file);
|
|
20282
|
+
await fs14.chmod(file, 384);
|
|
20311
20283
|
}
|
|
20312
20284
|
|
|
20313
20285
|
// src/mlclaw/telegram.ts
|
|
@@ -20347,116 +20319,13 @@ function delay(ms) {
|
|
|
20347
20319
|
|
|
20348
20320
|
// src/mlclaw/hf-broker-credential.ts
|
|
20349
20321
|
import { createHash as createHash3 } from "node:crypto";
|
|
20350
|
-
|
|
20351
|
-
|
|
20352
|
-
|
|
20353
|
-
|
|
20354
|
-
|
|
20355
|
-
token_form_url: "https://huggingface.co/settings/tokens/new",
|
|
20356
|
-
token_type: "fineGrained",
|
|
20357
|
-
requires_gated_repositories: true,
|
|
20358
|
-
personal_permissions: [
|
|
20359
|
-
"collection.read",
|
|
20360
|
-
"collection.write",
|
|
20361
|
-
"discussion.write",
|
|
20362
|
-
"inference.endpoints.infer.write",
|
|
20363
|
-
"inference.endpoints.write",
|
|
20364
|
-
"inference.serverless.write",
|
|
20365
|
-
"job.write",
|
|
20366
|
-
"repo.access.read",
|
|
20367
|
-
"repo.content.read",
|
|
20368
|
-
"repo.write",
|
|
20369
|
-
"sql-console.embed.write",
|
|
20370
|
-
"user.billing.read",
|
|
20371
|
-
"user.mcp.read",
|
|
20372
|
-
"user.notifications.read",
|
|
20373
|
-
"user.notifications.write",
|
|
20374
|
-
"user.papers.write",
|
|
20375
|
-
"user.preferences.write",
|
|
20376
|
-
"user.settings.notifications.write",
|
|
20377
|
-
"user.social.likes.write",
|
|
20378
|
-
"user.webhooks.read",
|
|
20379
|
-
"user.webhooks.write"
|
|
20380
|
-
],
|
|
20381
|
-
global_permissions: [
|
|
20382
|
-
"discussion.write",
|
|
20383
|
-
"post.write"
|
|
20384
|
-
],
|
|
20385
|
-
organization_permissions: [
|
|
20386
|
-
"collection.read",
|
|
20387
|
-
"collection.write",
|
|
20388
|
-
"discussion.write",
|
|
20389
|
-
"inference.endpoints.infer.write",
|
|
20390
|
-
"inference.endpoints.write",
|
|
20391
|
-
"inference.serverless.write",
|
|
20392
|
-
"job.write",
|
|
20393
|
-
"org.auditLog.write",
|
|
20394
|
-
"org.billing.read",
|
|
20395
|
-
"org.members.read",
|
|
20396
|
-
"org.members.write",
|
|
20397
|
-
"org.networkSecurity.read",
|
|
20398
|
-
"org.networkSecurity.write",
|
|
20399
|
-
"org.read",
|
|
20400
|
-
"org.repos.read",
|
|
20401
|
-
"org.serviceAccounts.read",
|
|
20402
|
-
"org.serviceAccounts.write",
|
|
20403
|
-
"org.write",
|
|
20404
|
-
"repo.access.read",
|
|
20405
|
-
"repo.content.read",
|
|
20406
|
-
"repo.write",
|
|
20407
|
-
"resourceGroup.write",
|
|
20408
|
-
"sql-console.embed.write"
|
|
20409
|
-
]
|
|
20410
|
-
};
|
|
20411
|
-
|
|
20412
|
-
// src/mlclaw/hf-broker-credential.ts
|
|
20413
|
-
var permissionListSchema = external_exports.array(external_exports.string().min(1)).min(1).superRefine((permissions, context) => {
|
|
20414
|
-
if (permissions.some((permission) => permission !== permission.trim())) {
|
|
20415
|
-
context.addIssue({ code: external_exports.ZodIssueCode.custom, message: "permissions must not contain outer whitespace" });
|
|
20416
|
-
}
|
|
20417
|
-
if (new Set(permissions).size !== permissions.length) {
|
|
20418
|
-
context.addIssue({ code: external_exports.ZodIssueCode.custom, message: "permissions must be unique" });
|
|
20419
|
-
}
|
|
20420
|
-
if (permissions.some((permission, index) => index > 0 && permission < permissions[index - 1])) {
|
|
20421
|
-
context.addIssue({ code: external_exports.ZodIssueCode.custom, message: "permissions must be sorted" });
|
|
20422
|
-
}
|
|
20423
|
-
});
|
|
20424
|
-
var profileSchema = external_exports.object({
|
|
20425
|
-
version: external_exports.literal(1),
|
|
20426
|
-
profile_id: external_exports.literal("hf-broker-complete-v1"),
|
|
20427
|
-
token_form_url: external_exports.literal("https://huggingface.co/settings/tokens/new"),
|
|
20428
|
-
token_type: external_exports.literal("fineGrained"),
|
|
20429
|
-
requires_gated_repositories: external_exports.literal(true),
|
|
20430
|
-
personal_permissions: permissionListSchema,
|
|
20431
|
-
global_permissions: permissionListSchema,
|
|
20432
|
-
organization_permissions: permissionListSchema
|
|
20433
|
-
}).strict();
|
|
20434
|
-
var BROKER_CREDENTIAL_PROFILE = Object.freeze(profileSchema.parse(hf_broker_credential_requirements_default));
|
|
20435
|
-
var HF_TOKEN_CREATE_URL = BROKER_CREDENTIAL_PROFILE.token_form_url;
|
|
20436
|
-
var BROKER_PERSONAL_PERMISSIONS = BROKER_CREDENTIAL_PROFILE.personal_permissions;
|
|
20437
|
-
var BROKER_GLOBAL_PERMISSIONS = BROKER_CREDENTIAL_PROFILE.global_permissions;
|
|
20438
|
-
var BROKER_ORGANIZATION_PERMISSIONS = BROKER_CREDENTIAL_PROFILE.organization_permissions;
|
|
20439
|
-
function buildBrokerTokenUrl(owner, accountName) {
|
|
20440
|
-
const url = new URL(HF_TOKEN_CREATE_URL);
|
|
20441
|
-
url.searchParams.set("tokenType", BROKER_CREDENTIAL_PROFILE.token_type);
|
|
20442
|
-
for (const permission of BROKER_PERSONAL_PERMISSIONS) {
|
|
20443
|
-
url.searchParams.append("ownUserPermissions", permission);
|
|
20444
|
-
}
|
|
20445
|
-
for (const permission of BROKER_GLOBAL_PERMISSIONS) {
|
|
20446
|
-
url.searchParams.append("globalPermissions", permission);
|
|
20447
|
-
}
|
|
20448
|
-
url.searchParams.set("canReadGatedRepos", String(BROKER_CREDENTIAL_PROFILE.requires_gated_repositories));
|
|
20449
|
-
if (owner !== accountName) {
|
|
20450
|
-
url.searchParams.append("orgs", owner);
|
|
20451
|
-
for (const permission of BROKER_ORGANIZATION_PERMISSIONS) {
|
|
20452
|
-
url.searchParams.append("orgPermissions", permission);
|
|
20453
|
-
}
|
|
20454
|
-
}
|
|
20455
|
-
return url.toString();
|
|
20456
|
-
}
|
|
20457
|
-
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) {
|
|
20458
20327
|
const accessToken = identity.auth?.accessToken;
|
|
20459
|
-
if (accessToken?.role !==
|
|
20328
|
+
if (accessToken?.role !== "fineGrained") {
|
|
20460
20329
|
return {
|
|
20461
20330
|
status: "unsupported",
|
|
20462
20331
|
reason: "HF Broker requires a dedicated fine-grained Hugging Face token"
|
|
@@ -20468,40 +20337,16 @@ function assessBrokerCredential(identity, owner) {
|
|
|
20468
20337
|
reason: "Hugging Face omitted this fine-grained token's permission details"
|
|
20469
20338
|
};
|
|
20470
20339
|
}
|
|
20471
|
-
|
|
20472
|
-
const globalAvailable = new Set(accessToken.fineGrained.global);
|
|
20473
|
-
const missing = BROKER_PERSONAL_PERMISSIONS.filter((permission) => !personalAvailable.has(permission));
|
|
20474
|
-
missing.push(
|
|
20475
|
-
...BROKER_GLOBAL_PERMISSIONS.filter((permission) => !globalAvailable.has(permission)).map(
|
|
20476
|
-
(permission) => `global:${permission}`
|
|
20477
|
-
)
|
|
20478
|
-
);
|
|
20479
|
-
if (!accessToken.fineGrained.canReadGatedRepos) {
|
|
20480
|
-
missing.push("canReadGatedRepos");
|
|
20481
|
-
}
|
|
20482
|
-
if (owner !== identity.name) {
|
|
20483
|
-
const organizationAvailable = new Set(scopedPermissions(accessToken.fineGrained.scoped, "org", owner));
|
|
20484
|
-
missing.push(
|
|
20485
|
-
...BROKER_ORGANIZATION_PERMISSIONS.filter((permission) => !organizationAvailable.has(permission)).map(
|
|
20486
|
-
(permission) => `org:${permission}`
|
|
20487
|
-
)
|
|
20488
|
-
);
|
|
20489
|
-
}
|
|
20490
|
-
missing.sort();
|
|
20491
|
-
return missing.length === 0 ? { status: "sufficient" } : { status: "insufficient", missing };
|
|
20340
|
+
return { status: "sufficient" };
|
|
20492
20341
|
}
|
|
20493
20342
|
function brokerCredentialMetadata(token, identity, verifiedAt) {
|
|
20494
20343
|
return {
|
|
20495
|
-
|
|
20344
|
+
credentialKind: "fine_grained_user_token",
|
|
20496
20345
|
account: identity.name,
|
|
20497
20346
|
fingerprintSha256: createHash3("sha256").update(token).digest("hex"),
|
|
20498
20347
|
verifiedAt: verifiedAt.toISOString()
|
|
20499
20348
|
};
|
|
20500
20349
|
}
|
|
20501
|
-
function scopedPermissions(scopes, type, name) {
|
|
20502
|
-
if (!Array.isArray(scopes)) return [];
|
|
20503
|
-
return scopes.filter((scope) => scope.entity.type === type && (!scope.entity.name || scope.entity.name === name)).flatMap((scope) => scope.permissions);
|
|
20504
|
-
}
|
|
20505
20350
|
|
|
20506
20351
|
// src/mlclaw/tailscale.ts
|
|
20507
20352
|
import { execFile as execFile3 } from "node:child_process";
|
|
@@ -21492,7 +21337,6 @@ async function resolveBootstrapPlan(params) {
|
|
|
21492
21337
|
const existingSecrets = await readSecretEnv(runtime.configRoot, agentName).catch(() => ({}));
|
|
21493
21338
|
const brokerCredential = await resolveBrokerHfToken({
|
|
21494
21339
|
opts,
|
|
21495
|
-
owner,
|
|
21496
21340
|
hfIdentity,
|
|
21497
21341
|
...providedBrokerHfToken ? { preferredToken: providedBrokerHfToken } : {},
|
|
21498
21342
|
existingSecrets,
|
|
@@ -22236,7 +22080,7 @@ async function deployLocalBootstrap(plan, opts, runtime, desiredChanged = true,
|
|
|
22236
22080
|
await writeSecretEnv(runtime.configRoot, plan.agentName, previousSecrets);
|
|
22237
22081
|
} else {
|
|
22238
22082
|
await assertLease();
|
|
22239
|
-
await
|
|
22083
|
+
await fs15.rm(secretEnvPath(runtime.configRoot, plan.agentName), { force: true });
|
|
22240
22084
|
}
|
|
22241
22085
|
if (previousContainer?.running && previousManifest) {
|
|
22242
22086
|
await startLocalGateway({ manifest: previousManifest, runtime, pull: false, refresh: true, assertLease });
|
|
@@ -22265,7 +22109,7 @@ async function deployLocalBootstrap(plan, opts, runtime, desiredChanged = true,
|
|
|
22265
22109
|
}
|
|
22266
22110
|
if (!previousManifest) {
|
|
22267
22111
|
await assertLease();
|
|
22268
|
-
await
|
|
22112
|
+
await fs15.rm(manifestPath(runtime.configRoot, plan.agentName), { force: true });
|
|
22269
22113
|
}
|
|
22270
22114
|
} catch (rollbackError) {
|
|
22271
22115
|
throw new AggregateError([error, rollbackError], "local bootstrap and rollback both failed");
|
|
@@ -24015,7 +23859,7 @@ async function readOptionalTelegramToken(opts, runtime) {
|
|
|
24015
23859
|
return direct;
|
|
24016
23860
|
}
|
|
24017
23861
|
if (opts.telegramTokenFile) {
|
|
24018
|
-
const raw = await
|
|
23862
|
+
const raw = await fs15.readFile(opts.telegramTokenFile, "utf8");
|
|
24019
23863
|
const match = raw.match(/(?:^|\n)\s*TELEGRAM_BOT_TOKEN\s*=\s*['"]?([^'"\n]+)['"]?/);
|
|
24020
23864
|
return (match?.[1] ?? raw.trim()).trim();
|
|
24021
23865
|
}
|
|
@@ -24037,7 +23881,7 @@ async function readOptionalRouterTokenFile(file) {
|
|
|
24037
23881
|
if (!file) {
|
|
24038
23882
|
return void 0;
|
|
24039
23883
|
}
|
|
24040
|
-
const raw = await
|
|
23884
|
+
const raw = await fs15.readFile(file, "utf8");
|
|
24041
23885
|
const parsed = parseSecretEnv(raw);
|
|
24042
23886
|
return nonEmpty(parsed.MLCLAW_ROUTER_TOKEN) ?? nonEmpty(parsed.HF_ROUTER_TOKEN) ?? nonEmpty(raw);
|
|
24043
23887
|
}
|
|
@@ -24048,7 +23892,7 @@ async function credentialsStatus(requestedAgent, runtime) {
|
|
|
24048
23892
|
if (!metadata) throw new Error("verified HF Broker credential metadata is missing");
|
|
24049
23893
|
runtime.stdout.log(`Agent: ${manifest.agent}`);
|
|
24050
23894
|
runtime.stdout.log(`Status: healthy`);
|
|
24051
|
-
runtime.stdout.log(`
|
|
23895
|
+
runtime.stdout.log(`Credential kind: ${metadata.credentialKind}`);
|
|
24052
23896
|
runtime.stdout.log(`Account: ${metadata.account}`);
|
|
24053
23897
|
runtime.stdout.log(`Fingerprint: ${metadata.fingerprintSha256.slice(0, 12)}`);
|
|
24054
23898
|
runtime.stdout.log(`Verified: ${metadata.verifiedAt}`);
|
|
@@ -24062,7 +23906,7 @@ async function verifiedStoredBrokerCredential(manifest, runtime) {
|
|
|
24062
23906
|
`HF Broker credential metadata is missing; run \`mlclaw credentials repair ${manifest.agent}\` to complete the cutover`
|
|
24063
23907
|
);
|
|
24064
23908
|
}
|
|
24065
|
-
const verified = await verifyBrokerHfToken(token, manifest.
|
|
23909
|
+
const verified = await verifyBrokerHfToken(token, manifest.brokerCredential.account, runtime);
|
|
24066
23910
|
const observed = brokerCredentialMetadata(token, verified.identity, runtime.now());
|
|
24067
23911
|
if (observed.fingerprintSha256 !== manifest.brokerCredential.fingerprintSha256) {
|
|
24068
23912
|
throw new Error(
|
|
@@ -24082,14 +23926,14 @@ async function credentialsRepair(requestedAgent, opts, runtime) {
|
|
|
24082
23926
|
const suppliedToken = fileToken ?? nonEmpty(runtime.env.MLCLAW_BROKER_HF_TOKEN);
|
|
24083
23927
|
let replacement;
|
|
24084
23928
|
if (suppliedToken) {
|
|
24085
|
-
replacement = await verifyBrokerHfToken(suppliedToken,
|
|
23929
|
+
replacement = await verifyBrokerHfToken(suppliedToken, account, runtime);
|
|
24086
23930
|
} else {
|
|
24087
23931
|
if (!runtime.prompt.isInteractive()) {
|
|
24088
23932
|
throw new Error(
|
|
24089
23933
|
"credential repair requires --broker-hf-token-file, MLCLAW_BROKER_HF_TOKEN, or an interactive terminal"
|
|
24090
23934
|
);
|
|
24091
23935
|
}
|
|
24092
|
-
replacement = await promptForBrokerHfToken(
|
|
23936
|
+
replacement = await promptForBrokerHfToken(account, runtime);
|
|
24093
23937
|
}
|
|
24094
23938
|
const updatedManifest = {
|
|
24095
23939
|
...manifest,
|
|
@@ -24260,7 +24104,7 @@ async function resolveBrokerHfToken(params) {
|
|
|
24260
24104
|
const configuredToken = fileToken ?? environmentToken ?? nonEmpty(params.preferredToken) ?? nonEmpty(params.existingSecrets.MLCLAW_BROKER_HF_TOKEN);
|
|
24261
24105
|
if (configuredToken) {
|
|
24262
24106
|
try {
|
|
24263
|
-
return await verifyBrokerHfToken(configuredToken, params.
|
|
24107
|
+
return await verifyBrokerHfToken(configuredToken, params.hfIdentity.name, params.runtime);
|
|
24264
24108
|
} catch (error) {
|
|
24265
24109
|
if (fileToken || environmentToken || params.preferredToken) throw error;
|
|
24266
24110
|
if (params.runtime.prompt.isInteractive()) {
|
|
@@ -24280,19 +24124,19 @@ async function resolveBrokerHfToken(params) {
|
|
|
24280
24124
|
"a dedicated HF Broker credential is required; set MLCLAW_BROKER_HF_TOKEN, pass --broker-hf-token-file, or run bootstrap interactively"
|
|
24281
24125
|
);
|
|
24282
24126
|
}
|
|
24283
|
-
return await promptForBrokerHfToken(params.
|
|
24127
|
+
return await promptForBrokerHfToken(params.hfIdentity.name, params.runtime);
|
|
24284
24128
|
}
|
|
24285
|
-
async function promptForBrokerHfToken(
|
|
24129
|
+
async function promptForBrokerHfToken(account, runtime) {
|
|
24286
24130
|
runtime.prompt.note(
|
|
24287
|
-
"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.",
|
|
24288
24132
|
"HF Broker credential"
|
|
24289
24133
|
);
|
|
24290
|
-
const url = buildBrokerTokenUrl(
|
|
24134
|
+
const url = buildBrokerTokenUrl();
|
|
24291
24135
|
const opened = await runtime.hfCli.openUrl(url);
|
|
24292
24136
|
runtime.prompt.note(
|
|
24293
24137
|
`${opened ? "The token form was opened in your browser." : "Open this token form in your browser."}
|
|
24294
24138
|
|
|
24295
|
-
|
|
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.`,
|
|
24296
24140
|
"Create the broker token"
|
|
24297
24141
|
);
|
|
24298
24142
|
runtime.stdout.log(url);
|
|
@@ -24302,7 +24146,7 @@ Name and create the token, then copy it. The URL contains permission names only;
|
|
|
24302
24146
|
"Hugging Face broker token"
|
|
24303
24147
|
);
|
|
24304
24148
|
try {
|
|
24305
|
-
const verified = await verifyBrokerHfToken(replacement,
|
|
24149
|
+
const verified = await verifyBrokerHfToken(replacement, account, runtime);
|
|
24306
24150
|
runtime.prompt.note(
|
|
24307
24151
|
"The dedicated broker token was verified. It will be stored only in ML Claw's trusted broker configuration.",
|
|
24308
24152
|
"HF Broker credential ready"
|
|
@@ -24316,12 +24160,12 @@ Name and create the token, then copy it. The URL contains permission names only;
|
|
|
24316
24160
|
}
|
|
24317
24161
|
}
|
|
24318
24162
|
}
|
|
24319
|
-
async function verifyBrokerHfToken(token,
|
|
24163
|
+
async function verifyBrokerHfToken(token, expectedAccount, runtime) {
|
|
24320
24164
|
const identity = await runtime.hubFactory(token).whoami();
|
|
24321
24165
|
if (identity.name !== expectedAccount) {
|
|
24322
24166
|
throw new Error(`broker token belongs to ${identity.name}, not ${expectedAccount}`);
|
|
24323
24167
|
}
|
|
24324
|
-
const assessment = assessBrokerCredential(identity
|
|
24168
|
+
const assessment = assessBrokerCredential(identity);
|
|
24325
24169
|
if (assessment.status !== "sufficient") {
|
|
24326
24170
|
throw new Error(brokerCredentialAssessmentDetail(assessment));
|
|
24327
24171
|
}
|
|
@@ -24329,17 +24173,14 @@ async function verifyBrokerHfToken(token, owner, expectedAccount, runtime) {
|
|
|
24329
24173
|
}
|
|
24330
24174
|
async function readOptionalBrokerHfTokenFile(file) {
|
|
24331
24175
|
if (!file) return void 0;
|
|
24332
|
-
const raw = await
|
|
24176
|
+
const raw = await fs15.readFile(file, "utf8");
|
|
24333
24177
|
const parsed = parseSecretEnv(raw);
|
|
24334
24178
|
const token = nonEmpty(parsed.MLCLAW_BROKER_HF_TOKEN) ?? nonEmpty(raw);
|
|
24335
24179
|
if (!token) throw new Error("HF Broker token file is empty");
|
|
24336
24180
|
return token;
|
|
24337
24181
|
}
|
|
24338
24182
|
function brokerCredentialAssessmentDetail(assessment) {
|
|
24339
|
-
|
|
24340
|
-
const shown = assessment.missing.slice(0, 8);
|
|
24341
|
-
const remaining = assessment.missing.length - shown.length;
|
|
24342
|
-
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;
|
|
24343
24184
|
}
|
|
24344
24185
|
function errorMessage(error) {
|
|
24345
24186
|
return error instanceof Error ? error.message : String(error);
|