mlclaw 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/mlclaw/SKILL.md +58 -19
- package/Dockerfile +47 -8
- package/README.md +75 -17
- package/assets/mlclaw-control-ui/assets/index-92PjW54g.js +9 -0
- package/assets/mlclaw-control-ui/assets/index-D1bHaYpf.css +1 -0
- package/assets/mlclaw-control-ui/index.html +2 -2
- package/dist/hf-state-sync.js +438 -76
- package/dist/hf-tooling-seed.js +64 -11
- package/dist/mlclaw-space-runtime.js +8403 -5935
- package/dist/mlclaw.mjs +407 -42
- package/entrypoint.sh +167 -17
- package/hf-broker.scope.json +38 -0
- package/package.json +26 -2
- package/space/README.md +14 -4
- package/src/hf-state-sync/archive.ts +15 -8
- package/src/hf-state-sync/cli.ts +33 -6
- package/src/hf-state-sync/hub.ts +82 -1
- package/src/hf-state-sync/paths.ts +23 -4
- package/src/hf-state-sync/prepare.ts +79 -0
- package/src/hf-state-sync/snapshot.ts +31 -9
- package/src/hf-state-sync/stage-worker.ts +165 -0
- package/src/hf-state-sync/supervise.ts +29 -10
- package/src/vendor/hfjs-xet/utils/ChunkCache.ts +1 -1
- package/src/vendor/hfjs-xet/utils/XetBlob.ts +2 -2
- package/src/vendor/hfjs-xet/utils/createXorbs.ts +2 -2
- package/assets/mlclaw-control-ui/assets/index-D2TFes32.js +0 -9
- package/assets/mlclaw-control-ui/assets/index-DP72PFuv.css +0 -1
package/dist/mlclaw.mjs
CHANGED
|
@@ -14629,6 +14629,20 @@ var HubApi = class {
|
|
|
14629
14629
|
const raw = await this.requestJson(`/api/spaces/${repoId}/secrets`);
|
|
14630
14630
|
return new Map(Object.entries(raw));
|
|
14631
14631
|
}
|
|
14632
|
+
async deleteSpaceSecret(repoId, key) {
|
|
14633
|
+
try {
|
|
14634
|
+
await this.requestJson(`/api/spaces/${repoId}/secrets`, {
|
|
14635
|
+
method: "DELETE",
|
|
14636
|
+
body: JSON.stringify({ key }),
|
|
14637
|
+
headers: { "Content-Type": "application/json" }
|
|
14638
|
+
});
|
|
14639
|
+
} catch (err) {
|
|
14640
|
+
if (err instanceof HubApiError2 && err.status === 404) {
|
|
14641
|
+
return;
|
|
14642
|
+
}
|
|
14643
|
+
throw err;
|
|
14644
|
+
}
|
|
14645
|
+
}
|
|
14632
14646
|
async restartSpace(repoId, factoryReboot = false) {
|
|
14633
14647
|
await this.requestJson(`/api/spaces/${repoId}/restart`, {
|
|
14634
14648
|
method: "POST",
|
|
@@ -14660,7 +14674,25 @@ var HubApi = class {
|
|
|
14660
14674
|
});
|
|
14661
14675
|
}
|
|
14662
14676
|
async getSpaceRuntime(repoId) {
|
|
14663
|
-
|
|
14677
|
+
const runtime = await this.requestJson(`/api/spaces/${repoId}/runtime`);
|
|
14678
|
+
if (Array.isArray(runtime.volumes)) {
|
|
14679
|
+
return runtime;
|
|
14680
|
+
}
|
|
14681
|
+
try {
|
|
14682
|
+
const info = await this.requestJson(`/api/spaces/${repoId}`);
|
|
14683
|
+
if (Array.isArray(info.runtime?.volumes)) {
|
|
14684
|
+
return { ...runtime, volumes: info.runtime.volumes };
|
|
14685
|
+
}
|
|
14686
|
+
} catch {
|
|
14687
|
+
}
|
|
14688
|
+
return runtime;
|
|
14689
|
+
}
|
|
14690
|
+
async setSpaceVolumes(repoId, volumes) {
|
|
14691
|
+
await this.requestJson(`/api/spaces/${repoId}/volumes`, {
|
|
14692
|
+
method: "PUT",
|
|
14693
|
+
body: JSON.stringify({ volumes }),
|
|
14694
|
+
headers: { "Content-Type": "application/json" }
|
|
14695
|
+
});
|
|
14664
14696
|
}
|
|
14665
14697
|
async fetchSpaceLogs(repoId, kind = "run") {
|
|
14666
14698
|
const url = `${this.hubUrl}/api/spaces/${repoId}/logs/${kind}`;
|
|
@@ -14804,12 +14836,19 @@ function sseDataToText(raw) {
|
|
|
14804
14836
|
import fs11 from "node:fs";
|
|
14805
14837
|
import path12 from "node:path";
|
|
14806
14838
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
14807
|
-
var DEFAULT_OPENCLAW_VERSION = "2026.7.1-beta.
|
|
14839
|
+
var DEFAULT_OPENCLAW_VERSION = "2026.7.1-beta.5";
|
|
14840
|
+
var DEFAULT_BROKERKIT_PLUGIN_VERSION = "0.1.0";
|
|
14841
|
+
var DEFAULT_BROKERKIT_VERSION = "fcfc5f2304436d952b18e7c9583bb378d1952776";
|
|
14808
14842
|
var DEFAULT_RUNTIME_IMAGE_REPOSITORY = "ghcr.io/osolmaz/mlclaw";
|
|
14809
14843
|
var PACKAGE_METADATA = readPackageMetadata();
|
|
14810
14844
|
var PACKAGE_VERSION = packageString("version", "unknown");
|
|
14811
14845
|
var OPENCLAW_VERSION = packageConfigString("openclawVersion", DEFAULT_OPENCLAW_VERSION);
|
|
14812
14846
|
var OPENCLAW_BASE_IMAGE = `ghcr.io/openclaw/openclaw:${OPENCLAW_VERSION}`;
|
|
14847
|
+
var BROKERKIT_PLUGIN_VERSION = packageConfigString(
|
|
14848
|
+
"brokerkitPluginVersion",
|
|
14849
|
+
DEFAULT_BROKERKIT_PLUGIN_VERSION
|
|
14850
|
+
);
|
|
14851
|
+
var BROKERKIT_VERSION = packageConfigString("brokerkitVersion", DEFAULT_BROKERKIT_VERSION);
|
|
14813
14852
|
var RUNTIME_IMAGE_REPOSITORY = packageConfigString("runtimeImageRepository", DEFAULT_RUNTIME_IMAGE_REPOSITORY);
|
|
14814
14853
|
var DEFAULT_RUNTIME_IMAGE_TAG = `${PACKAGE_VERSION}-openclaw-${OPENCLAW_VERSION}`;
|
|
14815
14854
|
var DEFAULT_RUNTIME_IMAGE = `${RUNTIME_IMAGE_REPOSITORY}:${DEFAULT_RUNTIME_IMAGE_TAG}`;
|
|
@@ -14916,6 +14955,7 @@ async function generateSpaceRepo(sourceDir, outDir, options = {}) {
|
|
|
14916
14955
|
["dist/hf-tooling-seed.js", "runtime/hf-tooling-seed.js"],
|
|
14917
14956
|
["dist/mlclaw-space-runtime.js", "runtime/mlclaw-space-runtime.js"],
|
|
14918
14957
|
["entrypoint.sh", "runtime/entrypoint.sh"],
|
|
14958
|
+
["hf-broker.scope.json", "runtime/hf-broker.scope.json"],
|
|
14919
14959
|
["openclaw.default.json", "runtime/openclaw.default.json"],
|
|
14920
14960
|
["scripts/configure-huggingface-model.mjs", "runtime/scripts/configure-huggingface-model.mjs"],
|
|
14921
14961
|
["scripts/configure-telegram.mjs", "runtime/scripts/configure-telegram.mjs"],
|
|
@@ -14936,14 +14976,34 @@ function imageDockerfile(runtimeImage) {
|
|
|
14936
14976
|
`;
|
|
14937
14977
|
}
|
|
14938
14978
|
function bundledDockerfile() {
|
|
14939
|
-
return `
|
|
14979
|
+
return `ARG HF_BROKER_VERSION=bb65192b4dca845289427e63e1d5fa72f64914d8
|
|
14980
|
+
ARG BROKERKIT_PLUGIN_VERSION=${BROKERKIT_PLUGIN_VERSION}
|
|
14981
|
+
ARG BROKERKIT_VERSION=${BROKERKIT_VERSION}
|
|
14982
|
+
|
|
14983
|
+
FROM golang:1.26.5-bookworm AS hf-broker-build
|
|
14984
|
+
ARG HF_BROKER_VERSION=bb65192b4dca845289427e63e1d5fa72f64914d8
|
|
14985
|
+
RUN git init /src \\
|
|
14986
|
+
&& git -C /src fetch --depth=1 https://github.com/osolmaz/hf-broker.git "$HF_BROKER_VERSION" \\
|
|
14987
|
+
&& git -C /src checkout --detach FETCH_HEAD \\
|
|
14988
|
+
&& test "$(git -C /src rev-parse HEAD)" = "$HF_BROKER_VERSION" \\
|
|
14989
|
+
&& cd /src \\
|
|
14990
|
+
&& GOWORK=off go build -trimpath -o /out/hf-broker ./cmd/hf-broker
|
|
14991
|
+
|
|
14992
|
+
FROM node:24-bookworm-slim AS brokerkit-plugin-build
|
|
14993
|
+
ARG BROKERKIT_VERSION
|
|
14994
|
+
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates git && rm -rf /var/lib/apt/lists/* && git init /src && git -C /src fetch --depth=1 https://github.com/osolmaz/brokerkit.git "$BROKERKIT_VERSION" && git -C /src checkout --detach FETCH_HEAD && test "$(git -C /src rev-parse HEAD)" = "$BROKERKIT_VERSION"
|
|
14995
|
+
WORKDIR /src
|
|
14996
|
+
RUN corepack enable && pnpm install --frozen-lockfile && pnpm --filter openclaw-brokerkit build && pnpm --filter openclaw-brokerkit pack --pack-destination /out
|
|
14997
|
+
|
|
14998
|
+
FROM ${OPENCLAW_BASE_IMAGE}
|
|
14940
14999
|
|
|
14941
15000
|
LABEL org.opencontainers.image.source="https://github.com/osolmaz/mlclaw"
|
|
14942
15001
|
LABEL org.opencontainers.image.description="ML Claw runtime for OpenClaw on Hugging Face"
|
|
14943
15002
|
|
|
14944
15003
|
USER root
|
|
14945
15004
|
RUN apt-get update \\
|
|
14946
|
-
&& apt-get install -y --no-install-recommends gosu python3 python3-pip python3-venv zstd \\
|
|
15005
|
+
&& apt-get install -y --no-install-recommends ca-certificates gosu python3 python3-pip python3-venv zstd \\
|
|
15006
|
+
&& useradd --system --home-dir /var/lib/hf-broker --create-home --shell /usr/sbin/nologin hf-broker \\
|
|
14947
15007
|
&& rm -rf /var/lib/apt/lists/*
|
|
14948
15008
|
RUN python3 -m pip install --break-system-packages --no-cache-dir \\
|
|
14949
15009
|
"huggingface_hub==1.19.0" \\
|
|
@@ -14957,10 +15017,15 @@ RUN python3 -m pip install --break-system-packages --no-cache-dir \\
|
|
|
14957
15017
|
"uvicorn==0.49.0" \\
|
|
14958
15018
|
"uv==0.11.28" \\
|
|
14959
15019
|
"hf-discover==1.3.7"
|
|
15020
|
+
ARG BROKERKIT_PLUGIN_VERSION
|
|
15021
|
+
COPY --from=brokerkit-plugin-build /out/openclaw-brokerkit-\${BROKERKIT_PLUGIN_VERSION}.tgz /tmp/openclaw-brokerkit.tgz
|
|
15022
|
+
RUN npm install --omit=dev --omit=peer --no-audit --no-fund --prefix /opt/openclaw-plugins /tmp/openclaw-brokerkit.tgz && rm /tmp/openclaw-brokerkit.tgz && test -f /opt/openclaw-plugins/node_modules/openclaw-brokerkit/openclaw.plugin.json
|
|
14960
15023
|
|
|
14961
15024
|
COPY --chown=node:node runtime/hf-state-sync.js /app/hf-state-sync.js
|
|
14962
15025
|
COPY --chown=node:node runtime/hf-tooling-seed.js /app/hf-tooling-seed.js
|
|
14963
15026
|
COPY --chown=node:node runtime/mlclaw-space-runtime.js /app/mlclaw-space-runtime.js
|
|
15027
|
+
COPY --from=hf-broker-build /out/hf-broker /usr/local/bin/hf-broker
|
|
15028
|
+
COPY runtime/hf-broker.scope.json /app/hf-broker.scope.json
|
|
14964
15029
|
COPY --chown=node:node runtime/openclaw.default.json /app/openclaw.default.json
|
|
14965
15030
|
COPY --chown=node:node runtime/entrypoint.sh /app/entrypoint.sh
|
|
14966
15031
|
COPY --chown=node:node runtime/scripts/ /app/scripts/
|
|
@@ -14970,11 +15035,12 @@ RUN chmod +x /app/entrypoint.sh
|
|
|
14970
15035
|
ENV PORT=7860
|
|
14971
15036
|
ENV MLCLAW_OPENCLAW_PORT=7861
|
|
14972
15037
|
ENV OPENCLAW_GATEWAY_PORT=7861
|
|
14973
|
-
ENV OPENCLAW_LIVE_DIR=/
|
|
14974
|
-
ENV OPENCLAW_STATE_DIR=/
|
|
14975
|
-
ENV OPENCLAW_WORKSPACE_DIR=/
|
|
14976
|
-
ENV OPENCLAW_CONFIG_PATH=/
|
|
15038
|
+
ENV OPENCLAW_LIVE_DIR=/home/node/.local/share/mlclaw/live
|
|
15039
|
+
ENV OPENCLAW_STATE_DIR=/home/node/.local/share/mlclaw/live/.openclaw
|
|
15040
|
+
ENV OPENCLAW_WORKSPACE_DIR=/home/node/.local/share/mlclaw/live/workspace
|
|
15041
|
+
ENV OPENCLAW_CONFIG_PATH=/home/node/.local/share/mlclaw/live/.openclaw/openclaw.json
|
|
14977
15042
|
ENV OPENCLAW_DISABLE_BONJOUR=1
|
|
15043
|
+
ENV MLCLAW_BROKERKIT_PLUGIN_PATH=/opt/openclaw-plugins/node_modules/openclaw-brokerkit
|
|
14978
15044
|
|
|
14979
15045
|
EXPOSE 7860
|
|
14980
15046
|
|
|
@@ -15259,6 +15325,8 @@ var DEFAULT_LOCAL_PORT = 7860;
|
|
|
15259
15325
|
var DEFAULT_SPACE_OPENCLAW_PORT = 7861;
|
|
15260
15326
|
var LOCAL_VOLUME_MOUNT_PATH = "/tmp/mlclaw-local";
|
|
15261
15327
|
var LOCAL_LIVE_DIR = `${LOCAL_VOLUME_MOUNT_PATH}/openclaw-live`;
|
|
15328
|
+
var SPACE_STATE_MOUNT_DIR = "/data/mlclaw-state";
|
|
15329
|
+
var SPACE_LIVE_DIR = "/home/node/.local/share/mlclaw/live";
|
|
15262
15330
|
var SPACE_HANDOFF_TIMEOUT_MS = 12e4;
|
|
15263
15331
|
var SPACE_HANDOFF_POLL_MS = 5e3;
|
|
15264
15332
|
var STALE_PATH_VARS = ["OPENCLAW_STATE_DIR", "OPENCLAW_WORKSPACE_DIR", "OPENCLAW_CONFIG_PATH"];
|
|
@@ -15296,10 +15364,10 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15296
15364
|
program2.name("mlclaw").description("Deploy OpenClaw to a Hugging Face Space and private bucket").showHelpAfterError().exitOverride((err) => {
|
|
15297
15365
|
throw err;
|
|
15298
15366
|
});
|
|
15299
|
-
program2.command("bootstrap", { isDefault: true }).description("Create or update a Hugging Face OpenClaw deployment").option("--owner <owner>", "Hugging Face user or organization").option("--name <name>", "Agent and runtime resource base name").option("--bucket <owner/bucket>", "State bucket to create or adopt").option("--gateway <local|space>", "Where the live gateway runs").option("--telegram-token <token>", "Optional Telegram bot token").option("--telegram-token-file <path>", "File containing TELEGRAM_BOT_TOKEN=... or a raw token").option("--telegram-user-id <id>", "Allowed Telegram user ID").option("--telegram-api-root <url>", "Telegram API root override").option("--telegram-proxy <url>", "Telegram proxy URL override").option("--hardware <flavor>", "Hugging Face Space hardware flavor").option("--sleep-time <seconds>", "Space sleep timeout in seconds; -1 means never sleep", parseInteger).option("--model <model>", "OpenClaw model identifier", DEFAULT_MODEL).option("--runtime-image <image>", "ML Claw runtime image").option("--bundled-runtime", "Generate a bundled Space runtime instead of using the prebuilt ML Claw image", false).option("--public-space", "Create the Hugging Face Space as public instead of private", false).addOption(new Option("--gateway-token <token>").hideHelp()).option("--docker-context <name>", "Docker context for local gateway mode").option("--no-pull", "Do not docker pull before starting a local gateway").option("--takeover", "Start even if a stale runtime lease is present", false).option("--yes", "Confirm paid hardware prompts for automation", false).action(async (opts) => {
|
|
15367
|
+
program2.command("bootstrap", { isDefault: true }).description("Create or update a Hugging Face OpenClaw deployment").option("--owner <owner>", "Hugging Face user or organization").option("--name <name>", "Agent and runtime resource base name").option("--bucket <owner/bucket>", "State bucket to create or adopt").option("--gateway <local|space>", "Where the live gateway runs").option("--telegram-token <token>", "Optional Telegram bot token").option("--telegram-token-file <path>", "File containing TELEGRAM_BOT_TOKEN=... or a raw token").option("--telegram-user-id <id>", "Allowed Telegram user ID").option("--telegram-api-root <url>", "Telegram API root override").option("--telegram-proxy <url>", "Telegram proxy URL override").option("--hardware <flavor>", "Hugging Face Space hardware flavor").option("--sleep-time <seconds>", "Space sleep timeout in seconds; -1 means never sleep", parseInteger).option("--model <model>", "OpenClaw model identifier", DEFAULT_MODEL).option("--runtime-image <image>", "ML Claw runtime image").option("--bundled-runtime", "Generate a bundled Space runtime instead of using the prebuilt ML Claw image", false).option("--public-space", "Create the Hugging Face Space as public instead of private", false).addOption(new Option("--gateway-token <token>").hideHelp()).option("--router-token <token>", "Hugging Face Router inference token for Space gateway model calls").option("--router-token-file <path>", "File containing MLCLAW_ROUTER_TOKEN=..., HF_ROUTER_TOKEN=..., or a raw token").option("--docker-context <name>", "Docker context for local gateway mode").option("--no-pull", "Do not docker pull before starting a local gateway").option("--takeover", "Start even if a stale runtime lease is present", false).option("--yes", "Confirm paid hardware prompts for automation", false).action(async (opts) => {
|
|
15300
15368
|
await bootstrap(opts, runtime);
|
|
15301
15369
|
});
|
|
15302
|
-
program2.command("update").description("Regenerate and upload current ML Claw Space files").argument("<owner/space>", "Hugging Face Space repo ID").option("--runtime-image <image>", "Runtime image to write into the generated Space Dockerfile").option("--bundled-runtime", "Generate a bundled Space runtime instead of using the prebuilt ML Claw image", false).option("--force", "Update even if the Space does not look like ML Claw", false).action(async (repoId, opts) => {
|
|
15370
|
+
program2.command("update").description("Regenerate and upload current ML Claw Space files").argument("<owner/space>", "Hugging Face Space repo ID").option("--runtime-image <image>", "Runtime image to write into the generated Space Dockerfile").option("--bundled-runtime", "Generate a bundled Space runtime instead of using the prebuilt ML Claw image", false).option("--router-token <token>", "Dedicated Hugging Face Router inference token").option("--router-token-file <path>", "File containing MLCLAW_ROUTER_TOKEN=..., HF_ROUTER_TOKEN=..., or a raw token").option("--force", "Update even if the Space does not look like ML Claw", false).action(async (repoId, opts) => {
|
|
15303
15371
|
const token = await runtime.readToken(runtime.env);
|
|
15304
15372
|
const hub = runtime.hubFactory(token);
|
|
15305
15373
|
await update(repoId, opts, hub, token, runtime);
|
|
@@ -15322,8 +15390,7 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15322
15390
|
await gatewayStop(agent, runtime);
|
|
15323
15391
|
});
|
|
15324
15392
|
gateway.command("restart").argument("<agent>", "Agent name").option("--no-pull", "Do not docker pull before starting a local gateway").option("--takeover", "Start even if another live runtime lease is present", false).action(async (agent, opts) => {
|
|
15325
|
-
await
|
|
15326
|
-
await gatewayStart(agent, opts, runtime);
|
|
15393
|
+
await gatewayRestart(agent, opts, runtime);
|
|
15327
15394
|
});
|
|
15328
15395
|
gateway.command("status").argument("<agent>", "Agent name").action(async (agent) => {
|
|
15329
15396
|
await gatewayStatus(agent, runtime);
|
|
@@ -15331,7 +15398,7 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15331
15398
|
gateway.command("logs").argument("<agent>", "Agent name").option("--tail <lines>", "Number of log lines", parseInteger, 200).action(async (agent, opts) => {
|
|
15332
15399
|
await gatewayLogs(agent, opts, runtime);
|
|
15333
15400
|
});
|
|
15334
|
-
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", "Create the Hugging Face Space as public instead of private", false).option("--docker-context <name>", "Docker context for local gateway startup when migrating to local").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) => {
|
|
15401
|
+
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", "Create the Hugging Face Space as public instead of private", false).option("--router-token <token>", "Hugging Face Router inference token for Space gateway model calls").option("--router-token-file <path>", "File containing MLCLAW_ROUTER_TOKEN=..., HF_ROUTER_TOKEN=..., or a raw token").option("--docker-context <name>", "Docker context for local gateway startup when migrating to local").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) => {
|
|
15335
15402
|
await gatewayMigrate(agent, opts, runtime);
|
|
15336
15403
|
});
|
|
15337
15404
|
gateway.command("rebind").argument("<agent>", "Agent name").requiredOption("--docker-context <name>", "Target Docker context").option("--no-pull", "Do not docker pull before starting the rebound local gateway").option("--takeover", "Rebind even if the old Docker context is unavailable", false).action(async (agent, opts) => {
|
|
@@ -15404,8 +15471,9 @@ async function bootstrap(opts, runtime) {
|
|
|
15404
15471
|
throw new Error("internal error: Space plan was not resolved");
|
|
15405
15472
|
}
|
|
15406
15473
|
const paidHardware = await resolveHardware({
|
|
15407
|
-
requestedHardware: opts.hardware
|
|
15474
|
+
...opts.hardware ? { requestedHardware: opts.hardware } : {},
|
|
15408
15475
|
...typeof opts.sleepTime === "number" ? { requestedSleepTime: opts.sleepTime } : telegramToken ? { requestedSleepTime: TELEGRAM_SLEEP_TIME } : {},
|
|
15476
|
+
defaultLabel: spacePlan.exists ? "unchanged Space hardware" : "default free CPU",
|
|
15409
15477
|
requiresMessagingEgress: Boolean(telegramToken),
|
|
15410
15478
|
yes: Boolean(opts.yes),
|
|
15411
15479
|
runtime
|
|
@@ -15415,7 +15483,7 @@ async function bootstrap(opts, runtime) {
|
|
|
15415
15483
|
bucketPlan,
|
|
15416
15484
|
spacePlan,
|
|
15417
15485
|
hasExistingManifest: plan.hasExistingManifest,
|
|
15418
|
-
hardware: paidHardware.
|
|
15486
|
+
hardware: paidHardware.label,
|
|
15419
15487
|
...typeof paidHardware.sleepTime === "number" ? { sleepTime: paidHardware.sleepTime } : {},
|
|
15420
15488
|
yes: Boolean(opts.yes),
|
|
15421
15489
|
runtime
|
|
@@ -15435,9 +15503,9 @@ async function bootstrap(opts, runtime) {
|
|
|
15435
15503
|
manifest,
|
|
15436
15504
|
secrets,
|
|
15437
15505
|
allowedUsers: me2.name,
|
|
15438
|
-
hardware: paidHardware.hardware,
|
|
15439
15506
|
spaceExists: spacePlan.exists,
|
|
15440
15507
|
publicSpace: Boolean(opts.publicSpace),
|
|
15508
|
+
...paidHardware.kind === "explicit" ? { hardware: paidHardware.hardware } : {},
|
|
15441
15509
|
...typeof paidHardware.sleepTime === "number" ? { sleepTime: paidHardware.sleepTime } : {},
|
|
15442
15510
|
...templateRuntimeImage ? { templateRuntimeImage } : {}
|
|
15443
15511
|
});
|
|
@@ -15493,10 +15561,11 @@ function spacePublicUrl(repoId) {
|
|
|
15493
15561
|
async function resolveBootstrapPlan(params) {
|
|
15494
15562
|
const { opts, owner, agentName, requestedGatewayLocation, hfToken, telegramToken, telegramUserId, model, runtimeImage, hub, runtime } = params;
|
|
15495
15563
|
const names = namesFor(owner, agentName);
|
|
15496
|
-
const sessionSecret = randomBytes(48).toString("base64url");
|
|
15497
15564
|
const now = runtime.now().toISOString();
|
|
15498
15565
|
const existingManifest = await readManifest(runtime.configRoot, agentName).catch(() => null);
|
|
15499
15566
|
const existingSecrets = await readSecretEnv(runtime.configRoot, agentName).catch(() => ({}));
|
|
15567
|
+
const sessionSecret = existingSecrets.MLCLAW_SESSION_SECRET ?? randomBytes(48).toString("base64url");
|
|
15568
|
+
const credentialKey = existingSecrets.MLCLAW_CREDENTIAL_KEY ?? randomBytes(32).toString("base64url");
|
|
15500
15569
|
const gatewayLocation = requestedGatewayLocation ?? existingManifest?.gatewayLocation ?? DEFAULT_GATEWAY_LOCATION;
|
|
15501
15570
|
if (opts.dockerContext && gatewayLocation !== "local") {
|
|
15502
15571
|
throw new Error("--docker-context only applies to local gateway mode");
|
|
@@ -15517,6 +15586,12 @@ async function resolveBootstrapPlan(params) {
|
|
|
15517
15586
|
hub
|
|
15518
15587
|
});
|
|
15519
15588
|
const bucket = bucketPlan.bucket;
|
|
15589
|
+
const routerToken = await resolveRouterToken({
|
|
15590
|
+
opts,
|
|
15591
|
+
runtime,
|
|
15592
|
+
existingSecrets,
|
|
15593
|
+
model
|
|
15594
|
+
});
|
|
15520
15595
|
const spacePlan = gatewayLocation === "space" ? {
|
|
15521
15596
|
space: names.space,
|
|
15522
15597
|
exists: await hub.spaceExists(names.space),
|
|
@@ -15541,6 +15616,7 @@ async function resolveBootstrapPlan(params) {
|
|
|
15541
15616
|
...telegramToken ? { telegramToken } : {},
|
|
15542
15617
|
...telegramUserId ? { telegramUserId } : {},
|
|
15543
15618
|
sessionSecret,
|
|
15619
|
+
credentialKey,
|
|
15544
15620
|
bucket,
|
|
15545
15621
|
model,
|
|
15546
15622
|
agentName,
|
|
@@ -15549,7 +15625,8 @@ async function resolveBootstrapPlan(params) {
|
|
|
15549
15625
|
runtimeId: gatewayLocation === "local" ? manifest.localRuntimeId : spaceRuntimeId(agentName),
|
|
15550
15626
|
...bucketPrefix ? { bucketPrefix } : {},
|
|
15551
15627
|
...opts.telegramProxy ? { telegramProxy: opts.telegramProxy } : {},
|
|
15552
|
-
...opts.telegramApiRoot ? { telegramApiRoot: opts.telegramApiRoot } : {}
|
|
15628
|
+
...opts.telegramApiRoot ? { telegramApiRoot: opts.telegramApiRoot } : {},
|
|
15629
|
+
...routerToken ? { routerToken } : {}
|
|
15553
15630
|
});
|
|
15554
15631
|
return {
|
|
15555
15632
|
agentName,
|
|
@@ -15611,6 +15688,8 @@ async function confirmBootstrapPlan(params) {
|
|
|
15611
15688
|
if (params.spacePlan) {
|
|
15612
15689
|
lines.push(`Space: ${params.spacePlan.space} (${params.spacePlan.exists ? "exists; files, variables, secrets, and runtime will be updated" : `will be created as ${params.spacePlan.visibility}`})`);
|
|
15613
15690
|
lines.push(`Hardware: ${params.hardware}`);
|
|
15691
|
+
lines.push(`Bucket mount: ${SPACE_STATE_MOUNT_DIR}`);
|
|
15692
|
+
lines.push(`Live state: ${SPACE_LIVE_DIR}`);
|
|
15614
15693
|
if (typeof params.sleepTime === "number") {
|
|
15615
15694
|
lines.push(`Sleep time: ${params.sleepTime}`);
|
|
15616
15695
|
}
|
|
@@ -15650,6 +15729,10 @@ async function stateAdopt(agent, opts, runtime) {
|
|
|
15650
15729
|
const hub = runtime.hubFactory(token);
|
|
15651
15730
|
const secrets = await readSecretEnv(runtime.configRoot, agent);
|
|
15652
15731
|
const bucketPrefix = persistedBucketPrefix(secrets);
|
|
15732
|
+
const bucketChanged = current.bucket !== bucket;
|
|
15733
|
+
if (bucketChanged && current.gatewayLocation === "local") {
|
|
15734
|
+
assertDedicatedRouterToken(current.model, secrets);
|
|
15735
|
+
}
|
|
15653
15736
|
runtime.stdout.log(`Creating or adopting private bucket ${bucket}`);
|
|
15654
15737
|
await hub.createBucket(bucket, true);
|
|
15655
15738
|
await inspectStateBucket(hub, bucket, bucketPrefix);
|
|
@@ -15660,7 +15743,6 @@ async function stateAdopt(agent, opts, runtime) {
|
|
|
15660
15743
|
runtimeId: runtimeIdFor(current),
|
|
15661
15744
|
takeover: Boolean(opts.takeover)
|
|
15662
15745
|
});
|
|
15663
|
-
const bucketChanged = current.bucket !== bucket;
|
|
15664
15746
|
if (bucketChanged) {
|
|
15665
15747
|
await confirmBucketChange({
|
|
15666
15748
|
message: `Adopt state bucket ${bucket} for ${agent}, replacing ${current.bucket}?`,
|
|
@@ -15711,9 +15793,16 @@ async function stateAdopt(agent, opts, runtime) {
|
|
|
15711
15793
|
} else {
|
|
15712
15794
|
await setDeploymentVariables(hub, updated.space, {
|
|
15713
15795
|
OPENCLAW_HF_STATE_BUCKET: bucket,
|
|
15796
|
+
MLCLAW_STATE_MOUNT_DIR: SPACE_STATE_MOUNT_DIR,
|
|
15797
|
+
OPENCLAW_LIVE_DIR: SPACE_LIVE_DIR,
|
|
15798
|
+
MLCLAW_RUNTIME_SETTINGS_FILE: `${SPACE_LIVE_DIR}/.mlclaw/settings.json`,
|
|
15714
15799
|
MLCLAW_GATEWAY_LOCATION: "space",
|
|
15715
15800
|
MLCLAW_RUNTIME_ID: spaceRuntimeId(updated.agent)
|
|
15716
15801
|
});
|
|
15802
|
+
await ensureSpaceStateVolume(hub, updated.space, bucket);
|
|
15803
|
+
if (canDeleteBroadTokenSecrets({ model: updated.model, routerTokenPresent: hasBrokerOrRouterTokenSecretRecord(secrets) })) {
|
|
15804
|
+
await deleteStaleSpaceTokenSecrets(hub, updated.space);
|
|
15805
|
+
}
|
|
15717
15806
|
await clearSpaceGatewayDisabled(hub, updated.space);
|
|
15718
15807
|
if (bucketChanged) {
|
|
15719
15808
|
await hub.restartSpace(updated.space, true);
|
|
@@ -15788,8 +15877,8 @@ function parseBucketId(raw) {
|
|
|
15788
15877
|
}
|
|
15789
15878
|
function deploymentSecrets(params) {
|
|
15790
15879
|
return {
|
|
15791
|
-
|
|
15792
|
-
|
|
15880
|
+
MLCLAW_BROKER_HF_TOKEN: params.hfToken,
|
|
15881
|
+
...params.routerToken ? { MLCLAW_ROUTER_TOKEN: params.routerToken } : {},
|
|
15793
15882
|
OPENCLAW_HF_STATE_BUCKET: params.bucket,
|
|
15794
15883
|
OPENCLAW_MODEL: params.model,
|
|
15795
15884
|
OPENCLAW_AGENT_NAME: params.agentName,
|
|
@@ -15797,6 +15886,7 @@ function deploymentSecrets(params) {
|
|
|
15797
15886
|
MLCLAW_RUNTIME_IMAGE: params.runtimeImage,
|
|
15798
15887
|
MLCLAW_RUNTIME_ID: params.runtimeId,
|
|
15799
15888
|
MLCLAW_SESSION_SECRET: params.sessionSecret,
|
|
15889
|
+
MLCLAW_CREDENTIAL_KEY: params.credentialKey,
|
|
15800
15890
|
MLCLAW_OPENCLAW_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT),
|
|
15801
15891
|
OPENCLAW_GATEWAY_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT),
|
|
15802
15892
|
...params.telegramToken ? { TELEGRAM_BOT_TOKEN: params.telegramToken } : {},
|
|
@@ -15815,10 +15905,14 @@ async function deploySpaceGateway(params) {
|
|
|
15815
15905
|
runtime.stdout.log(params.spaceExists ? `Updating existing Space ${manifest.space}` : `Creating ${params.publicSpace ? "public" : "private"} Space ${manifest.space}`);
|
|
15816
15906
|
await hub.createDockerSpace(manifest.space, {
|
|
15817
15907
|
private: !params.publicSpace,
|
|
15818
|
-
hardware: params.hardware,
|
|
15908
|
+
...params.hardware && !params.spaceExists ? { hardware: params.hardware } : {},
|
|
15819
15909
|
...typeof params.sleepTime === "number" ? { sleepTimeSeconds: params.sleepTime } : {}
|
|
15820
15910
|
});
|
|
15821
|
-
|
|
15911
|
+
if (params.hardware && params.spaceExists) {
|
|
15912
|
+
await hub.requestSpaceHardware(manifest.space, params.hardware, params.sleepTime);
|
|
15913
|
+
} else if (!params.hardware && params.spaceExists && typeof params.sleepTime === "number") {
|
|
15914
|
+
await hub.setSpaceSleepTime(manifest.space, params.sleepTime);
|
|
15915
|
+
}
|
|
15822
15916
|
runtime.stdout.log(params.templateRuntimeImage ? "Generating Space files from prebuilt runtime image" : "Generating bundled Space runtime files");
|
|
15823
15917
|
const { templateRev } = await runtime.pushTemplateToSpace({
|
|
15824
15918
|
targetRepo: manifest.space,
|
|
@@ -15828,6 +15922,9 @@ async function deploySpaceGateway(params) {
|
|
|
15828
15922
|
const spaceRuntimeRef = params.templateRuntimeImage ?? bundledSpaceRuntimeRef(templateRev);
|
|
15829
15923
|
await setDeploymentVariables(hub, manifest.space, {
|
|
15830
15924
|
OPENCLAW_HF_STATE_BUCKET: manifest.bucket,
|
|
15925
|
+
MLCLAW_STATE_MOUNT_DIR: SPACE_STATE_MOUNT_DIR,
|
|
15926
|
+
OPENCLAW_LIVE_DIR: SPACE_LIVE_DIR,
|
|
15927
|
+
MLCLAW_RUNTIME_SETTINGS_FILE: `${SPACE_LIVE_DIR}/.mlclaw/settings.json`,
|
|
15831
15928
|
...secrets.OPENCLAW_HF_STATE_PREFIX ? { OPENCLAW_HF_STATE_PREFIX: secrets.OPENCLAW_HF_STATE_PREFIX } : {},
|
|
15832
15929
|
MLCLAW_TEMPLATE_REV: templateRev,
|
|
15833
15930
|
OPENCLAW_MODEL: manifest.model,
|
|
@@ -15841,21 +15938,30 @@ async function deploySpaceGateway(params) {
|
|
|
15841
15938
|
MLCLAW_OPENCLAW_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT),
|
|
15842
15939
|
OPENCLAW_GATEWAY_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT)
|
|
15843
15940
|
});
|
|
15941
|
+
await ensureSpaceStateVolume(hub, manifest.space, manifest.bucket, { allowMissingVolumes: !params.spaceExists });
|
|
15844
15942
|
await clearSpaceGatewayDisabled(hub, manifest.space);
|
|
15845
15943
|
await setDeploymentSecrets(hub, manifest.space, {
|
|
15846
|
-
HF_TOKEN: requiredSecret(secrets, "HF_TOKEN"),
|
|
15847
|
-
HUGGINGFACE_HUB_TOKEN: requiredSecret(secrets, "HF_TOKEN"),
|
|
15848
15944
|
MLCLAW_SESSION_SECRET: requiredSecret(secrets, "MLCLAW_SESSION_SECRET"),
|
|
15945
|
+
MLCLAW_CREDENTIAL_KEY: requiredSecret(secrets, "MLCLAW_CREDENTIAL_KEY"),
|
|
15946
|
+
MLCLAW_BROKER_HF_TOKEN: hfToken,
|
|
15947
|
+
...secrets.MLCLAW_ROUTER_TOKEN ? { MLCLAW_ROUTER_TOKEN: secrets.MLCLAW_ROUTER_TOKEN } : {},
|
|
15849
15948
|
...secrets.TELEGRAM_BOT_TOKEN ? { TELEGRAM_BOT_TOKEN: secrets.TELEGRAM_BOT_TOKEN } : {},
|
|
15850
15949
|
...secrets.TELEGRAM_ALLOWED_USERS ? { TELEGRAM_ALLOWED_USERS: secrets.TELEGRAM_ALLOWED_USERS } : {},
|
|
15851
15950
|
...secrets.TELEGRAM_PROXY ? { TELEGRAM_PROXY: secrets.TELEGRAM_PROXY } : {},
|
|
15852
15951
|
...secrets.TELEGRAM_API_ROOT ? { TELEGRAM_API_ROOT: secrets.TELEGRAM_API_ROOT } : {}
|
|
15853
15952
|
});
|
|
15953
|
+
if (canDeleteBroadTokenSecrets({ model: manifest.model, routerTokenPresent: hasBrokerOrRouterTokenSecretRecord(secrets) })) {
|
|
15954
|
+
await deleteStaleSpaceTokenSecrets(hub, manifest.space);
|
|
15955
|
+
} else {
|
|
15956
|
+
runtime.stdout.log("Keeping legacy broad Hub token secrets until an HF Broker or Router credential is configured");
|
|
15957
|
+
}
|
|
15854
15958
|
await hub.restartSpace(manifest.space, true);
|
|
15855
15959
|
return { runtimeImage: spaceRuntimeRef };
|
|
15856
15960
|
}
|
|
15857
15961
|
async function startLocalGateway(params) {
|
|
15858
15962
|
const { manifest, runtime } = params;
|
|
15963
|
+
const secrets = await ensureDeploymentCredentialKey(runtime, manifest.agent);
|
|
15964
|
+
assertDedicatedRouterToken(manifest.model, secrets);
|
|
15859
15965
|
const containerName = containerNameFor(manifest.agent);
|
|
15860
15966
|
const volumeName = volumeNameFor(manifest.agent);
|
|
15861
15967
|
const dockerContext = dockerContextFor(manifest);
|
|
@@ -15928,6 +16034,17 @@ async function gatewayStart(agent, opts, runtime) {
|
|
|
15928
16034
|
runtime.stdout.log(`Space gateway restart requested: ${manifest.space}`);
|
|
15929
16035
|
}
|
|
15930
16036
|
}
|
|
16037
|
+
async function gatewayRestart(agent, opts, runtime) {
|
|
16038
|
+
const manifest = await readDeploymentManifest(runtime, agent, { requestedDockerContext: opts.dockerContext });
|
|
16039
|
+
if (manifest.gatewayLocation === "local") {
|
|
16040
|
+
assertDedicatedRouterToken(
|
|
16041
|
+
manifest.model,
|
|
16042
|
+
await readSecretEnv(runtime.configRoot, agent).catch(() => ({}))
|
|
16043
|
+
);
|
|
16044
|
+
}
|
|
16045
|
+
await gatewayStop(agent, runtime);
|
|
16046
|
+
await gatewayStart(agent, opts, runtime);
|
|
16047
|
+
}
|
|
15931
16048
|
async function gatewayStop(agent, runtime) {
|
|
15932
16049
|
const manifest = await readDeploymentManifest(runtime, agent);
|
|
15933
16050
|
const bucketPrefix = await readDeploymentBucketPrefix(runtime, agent);
|
|
@@ -15995,7 +16112,7 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
15995
16112
|
}
|
|
15996
16113
|
const token = await runtime.readToken(runtime.env);
|
|
15997
16114
|
const hub = runtime.hubFactory(token);
|
|
15998
|
-
const secrets = await
|
|
16115
|
+
const secrets = await ensureDeploymentCredentialKey(runtime, agent);
|
|
15999
16116
|
const bucketPrefix = persistedBucketPrefix(secrets);
|
|
16000
16117
|
const updated = {
|
|
16001
16118
|
...current,
|
|
@@ -16003,10 +16120,23 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
16003
16120
|
runtimeImage: resolveRuntimeImage(opts.runtimeImage ?? current.runtimeImage, runtime.env),
|
|
16004
16121
|
updatedAt: runtime.now().toISOString()
|
|
16005
16122
|
};
|
|
16123
|
+
const routerToken = await resolveRouterToken({
|
|
16124
|
+
opts,
|
|
16125
|
+
runtime,
|
|
16126
|
+
existingSecrets: secrets,
|
|
16127
|
+
model: updated.model
|
|
16128
|
+
});
|
|
16006
16129
|
if (target === "space") {
|
|
16130
|
+
const deploymentSecrets2 = {
|
|
16131
|
+
...secrets,
|
|
16132
|
+
MLCLAW_GATEWAY_LOCATION: "space",
|
|
16133
|
+
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage,
|
|
16134
|
+
...routerToken ? { MLCLAW_ROUTER_TOKEN: routerToken } : {}
|
|
16135
|
+
};
|
|
16007
16136
|
const paidHardware = await resolveHardware({
|
|
16008
|
-
requestedHardware: opts.hardware
|
|
16137
|
+
...opts.hardware ? { requestedHardware: opts.hardware } : {},
|
|
16009
16138
|
...typeof opts.sleepTime === "number" ? { requestedSleepTime: opts.sleepTime } : secrets.TELEGRAM_BOT_TOKEN ? { requestedSleepTime: TELEGRAM_SLEEP_TIME } : {},
|
|
16139
|
+
defaultLabel: "unchanged Space hardware",
|
|
16010
16140
|
requiresMessagingEgress: Boolean(secrets.TELEGRAM_BOT_TOKEN),
|
|
16011
16141
|
yes: Boolean(opts.yes),
|
|
16012
16142
|
runtime
|
|
@@ -16021,24 +16151,22 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
16021
16151
|
await handoffAndStopLocalGateway({ manifest: current, hub, runtime, bucketPrefix });
|
|
16022
16152
|
const me2 = await hub.whoami();
|
|
16023
16153
|
const templateRuntimeImage = resolveSpaceRuntimeImage(opts, runtime.env);
|
|
16154
|
+
const spaceExists = await hub.spaceExists(updated.space);
|
|
16024
16155
|
await deploySpaceGateway({
|
|
16025
16156
|
hub,
|
|
16026
16157
|
runtime,
|
|
16027
16158
|
hfToken: token,
|
|
16028
16159
|
manifest: updated,
|
|
16029
|
-
secrets:
|
|
16030
|
-
...secrets,
|
|
16031
|
-
MLCLAW_GATEWAY_LOCATION: "space",
|
|
16032
|
-
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage
|
|
16033
|
-
},
|
|
16160
|
+
secrets: deploymentSecrets2,
|
|
16034
16161
|
allowedUsers: me2.name,
|
|
16035
|
-
hardware: paidHardware.hardware,
|
|
16036
16162
|
publicSpace: Boolean(opts.publicSpace),
|
|
16163
|
+
spaceExists,
|
|
16164
|
+
...paidHardware.kind === "explicit" ? { hardware: paidHardware.hardware } : {},
|
|
16037
16165
|
...typeof paidHardware.sleepTime === "number" ? { sleepTime: paidHardware.sleepTime } : {},
|
|
16038
16166
|
...templateRuntimeImage ? { templateRuntimeImage } : {}
|
|
16039
16167
|
});
|
|
16040
16168
|
await writeSecretEnv(runtime.configRoot, agent, {
|
|
16041
|
-
...
|
|
16169
|
+
...deploymentSecrets2,
|
|
16042
16170
|
MLCLAW_GATEWAY_LOCATION: "space",
|
|
16043
16171
|
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage,
|
|
16044
16172
|
MLCLAW_RUNTIME_ID: spaceRuntimeId(agent)
|
|
@@ -16070,6 +16198,7 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
16070
16198
|
});
|
|
16071
16199
|
await writeSecretEnv(runtime.configRoot, agent, {
|
|
16072
16200
|
...secrets,
|
|
16201
|
+
...routerToken ? { MLCLAW_ROUTER_TOKEN: routerToken } : {},
|
|
16073
16202
|
MLCLAW_GATEWAY_LOCATION: "local",
|
|
16074
16203
|
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage,
|
|
16075
16204
|
MLCLAW_RUNTIME_ID: updated.localRuntimeId
|
|
@@ -16085,6 +16214,10 @@ async function gatewayRebind(agent, opts, runtime) {
|
|
|
16085
16214
|
if (current.gatewayLocation !== "local") {
|
|
16086
16215
|
throw new Error("Docker context rebind only applies to local gateway deployments");
|
|
16087
16216
|
}
|
|
16217
|
+
assertDedicatedRouterToken(
|
|
16218
|
+
current.model,
|
|
16219
|
+
await readSecretEnv(runtime.configRoot, agent).catch(() => ({}))
|
|
16220
|
+
);
|
|
16088
16221
|
const targetBinding = await resolveLocalGatewayBinding({
|
|
16089
16222
|
manifest: void 0,
|
|
16090
16223
|
requestedContext: targetContext,
|
|
@@ -16358,6 +16491,18 @@ function requiredSecret(secrets, key) {
|
|
|
16358
16491
|
}
|
|
16359
16492
|
return value;
|
|
16360
16493
|
}
|
|
16494
|
+
async function ensureDeploymentCredentialKey(runtime, agent, existing) {
|
|
16495
|
+
const secrets = existing ?? await readSecretEnv(runtime.configRoot, agent).catch(() => ({}));
|
|
16496
|
+
if (secrets.MLCLAW_CREDENTIAL_KEY) {
|
|
16497
|
+
return secrets;
|
|
16498
|
+
}
|
|
16499
|
+
const updated = {
|
|
16500
|
+
...secrets,
|
|
16501
|
+
MLCLAW_CREDENTIAL_KEY: randomBytes(32).toString("base64url")
|
|
16502
|
+
};
|
|
16503
|
+
await writeSecretEnv(runtime.configRoot, agent, updated);
|
|
16504
|
+
return updated;
|
|
16505
|
+
}
|
|
16361
16506
|
function requiredOption(value, label) {
|
|
16362
16507
|
if (!value) {
|
|
16363
16508
|
throw new Error(`${label} is required`);
|
|
@@ -16375,6 +16520,16 @@ async function update(repoId, opts, hub, hfToken, runtime) {
|
|
|
16375
16520
|
}
|
|
16376
16521
|
const runtimeImage = resolveSpaceRuntimeImage(opts, runtime.env);
|
|
16377
16522
|
const agentName = variables.get("OPENCLAW_AGENT_NAME")?.value?.trim() || repoId.split("/")[1] || "openclaw";
|
|
16523
|
+
if (!canonicalTemplate) {
|
|
16524
|
+
await ensureUpdateRouterToken({
|
|
16525
|
+
repoId,
|
|
16526
|
+
agentName,
|
|
16527
|
+
model: variables.get("OPENCLAW_MODEL")?.value ?? DEFAULT_MODEL,
|
|
16528
|
+
opts,
|
|
16529
|
+
hub,
|
|
16530
|
+
runtime
|
|
16531
|
+
});
|
|
16532
|
+
}
|
|
16378
16533
|
runtime.stdout.log(`Generating current Space files into ${repoId}`);
|
|
16379
16534
|
const { templateRev } = await runtime.pushTemplateToSpace({
|
|
16380
16535
|
targetRepo: repoId,
|
|
@@ -16393,9 +16548,50 @@ async function update(repoId, opts, hub, hfToken, runtime) {
|
|
|
16393
16548
|
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_ID", spaceRuntimeId(agentName));
|
|
16394
16549
|
await hub.addSpaceVariable(repoId, "MLCLAW_OPENCLAW_PORT", String(DEFAULT_SPACE_OPENCLAW_PORT));
|
|
16395
16550
|
await hub.addSpaceVariable(repoId, "OPENCLAW_GATEWAY_PORT", String(DEFAULT_SPACE_OPENCLAW_PORT));
|
|
16551
|
+
const bucket = variables.get("OPENCLAW_HF_STATE_BUCKET")?.value;
|
|
16552
|
+
if (bucket) {
|
|
16553
|
+
await hub.addSpaceVariable(repoId, "MLCLAW_STATE_MOUNT_DIR", SPACE_STATE_MOUNT_DIR);
|
|
16554
|
+
await hub.addSpaceVariable(repoId, "OPENCLAW_LIVE_DIR", SPACE_LIVE_DIR);
|
|
16555
|
+
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_SETTINGS_FILE", `${SPACE_LIVE_DIR}/.mlclaw/settings.json`);
|
|
16556
|
+
await ensureSpaceStateVolume(hub, repoId, bucket);
|
|
16557
|
+
}
|
|
16396
16558
|
await doctor(repoId, { fix: true }, hub, runtime);
|
|
16397
16559
|
await hub.restartSpace(repoId, true);
|
|
16398
16560
|
}
|
|
16561
|
+
async function ensureUpdateRouterToken(params) {
|
|
16562
|
+
if (!isHuggingFaceRouterModel(params.model)) {
|
|
16563
|
+
return;
|
|
16564
|
+
}
|
|
16565
|
+
const spaceSecrets = await params.hub.getSpaceSecrets(params.repoId);
|
|
16566
|
+
const hasExplicitOverride = params.opts.routerToken !== void 0 || params.opts.routerTokenFile !== void 0;
|
|
16567
|
+
if (hasBrokerOrRouterTokenSecretMap(spaceSecrets) && !hasExplicitOverride) {
|
|
16568
|
+
return;
|
|
16569
|
+
}
|
|
16570
|
+
const hasManifest = await manifestExists(params.runtime.configRoot, params.agentName);
|
|
16571
|
+
const localSecrets = hasManifest ? await readSecretEnv(params.runtime.configRoot, params.agentName).catch(() => ({})) : {};
|
|
16572
|
+
const routerToken = hasExplicitOverride ? await resolveRouterToken({
|
|
16573
|
+
opts: params.opts,
|
|
16574
|
+
runtime: params.runtime,
|
|
16575
|
+
existingSecrets: localSecrets,
|
|
16576
|
+
model: params.model
|
|
16577
|
+
}) : void 0;
|
|
16578
|
+
const brokerToken = routerToken ? void 0 : await params.runtime.readToken(params.runtime.env);
|
|
16579
|
+
const credential = routerToken ?? brokerToken;
|
|
16580
|
+
if (!credential) {
|
|
16581
|
+
throw new Error("Hugging Face broker credential is unavailable");
|
|
16582
|
+
}
|
|
16583
|
+
await params.hub.addSpaceSecret(
|
|
16584
|
+
params.repoId,
|
|
16585
|
+
routerToken ? "MLCLAW_ROUTER_TOKEN" : "MLCLAW_BROKER_HF_TOKEN",
|
|
16586
|
+
credential
|
|
16587
|
+
);
|
|
16588
|
+
if (hasManifest) {
|
|
16589
|
+
await writeSecretEnv(params.runtime.configRoot, params.agentName, {
|
|
16590
|
+
...localSecrets,
|
|
16591
|
+
...routerToken ? { MLCLAW_ROUTER_TOKEN: routerToken } : { MLCLAW_BROKER_HF_TOKEN: brokerToken }
|
|
16592
|
+
});
|
|
16593
|
+
}
|
|
16594
|
+
}
|
|
16399
16595
|
async function doctor(repoId, opts, hub, runtime) {
|
|
16400
16596
|
if (!repoId.includes("/") && await manifestExists(runtime.configRoot, repoId)) {
|
|
16401
16597
|
await gatewayStatus(repoId, runtime);
|
|
@@ -16454,6 +16650,31 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16454
16650
|
await hub.addSpaceVariable(repoId, "OPENCLAW_HF_STATE_BUCKET", bucket);
|
|
16455
16651
|
fixed.push("set OPENCLAW_HF_STATE_BUCKET");
|
|
16456
16652
|
}
|
|
16653
|
+
if ((variables.get("MLCLAW_STATE_MOUNT_DIR")?.value ?? "") !== SPACE_STATE_MOUNT_DIR) {
|
|
16654
|
+
if (fix) {
|
|
16655
|
+
await hub.addSpaceVariable(repoId, "MLCLAW_STATE_MOUNT_DIR", SPACE_STATE_MOUNT_DIR);
|
|
16656
|
+
fixed.push("set MLCLAW_STATE_MOUNT_DIR");
|
|
16657
|
+
} else {
|
|
16658
|
+
issues.push(`MLCLAW_STATE_MOUNT_DIR is not ${SPACE_STATE_MOUNT_DIR}`);
|
|
16659
|
+
}
|
|
16660
|
+
}
|
|
16661
|
+
if ((variables.get("OPENCLAW_LIVE_DIR")?.value ?? "") !== SPACE_LIVE_DIR) {
|
|
16662
|
+
if (fix) {
|
|
16663
|
+
await hub.addSpaceVariable(repoId, "OPENCLAW_LIVE_DIR", SPACE_LIVE_DIR);
|
|
16664
|
+
fixed.push("set OPENCLAW_LIVE_DIR");
|
|
16665
|
+
} else {
|
|
16666
|
+
issues.push(`OPENCLAW_LIVE_DIR is not ${SPACE_LIVE_DIR}`);
|
|
16667
|
+
}
|
|
16668
|
+
}
|
|
16669
|
+
const expectedRuntimeSettingsFile = `${SPACE_LIVE_DIR}/.mlclaw/settings.json`;
|
|
16670
|
+
if ((variables.get("MLCLAW_RUNTIME_SETTINGS_FILE")?.value ?? "") !== expectedRuntimeSettingsFile) {
|
|
16671
|
+
if (fix) {
|
|
16672
|
+
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_SETTINGS_FILE", expectedRuntimeSettingsFile);
|
|
16673
|
+
fixed.push("set MLCLAW_RUNTIME_SETTINGS_FILE");
|
|
16674
|
+
} else {
|
|
16675
|
+
issues.push(`MLCLAW_RUNTIME_SETTINGS_FILE is not ${expectedRuntimeSettingsFile}`);
|
|
16676
|
+
}
|
|
16677
|
+
}
|
|
16457
16678
|
for (const key of STALE_PATH_VARS) {
|
|
16458
16679
|
if (variables.has(key)) {
|
|
16459
16680
|
if (fix) {
|
|
@@ -16464,8 +16685,30 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16464
16685
|
}
|
|
16465
16686
|
}
|
|
16466
16687
|
}
|
|
16467
|
-
if (!secrets.has("
|
|
16468
|
-
|
|
16688
|
+
if (!secrets.has("MLCLAW_BROKER_HF_TOKEN")) {
|
|
16689
|
+
if (fix) {
|
|
16690
|
+
await hub.addSpaceSecret(repoId, "MLCLAW_BROKER_HF_TOKEN", await runtime.readToken(runtime.env));
|
|
16691
|
+
secrets.set("MLCLAW_BROKER_HF_TOKEN", { key: "MLCLAW_BROKER_HF_TOKEN" });
|
|
16692
|
+
fixed.push("set secret MLCLAW_BROKER_HF_TOKEN");
|
|
16693
|
+
} else {
|
|
16694
|
+
issues.push("secret MLCLAW_BROKER_HF_TOKEN is missing");
|
|
16695
|
+
}
|
|
16696
|
+
}
|
|
16697
|
+
const staleTokenSecrets = ["HF_TOKEN", "HUGGINGFACE_HUB_TOKEN"].filter((key) => secrets.has(key));
|
|
16698
|
+
if (staleTokenSecrets.length > 0) {
|
|
16699
|
+
const model = variables.get("OPENCLAW_MODEL")?.value ?? DEFAULT_MODEL;
|
|
16700
|
+
const canDelete = canDeleteBroadTokenSecrets({
|
|
16701
|
+
model,
|
|
16702
|
+
routerTokenPresent: hasBrokerOrRouterTokenSecretMap(secrets)
|
|
16703
|
+
});
|
|
16704
|
+
if (fix && canDelete) {
|
|
16705
|
+
await deleteStaleSpaceTokenSecrets(hub, repoId);
|
|
16706
|
+
fixed.push(`deleted stale secret${staleTokenSecrets.length === 1 ? "" : "s"} ${staleTokenSecrets.join(", ")}`);
|
|
16707
|
+
} else if (fix) {
|
|
16708
|
+
issues.push(`stale broad Hub token secret${staleTokenSecrets.length === 1 ? "" : "s"} present: ${staleTokenSecrets.join(", ")}; add MLCLAW_BROKER_HF_TOKEN before removing`);
|
|
16709
|
+
} else {
|
|
16710
|
+
issues.push(`stale broad Hub token secret${staleTokenSecrets.length === 1 ? "" : "s"} present: ${staleTokenSecrets.join(", ")}`);
|
|
16711
|
+
}
|
|
16469
16712
|
}
|
|
16470
16713
|
if (!secrets.has("MLCLAW_SESSION_SECRET")) {
|
|
16471
16714
|
if (fix) {
|
|
@@ -16475,6 +16718,16 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16475
16718
|
issues.push("secret MLCLAW_SESSION_SECRET is missing");
|
|
16476
16719
|
}
|
|
16477
16720
|
}
|
|
16721
|
+
if (!secrets.has("MLCLAW_CREDENTIAL_KEY")) {
|
|
16722
|
+
if (fix) {
|
|
16723
|
+
const agent = variables.get("OPENCLAW_AGENT_NAME")?.value?.trim() || repoId.split("/")[1] || "openclaw";
|
|
16724
|
+
const credentialKey = await manifestExists(runtime.configRoot, agent) ? requiredSecret(await ensureDeploymentCredentialKey(runtime, agent), "MLCLAW_CREDENTIAL_KEY") : randomBytes(32).toString("base64url");
|
|
16725
|
+
await hub.addSpaceSecret(repoId, "MLCLAW_CREDENTIAL_KEY", credentialKey);
|
|
16726
|
+
fixed.push("set secret MLCLAW_CREDENTIAL_KEY");
|
|
16727
|
+
} else {
|
|
16728
|
+
issues.push("secret MLCLAW_CREDENTIAL_KEY is missing");
|
|
16729
|
+
}
|
|
16730
|
+
}
|
|
16478
16731
|
if (!variables.has("MLCLAW_TEMPLATE_REV") && !variables.has("OPENCLAW_HF_TEMPLATE_REV")) {
|
|
16479
16732
|
issues.push("MLCLAW_TEMPLATE_REV is missing; updates cannot verify template lineage");
|
|
16480
16733
|
}
|
|
@@ -16514,6 +16767,14 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16514
16767
|
await hub.assertBucketAccessible(bucket);
|
|
16515
16768
|
}
|
|
16516
16769
|
const runtimeInfo = await hub.getSpaceRuntime(repoId);
|
|
16770
|
+
if (bucket && !hasStateVolume(runtimeInfo.volumes, bucket)) {
|
|
16771
|
+
if (fix) {
|
|
16772
|
+
await hub.setSpaceVolumes(repoId, mergeStateVolume(requireRuntimeVolumes(runtimeInfo, repoId), bucket));
|
|
16773
|
+
fixed.push(`mounted bucket ${bucket} at ${SPACE_STATE_MOUNT_DIR}`);
|
|
16774
|
+
} else {
|
|
16775
|
+
issues.push(`bucket ${bucket} is not mounted read-write at ${SPACE_STATE_MOUNT_DIR}`);
|
|
16776
|
+
}
|
|
16777
|
+
}
|
|
16517
16778
|
let logs = "";
|
|
16518
16779
|
try {
|
|
16519
16780
|
logs = await hub.fetchSpaceLogs(repoId, "run");
|
|
@@ -16614,6 +16875,79 @@ async function setDeploymentSecrets(hub, repoId, secrets) {
|
|
|
16614
16875
|
await hub.addSpaceSecret(repoId, key, value);
|
|
16615
16876
|
}
|
|
16616
16877
|
}
|
|
16878
|
+
async function deleteStaleSpaceTokenSecrets(hub, repoId) {
|
|
16879
|
+
await Promise.all([
|
|
16880
|
+
hub.deleteSpaceSecret(repoId, "HF_TOKEN"),
|
|
16881
|
+
hub.deleteSpaceSecret(repoId, "HUGGINGFACE_HUB_TOKEN")
|
|
16882
|
+
]);
|
|
16883
|
+
}
|
|
16884
|
+
function canDeleteBroadTokenSecrets(params) {
|
|
16885
|
+
return params.routerTokenPresent || !isHuggingFaceRouterModel(params.model);
|
|
16886
|
+
}
|
|
16887
|
+
function hasRouterTokenSecretRecord(secrets) {
|
|
16888
|
+
return Boolean(secrets.MLCLAW_ROUTER_TOKEN || secrets.HF_ROUTER_TOKEN);
|
|
16889
|
+
}
|
|
16890
|
+
function hasRouterTokenSecretMap(secrets) {
|
|
16891
|
+
return secrets.has("MLCLAW_ROUTER_TOKEN") || secrets.has("HF_ROUTER_TOKEN");
|
|
16892
|
+
}
|
|
16893
|
+
function hasBrokerOrRouterTokenSecretRecord(secrets) {
|
|
16894
|
+
return Boolean(secrets.MLCLAW_BROKER_HF_TOKEN) || hasRouterTokenSecretRecord(secrets);
|
|
16895
|
+
}
|
|
16896
|
+
function hasBrokerOrRouterTokenSecretMap(secrets) {
|
|
16897
|
+
return secrets.has("MLCLAW_BROKER_HF_TOKEN") || hasRouterTokenSecretMap(secrets);
|
|
16898
|
+
}
|
|
16899
|
+
function assertDedicatedRouterToken(model, secrets) {
|
|
16900
|
+
if (isHuggingFaceRouterModel(model) && !hasBrokerOrRouterTokenSecretRecord(secrets)) {
|
|
16901
|
+
throw new Error(
|
|
16902
|
+
"Hugging Face Router models require MLCLAW_BROKER_HF_TOKEN or a dedicated inference token"
|
|
16903
|
+
);
|
|
16904
|
+
}
|
|
16905
|
+
}
|
|
16906
|
+
async function ensureSpaceStateVolume(hub, repoId, bucket, opts = {}) {
|
|
16907
|
+
const runtime = await hub.getSpaceRuntime(repoId);
|
|
16908
|
+
const volumes = Array.isArray(runtime.volumes) ? runtime.volumes : opts.allowMissingVolumes ? [] : requireRuntimeVolumes(runtime, repoId);
|
|
16909
|
+
await hub.setSpaceVolumes(repoId, mergeStateVolume(volumes, bucket));
|
|
16910
|
+
}
|
|
16911
|
+
function requireRuntimeVolumes(runtime, repoId) {
|
|
16912
|
+
if (!Array.isArray(runtime.volumes)) {
|
|
16913
|
+
throw new Error(`Space runtime metadata for ${repoId} did not include volumes; refusing to replace mounts`);
|
|
16914
|
+
}
|
|
16915
|
+
return runtime.volumes;
|
|
16916
|
+
}
|
|
16917
|
+
function mergeStateVolume(existing, bucket) {
|
|
16918
|
+
return [
|
|
16919
|
+
...existing.filter((volume) => volumeMountPath(volume) !== SPACE_STATE_MOUNT_DIR).map(normalizeSpaceVolume),
|
|
16920
|
+
{
|
|
16921
|
+
type: "bucket",
|
|
16922
|
+
source: bucket,
|
|
16923
|
+
mountPath: SPACE_STATE_MOUNT_DIR,
|
|
16924
|
+
readOnly: false
|
|
16925
|
+
}
|
|
16926
|
+
];
|
|
16927
|
+
}
|
|
16928
|
+
function hasStateVolume(volumes, bucket) {
|
|
16929
|
+
return Boolean(volumes?.some(
|
|
16930
|
+
(volume) => volume.type === "bucket" && volume.source === bucket && volumeMountPath(volume) === SPACE_STATE_MOUNT_DIR && volumeReadOnly(volume) !== true
|
|
16931
|
+
));
|
|
16932
|
+
}
|
|
16933
|
+
function normalizeSpaceVolume(volume) {
|
|
16934
|
+
const normalized = { ...volume };
|
|
16935
|
+
const mountPath = volumeMountPath(volume);
|
|
16936
|
+
if (mountPath) {
|
|
16937
|
+
normalized.mountPath = mountPath;
|
|
16938
|
+
}
|
|
16939
|
+
const readOnly = volumeReadOnly(volume);
|
|
16940
|
+
if (typeof readOnly === "boolean") {
|
|
16941
|
+
normalized.readOnly = readOnly;
|
|
16942
|
+
}
|
|
16943
|
+
return normalized;
|
|
16944
|
+
}
|
|
16945
|
+
function volumeMountPath(volume) {
|
|
16946
|
+
return volume.mountPath ?? volume.mount_path;
|
|
16947
|
+
}
|
|
16948
|
+
function volumeReadOnly(volume) {
|
|
16949
|
+
return volume.readOnly ?? volume.read_only;
|
|
16950
|
+
}
|
|
16617
16951
|
async function clearSpaceGatewayDisabled(hub, repoId) {
|
|
16618
16952
|
try {
|
|
16619
16953
|
await hub.deleteSpaceVariable(repoId, "MLCLAW_GATEWAY_DISABLED");
|
|
@@ -16636,6 +16970,29 @@ async function readOptionalTelegramToken(opts, runtime) {
|
|
|
16636
16970
|
}
|
|
16637
16971
|
return void 0;
|
|
16638
16972
|
}
|
|
16973
|
+
async function resolveRouterToken(params) {
|
|
16974
|
+
const explicit = nonEmpty(params.opts.routerToken) ?? await readOptionalRouterTokenFile(params.opts.routerTokenFile);
|
|
16975
|
+
const direct = explicit ?? params.runtime.env.MLCLAW_ROUTER_TOKEN ?? params.runtime.env.HF_ROUTER_TOKEN ?? params.existingSecrets?.MLCLAW_ROUTER_TOKEN ?? params.existingSecrets?.HF_ROUTER_TOKEN;
|
|
16976
|
+
const existing = nonEmpty(direct);
|
|
16977
|
+
if (existing) {
|
|
16978
|
+
return existing;
|
|
16979
|
+
}
|
|
16980
|
+
if (!isHuggingFaceRouterModel(params.model)) {
|
|
16981
|
+
return void 0;
|
|
16982
|
+
}
|
|
16983
|
+
return void 0;
|
|
16984
|
+
}
|
|
16985
|
+
async function readOptionalRouterTokenFile(file) {
|
|
16986
|
+
if (!file) {
|
|
16987
|
+
return void 0;
|
|
16988
|
+
}
|
|
16989
|
+
const raw = await fs14.readFile(file, "utf8");
|
|
16990
|
+
const parsed = parseSecretEnv(raw);
|
|
16991
|
+
return nonEmpty(parsed.MLCLAW_ROUTER_TOKEN) ?? nonEmpty(parsed.HF_ROUTER_TOKEN) ?? nonEmpty(raw);
|
|
16992
|
+
}
|
|
16993
|
+
function isHuggingFaceRouterModel(model) {
|
|
16994
|
+
return model.trim().startsWith("huggingface/");
|
|
16995
|
+
}
|
|
16639
16996
|
async function promptAgentName(runtime) {
|
|
16640
16997
|
if (!runtime.prompt.isInteractive()) {
|
|
16641
16998
|
return "mlclaw";
|
|
@@ -16648,21 +17005,26 @@ async function promptAgentName(runtime) {
|
|
|
16648
17005
|
return readPromptValue(value, "Agent name");
|
|
16649
17006
|
}
|
|
16650
17007
|
async function resolveHardware(params) {
|
|
16651
|
-
const hardware = params.requestedHardware;
|
|
16652
|
-
|
|
17008
|
+
const hardware = params.requestedHardware ?? (params.requiresMessagingEgress ? TELEGRAM_HARDWARE : void 0);
|
|
17009
|
+
if (!hardware) {
|
|
17010
|
+
const label = params.defaultLabel ?? "default free CPU";
|
|
17011
|
+
return typeof params.requestedSleepTime === "number" ? { kind: "default", label, sleepTime: params.requestedSleepTime } : { kind: "default", label };
|
|
17012
|
+
}
|
|
17013
|
+
const sleepTime = isPaidHardware(hardware) ? params.requestedSleepTime ?? TELEGRAM_SLEEP_TIME : params.requestedSleepTime;
|
|
16653
17014
|
if (params.requiresMessagingEgress && !isPaidHardware(hardware)) {
|
|
16654
17015
|
throw new Error(`Telegram requires upgraded paid Space hardware today; use --hardware ${TELEGRAM_HARDWARE} or --gateway local`);
|
|
16655
17016
|
}
|
|
16656
17017
|
if (isPaidHardware(hardware)) {
|
|
17018
|
+
const paidSleepTime = params.requestedSleepTime ?? TELEGRAM_SLEEP_TIME;
|
|
16657
17019
|
await confirmPaidHardware({
|
|
16658
17020
|
hardware,
|
|
16659
|
-
sleepTime,
|
|
17021
|
+
sleepTime: paidSleepTime,
|
|
16660
17022
|
yes: params.yes,
|
|
16661
17023
|
runtime: params.runtime
|
|
16662
17024
|
});
|
|
16663
|
-
return { hardware, sleepTime };
|
|
17025
|
+
return { kind: "explicit", hardware, label: hardware, sleepTime: paidSleepTime };
|
|
16664
17026
|
}
|
|
16665
|
-
return typeof
|
|
17027
|
+
return typeof sleepTime === "number" ? { kind: "explicit", hardware, label: hardware, sleepTime } : { kind: "explicit", hardware, label: hardware };
|
|
16666
17028
|
}
|
|
16667
17029
|
async function confirmPaidHardware(params) {
|
|
16668
17030
|
if (params.yes) {
|
|
@@ -16746,8 +17108,11 @@ export {
|
|
|
16746
17108
|
LOCAL_VOLUME_MOUNT_PATH,
|
|
16747
17109
|
SPACE_HANDOFF_POLL_MS,
|
|
16748
17110
|
SPACE_HANDOFF_TIMEOUT_MS,
|
|
17111
|
+
SPACE_LIVE_DIR,
|
|
17112
|
+
SPACE_STATE_MOUNT_DIR,
|
|
16749
17113
|
TELEGRAM_HARDWARE,
|
|
16750
17114
|
TELEGRAM_SLEEP_TIME,
|
|
16751
17115
|
createProgram,
|
|
16752
|
-
main
|
|
17116
|
+
main,
|
|
17117
|
+
mergeStateVolume
|
|
16753
17118
|
};
|