mlclaw 0.2.3 → 0.3.1
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 +46 -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 +406 -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,33 @@ function imageDockerfile(runtimeImage) {
|
|
|
14936
14976
|
`;
|
|
14937
14977
|
}
|
|
14938
14978
|
function bundledDockerfile() {
|
|
14939
|
-
return `
|
|
14979
|
+
return `ARG BROKERKIT_PLUGIN_VERSION=${BROKERKIT_PLUGIN_VERSION}
|
|
14980
|
+
ARG BROKERKIT_VERSION=${BROKERKIT_VERSION}
|
|
14981
|
+
|
|
14982
|
+
FROM golang:1.26.5-bookworm AS hf-broker-build
|
|
14983
|
+
ARG BROKERKIT_VERSION
|
|
14984
|
+
RUN git init /src \\
|
|
14985
|
+
&& git -C /src fetch --depth=1 https://github.com/osolmaz/brokerkit.git "$BROKERKIT_VERSION" \\
|
|
14986
|
+
&& git -C /src checkout --detach FETCH_HEAD \\
|
|
14987
|
+
&& test "$(git -C /src rev-parse HEAD)" = "$BROKERKIT_VERSION" \\
|
|
14988
|
+
&& cd /src \\
|
|
14989
|
+
&& GOWORK=off go build -trimpath -o /out/hf-broker ./brokers/huggingface/cmd/hf-broker
|
|
14990
|
+
|
|
14991
|
+
FROM node:24-bookworm-slim AS brokerkit-plugin-build
|
|
14992
|
+
ARG BROKERKIT_VERSION
|
|
14993
|
+
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"
|
|
14994
|
+
WORKDIR /src
|
|
14995
|
+
RUN corepack enable && pnpm install --frozen-lockfile && pnpm --filter openclaw-brokerkit build && pnpm --filter openclaw-brokerkit pack --pack-destination /out
|
|
14996
|
+
|
|
14997
|
+
FROM ${OPENCLAW_BASE_IMAGE}
|
|
14940
14998
|
|
|
14941
14999
|
LABEL org.opencontainers.image.source="https://github.com/osolmaz/mlclaw"
|
|
14942
15000
|
LABEL org.opencontainers.image.description="ML Claw runtime for OpenClaw on Hugging Face"
|
|
14943
15001
|
|
|
14944
15002
|
USER root
|
|
14945
15003
|
RUN apt-get update \\
|
|
14946
|
-
&& apt-get install -y --no-install-recommends gosu python3 python3-pip python3-venv zstd \\
|
|
15004
|
+
&& apt-get install -y --no-install-recommends ca-certificates gosu python3 python3-pip python3-venv zstd \\
|
|
15005
|
+
&& useradd --system --home-dir /var/lib/hf-broker --create-home --shell /usr/sbin/nologin hf-broker \\
|
|
14947
15006
|
&& rm -rf /var/lib/apt/lists/*
|
|
14948
15007
|
RUN python3 -m pip install --break-system-packages --no-cache-dir \\
|
|
14949
15008
|
"huggingface_hub==1.19.0" \\
|
|
@@ -14957,10 +15016,15 @@ RUN python3 -m pip install --break-system-packages --no-cache-dir \\
|
|
|
14957
15016
|
"uvicorn==0.49.0" \\
|
|
14958
15017
|
"uv==0.11.28" \\
|
|
14959
15018
|
"hf-discover==1.3.7"
|
|
15019
|
+
ARG BROKERKIT_PLUGIN_VERSION
|
|
15020
|
+
COPY --from=brokerkit-plugin-build /out/openclaw-brokerkit-\${BROKERKIT_PLUGIN_VERSION}.tgz /tmp/openclaw-brokerkit.tgz
|
|
15021
|
+
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
15022
|
|
|
14961
15023
|
COPY --chown=node:node runtime/hf-state-sync.js /app/hf-state-sync.js
|
|
14962
15024
|
COPY --chown=node:node runtime/hf-tooling-seed.js /app/hf-tooling-seed.js
|
|
14963
15025
|
COPY --chown=node:node runtime/mlclaw-space-runtime.js /app/mlclaw-space-runtime.js
|
|
15026
|
+
COPY --from=hf-broker-build /out/hf-broker /usr/local/bin/hf-broker
|
|
15027
|
+
COPY runtime/hf-broker.scope.json /app/hf-broker.scope.json
|
|
14964
15028
|
COPY --chown=node:node runtime/openclaw.default.json /app/openclaw.default.json
|
|
14965
15029
|
COPY --chown=node:node runtime/entrypoint.sh /app/entrypoint.sh
|
|
14966
15030
|
COPY --chown=node:node runtime/scripts/ /app/scripts/
|
|
@@ -14970,11 +15034,12 @@ RUN chmod +x /app/entrypoint.sh
|
|
|
14970
15034
|
ENV PORT=7860
|
|
14971
15035
|
ENV MLCLAW_OPENCLAW_PORT=7861
|
|
14972
15036
|
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=/
|
|
15037
|
+
ENV OPENCLAW_LIVE_DIR=/home/node/.local/share/mlclaw/live
|
|
15038
|
+
ENV OPENCLAW_STATE_DIR=/home/node/.local/share/mlclaw/live/.openclaw
|
|
15039
|
+
ENV OPENCLAW_WORKSPACE_DIR=/home/node/.local/share/mlclaw/live/workspace
|
|
15040
|
+
ENV OPENCLAW_CONFIG_PATH=/home/node/.local/share/mlclaw/live/.openclaw/openclaw.json
|
|
14977
15041
|
ENV OPENCLAW_DISABLE_BONJOUR=1
|
|
15042
|
+
ENV MLCLAW_BROKERKIT_PLUGIN_PATH=/opt/openclaw-plugins/node_modules/openclaw-brokerkit
|
|
14978
15043
|
|
|
14979
15044
|
EXPOSE 7860
|
|
14980
15045
|
|
|
@@ -15259,6 +15324,8 @@ var DEFAULT_LOCAL_PORT = 7860;
|
|
|
15259
15324
|
var DEFAULT_SPACE_OPENCLAW_PORT = 7861;
|
|
15260
15325
|
var LOCAL_VOLUME_MOUNT_PATH = "/tmp/mlclaw-local";
|
|
15261
15326
|
var LOCAL_LIVE_DIR = `${LOCAL_VOLUME_MOUNT_PATH}/openclaw-live`;
|
|
15327
|
+
var SPACE_STATE_MOUNT_DIR = "/data/mlclaw-state";
|
|
15328
|
+
var SPACE_LIVE_DIR = "/home/node/.local/share/mlclaw/live";
|
|
15262
15329
|
var SPACE_HANDOFF_TIMEOUT_MS = 12e4;
|
|
15263
15330
|
var SPACE_HANDOFF_POLL_MS = 5e3;
|
|
15264
15331
|
var STALE_PATH_VARS = ["OPENCLAW_STATE_DIR", "OPENCLAW_WORKSPACE_DIR", "OPENCLAW_CONFIG_PATH"];
|
|
@@ -15296,10 +15363,10 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15296
15363
|
program2.name("mlclaw").description("Deploy OpenClaw to a Hugging Face Space and private bucket").showHelpAfterError().exitOverride((err) => {
|
|
15297
15364
|
throw err;
|
|
15298
15365
|
});
|
|
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) => {
|
|
15366
|
+
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
15367
|
await bootstrap(opts, runtime);
|
|
15301
15368
|
});
|
|
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) => {
|
|
15369
|
+
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
15370
|
const token = await runtime.readToken(runtime.env);
|
|
15304
15371
|
const hub = runtime.hubFactory(token);
|
|
15305
15372
|
await update(repoId, opts, hub, token, runtime);
|
|
@@ -15322,8 +15389,7 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15322
15389
|
await gatewayStop(agent, runtime);
|
|
15323
15390
|
});
|
|
15324
15391
|
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);
|
|
15392
|
+
await gatewayRestart(agent, opts, runtime);
|
|
15327
15393
|
});
|
|
15328
15394
|
gateway.command("status").argument("<agent>", "Agent name").action(async (agent) => {
|
|
15329
15395
|
await gatewayStatus(agent, runtime);
|
|
@@ -15331,7 +15397,7 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15331
15397
|
gateway.command("logs").argument("<agent>", "Agent name").option("--tail <lines>", "Number of log lines", parseInteger, 200).action(async (agent, opts) => {
|
|
15332
15398
|
await gatewayLogs(agent, opts, runtime);
|
|
15333
15399
|
});
|
|
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) => {
|
|
15400
|
+
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
15401
|
await gatewayMigrate(agent, opts, runtime);
|
|
15336
15402
|
});
|
|
15337
15403
|
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 +15470,9 @@ async function bootstrap(opts, runtime) {
|
|
|
15404
15470
|
throw new Error("internal error: Space plan was not resolved");
|
|
15405
15471
|
}
|
|
15406
15472
|
const paidHardware = await resolveHardware({
|
|
15407
|
-
requestedHardware: opts.hardware
|
|
15473
|
+
...opts.hardware ? { requestedHardware: opts.hardware } : {},
|
|
15408
15474
|
...typeof opts.sleepTime === "number" ? { requestedSleepTime: opts.sleepTime } : telegramToken ? { requestedSleepTime: TELEGRAM_SLEEP_TIME } : {},
|
|
15475
|
+
defaultLabel: spacePlan.exists ? "unchanged Space hardware" : "default free CPU",
|
|
15409
15476
|
requiresMessagingEgress: Boolean(telegramToken),
|
|
15410
15477
|
yes: Boolean(opts.yes),
|
|
15411
15478
|
runtime
|
|
@@ -15415,7 +15482,7 @@ async function bootstrap(opts, runtime) {
|
|
|
15415
15482
|
bucketPlan,
|
|
15416
15483
|
spacePlan,
|
|
15417
15484
|
hasExistingManifest: plan.hasExistingManifest,
|
|
15418
|
-
hardware: paidHardware.
|
|
15485
|
+
hardware: paidHardware.label,
|
|
15419
15486
|
...typeof paidHardware.sleepTime === "number" ? { sleepTime: paidHardware.sleepTime } : {},
|
|
15420
15487
|
yes: Boolean(opts.yes),
|
|
15421
15488
|
runtime
|
|
@@ -15435,9 +15502,9 @@ async function bootstrap(opts, runtime) {
|
|
|
15435
15502
|
manifest,
|
|
15436
15503
|
secrets,
|
|
15437
15504
|
allowedUsers: me2.name,
|
|
15438
|
-
hardware: paidHardware.hardware,
|
|
15439
15505
|
spaceExists: spacePlan.exists,
|
|
15440
15506
|
publicSpace: Boolean(opts.publicSpace),
|
|
15507
|
+
...paidHardware.kind === "explicit" ? { hardware: paidHardware.hardware } : {},
|
|
15441
15508
|
...typeof paidHardware.sleepTime === "number" ? { sleepTime: paidHardware.sleepTime } : {},
|
|
15442
15509
|
...templateRuntimeImage ? { templateRuntimeImage } : {}
|
|
15443
15510
|
});
|
|
@@ -15493,10 +15560,11 @@ function spacePublicUrl(repoId) {
|
|
|
15493
15560
|
async function resolveBootstrapPlan(params) {
|
|
15494
15561
|
const { opts, owner, agentName, requestedGatewayLocation, hfToken, telegramToken, telegramUserId, model, runtimeImage, hub, runtime } = params;
|
|
15495
15562
|
const names = namesFor(owner, agentName);
|
|
15496
|
-
const sessionSecret = randomBytes(48).toString("base64url");
|
|
15497
15563
|
const now = runtime.now().toISOString();
|
|
15498
15564
|
const existingManifest = await readManifest(runtime.configRoot, agentName).catch(() => null);
|
|
15499
15565
|
const existingSecrets = await readSecretEnv(runtime.configRoot, agentName).catch(() => ({}));
|
|
15566
|
+
const sessionSecret = existingSecrets.MLCLAW_SESSION_SECRET ?? randomBytes(48).toString("base64url");
|
|
15567
|
+
const credentialKey = existingSecrets.MLCLAW_CREDENTIAL_KEY ?? randomBytes(32).toString("base64url");
|
|
15500
15568
|
const gatewayLocation = requestedGatewayLocation ?? existingManifest?.gatewayLocation ?? DEFAULT_GATEWAY_LOCATION;
|
|
15501
15569
|
if (opts.dockerContext && gatewayLocation !== "local") {
|
|
15502
15570
|
throw new Error("--docker-context only applies to local gateway mode");
|
|
@@ -15517,6 +15585,12 @@ async function resolveBootstrapPlan(params) {
|
|
|
15517
15585
|
hub
|
|
15518
15586
|
});
|
|
15519
15587
|
const bucket = bucketPlan.bucket;
|
|
15588
|
+
const routerToken = await resolveRouterToken({
|
|
15589
|
+
opts,
|
|
15590
|
+
runtime,
|
|
15591
|
+
existingSecrets,
|
|
15592
|
+
model
|
|
15593
|
+
});
|
|
15520
15594
|
const spacePlan = gatewayLocation === "space" ? {
|
|
15521
15595
|
space: names.space,
|
|
15522
15596
|
exists: await hub.spaceExists(names.space),
|
|
@@ -15541,6 +15615,7 @@ async function resolveBootstrapPlan(params) {
|
|
|
15541
15615
|
...telegramToken ? { telegramToken } : {},
|
|
15542
15616
|
...telegramUserId ? { telegramUserId } : {},
|
|
15543
15617
|
sessionSecret,
|
|
15618
|
+
credentialKey,
|
|
15544
15619
|
bucket,
|
|
15545
15620
|
model,
|
|
15546
15621
|
agentName,
|
|
@@ -15549,7 +15624,8 @@ async function resolveBootstrapPlan(params) {
|
|
|
15549
15624
|
runtimeId: gatewayLocation === "local" ? manifest.localRuntimeId : spaceRuntimeId(agentName),
|
|
15550
15625
|
...bucketPrefix ? { bucketPrefix } : {},
|
|
15551
15626
|
...opts.telegramProxy ? { telegramProxy: opts.telegramProxy } : {},
|
|
15552
|
-
...opts.telegramApiRoot ? { telegramApiRoot: opts.telegramApiRoot } : {}
|
|
15627
|
+
...opts.telegramApiRoot ? { telegramApiRoot: opts.telegramApiRoot } : {},
|
|
15628
|
+
...routerToken ? { routerToken } : {}
|
|
15553
15629
|
});
|
|
15554
15630
|
return {
|
|
15555
15631
|
agentName,
|
|
@@ -15611,6 +15687,8 @@ async function confirmBootstrapPlan(params) {
|
|
|
15611
15687
|
if (params.spacePlan) {
|
|
15612
15688
|
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
15689
|
lines.push(`Hardware: ${params.hardware}`);
|
|
15690
|
+
lines.push(`Bucket mount: ${SPACE_STATE_MOUNT_DIR}`);
|
|
15691
|
+
lines.push(`Live state: ${SPACE_LIVE_DIR}`);
|
|
15614
15692
|
if (typeof params.sleepTime === "number") {
|
|
15615
15693
|
lines.push(`Sleep time: ${params.sleepTime}`);
|
|
15616
15694
|
}
|
|
@@ -15650,6 +15728,10 @@ async function stateAdopt(agent, opts, runtime) {
|
|
|
15650
15728
|
const hub = runtime.hubFactory(token);
|
|
15651
15729
|
const secrets = await readSecretEnv(runtime.configRoot, agent);
|
|
15652
15730
|
const bucketPrefix = persistedBucketPrefix(secrets);
|
|
15731
|
+
const bucketChanged = current.bucket !== bucket;
|
|
15732
|
+
if (bucketChanged && current.gatewayLocation === "local") {
|
|
15733
|
+
assertDedicatedRouterToken(current.model, secrets);
|
|
15734
|
+
}
|
|
15653
15735
|
runtime.stdout.log(`Creating or adopting private bucket ${bucket}`);
|
|
15654
15736
|
await hub.createBucket(bucket, true);
|
|
15655
15737
|
await inspectStateBucket(hub, bucket, bucketPrefix);
|
|
@@ -15660,7 +15742,6 @@ async function stateAdopt(agent, opts, runtime) {
|
|
|
15660
15742
|
runtimeId: runtimeIdFor(current),
|
|
15661
15743
|
takeover: Boolean(opts.takeover)
|
|
15662
15744
|
});
|
|
15663
|
-
const bucketChanged = current.bucket !== bucket;
|
|
15664
15745
|
if (bucketChanged) {
|
|
15665
15746
|
await confirmBucketChange({
|
|
15666
15747
|
message: `Adopt state bucket ${bucket} for ${agent}, replacing ${current.bucket}?`,
|
|
@@ -15711,9 +15792,16 @@ async function stateAdopt(agent, opts, runtime) {
|
|
|
15711
15792
|
} else {
|
|
15712
15793
|
await setDeploymentVariables(hub, updated.space, {
|
|
15713
15794
|
OPENCLAW_HF_STATE_BUCKET: bucket,
|
|
15795
|
+
MLCLAW_STATE_MOUNT_DIR: SPACE_STATE_MOUNT_DIR,
|
|
15796
|
+
OPENCLAW_LIVE_DIR: SPACE_LIVE_DIR,
|
|
15797
|
+
MLCLAW_RUNTIME_SETTINGS_FILE: `${SPACE_LIVE_DIR}/.mlclaw/settings.json`,
|
|
15714
15798
|
MLCLAW_GATEWAY_LOCATION: "space",
|
|
15715
15799
|
MLCLAW_RUNTIME_ID: spaceRuntimeId(updated.agent)
|
|
15716
15800
|
});
|
|
15801
|
+
await ensureSpaceStateVolume(hub, updated.space, bucket);
|
|
15802
|
+
if (canDeleteBroadTokenSecrets({ model: updated.model, routerTokenPresent: hasBrokerOrRouterTokenSecretRecord(secrets) })) {
|
|
15803
|
+
await deleteStaleSpaceTokenSecrets(hub, updated.space);
|
|
15804
|
+
}
|
|
15717
15805
|
await clearSpaceGatewayDisabled(hub, updated.space);
|
|
15718
15806
|
if (bucketChanged) {
|
|
15719
15807
|
await hub.restartSpace(updated.space, true);
|
|
@@ -15788,8 +15876,8 @@ function parseBucketId(raw) {
|
|
|
15788
15876
|
}
|
|
15789
15877
|
function deploymentSecrets(params) {
|
|
15790
15878
|
return {
|
|
15791
|
-
|
|
15792
|
-
|
|
15879
|
+
MLCLAW_BROKER_HF_TOKEN: params.hfToken,
|
|
15880
|
+
...params.routerToken ? { MLCLAW_ROUTER_TOKEN: params.routerToken } : {},
|
|
15793
15881
|
OPENCLAW_HF_STATE_BUCKET: params.bucket,
|
|
15794
15882
|
OPENCLAW_MODEL: params.model,
|
|
15795
15883
|
OPENCLAW_AGENT_NAME: params.agentName,
|
|
@@ -15797,6 +15885,7 @@ function deploymentSecrets(params) {
|
|
|
15797
15885
|
MLCLAW_RUNTIME_IMAGE: params.runtimeImage,
|
|
15798
15886
|
MLCLAW_RUNTIME_ID: params.runtimeId,
|
|
15799
15887
|
MLCLAW_SESSION_SECRET: params.sessionSecret,
|
|
15888
|
+
MLCLAW_CREDENTIAL_KEY: params.credentialKey,
|
|
15800
15889
|
MLCLAW_OPENCLAW_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT),
|
|
15801
15890
|
OPENCLAW_GATEWAY_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT),
|
|
15802
15891
|
...params.telegramToken ? { TELEGRAM_BOT_TOKEN: params.telegramToken } : {},
|
|
@@ -15815,10 +15904,14 @@ async function deploySpaceGateway(params) {
|
|
|
15815
15904
|
runtime.stdout.log(params.spaceExists ? `Updating existing Space ${manifest.space}` : `Creating ${params.publicSpace ? "public" : "private"} Space ${manifest.space}`);
|
|
15816
15905
|
await hub.createDockerSpace(manifest.space, {
|
|
15817
15906
|
private: !params.publicSpace,
|
|
15818
|
-
hardware: params.hardware,
|
|
15907
|
+
...params.hardware && !params.spaceExists ? { hardware: params.hardware } : {},
|
|
15819
15908
|
...typeof params.sleepTime === "number" ? { sleepTimeSeconds: params.sleepTime } : {}
|
|
15820
15909
|
});
|
|
15821
|
-
|
|
15910
|
+
if (params.hardware && params.spaceExists) {
|
|
15911
|
+
await hub.requestSpaceHardware(manifest.space, params.hardware, params.sleepTime);
|
|
15912
|
+
} else if (!params.hardware && params.spaceExists && typeof params.sleepTime === "number") {
|
|
15913
|
+
await hub.setSpaceSleepTime(manifest.space, params.sleepTime);
|
|
15914
|
+
}
|
|
15822
15915
|
runtime.stdout.log(params.templateRuntimeImage ? "Generating Space files from prebuilt runtime image" : "Generating bundled Space runtime files");
|
|
15823
15916
|
const { templateRev } = await runtime.pushTemplateToSpace({
|
|
15824
15917
|
targetRepo: manifest.space,
|
|
@@ -15828,6 +15921,9 @@ async function deploySpaceGateway(params) {
|
|
|
15828
15921
|
const spaceRuntimeRef = params.templateRuntimeImage ?? bundledSpaceRuntimeRef(templateRev);
|
|
15829
15922
|
await setDeploymentVariables(hub, manifest.space, {
|
|
15830
15923
|
OPENCLAW_HF_STATE_BUCKET: manifest.bucket,
|
|
15924
|
+
MLCLAW_STATE_MOUNT_DIR: SPACE_STATE_MOUNT_DIR,
|
|
15925
|
+
OPENCLAW_LIVE_DIR: SPACE_LIVE_DIR,
|
|
15926
|
+
MLCLAW_RUNTIME_SETTINGS_FILE: `${SPACE_LIVE_DIR}/.mlclaw/settings.json`,
|
|
15831
15927
|
...secrets.OPENCLAW_HF_STATE_PREFIX ? { OPENCLAW_HF_STATE_PREFIX: secrets.OPENCLAW_HF_STATE_PREFIX } : {},
|
|
15832
15928
|
MLCLAW_TEMPLATE_REV: templateRev,
|
|
15833
15929
|
OPENCLAW_MODEL: manifest.model,
|
|
@@ -15841,21 +15937,30 @@ async function deploySpaceGateway(params) {
|
|
|
15841
15937
|
MLCLAW_OPENCLAW_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT),
|
|
15842
15938
|
OPENCLAW_GATEWAY_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT)
|
|
15843
15939
|
});
|
|
15940
|
+
await ensureSpaceStateVolume(hub, manifest.space, manifest.bucket, { allowMissingVolumes: !params.spaceExists });
|
|
15844
15941
|
await clearSpaceGatewayDisabled(hub, manifest.space);
|
|
15845
15942
|
await setDeploymentSecrets(hub, manifest.space, {
|
|
15846
|
-
HF_TOKEN: requiredSecret(secrets, "HF_TOKEN"),
|
|
15847
|
-
HUGGINGFACE_HUB_TOKEN: requiredSecret(secrets, "HF_TOKEN"),
|
|
15848
15943
|
MLCLAW_SESSION_SECRET: requiredSecret(secrets, "MLCLAW_SESSION_SECRET"),
|
|
15944
|
+
MLCLAW_CREDENTIAL_KEY: requiredSecret(secrets, "MLCLAW_CREDENTIAL_KEY"),
|
|
15945
|
+
MLCLAW_BROKER_HF_TOKEN: hfToken,
|
|
15946
|
+
...secrets.MLCLAW_ROUTER_TOKEN ? { MLCLAW_ROUTER_TOKEN: secrets.MLCLAW_ROUTER_TOKEN } : {},
|
|
15849
15947
|
...secrets.TELEGRAM_BOT_TOKEN ? { TELEGRAM_BOT_TOKEN: secrets.TELEGRAM_BOT_TOKEN } : {},
|
|
15850
15948
|
...secrets.TELEGRAM_ALLOWED_USERS ? { TELEGRAM_ALLOWED_USERS: secrets.TELEGRAM_ALLOWED_USERS } : {},
|
|
15851
15949
|
...secrets.TELEGRAM_PROXY ? { TELEGRAM_PROXY: secrets.TELEGRAM_PROXY } : {},
|
|
15852
15950
|
...secrets.TELEGRAM_API_ROOT ? { TELEGRAM_API_ROOT: secrets.TELEGRAM_API_ROOT } : {}
|
|
15853
15951
|
});
|
|
15952
|
+
if (canDeleteBroadTokenSecrets({ model: manifest.model, routerTokenPresent: hasBrokerOrRouterTokenSecretRecord(secrets) })) {
|
|
15953
|
+
await deleteStaleSpaceTokenSecrets(hub, manifest.space);
|
|
15954
|
+
} else {
|
|
15955
|
+
runtime.stdout.log("Keeping legacy broad Hub token secrets until an HF Broker or Router credential is configured");
|
|
15956
|
+
}
|
|
15854
15957
|
await hub.restartSpace(manifest.space, true);
|
|
15855
15958
|
return { runtimeImage: spaceRuntimeRef };
|
|
15856
15959
|
}
|
|
15857
15960
|
async function startLocalGateway(params) {
|
|
15858
15961
|
const { manifest, runtime } = params;
|
|
15962
|
+
const secrets = await ensureDeploymentCredentialKey(runtime, manifest.agent);
|
|
15963
|
+
assertDedicatedRouterToken(manifest.model, secrets);
|
|
15859
15964
|
const containerName = containerNameFor(manifest.agent);
|
|
15860
15965
|
const volumeName = volumeNameFor(manifest.agent);
|
|
15861
15966
|
const dockerContext = dockerContextFor(manifest);
|
|
@@ -15928,6 +16033,17 @@ async function gatewayStart(agent, opts, runtime) {
|
|
|
15928
16033
|
runtime.stdout.log(`Space gateway restart requested: ${manifest.space}`);
|
|
15929
16034
|
}
|
|
15930
16035
|
}
|
|
16036
|
+
async function gatewayRestart(agent, opts, runtime) {
|
|
16037
|
+
const manifest = await readDeploymentManifest(runtime, agent, { requestedDockerContext: opts.dockerContext });
|
|
16038
|
+
if (manifest.gatewayLocation === "local") {
|
|
16039
|
+
assertDedicatedRouterToken(
|
|
16040
|
+
manifest.model,
|
|
16041
|
+
await readSecretEnv(runtime.configRoot, agent).catch(() => ({}))
|
|
16042
|
+
);
|
|
16043
|
+
}
|
|
16044
|
+
await gatewayStop(agent, runtime);
|
|
16045
|
+
await gatewayStart(agent, opts, runtime);
|
|
16046
|
+
}
|
|
15931
16047
|
async function gatewayStop(agent, runtime) {
|
|
15932
16048
|
const manifest = await readDeploymentManifest(runtime, agent);
|
|
15933
16049
|
const bucketPrefix = await readDeploymentBucketPrefix(runtime, agent);
|
|
@@ -15995,7 +16111,7 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
15995
16111
|
}
|
|
15996
16112
|
const token = await runtime.readToken(runtime.env);
|
|
15997
16113
|
const hub = runtime.hubFactory(token);
|
|
15998
|
-
const secrets = await
|
|
16114
|
+
const secrets = await ensureDeploymentCredentialKey(runtime, agent);
|
|
15999
16115
|
const bucketPrefix = persistedBucketPrefix(secrets);
|
|
16000
16116
|
const updated = {
|
|
16001
16117
|
...current,
|
|
@@ -16003,10 +16119,23 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
16003
16119
|
runtimeImage: resolveRuntimeImage(opts.runtimeImage ?? current.runtimeImage, runtime.env),
|
|
16004
16120
|
updatedAt: runtime.now().toISOString()
|
|
16005
16121
|
};
|
|
16122
|
+
const routerToken = await resolveRouterToken({
|
|
16123
|
+
opts,
|
|
16124
|
+
runtime,
|
|
16125
|
+
existingSecrets: secrets,
|
|
16126
|
+
model: updated.model
|
|
16127
|
+
});
|
|
16006
16128
|
if (target === "space") {
|
|
16129
|
+
const deploymentSecrets2 = {
|
|
16130
|
+
...secrets,
|
|
16131
|
+
MLCLAW_GATEWAY_LOCATION: "space",
|
|
16132
|
+
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage,
|
|
16133
|
+
...routerToken ? { MLCLAW_ROUTER_TOKEN: routerToken } : {}
|
|
16134
|
+
};
|
|
16007
16135
|
const paidHardware = await resolveHardware({
|
|
16008
|
-
requestedHardware: opts.hardware
|
|
16136
|
+
...opts.hardware ? { requestedHardware: opts.hardware } : {},
|
|
16009
16137
|
...typeof opts.sleepTime === "number" ? { requestedSleepTime: opts.sleepTime } : secrets.TELEGRAM_BOT_TOKEN ? { requestedSleepTime: TELEGRAM_SLEEP_TIME } : {},
|
|
16138
|
+
defaultLabel: "unchanged Space hardware",
|
|
16010
16139
|
requiresMessagingEgress: Boolean(secrets.TELEGRAM_BOT_TOKEN),
|
|
16011
16140
|
yes: Boolean(opts.yes),
|
|
16012
16141
|
runtime
|
|
@@ -16021,24 +16150,22 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
16021
16150
|
await handoffAndStopLocalGateway({ manifest: current, hub, runtime, bucketPrefix });
|
|
16022
16151
|
const me2 = await hub.whoami();
|
|
16023
16152
|
const templateRuntimeImage = resolveSpaceRuntimeImage(opts, runtime.env);
|
|
16153
|
+
const spaceExists = await hub.spaceExists(updated.space);
|
|
16024
16154
|
await deploySpaceGateway({
|
|
16025
16155
|
hub,
|
|
16026
16156
|
runtime,
|
|
16027
16157
|
hfToken: token,
|
|
16028
16158
|
manifest: updated,
|
|
16029
|
-
secrets:
|
|
16030
|
-
...secrets,
|
|
16031
|
-
MLCLAW_GATEWAY_LOCATION: "space",
|
|
16032
|
-
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage
|
|
16033
|
-
},
|
|
16159
|
+
secrets: deploymentSecrets2,
|
|
16034
16160
|
allowedUsers: me2.name,
|
|
16035
|
-
hardware: paidHardware.hardware,
|
|
16036
16161
|
publicSpace: Boolean(opts.publicSpace),
|
|
16162
|
+
spaceExists,
|
|
16163
|
+
...paidHardware.kind === "explicit" ? { hardware: paidHardware.hardware } : {},
|
|
16037
16164
|
...typeof paidHardware.sleepTime === "number" ? { sleepTime: paidHardware.sleepTime } : {},
|
|
16038
16165
|
...templateRuntimeImage ? { templateRuntimeImage } : {}
|
|
16039
16166
|
});
|
|
16040
16167
|
await writeSecretEnv(runtime.configRoot, agent, {
|
|
16041
|
-
...
|
|
16168
|
+
...deploymentSecrets2,
|
|
16042
16169
|
MLCLAW_GATEWAY_LOCATION: "space",
|
|
16043
16170
|
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage,
|
|
16044
16171
|
MLCLAW_RUNTIME_ID: spaceRuntimeId(agent)
|
|
@@ -16070,6 +16197,7 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
16070
16197
|
});
|
|
16071
16198
|
await writeSecretEnv(runtime.configRoot, agent, {
|
|
16072
16199
|
...secrets,
|
|
16200
|
+
...routerToken ? { MLCLAW_ROUTER_TOKEN: routerToken } : {},
|
|
16073
16201
|
MLCLAW_GATEWAY_LOCATION: "local",
|
|
16074
16202
|
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage,
|
|
16075
16203
|
MLCLAW_RUNTIME_ID: updated.localRuntimeId
|
|
@@ -16085,6 +16213,10 @@ async function gatewayRebind(agent, opts, runtime) {
|
|
|
16085
16213
|
if (current.gatewayLocation !== "local") {
|
|
16086
16214
|
throw new Error("Docker context rebind only applies to local gateway deployments");
|
|
16087
16215
|
}
|
|
16216
|
+
assertDedicatedRouterToken(
|
|
16217
|
+
current.model,
|
|
16218
|
+
await readSecretEnv(runtime.configRoot, agent).catch(() => ({}))
|
|
16219
|
+
);
|
|
16088
16220
|
const targetBinding = await resolveLocalGatewayBinding({
|
|
16089
16221
|
manifest: void 0,
|
|
16090
16222
|
requestedContext: targetContext,
|
|
@@ -16358,6 +16490,18 @@ function requiredSecret(secrets, key) {
|
|
|
16358
16490
|
}
|
|
16359
16491
|
return value;
|
|
16360
16492
|
}
|
|
16493
|
+
async function ensureDeploymentCredentialKey(runtime, agent, existing) {
|
|
16494
|
+
const secrets = existing ?? await readSecretEnv(runtime.configRoot, agent).catch(() => ({}));
|
|
16495
|
+
if (secrets.MLCLAW_CREDENTIAL_KEY) {
|
|
16496
|
+
return secrets;
|
|
16497
|
+
}
|
|
16498
|
+
const updated = {
|
|
16499
|
+
...secrets,
|
|
16500
|
+
MLCLAW_CREDENTIAL_KEY: randomBytes(32).toString("base64url")
|
|
16501
|
+
};
|
|
16502
|
+
await writeSecretEnv(runtime.configRoot, agent, updated);
|
|
16503
|
+
return updated;
|
|
16504
|
+
}
|
|
16361
16505
|
function requiredOption(value, label) {
|
|
16362
16506
|
if (!value) {
|
|
16363
16507
|
throw new Error(`${label} is required`);
|
|
@@ -16375,6 +16519,16 @@ async function update(repoId, opts, hub, hfToken, runtime) {
|
|
|
16375
16519
|
}
|
|
16376
16520
|
const runtimeImage = resolveSpaceRuntimeImage(opts, runtime.env);
|
|
16377
16521
|
const agentName = variables.get("OPENCLAW_AGENT_NAME")?.value?.trim() || repoId.split("/")[1] || "openclaw";
|
|
16522
|
+
if (!canonicalTemplate) {
|
|
16523
|
+
await ensureUpdateRouterToken({
|
|
16524
|
+
repoId,
|
|
16525
|
+
agentName,
|
|
16526
|
+
model: variables.get("OPENCLAW_MODEL")?.value ?? DEFAULT_MODEL,
|
|
16527
|
+
opts,
|
|
16528
|
+
hub,
|
|
16529
|
+
runtime
|
|
16530
|
+
});
|
|
16531
|
+
}
|
|
16378
16532
|
runtime.stdout.log(`Generating current Space files into ${repoId}`);
|
|
16379
16533
|
const { templateRev } = await runtime.pushTemplateToSpace({
|
|
16380
16534
|
targetRepo: repoId,
|
|
@@ -16393,9 +16547,50 @@ async function update(repoId, opts, hub, hfToken, runtime) {
|
|
|
16393
16547
|
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_ID", spaceRuntimeId(agentName));
|
|
16394
16548
|
await hub.addSpaceVariable(repoId, "MLCLAW_OPENCLAW_PORT", String(DEFAULT_SPACE_OPENCLAW_PORT));
|
|
16395
16549
|
await hub.addSpaceVariable(repoId, "OPENCLAW_GATEWAY_PORT", String(DEFAULT_SPACE_OPENCLAW_PORT));
|
|
16550
|
+
const bucket = variables.get("OPENCLAW_HF_STATE_BUCKET")?.value;
|
|
16551
|
+
if (bucket) {
|
|
16552
|
+
await hub.addSpaceVariable(repoId, "MLCLAW_STATE_MOUNT_DIR", SPACE_STATE_MOUNT_DIR);
|
|
16553
|
+
await hub.addSpaceVariable(repoId, "OPENCLAW_LIVE_DIR", SPACE_LIVE_DIR);
|
|
16554
|
+
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_SETTINGS_FILE", `${SPACE_LIVE_DIR}/.mlclaw/settings.json`);
|
|
16555
|
+
await ensureSpaceStateVolume(hub, repoId, bucket);
|
|
16556
|
+
}
|
|
16396
16557
|
await doctor(repoId, { fix: true }, hub, runtime);
|
|
16397
16558
|
await hub.restartSpace(repoId, true);
|
|
16398
16559
|
}
|
|
16560
|
+
async function ensureUpdateRouterToken(params) {
|
|
16561
|
+
if (!isHuggingFaceRouterModel(params.model)) {
|
|
16562
|
+
return;
|
|
16563
|
+
}
|
|
16564
|
+
const spaceSecrets = await params.hub.getSpaceSecrets(params.repoId);
|
|
16565
|
+
const hasExplicitOverride = params.opts.routerToken !== void 0 || params.opts.routerTokenFile !== void 0;
|
|
16566
|
+
if (hasBrokerOrRouterTokenSecretMap(spaceSecrets) && !hasExplicitOverride) {
|
|
16567
|
+
return;
|
|
16568
|
+
}
|
|
16569
|
+
const hasManifest = await manifestExists(params.runtime.configRoot, params.agentName);
|
|
16570
|
+
const localSecrets = hasManifest ? await readSecretEnv(params.runtime.configRoot, params.agentName).catch(() => ({})) : {};
|
|
16571
|
+
const routerToken = hasExplicitOverride ? await resolveRouterToken({
|
|
16572
|
+
opts: params.opts,
|
|
16573
|
+
runtime: params.runtime,
|
|
16574
|
+
existingSecrets: localSecrets,
|
|
16575
|
+
model: params.model
|
|
16576
|
+
}) : void 0;
|
|
16577
|
+
const brokerToken = routerToken ? void 0 : await params.runtime.readToken(params.runtime.env);
|
|
16578
|
+
const credential = routerToken ?? brokerToken;
|
|
16579
|
+
if (!credential) {
|
|
16580
|
+
throw new Error("Hugging Face broker credential is unavailable");
|
|
16581
|
+
}
|
|
16582
|
+
await params.hub.addSpaceSecret(
|
|
16583
|
+
params.repoId,
|
|
16584
|
+
routerToken ? "MLCLAW_ROUTER_TOKEN" : "MLCLAW_BROKER_HF_TOKEN",
|
|
16585
|
+
credential
|
|
16586
|
+
);
|
|
16587
|
+
if (hasManifest) {
|
|
16588
|
+
await writeSecretEnv(params.runtime.configRoot, params.agentName, {
|
|
16589
|
+
...localSecrets,
|
|
16590
|
+
...routerToken ? { MLCLAW_ROUTER_TOKEN: routerToken } : { MLCLAW_BROKER_HF_TOKEN: brokerToken }
|
|
16591
|
+
});
|
|
16592
|
+
}
|
|
16593
|
+
}
|
|
16399
16594
|
async function doctor(repoId, opts, hub, runtime) {
|
|
16400
16595
|
if (!repoId.includes("/") && await manifestExists(runtime.configRoot, repoId)) {
|
|
16401
16596
|
await gatewayStatus(repoId, runtime);
|
|
@@ -16454,6 +16649,31 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16454
16649
|
await hub.addSpaceVariable(repoId, "OPENCLAW_HF_STATE_BUCKET", bucket);
|
|
16455
16650
|
fixed.push("set OPENCLAW_HF_STATE_BUCKET");
|
|
16456
16651
|
}
|
|
16652
|
+
if ((variables.get("MLCLAW_STATE_MOUNT_DIR")?.value ?? "") !== SPACE_STATE_MOUNT_DIR) {
|
|
16653
|
+
if (fix) {
|
|
16654
|
+
await hub.addSpaceVariable(repoId, "MLCLAW_STATE_MOUNT_DIR", SPACE_STATE_MOUNT_DIR);
|
|
16655
|
+
fixed.push("set MLCLAW_STATE_MOUNT_DIR");
|
|
16656
|
+
} else {
|
|
16657
|
+
issues.push(`MLCLAW_STATE_MOUNT_DIR is not ${SPACE_STATE_MOUNT_DIR}`);
|
|
16658
|
+
}
|
|
16659
|
+
}
|
|
16660
|
+
if ((variables.get("OPENCLAW_LIVE_DIR")?.value ?? "") !== SPACE_LIVE_DIR) {
|
|
16661
|
+
if (fix) {
|
|
16662
|
+
await hub.addSpaceVariable(repoId, "OPENCLAW_LIVE_DIR", SPACE_LIVE_DIR);
|
|
16663
|
+
fixed.push("set OPENCLAW_LIVE_DIR");
|
|
16664
|
+
} else {
|
|
16665
|
+
issues.push(`OPENCLAW_LIVE_DIR is not ${SPACE_LIVE_DIR}`);
|
|
16666
|
+
}
|
|
16667
|
+
}
|
|
16668
|
+
const expectedRuntimeSettingsFile = `${SPACE_LIVE_DIR}/.mlclaw/settings.json`;
|
|
16669
|
+
if ((variables.get("MLCLAW_RUNTIME_SETTINGS_FILE")?.value ?? "") !== expectedRuntimeSettingsFile) {
|
|
16670
|
+
if (fix) {
|
|
16671
|
+
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_SETTINGS_FILE", expectedRuntimeSettingsFile);
|
|
16672
|
+
fixed.push("set MLCLAW_RUNTIME_SETTINGS_FILE");
|
|
16673
|
+
} else {
|
|
16674
|
+
issues.push(`MLCLAW_RUNTIME_SETTINGS_FILE is not ${expectedRuntimeSettingsFile}`);
|
|
16675
|
+
}
|
|
16676
|
+
}
|
|
16457
16677
|
for (const key of STALE_PATH_VARS) {
|
|
16458
16678
|
if (variables.has(key)) {
|
|
16459
16679
|
if (fix) {
|
|
@@ -16464,8 +16684,30 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16464
16684
|
}
|
|
16465
16685
|
}
|
|
16466
16686
|
}
|
|
16467
|
-
if (!secrets.has("
|
|
16468
|
-
|
|
16687
|
+
if (!secrets.has("MLCLAW_BROKER_HF_TOKEN")) {
|
|
16688
|
+
if (fix) {
|
|
16689
|
+
await hub.addSpaceSecret(repoId, "MLCLAW_BROKER_HF_TOKEN", await runtime.readToken(runtime.env));
|
|
16690
|
+
secrets.set("MLCLAW_BROKER_HF_TOKEN", { key: "MLCLAW_BROKER_HF_TOKEN" });
|
|
16691
|
+
fixed.push("set secret MLCLAW_BROKER_HF_TOKEN");
|
|
16692
|
+
} else {
|
|
16693
|
+
issues.push("secret MLCLAW_BROKER_HF_TOKEN is missing");
|
|
16694
|
+
}
|
|
16695
|
+
}
|
|
16696
|
+
const staleTokenSecrets = ["HF_TOKEN", "HUGGINGFACE_HUB_TOKEN"].filter((key) => secrets.has(key));
|
|
16697
|
+
if (staleTokenSecrets.length > 0) {
|
|
16698
|
+
const model = variables.get("OPENCLAW_MODEL")?.value ?? DEFAULT_MODEL;
|
|
16699
|
+
const canDelete = canDeleteBroadTokenSecrets({
|
|
16700
|
+
model,
|
|
16701
|
+
routerTokenPresent: hasBrokerOrRouterTokenSecretMap(secrets)
|
|
16702
|
+
});
|
|
16703
|
+
if (fix && canDelete) {
|
|
16704
|
+
await deleteStaleSpaceTokenSecrets(hub, repoId);
|
|
16705
|
+
fixed.push(`deleted stale secret${staleTokenSecrets.length === 1 ? "" : "s"} ${staleTokenSecrets.join(", ")}`);
|
|
16706
|
+
} else if (fix) {
|
|
16707
|
+
issues.push(`stale broad Hub token secret${staleTokenSecrets.length === 1 ? "" : "s"} present: ${staleTokenSecrets.join(", ")}; add MLCLAW_BROKER_HF_TOKEN before removing`);
|
|
16708
|
+
} else {
|
|
16709
|
+
issues.push(`stale broad Hub token secret${staleTokenSecrets.length === 1 ? "" : "s"} present: ${staleTokenSecrets.join(", ")}`);
|
|
16710
|
+
}
|
|
16469
16711
|
}
|
|
16470
16712
|
if (!secrets.has("MLCLAW_SESSION_SECRET")) {
|
|
16471
16713
|
if (fix) {
|
|
@@ -16475,6 +16717,16 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16475
16717
|
issues.push("secret MLCLAW_SESSION_SECRET is missing");
|
|
16476
16718
|
}
|
|
16477
16719
|
}
|
|
16720
|
+
if (!secrets.has("MLCLAW_CREDENTIAL_KEY")) {
|
|
16721
|
+
if (fix) {
|
|
16722
|
+
const agent = variables.get("OPENCLAW_AGENT_NAME")?.value?.trim() || repoId.split("/")[1] || "openclaw";
|
|
16723
|
+
const credentialKey = await manifestExists(runtime.configRoot, agent) ? requiredSecret(await ensureDeploymentCredentialKey(runtime, agent), "MLCLAW_CREDENTIAL_KEY") : randomBytes(32).toString("base64url");
|
|
16724
|
+
await hub.addSpaceSecret(repoId, "MLCLAW_CREDENTIAL_KEY", credentialKey);
|
|
16725
|
+
fixed.push("set secret MLCLAW_CREDENTIAL_KEY");
|
|
16726
|
+
} else {
|
|
16727
|
+
issues.push("secret MLCLAW_CREDENTIAL_KEY is missing");
|
|
16728
|
+
}
|
|
16729
|
+
}
|
|
16478
16730
|
if (!variables.has("MLCLAW_TEMPLATE_REV") && !variables.has("OPENCLAW_HF_TEMPLATE_REV")) {
|
|
16479
16731
|
issues.push("MLCLAW_TEMPLATE_REV is missing; updates cannot verify template lineage");
|
|
16480
16732
|
}
|
|
@@ -16514,6 +16766,14 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16514
16766
|
await hub.assertBucketAccessible(bucket);
|
|
16515
16767
|
}
|
|
16516
16768
|
const runtimeInfo = await hub.getSpaceRuntime(repoId);
|
|
16769
|
+
if (bucket && !hasStateVolume(runtimeInfo.volumes, bucket)) {
|
|
16770
|
+
if (fix) {
|
|
16771
|
+
await hub.setSpaceVolumes(repoId, mergeStateVolume(requireRuntimeVolumes(runtimeInfo, repoId), bucket));
|
|
16772
|
+
fixed.push(`mounted bucket ${bucket} at ${SPACE_STATE_MOUNT_DIR}`);
|
|
16773
|
+
} else {
|
|
16774
|
+
issues.push(`bucket ${bucket} is not mounted read-write at ${SPACE_STATE_MOUNT_DIR}`);
|
|
16775
|
+
}
|
|
16776
|
+
}
|
|
16517
16777
|
let logs = "";
|
|
16518
16778
|
try {
|
|
16519
16779
|
logs = await hub.fetchSpaceLogs(repoId, "run");
|
|
@@ -16614,6 +16874,79 @@ async function setDeploymentSecrets(hub, repoId, secrets) {
|
|
|
16614
16874
|
await hub.addSpaceSecret(repoId, key, value);
|
|
16615
16875
|
}
|
|
16616
16876
|
}
|
|
16877
|
+
async function deleteStaleSpaceTokenSecrets(hub, repoId) {
|
|
16878
|
+
await Promise.all([
|
|
16879
|
+
hub.deleteSpaceSecret(repoId, "HF_TOKEN"),
|
|
16880
|
+
hub.deleteSpaceSecret(repoId, "HUGGINGFACE_HUB_TOKEN")
|
|
16881
|
+
]);
|
|
16882
|
+
}
|
|
16883
|
+
function canDeleteBroadTokenSecrets(params) {
|
|
16884
|
+
return params.routerTokenPresent || !isHuggingFaceRouterModel(params.model);
|
|
16885
|
+
}
|
|
16886
|
+
function hasRouterTokenSecretRecord(secrets) {
|
|
16887
|
+
return Boolean(secrets.MLCLAW_ROUTER_TOKEN || secrets.HF_ROUTER_TOKEN);
|
|
16888
|
+
}
|
|
16889
|
+
function hasRouterTokenSecretMap(secrets) {
|
|
16890
|
+
return secrets.has("MLCLAW_ROUTER_TOKEN") || secrets.has("HF_ROUTER_TOKEN");
|
|
16891
|
+
}
|
|
16892
|
+
function hasBrokerOrRouterTokenSecretRecord(secrets) {
|
|
16893
|
+
return Boolean(secrets.MLCLAW_BROKER_HF_TOKEN) || hasRouterTokenSecretRecord(secrets);
|
|
16894
|
+
}
|
|
16895
|
+
function hasBrokerOrRouterTokenSecretMap(secrets) {
|
|
16896
|
+
return secrets.has("MLCLAW_BROKER_HF_TOKEN") || hasRouterTokenSecretMap(secrets);
|
|
16897
|
+
}
|
|
16898
|
+
function assertDedicatedRouterToken(model, secrets) {
|
|
16899
|
+
if (isHuggingFaceRouterModel(model) && !hasBrokerOrRouterTokenSecretRecord(secrets)) {
|
|
16900
|
+
throw new Error(
|
|
16901
|
+
"Hugging Face Router models require MLCLAW_BROKER_HF_TOKEN or a dedicated inference token"
|
|
16902
|
+
);
|
|
16903
|
+
}
|
|
16904
|
+
}
|
|
16905
|
+
async function ensureSpaceStateVolume(hub, repoId, bucket, opts = {}) {
|
|
16906
|
+
const runtime = await hub.getSpaceRuntime(repoId);
|
|
16907
|
+
const volumes = Array.isArray(runtime.volumes) ? runtime.volumes : opts.allowMissingVolumes ? [] : requireRuntimeVolumes(runtime, repoId);
|
|
16908
|
+
await hub.setSpaceVolumes(repoId, mergeStateVolume(volumes, bucket));
|
|
16909
|
+
}
|
|
16910
|
+
function requireRuntimeVolumes(runtime, repoId) {
|
|
16911
|
+
if (!Array.isArray(runtime.volumes)) {
|
|
16912
|
+
throw new Error(`Space runtime metadata for ${repoId} did not include volumes; refusing to replace mounts`);
|
|
16913
|
+
}
|
|
16914
|
+
return runtime.volumes;
|
|
16915
|
+
}
|
|
16916
|
+
function mergeStateVolume(existing, bucket) {
|
|
16917
|
+
return [
|
|
16918
|
+
...existing.filter((volume) => volumeMountPath(volume) !== SPACE_STATE_MOUNT_DIR).map(normalizeSpaceVolume),
|
|
16919
|
+
{
|
|
16920
|
+
type: "bucket",
|
|
16921
|
+
source: bucket,
|
|
16922
|
+
mountPath: SPACE_STATE_MOUNT_DIR,
|
|
16923
|
+
readOnly: false
|
|
16924
|
+
}
|
|
16925
|
+
];
|
|
16926
|
+
}
|
|
16927
|
+
function hasStateVolume(volumes, bucket) {
|
|
16928
|
+
return Boolean(volumes?.some(
|
|
16929
|
+
(volume) => volume.type === "bucket" && volume.source === bucket && volumeMountPath(volume) === SPACE_STATE_MOUNT_DIR && volumeReadOnly(volume) !== true
|
|
16930
|
+
));
|
|
16931
|
+
}
|
|
16932
|
+
function normalizeSpaceVolume(volume) {
|
|
16933
|
+
const normalized = { ...volume };
|
|
16934
|
+
const mountPath = volumeMountPath(volume);
|
|
16935
|
+
if (mountPath) {
|
|
16936
|
+
normalized.mountPath = mountPath;
|
|
16937
|
+
}
|
|
16938
|
+
const readOnly = volumeReadOnly(volume);
|
|
16939
|
+
if (typeof readOnly === "boolean") {
|
|
16940
|
+
normalized.readOnly = readOnly;
|
|
16941
|
+
}
|
|
16942
|
+
return normalized;
|
|
16943
|
+
}
|
|
16944
|
+
function volumeMountPath(volume) {
|
|
16945
|
+
return volume.mountPath ?? volume.mount_path;
|
|
16946
|
+
}
|
|
16947
|
+
function volumeReadOnly(volume) {
|
|
16948
|
+
return volume.readOnly ?? volume.read_only;
|
|
16949
|
+
}
|
|
16617
16950
|
async function clearSpaceGatewayDisabled(hub, repoId) {
|
|
16618
16951
|
try {
|
|
16619
16952
|
await hub.deleteSpaceVariable(repoId, "MLCLAW_GATEWAY_DISABLED");
|
|
@@ -16636,6 +16969,29 @@ async function readOptionalTelegramToken(opts, runtime) {
|
|
|
16636
16969
|
}
|
|
16637
16970
|
return void 0;
|
|
16638
16971
|
}
|
|
16972
|
+
async function resolveRouterToken(params) {
|
|
16973
|
+
const explicit = nonEmpty(params.opts.routerToken) ?? await readOptionalRouterTokenFile(params.opts.routerTokenFile);
|
|
16974
|
+
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;
|
|
16975
|
+
const existing = nonEmpty(direct);
|
|
16976
|
+
if (existing) {
|
|
16977
|
+
return existing;
|
|
16978
|
+
}
|
|
16979
|
+
if (!isHuggingFaceRouterModel(params.model)) {
|
|
16980
|
+
return void 0;
|
|
16981
|
+
}
|
|
16982
|
+
return void 0;
|
|
16983
|
+
}
|
|
16984
|
+
async function readOptionalRouterTokenFile(file) {
|
|
16985
|
+
if (!file) {
|
|
16986
|
+
return void 0;
|
|
16987
|
+
}
|
|
16988
|
+
const raw = await fs14.readFile(file, "utf8");
|
|
16989
|
+
const parsed = parseSecretEnv(raw);
|
|
16990
|
+
return nonEmpty(parsed.MLCLAW_ROUTER_TOKEN) ?? nonEmpty(parsed.HF_ROUTER_TOKEN) ?? nonEmpty(raw);
|
|
16991
|
+
}
|
|
16992
|
+
function isHuggingFaceRouterModel(model) {
|
|
16993
|
+
return model.trim().startsWith("huggingface/");
|
|
16994
|
+
}
|
|
16639
16995
|
async function promptAgentName(runtime) {
|
|
16640
16996
|
if (!runtime.prompt.isInteractive()) {
|
|
16641
16997
|
return "mlclaw";
|
|
@@ -16648,21 +17004,26 @@ async function promptAgentName(runtime) {
|
|
|
16648
17004
|
return readPromptValue(value, "Agent name");
|
|
16649
17005
|
}
|
|
16650
17006
|
async function resolveHardware(params) {
|
|
16651
|
-
const hardware = params.requestedHardware;
|
|
16652
|
-
|
|
17007
|
+
const hardware = params.requestedHardware ?? (params.requiresMessagingEgress ? TELEGRAM_HARDWARE : void 0);
|
|
17008
|
+
if (!hardware) {
|
|
17009
|
+
const label = params.defaultLabel ?? "default free CPU";
|
|
17010
|
+
return typeof params.requestedSleepTime === "number" ? { kind: "default", label, sleepTime: params.requestedSleepTime } : { kind: "default", label };
|
|
17011
|
+
}
|
|
17012
|
+
const sleepTime = isPaidHardware(hardware) ? params.requestedSleepTime ?? TELEGRAM_SLEEP_TIME : params.requestedSleepTime;
|
|
16653
17013
|
if (params.requiresMessagingEgress && !isPaidHardware(hardware)) {
|
|
16654
17014
|
throw new Error(`Telegram requires upgraded paid Space hardware today; use --hardware ${TELEGRAM_HARDWARE} or --gateway local`);
|
|
16655
17015
|
}
|
|
16656
17016
|
if (isPaidHardware(hardware)) {
|
|
17017
|
+
const paidSleepTime = params.requestedSleepTime ?? TELEGRAM_SLEEP_TIME;
|
|
16657
17018
|
await confirmPaidHardware({
|
|
16658
17019
|
hardware,
|
|
16659
|
-
sleepTime,
|
|
17020
|
+
sleepTime: paidSleepTime,
|
|
16660
17021
|
yes: params.yes,
|
|
16661
17022
|
runtime: params.runtime
|
|
16662
17023
|
});
|
|
16663
|
-
return { hardware, sleepTime };
|
|
17024
|
+
return { kind: "explicit", hardware, label: hardware, sleepTime: paidSleepTime };
|
|
16664
17025
|
}
|
|
16665
|
-
return typeof
|
|
17026
|
+
return typeof sleepTime === "number" ? { kind: "explicit", hardware, label: hardware, sleepTime } : { kind: "explicit", hardware, label: hardware };
|
|
16666
17027
|
}
|
|
16667
17028
|
async function confirmPaidHardware(params) {
|
|
16668
17029
|
if (params.yes) {
|
|
@@ -16746,8 +17107,11 @@ export {
|
|
|
16746
17107
|
LOCAL_VOLUME_MOUNT_PATH,
|
|
16747
17108
|
SPACE_HANDOFF_POLL_MS,
|
|
16748
17109
|
SPACE_HANDOFF_TIMEOUT_MS,
|
|
17110
|
+
SPACE_LIVE_DIR,
|
|
17111
|
+
SPACE_STATE_MOUNT_DIR,
|
|
16749
17112
|
TELEGRAM_HARDWARE,
|
|
16750
17113
|
TELEGRAM_SLEEP_TIME,
|
|
16751
17114
|
createProgram,
|
|
16752
|
-
main
|
|
17115
|
+
main,
|
|
17116
|
+
mergeStateVolume
|
|
16753
17117
|
};
|