mlclaw 0.2.2 → 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 +74 -33
- package/Dockerfile +47 -8
- package/README.md +89 -29
- package/assets/assistant-avatar.svg +5 -0
- 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 +8435 -5949
- package/dist/mlclaw.mjs +408 -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}`;
|
|
@@ -14903,6 +14942,7 @@ async function currentTemplateRev(sourceDir) {
|
|
|
14903
14942
|
async function generateSpaceRepo(sourceDir, outDir, options = {}) {
|
|
14904
14943
|
const copies = [
|
|
14905
14944
|
[".gitattributes", ".gitattributes"],
|
|
14945
|
+
["assets/assistant-avatar.svg", "assets/assistant-avatar.svg"],
|
|
14906
14946
|
["assets/hf-logo.svg", "assets/hf-logo.svg"],
|
|
14907
14947
|
["assets/mlclaw.svg", "assets/mlclaw.svg"],
|
|
14908
14948
|
["assets/mlclaw-control-ui", "assets/mlclaw-control-ui"],
|
|
@@ -14915,6 +14955,7 @@ async function generateSpaceRepo(sourceDir, outDir, options = {}) {
|
|
|
14915
14955
|
["dist/hf-tooling-seed.js", "runtime/hf-tooling-seed.js"],
|
|
14916
14956
|
["dist/mlclaw-space-runtime.js", "runtime/mlclaw-space-runtime.js"],
|
|
14917
14957
|
["entrypoint.sh", "runtime/entrypoint.sh"],
|
|
14958
|
+
["hf-broker.scope.json", "runtime/hf-broker.scope.json"],
|
|
14918
14959
|
["openclaw.default.json", "runtime/openclaw.default.json"],
|
|
14919
14960
|
["scripts/configure-huggingface-model.mjs", "runtime/scripts/configure-huggingface-model.mjs"],
|
|
14920
14961
|
["scripts/configure-telegram.mjs", "runtime/scripts/configure-telegram.mjs"],
|
|
@@ -14935,14 +14976,34 @@ function imageDockerfile(runtimeImage) {
|
|
|
14935
14976
|
`;
|
|
14936
14977
|
}
|
|
14937
14978
|
function bundledDockerfile() {
|
|
14938
|
-
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}
|
|
14939
14999
|
|
|
14940
15000
|
LABEL org.opencontainers.image.source="https://github.com/osolmaz/mlclaw"
|
|
14941
15001
|
LABEL org.opencontainers.image.description="ML Claw runtime for OpenClaw on Hugging Face"
|
|
14942
15002
|
|
|
14943
15003
|
USER root
|
|
14944
15004
|
RUN apt-get update \\
|
|
14945
|
-
&& 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 \\
|
|
14946
15007
|
&& rm -rf /var/lib/apt/lists/*
|
|
14947
15008
|
RUN python3 -m pip install --break-system-packages --no-cache-dir \\
|
|
14948
15009
|
"huggingface_hub==1.19.0" \\
|
|
@@ -14956,10 +15017,15 @@ RUN python3 -m pip install --break-system-packages --no-cache-dir \\
|
|
|
14956
15017
|
"uvicorn==0.49.0" \\
|
|
14957
15018
|
"uv==0.11.28" \\
|
|
14958
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
|
|
14959
15023
|
|
|
14960
15024
|
COPY --chown=node:node runtime/hf-state-sync.js /app/hf-state-sync.js
|
|
14961
15025
|
COPY --chown=node:node runtime/hf-tooling-seed.js /app/hf-tooling-seed.js
|
|
14962
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
|
|
14963
15029
|
COPY --chown=node:node runtime/openclaw.default.json /app/openclaw.default.json
|
|
14964
15030
|
COPY --chown=node:node runtime/entrypoint.sh /app/entrypoint.sh
|
|
14965
15031
|
COPY --chown=node:node runtime/scripts/ /app/scripts/
|
|
@@ -14969,11 +15035,12 @@ RUN chmod +x /app/entrypoint.sh
|
|
|
14969
15035
|
ENV PORT=7860
|
|
14970
15036
|
ENV MLCLAW_OPENCLAW_PORT=7861
|
|
14971
15037
|
ENV OPENCLAW_GATEWAY_PORT=7861
|
|
14972
|
-
ENV OPENCLAW_LIVE_DIR=/
|
|
14973
|
-
ENV OPENCLAW_STATE_DIR=/
|
|
14974
|
-
ENV OPENCLAW_WORKSPACE_DIR=/
|
|
14975
|
-
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
|
|
14976
15042
|
ENV OPENCLAW_DISABLE_BONJOUR=1
|
|
15043
|
+
ENV MLCLAW_BROKERKIT_PLUGIN_PATH=/opt/openclaw-plugins/node_modules/openclaw-brokerkit
|
|
14977
15044
|
|
|
14978
15045
|
EXPOSE 7860
|
|
14979
15046
|
|
|
@@ -15258,6 +15325,8 @@ var DEFAULT_LOCAL_PORT = 7860;
|
|
|
15258
15325
|
var DEFAULT_SPACE_OPENCLAW_PORT = 7861;
|
|
15259
15326
|
var LOCAL_VOLUME_MOUNT_PATH = "/tmp/mlclaw-local";
|
|
15260
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";
|
|
15261
15330
|
var SPACE_HANDOFF_TIMEOUT_MS = 12e4;
|
|
15262
15331
|
var SPACE_HANDOFF_POLL_MS = 5e3;
|
|
15263
15332
|
var STALE_PATH_VARS = ["OPENCLAW_STATE_DIR", "OPENCLAW_WORKSPACE_DIR", "OPENCLAW_CONFIG_PATH"];
|
|
@@ -15295,10 +15364,10 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15295
15364
|
program2.name("mlclaw").description("Deploy OpenClaw to a Hugging Face Space and private bucket").showHelpAfterError().exitOverride((err) => {
|
|
15296
15365
|
throw err;
|
|
15297
15366
|
});
|
|
15298
|
-
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) => {
|
|
15299
15368
|
await bootstrap(opts, runtime);
|
|
15300
15369
|
});
|
|
15301
|
-
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) => {
|
|
15302
15371
|
const token = await runtime.readToken(runtime.env);
|
|
15303
15372
|
const hub = runtime.hubFactory(token);
|
|
15304
15373
|
await update(repoId, opts, hub, token, runtime);
|
|
@@ -15321,8 +15390,7 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15321
15390
|
await gatewayStop(agent, runtime);
|
|
15322
15391
|
});
|
|
15323
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) => {
|
|
15324
|
-
await
|
|
15325
|
-
await gatewayStart(agent, opts, runtime);
|
|
15393
|
+
await gatewayRestart(agent, opts, runtime);
|
|
15326
15394
|
});
|
|
15327
15395
|
gateway.command("status").argument("<agent>", "Agent name").action(async (agent) => {
|
|
15328
15396
|
await gatewayStatus(agent, runtime);
|
|
@@ -15330,7 +15398,7 @@ function createProgram(runtimeOverrides = {}) {
|
|
|
15330
15398
|
gateway.command("logs").argument("<agent>", "Agent name").option("--tail <lines>", "Number of log lines", parseInteger, 200).action(async (agent, opts) => {
|
|
15331
15399
|
await gatewayLogs(agent, opts, runtime);
|
|
15332
15400
|
});
|
|
15333
|
-
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) => {
|
|
15334
15402
|
await gatewayMigrate(agent, opts, runtime);
|
|
15335
15403
|
});
|
|
15336
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) => {
|
|
@@ -15403,8 +15471,9 @@ async function bootstrap(opts, runtime) {
|
|
|
15403
15471
|
throw new Error("internal error: Space plan was not resolved");
|
|
15404
15472
|
}
|
|
15405
15473
|
const paidHardware = await resolveHardware({
|
|
15406
|
-
requestedHardware: opts.hardware
|
|
15474
|
+
...opts.hardware ? { requestedHardware: opts.hardware } : {},
|
|
15407
15475
|
...typeof opts.sleepTime === "number" ? { requestedSleepTime: opts.sleepTime } : telegramToken ? { requestedSleepTime: TELEGRAM_SLEEP_TIME } : {},
|
|
15476
|
+
defaultLabel: spacePlan.exists ? "unchanged Space hardware" : "default free CPU",
|
|
15408
15477
|
requiresMessagingEgress: Boolean(telegramToken),
|
|
15409
15478
|
yes: Boolean(opts.yes),
|
|
15410
15479
|
runtime
|
|
@@ -15414,7 +15483,7 @@ async function bootstrap(opts, runtime) {
|
|
|
15414
15483
|
bucketPlan,
|
|
15415
15484
|
spacePlan,
|
|
15416
15485
|
hasExistingManifest: plan.hasExistingManifest,
|
|
15417
|
-
hardware: paidHardware.
|
|
15486
|
+
hardware: paidHardware.label,
|
|
15418
15487
|
...typeof paidHardware.sleepTime === "number" ? { sleepTime: paidHardware.sleepTime } : {},
|
|
15419
15488
|
yes: Boolean(opts.yes),
|
|
15420
15489
|
runtime
|
|
@@ -15434,9 +15503,9 @@ async function bootstrap(opts, runtime) {
|
|
|
15434
15503
|
manifest,
|
|
15435
15504
|
secrets,
|
|
15436
15505
|
allowedUsers: me2.name,
|
|
15437
|
-
hardware: paidHardware.hardware,
|
|
15438
15506
|
spaceExists: spacePlan.exists,
|
|
15439
15507
|
publicSpace: Boolean(opts.publicSpace),
|
|
15508
|
+
...paidHardware.kind === "explicit" ? { hardware: paidHardware.hardware } : {},
|
|
15440
15509
|
...typeof paidHardware.sleepTime === "number" ? { sleepTime: paidHardware.sleepTime } : {},
|
|
15441
15510
|
...templateRuntimeImage ? { templateRuntimeImage } : {}
|
|
15442
15511
|
});
|
|
@@ -15492,10 +15561,11 @@ function spacePublicUrl(repoId) {
|
|
|
15492
15561
|
async function resolveBootstrapPlan(params) {
|
|
15493
15562
|
const { opts, owner, agentName, requestedGatewayLocation, hfToken, telegramToken, telegramUserId, model, runtimeImage, hub, runtime } = params;
|
|
15494
15563
|
const names = namesFor(owner, agentName);
|
|
15495
|
-
const sessionSecret = randomBytes(48).toString("base64url");
|
|
15496
15564
|
const now = runtime.now().toISOString();
|
|
15497
15565
|
const existingManifest = await readManifest(runtime.configRoot, agentName).catch(() => null);
|
|
15498
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");
|
|
15499
15569
|
const gatewayLocation = requestedGatewayLocation ?? existingManifest?.gatewayLocation ?? DEFAULT_GATEWAY_LOCATION;
|
|
15500
15570
|
if (opts.dockerContext && gatewayLocation !== "local") {
|
|
15501
15571
|
throw new Error("--docker-context only applies to local gateway mode");
|
|
@@ -15516,6 +15586,12 @@ async function resolveBootstrapPlan(params) {
|
|
|
15516
15586
|
hub
|
|
15517
15587
|
});
|
|
15518
15588
|
const bucket = bucketPlan.bucket;
|
|
15589
|
+
const routerToken = await resolveRouterToken({
|
|
15590
|
+
opts,
|
|
15591
|
+
runtime,
|
|
15592
|
+
existingSecrets,
|
|
15593
|
+
model
|
|
15594
|
+
});
|
|
15519
15595
|
const spacePlan = gatewayLocation === "space" ? {
|
|
15520
15596
|
space: names.space,
|
|
15521
15597
|
exists: await hub.spaceExists(names.space),
|
|
@@ -15540,6 +15616,7 @@ async function resolveBootstrapPlan(params) {
|
|
|
15540
15616
|
...telegramToken ? { telegramToken } : {},
|
|
15541
15617
|
...telegramUserId ? { telegramUserId } : {},
|
|
15542
15618
|
sessionSecret,
|
|
15619
|
+
credentialKey,
|
|
15543
15620
|
bucket,
|
|
15544
15621
|
model,
|
|
15545
15622
|
agentName,
|
|
@@ -15548,7 +15625,8 @@ async function resolveBootstrapPlan(params) {
|
|
|
15548
15625
|
runtimeId: gatewayLocation === "local" ? manifest.localRuntimeId : spaceRuntimeId(agentName),
|
|
15549
15626
|
...bucketPrefix ? { bucketPrefix } : {},
|
|
15550
15627
|
...opts.telegramProxy ? { telegramProxy: opts.telegramProxy } : {},
|
|
15551
|
-
...opts.telegramApiRoot ? { telegramApiRoot: opts.telegramApiRoot } : {}
|
|
15628
|
+
...opts.telegramApiRoot ? { telegramApiRoot: opts.telegramApiRoot } : {},
|
|
15629
|
+
...routerToken ? { routerToken } : {}
|
|
15552
15630
|
});
|
|
15553
15631
|
return {
|
|
15554
15632
|
agentName,
|
|
@@ -15610,6 +15688,8 @@ async function confirmBootstrapPlan(params) {
|
|
|
15610
15688
|
if (params.spacePlan) {
|
|
15611
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}`})`);
|
|
15612
15690
|
lines.push(`Hardware: ${params.hardware}`);
|
|
15691
|
+
lines.push(`Bucket mount: ${SPACE_STATE_MOUNT_DIR}`);
|
|
15692
|
+
lines.push(`Live state: ${SPACE_LIVE_DIR}`);
|
|
15613
15693
|
if (typeof params.sleepTime === "number") {
|
|
15614
15694
|
lines.push(`Sleep time: ${params.sleepTime}`);
|
|
15615
15695
|
}
|
|
@@ -15649,6 +15729,10 @@ async function stateAdopt(agent, opts, runtime) {
|
|
|
15649
15729
|
const hub = runtime.hubFactory(token);
|
|
15650
15730
|
const secrets = await readSecretEnv(runtime.configRoot, agent);
|
|
15651
15731
|
const bucketPrefix = persistedBucketPrefix(secrets);
|
|
15732
|
+
const bucketChanged = current.bucket !== bucket;
|
|
15733
|
+
if (bucketChanged && current.gatewayLocation === "local") {
|
|
15734
|
+
assertDedicatedRouterToken(current.model, secrets);
|
|
15735
|
+
}
|
|
15652
15736
|
runtime.stdout.log(`Creating or adopting private bucket ${bucket}`);
|
|
15653
15737
|
await hub.createBucket(bucket, true);
|
|
15654
15738
|
await inspectStateBucket(hub, bucket, bucketPrefix);
|
|
@@ -15659,7 +15743,6 @@ async function stateAdopt(agent, opts, runtime) {
|
|
|
15659
15743
|
runtimeId: runtimeIdFor(current),
|
|
15660
15744
|
takeover: Boolean(opts.takeover)
|
|
15661
15745
|
});
|
|
15662
|
-
const bucketChanged = current.bucket !== bucket;
|
|
15663
15746
|
if (bucketChanged) {
|
|
15664
15747
|
await confirmBucketChange({
|
|
15665
15748
|
message: `Adopt state bucket ${bucket} for ${agent}, replacing ${current.bucket}?`,
|
|
@@ -15710,9 +15793,16 @@ async function stateAdopt(agent, opts, runtime) {
|
|
|
15710
15793
|
} else {
|
|
15711
15794
|
await setDeploymentVariables(hub, updated.space, {
|
|
15712
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`,
|
|
15713
15799
|
MLCLAW_GATEWAY_LOCATION: "space",
|
|
15714
15800
|
MLCLAW_RUNTIME_ID: spaceRuntimeId(updated.agent)
|
|
15715
15801
|
});
|
|
15802
|
+
await ensureSpaceStateVolume(hub, updated.space, bucket);
|
|
15803
|
+
if (canDeleteBroadTokenSecrets({ model: updated.model, routerTokenPresent: hasBrokerOrRouterTokenSecretRecord(secrets) })) {
|
|
15804
|
+
await deleteStaleSpaceTokenSecrets(hub, updated.space);
|
|
15805
|
+
}
|
|
15716
15806
|
await clearSpaceGatewayDisabled(hub, updated.space);
|
|
15717
15807
|
if (bucketChanged) {
|
|
15718
15808
|
await hub.restartSpace(updated.space, true);
|
|
@@ -15787,8 +15877,8 @@ function parseBucketId(raw) {
|
|
|
15787
15877
|
}
|
|
15788
15878
|
function deploymentSecrets(params) {
|
|
15789
15879
|
return {
|
|
15790
|
-
|
|
15791
|
-
|
|
15880
|
+
MLCLAW_BROKER_HF_TOKEN: params.hfToken,
|
|
15881
|
+
...params.routerToken ? { MLCLAW_ROUTER_TOKEN: params.routerToken } : {},
|
|
15792
15882
|
OPENCLAW_HF_STATE_BUCKET: params.bucket,
|
|
15793
15883
|
OPENCLAW_MODEL: params.model,
|
|
15794
15884
|
OPENCLAW_AGENT_NAME: params.agentName,
|
|
@@ -15796,6 +15886,7 @@ function deploymentSecrets(params) {
|
|
|
15796
15886
|
MLCLAW_RUNTIME_IMAGE: params.runtimeImage,
|
|
15797
15887
|
MLCLAW_RUNTIME_ID: params.runtimeId,
|
|
15798
15888
|
MLCLAW_SESSION_SECRET: params.sessionSecret,
|
|
15889
|
+
MLCLAW_CREDENTIAL_KEY: params.credentialKey,
|
|
15799
15890
|
MLCLAW_OPENCLAW_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT),
|
|
15800
15891
|
OPENCLAW_GATEWAY_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT),
|
|
15801
15892
|
...params.telegramToken ? { TELEGRAM_BOT_TOKEN: params.telegramToken } : {},
|
|
@@ -15814,10 +15905,14 @@ async function deploySpaceGateway(params) {
|
|
|
15814
15905
|
runtime.stdout.log(params.spaceExists ? `Updating existing Space ${manifest.space}` : `Creating ${params.publicSpace ? "public" : "private"} Space ${manifest.space}`);
|
|
15815
15906
|
await hub.createDockerSpace(manifest.space, {
|
|
15816
15907
|
private: !params.publicSpace,
|
|
15817
|
-
hardware: params.hardware,
|
|
15908
|
+
...params.hardware && !params.spaceExists ? { hardware: params.hardware } : {},
|
|
15818
15909
|
...typeof params.sleepTime === "number" ? { sleepTimeSeconds: params.sleepTime } : {}
|
|
15819
15910
|
});
|
|
15820
|
-
|
|
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
|
+
}
|
|
15821
15916
|
runtime.stdout.log(params.templateRuntimeImage ? "Generating Space files from prebuilt runtime image" : "Generating bundled Space runtime files");
|
|
15822
15917
|
const { templateRev } = await runtime.pushTemplateToSpace({
|
|
15823
15918
|
targetRepo: manifest.space,
|
|
@@ -15827,6 +15922,9 @@ async function deploySpaceGateway(params) {
|
|
|
15827
15922
|
const spaceRuntimeRef = params.templateRuntimeImage ?? bundledSpaceRuntimeRef(templateRev);
|
|
15828
15923
|
await setDeploymentVariables(hub, manifest.space, {
|
|
15829
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`,
|
|
15830
15928
|
...secrets.OPENCLAW_HF_STATE_PREFIX ? { OPENCLAW_HF_STATE_PREFIX: secrets.OPENCLAW_HF_STATE_PREFIX } : {},
|
|
15831
15929
|
MLCLAW_TEMPLATE_REV: templateRev,
|
|
15832
15930
|
OPENCLAW_MODEL: manifest.model,
|
|
@@ -15840,21 +15938,30 @@ async function deploySpaceGateway(params) {
|
|
|
15840
15938
|
MLCLAW_OPENCLAW_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT),
|
|
15841
15939
|
OPENCLAW_GATEWAY_PORT: String(DEFAULT_SPACE_OPENCLAW_PORT)
|
|
15842
15940
|
});
|
|
15941
|
+
await ensureSpaceStateVolume(hub, manifest.space, manifest.bucket, { allowMissingVolumes: !params.spaceExists });
|
|
15843
15942
|
await clearSpaceGatewayDisabled(hub, manifest.space);
|
|
15844
15943
|
await setDeploymentSecrets(hub, manifest.space, {
|
|
15845
|
-
HF_TOKEN: requiredSecret(secrets, "HF_TOKEN"),
|
|
15846
|
-
HUGGINGFACE_HUB_TOKEN: requiredSecret(secrets, "HF_TOKEN"),
|
|
15847
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 } : {},
|
|
15848
15948
|
...secrets.TELEGRAM_BOT_TOKEN ? { TELEGRAM_BOT_TOKEN: secrets.TELEGRAM_BOT_TOKEN } : {},
|
|
15849
15949
|
...secrets.TELEGRAM_ALLOWED_USERS ? { TELEGRAM_ALLOWED_USERS: secrets.TELEGRAM_ALLOWED_USERS } : {},
|
|
15850
15950
|
...secrets.TELEGRAM_PROXY ? { TELEGRAM_PROXY: secrets.TELEGRAM_PROXY } : {},
|
|
15851
15951
|
...secrets.TELEGRAM_API_ROOT ? { TELEGRAM_API_ROOT: secrets.TELEGRAM_API_ROOT } : {}
|
|
15852
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
|
+
}
|
|
15853
15958
|
await hub.restartSpace(manifest.space, true);
|
|
15854
15959
|
return { runtimeImage: spaceRuntimeRef };
|
|
15855
15960
|
}
|
|
15856
15961
|
async function startLocalGateway(params) {
|
|
15857
15962
|
const { manifest, runtime } = params;
|
|
15963
|
+
const secrets = await ensureDeploymentCredentialKey(runtime, manifest.agent);
|
|
15964
|
+
assertDedicatedRouterToken(manifest.model, secrets);
|
|
15858
15965
|
const containerName = containerNameFor(manifest.agent);
|
|
15859
15966
|
const volumeName = volumeNameFor(manifest.agent);
|
|
15860
15967
|
const dockerContext = dockerContextFor(manifest);
|
|
@@ -15927,6 +16034,17 @@ async function gatewayStart(agent, opts, runtime) {
|
|
|
15927
16034
|
runtime.stdout.log(`Space gateway restart requested: ${manifest.space}`);
|
|
15928
16035
|
}
|
|
15929
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
|
+
}
|
|
15930
16048
|
async function gatewayStop(agent, runtime) {
|
|
15931
16049
|
const manifest = await readDeploymentManifest(runtime, agent);
|
|
15932
16050
|
const bucketPrefix = await readDeploymentBucketPrefix(runtime, agent);
|
|
@@ -15994,7 +16112,7 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
15994
16112
|
}
|
|
15995
16113
|
const token = await runtime.readToken(runtime.env);
|
|
15996
16114
|
const hub = runtime.hubFactory(token);
|
|
15997
|
-
const secrets = await
|
|
16115
|
+
const secrets = await ensureDeploymentCredentialKey(runtime, agent);
|
|
15998
16116
|
const bucketPrefix = persistedBucketPrefix(secrets);
|
|
15999
16117
|
const updated = {
|
|
16000
16118
|
...current,
|
|
@@ -16002,10 +16120,23 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
16002
16120
|
runtimeImage: resolveRuntimeImage(opts.runtimeImage ?? current.runtimeImage, runtime.env),
|
|
16003
16121
|
updatedAt: runtime.now().toISOString()
|
|
16004
16122
|
};
|
|
16123
|
+
const routerToken = await resolveRouterToken({
|
|
16124
|
+
opts,
|
|
16125
|
+
runtime,
|
|
16126
|
+
existingSecrets: secrets,
|
|
16127
|
+
model: updated.model
|
|
16128
|
+
});
|
|
16005
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
|
+
};
|
|
16006
16136
|
const paidHardware = await resolveHardware({
|
|
16007
|
-
requestedHardware: opts.hardware
|
|
16137
|
+
...opts.hardware ? { requestedHardware: opts.hardware } : {},
|
|
16008
16138
|
...typeof opts.sleepTime === "number" ? { requestedSleepTime: opts.sleepTime } : secrets.TELEGRAM_BOT_TOKEN ? { requestedSleepTime: TELEGRAM_SLEEP_TIME } : {},
|
|
16139
|
+
defaultLabel: "unchanged Space hardware",
|
|
16009
16140
|
requiresMessagingEgress: Boolean(secrets.TELEGRAM_BOT_TOKEN),
|
|
16010
16141
|
yes: Boolean(opts.yes),
|
|
16011
16142
|
runtime
|
|
@@ -16020,24 +16151,22 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
16020
16151
|
await handoffAndStopLocalGateway({ manifest: current, hub, runtime, bucketPrefix });
|
|
16021
16152
|
const me2 = await hub.whoami();
|
|
16022
16153
|
const templateRuntimeImage = resolveSpaceRuntimeImage(opts, runtime.env);
|
|
16154
|
+
const spaceExists = await hub.spaceExists(updated.space);
|
|
16023
16155
|
await deploySpaceGateway({
|
|
16024
16156
|
hub,
|
|
16025
16157
|
runtime,
|
|
16026
16158
|
hfToken: token,
|
|
16027
16159
|
manifest: updated,
|
|
16028
|
-
secrets:
|
|
16029
|
-
...secrets,
|
|
16030
|
-
MLCLAW_GATEWAY_LOCATION: "space",
|
|
16031
|
-
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage
|
|
16032
|
-
},
|
|
16160
|
+
secrets: deploymentSecrets2,
|
|
16033
16161
|
allowedUsers: me2.name,
|
|
16034
|
-
hardware: paidHardware.hardware,
|
|
16035
16162
|
publicSpace: Boolean(opts.publicSpace),
|
|
16163
|
+
spaceExists,
|
|
16164
|
+
...paidHardware.kind === "explicit" ? { hardware: paidHardware.hardware } : {},
|
|
16036
16165
|
...typeof paidHardware.sleepTime === "number" ? { sleepTime: paidHardware.sleepTime } : {},
|
|
16037
16166
|
...templateRuntimeImage ? { templateRuntimeImage } : {}
|
|
16038
16167
|
});
|
|
16039
16168
|
await writeSecretEnv(runtime.configRoot, agent, {
|
|
16040
|
-
...
|
|
16169
|
+
...deploymentSecrets2,
|
|
16041
16170
|
MLCLAW_GATEWAY_LOCATION: "space",
|
|
16042
16171
|
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage,
|
|
16043
16172
|
MLCLAW_RUNTIME_ID: spaceRuntimeId(agent)
|
|
@@ -16069,6 +16198,7 @@ async function gatewayMigrate(agent, opts, runtime) {
|
|
|
16069
16198
|
});
|
|
16070
16199
|
await writeSecretEnv(runtime.configRoot, agent, {
|
|
16071
16200
|
...secrets,
|
|
16201
|
+
...routerToken ? { MLCLAW_ROUTER_TOKEN: routerToken } : {},
|
|
16072
16202
|
MLCLAW_GATEWAY_LOCATION: "local",
|
|
16073
16203
|
MLCLAW_RUNTIME_IMAGE: updated.runtimeImage,
|
|
16074
16204
|
MLCLAW_RUNTIME_ID: updated.localRuntimeId
|
|
@@ -16084,6 +16214,10 @@ async function gatewayRebind(agent, opts, runtime) {
|
|
|
16084
16214
|
if (current.gatewayLocation !== "local") {
|
|
16085
16215
|
throw new Error("Docker context rebind only applies to local gateway deployments");
|
|
16086
16216
|
}
|
|
16217
|
+
assertDedicatedRouterToken(
|
|
16218
|
+
current.model,
|
|
16219
|
+
await readSecretEnv(runtime.configRoot, agent).catch(() => ({}))
|
|
16220
|
+
);
|
|
16087
16221
|
const targetBinding = await resolveLocalGatewayBinding({
|
|
16088
16222
|
manifest: void 0,
|
|
16089
16223
|
requestedContext: targetContext,
|
|
@@ -16357,6 +16491,18 @@ function requiredSecret(secrets, key) {
|
|
|
16357
16491
|
}
|
|
16358
16492
|
return value;
|
|
16359
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
|
+
}
|
|
16360
16506
|
function requiredOption(value, label) {
|
|
16361
16507
|
if (!value) {
|
|
16362
16508
|
throw new Error(`${label} is required`);
|
|
@@ -16374,6 +16520,16 @@ async function update(repoId, opts, hub, hfToken, runtime) {
|
|
|
16374
16520
|
}
|
|
16375
16521
|
const runtimeImage = resolveSpaceRuntimeImage(opts, runtime.env);
|
|
16376
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
|
+
}
|
|
16377
16533
|
runtime.stdout.log(`Generating current Space files into ${repoId}`);
|
|
16378
16534
|
const { templateRev } = await runtime.pushTemplateToSpace({
|
|
16379
16535
|
targetRepo: repoId,
|
|
@@ -16392,9 +16548,50 @@ async function update(repoId, opts, hub, hfToken, runtime) {
|
|
|
16392
16548
|
await hub.addSpaceVariable(repoId, "MLCLAW_RUNTIME_ID", spaceRuntimeId(agentName));
|
|
16393
16549
|
await hub.addSpaceVariable(repoId, "MLCLAW_OPENCLAW_PORT", String(DEFAULT_SPACE_OPENCLAW_PORT));
|
|
16394
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
|
+
}
|
|
16395
16558
|
await doctor(repoId, { fix: true }, hub, runtime);
|
|
16396
16559
|
await hub.restartSpace(repoId, true);
|
|
16397
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
|
+
}
|
|
16398
16595
|
async function doctor(repoId, opts, hub, runtime) {
|
|
16399
16596
|
if (!repoId.includes("/") && await manifestExists(runtime.configRoot, repoId)) {
|
|
16400
16597
|
await gatewayStatus(repoId, runtime);
|
|
@@ -16453,6 +16650,31 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16453
16650
|
await hub.addSpaceVariable(repoId, "OPENCLAW_HF_STATE_BUCKET", bucket);
|
|
16454
16651
|
fixed.push("set OPENCLAW_HF_STATE_BUCKET");
|
|
16455
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
|
+
}
|
|
16456
16678
|
for (const key of STALE_PATH_VARS) {
|
|
16457
16679
|
if (variables.has(key)) {
|
|
16458
16680
|
if (fix) {
|
|
@@ -16463,8 +16685,30 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16463
16685
|
}
|
|
16464
16686
|
}
|
|
16465
16687
|
}
|
|
16466
|
-
if (!secrets.has("
|
|
16467
|
-
|
|
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
|
+
}
|
|
16468
16712
|
}
|
|
16469
16713
|
if (!secrets.has("MLCLAW_SESSION_SECRET")) {
|
|
16470
16714
|
if (fix) {
|
|
@@ -16474,6 +16718,16 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16474
16718
|
issues.push("secret MLCLAW_SESSION_SECRET is missing");
|
|
16475
16719
|
}
|
|
16476
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
|
+
}
|
|
16477
16731
|
if (!variables.has("MLCLAW_TEMPLATE_REV") && !variables.has("OPENCLAW_HF_TEMPLATE_REV")) {
|
|
16478
16732
|
issues.push("MLCLAW_TEMPLATE_REV is missing; updates cannot verify template lineage");
|
|
16479
16733
|
}
|
|
@@ -16513,6 +16767,14 @@ async function doctor(repoId, opts, hub, runtime) {
|
|
|
16513
16767
|
await hub.assertBucketAccessible(bucket);
|
|
16514
16768
|
}
|
|
16515
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
|
+
}
|
|
16516
16778
|
let logs = "";
|
|
16517
16779
|
try {
|
|
16518
16780
|
logs = await hub.fetchSpaceLogs(repoId, "run");
|
|
@@ -16613,6 +16875,79 @@ async function setDeploymentSecrets(hub, repoId, secrets) {
|
|
|
16613
16875
|
await hub.addSpaceSecret(repoId, key, value);
|
|
16614
16876
|
}
|
|
16615
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
|
+
}
|
|
16616
16951
|
async function clearSpaceGatewayDisabled(hub, repoId) {
|
|
16617
16952
|
try {
|
|
16618
16953
|
await hub.deleteSpaceVariable(repoId, "MLCLAW_GATEWAY_DISABLED");
|
|
@@ -16635,6 +16970,29 @@ async function readOptionalTelegramToken(opts, runtime) {
|
|
|
16635
16970
|
}
|
|
16636
16971
|
return void 0;
|
|
16637
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
|
+
}
|
|
16638
16996
|
async function promptAgentName(runtime) {
|
|
16639
16997
|
if (!runtime.prompt.isInteractive()) {
|
|
16640
16998
|
return "mlclaw";
|
|
@@ -16647,21 +17005,26 @@ async function promptAgentName(runtime) {
|
|
|
16647
17005
|
return readPromptValue(value, "Agent name");
|
|
16648
17006
|
}
|
|
16649
17007
|
async function resolveHardware(params) {
|
|
16650
|
-
const hardware = params.requestedHardware;
|
|
16651
|
-
|
|
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;
|
|
16652
17014
|
if (params.requiresMessagingEgress && !isPaidHardware(hardware)) {
|
|
16653
17015
|
throw new Error(`Telegram requires upgraded paid Space hardware today; use --hardware ${TELEGRAM_HARDWARE} or --gateway local`);
|
|
16654
17016
|
}
|
|
16655
17017
|
if (isPaidHardware(hardware)) {
|
|
17018
|
+
const paidSleepTime = params.requestedSleepTime ?? TELEGRAM_SLEEP_TIME;
|
|
16656
17019
|
await confirmPaidHardware({
|
|
16657
17020
|
hardware,
|
|
16658
|
-
sleepTime,
|
|
17021
|
+
sleepTime: paidSleepTime,
|
|
16659
17022
|
yes: params.yes,
|
|
16660
17023
|
runtime: params.runtime
|
|
16661
17024
|
});
|
|
16662
|
-
return { hardware, sleepTime };
|
|
17025
|
+
return { kind: "explicit", hardware, label: hardware, sleepTime: paidSleepTime };
|
|
16663
17026
|
}
|
|
16664
|
-
return typeof
|
|
17027
|
+
return typeof sleepTime === "number" ? { kind: "explicit", hardware, label: hardware, sleepTime } : { kind: "explicit", hardware, label: hardware };
|
|
16665
17028
|
}
|
|
16666
17029
|
async function confirmPaidHardware(params) {
|
|
16667
17030
|
if (params.yes) {
|
|
@@ -16745,8 +17108,11 @@ export {
|
|
|
16745
17108
|
LOCAL_VOLUME_MOUNT_PATH,
|
|
16746
17109
|
SPACE_HANDOFF_POLL_MS,
|
|
16747
17110
|
SPACE_HANDOFF_TIMEOUT_MS,
|
|
17111
|
+
SPACE_LIVE_DIR,
|
|
17112
|
+
SPACE_STATE_MOUNT_DIR,
|
|
16748
17113
|
TELEGRAM_HARDWARE,
|
|
16749
17114
|
TELEGRAM_SLEEP_TIME,
|
|
16750
17115
|
createProgram,
|
|
16751
|
-
main
|
|
17116
|
+
main,
|
|
17117
|
+
mergeStateVolume
|
|
16752
17118
|
};
|