Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ ARG OPENCLAW_VERSION=2026.7.1
ARG OPENCLAW_BASE_IMAGE=ghcr.io/openclaw/openclaw:${OPENCLAW_VERSION}
ARG BROKERKIT_PLUGIN_VERSION=0.3.4
ARG BROKERKIT_VERSION=hf-broker/v0.4.2
ARG MLCLAW_RUNTIME_IMAGE=ghcr.io/huggingface/mlclaw:0.4.8-openclaw-2026.7.1
ARG MLCLAW_RUNTIME_IMAGE=ghcr.io/huggingface/mlclaw:0.4.9-openclaw-2026.7.1

FROM golang:1.26.5-bookworm AS hf-broker-build
ARG BROKERKIT_VERSION
Expand Down
37 changes: 34 additions & 3 deletions dist/mlclaw.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15383,7 +15383,7 @@ function nextLink(header) {

// src/mlclaw/release-config.generated.ts
var RELEASE_CONFIG = {
"packageVersion": "0.4.8",
"packageVersion": "0.4.9",
"openclawVersion": "2026.7.1",
"brokerkitVersion": "hf-broker/v0.4.2",
"brokerkitPluginVersion": "0.3.4",
Expand Down Expand Up @@ -23346,8 +23346,9 @@ async function update(repoId, opts, hub, hfToken, runtime) {
}
const runtimeImage = resolveSpaceRuntimeImage(opts, runtime.env);
const agentName = variables.get("OPENCLAW_AGENT_NAME")?.value?.trim() || repoId.split("/")[1] || "openclaw";
let localManifest;
if (!canonicalTemplate) {
await ensureUpdateCredentials({
localManifest = await ensureUpdateCredentials({
repoId,
agentName,
model: variables.get("OPENCLAW_MODEL")?.value ?? DEFAULT_MODEL2,
Expand All @@ -23362,8 +23363,9 @@ async function update(repoId, opts, hub, hfToken, runtime) {
token: hfToken,
...runtimeImage ? { runtimeImage } : {}
});
const effectiveRuntimeImage = runtimeImage ?? bundledSpaceRuntimeRef(templateRev);
await hub.addSpaceVariable(repoId, "MLCLAW_TEMPLATE_REV", templateRev);
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_IMAGE", runtimeImage ?? bundledSpaceRuntimeRef(templateRev));
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_IMAGE", effectiveRuntimeImage);
if (canonicalTemplate) {
await hub.addSpaceVariable(repoId, "MLCLAW_CANONICAL_SPACE_ID", canonicalTemplateSpaceId(runtime.env));
await doctor(repoId, { fix: true }, hub, runtime);
Expand All @@ -23382,8 +23384,36 @@ async function update(repoId, opts, hub, hfToken, runtime) {
await ensureSpaceStateVolume(hub, repoId, bucket);
}
await doctor(repoId, { fix: true }, hub, runtime);
if (!localManifest) {
throw new Error(`local deployment metadata is unavailable for ${repoId}`);
}
await reconcileUpdatedDeployment(localManifest, effectiveRuntimeImage, hub, runtime);
runtime.stdout.log(`Space deployment triggered: ${repoId}`);
}
async function reconcileUpdatedDeployment(localManifest, runtimeImage, hub, runtime) {
const secrets = await readSecretEnv(runtime.configRoot, localManifest.agent);
await reconcileManifest({
manifest: {
...localManifest,
gatewayLocation: "space",
runtimeImage,
updatedAt: runtime.now().toISOString()
},
bucketPrefix: persistedBucketPrefix(secrets),
hub,
runtime,
apply: async ({ manifest, assertLease }) => {
await assertLease();
const currentSecrets = await readSecretEnv(runtime.configRoot, manifest.agent);
await writeLocalDeployment(runtime.configRoot, manifest, {
...currentSecrets,
MLCLAW_GATEWAY_LOCATION: "space",
MLCLAW_RUNTIME_IMAGE: runtimeImage,
MLCLAW_RUNTIME_ID: spaceRuntimeId(manifest.agent)
});
}
});
}
async function ensureUpdateCredentials(params) {
const hasExplicitOverride = params.opts.routerToken !== void 0 || params.opts.routerTokenFile !== void 0;
const hasManifest = await manifestExists(params.runtime.configRoot, params.agentName);
Expand Down Expand Up @@ -23417,6 +23447,7 @@ async function ensureUpdateCredentials(params) {
MLCLAW_BROKER_HF_TOKEN: brokerCredential.token,
...routerToken ? { MLCLAW_ROUTER_TOKEN: routerToken } : {}
});
return localManifest;
}
async function doctor(repoId, opts, hub, runtime) {
if (!repoId.includes("/") && await manifestExists(runtime.configRoot, repoId)) {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mlclaw",
"version": "0.4.8",
"version": "0.4.9",
"license": "MIT",
"config": {
"openclawVersion": "2026.7.1",
Expand Down
43 changes: 40 additions & 3 deletions src/mlclaw/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3790,8 +3790,9 @@ async function update(
}
const runtimeImage = resolveSpaceRuntimeImage(opts, runtime.env);
const agentName = variables.get("OPENCLAW_AGENT_NAME")?.value?.trim() || repoId.split("/")[1] || "openclaw";
let localManifest: DeploymentManifest | undefined;
if (!canonicalTemplate) {
await ensureUpdateCredentials({
localManifest = await ensureUpdateCredentials({
repoId,
agentName,
model: variables.get("OPENCLAW_MODEL")?.value ?? DEFAULT_MODEL,
Expand All @@ -3806,8 +3807,9 @@ async function update(
token: hfToken,
...(runtimeImage ? { runtimeImage } : {}),
});
const effectiveRuntimeImage = runtimeImage ?? bundledSpaceRuntimeRef(templateRev);
await hub.addSpaceVariable(repoId, "MLCLAW_TEMPLATE_REV", templateRev);
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_IMAGE", runtimeImage ?? bundledSpaceRuntimeRef(templateRev));
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_IMAGE", effectiveRuntimeImage);
if (canonicalTemplate) {
await hub.addSpaceVariable(repoId, "MLCLAW_CANONICAL_SPACE_ID", canonicalTemplateSpaceId(runtime.env));
await doctor(repoId, { fix: true }, hub, runtime);
Expand All @@ -3826,17 +3828,51 @@ async function update(
await ensureSpaceStateVolume(hub, repoId, bucket);
}
await doctor(repoId, { fix: true }, hub, runtime);
if (!localManifest) {
throw new Error(`local deployment metadata is unavailable for ${repoId}`);
}
await reconcileUpdatedDeployment(localManifest, effectiveRuntimeImage, hub, runtime);
runtime.stdout.log(`Space deployment triggered: ${repoId}`);
}

async function reconcileUpdatedDeployment(
localManifest: DeploymentManifest,
runtimeImage: string,
hub: HubApi,
runtime: Required<CliRuntime>,
): Promise<void> {
const secrets = await readSecretEnv(runtime.configRoot, localManifest.agent);
await reconcileManifest({
manifest: {
...localManifest,
gatewayLocation: "space",
runtimeImage,
updatedAt: runtime.now().toISOString(),
},
bucketPrefix: persistedBucketPrefix(secrets),
hub,
runtime,
apply: async ({ manifest, assertLease }) => {
await assertLease();
const currentSecrets = await readSecretEnv(runtime.configRoot, manifest.agent);
await writeLocalDeployment(runtime.configRoot, manifest, {
...currentSecrets,
MLCLAW_GATEWAY_LOCATION: "space",
MLCLAW_RUNTIME_IMAGE: runtimeImage,
MLCLAW_RUNTIME_ID: spaceRuntimeId(manifest.agent),
});
},
});
}

async function ensureUpdateCredentials(params: {
repoId: string;
agentName: string;
model: string;
opts: UpdateOptions;
hub: HubApi;
runtime: Required<CliRuntime>;
}): Promise<void> {
}): Promise<DeploymentManifest> {
const hasExplicitOverride = params.opts.routerToken !== undefined || params.opts.routerTokenFile !== undefined;
const hasManifest = await manifestExists(params.runtime.configRoot, params.agentName);
if (!hasManifest) {
Expand Down Expand Up @@ -3871,6 +3907,7 @@ async function ensureUpdateCredentials(params: {
MLCLAW_BROKER_HF_TOKEN: brokerCredential.token,
...(routerToken ? { MLCLAW_ROUTER_TOKEN: routerToken } : {}),
});
return localManifest;
}

async function doctor(repoId: string, opts: DoctorOptions, hub: HubApi, runtime: Required<CliRuntime>): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion src/mlclaw/release-config.generated.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Generated from package.json by scripts/sync-release-config.mjs. Do not edit.
export const RELEASE_CONFIG = {
"packageVersion": "0.4.8",
"packageVersion": "0.4.9",
"openclawVersion": "2026.7.1",
"brokerkitVersion": "hf-broker/v0.4.2",
"brokerkitPluginVersion": "0.3.4",
Expand Down
28 changes: 28 additions & 0 deletions test/mlclaw.cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3108,6 +3108,20 @@ describe("mlclaw CLI", () => {
expect(allowedUsersIndex).toBeGreaterThanOrEqual(0);
expect(adminsIndex).toBeGreaterThanOrEqual(0);
expect(hub.calls.some((call) => call.name === "restartSpace")).toBe(false);
await expect(readManifest(runtime.configRoot, "research")).resolves.toMatchObject({
desiredGeneration: 1,
gatewayLocation: "space",
runtimeImage: DEFAULT_RUNTIME_IMAGE,
});
await expect(readSecretEnv(runtime.configRoot, "research")).resolves.toMatchObject({
MLCLAW_GATEWAY_LOCATION: "space",
MLCLAW_RUNTIME_IMAGE: DEFAULT_RUNTIME_IMAGE,
MLCLAW_RUNTIME_ID: "space-research",
});
expect(JSON.parse(hub.bucketObjects.get(".mlclaw/desired-state.json") ?? "null")).toMatchObject({
generation: 1,
runtimeImage: DEFAULT_RUNTIME_IMAGE,
});
});

it("upgrades a legacy Router Space to the broker credential during update", async () => {
Expand Down Expand Up @@ -3239,6 +3253,13 @@ describe("mlclaw CLI", () => {
name: "addSpaceVariable",
args: ["alice/research", "MLCLAW_RUNTIME_IMAGE", "bundled:test-template"],
});
await expect(readManifest(runtime.configRoot, "research")).resolves.toMatchObject({
desiredGeneration: 1,
runtimeImage: "bundled:test-template",
});
await expect(readSecretEnv(runtime.configRoot, "research")).resolves.toMatchObject({
MLCLAW_RUNTIME_IMAGE: "bundled:test-template",
});
});

it("honors an explicit runtime image override during update", async () => {
Expand Down Expand Up @@ -3273,6 +3294,13 @@ describe("mlclaw CLI", () => {
name: "addSpaceVariable",
args: ["alice/research", "MLCLAW_RUNTIME_ID", "space-research"],
});
await expect(readManifest(runtime.configRoot, "research")).resolves.toMatchObject({
desiredGeneration: 1,
runtimeImage: "registry.example/mlclaw:new",
});
await expect(readSecretEnv(runtime.configRoot, "research")).resolves.toMatchObject({
MLCLAW_RUNTIME_IMAGE: "registry.example/mlclaw:new",
});
});

it("updates the canonical template Space without deployment-only repairs", async () => {
Expand Down
Loading