Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

180 lines
6.7 KiB
JavaScript

#!/usr/bin/env node
/**
* Arm the apps (Product 2) deploy backend on a provisioning-worker control
* plane — idempotently, over SSH, no hand-editing the box.
*
* The daemon (`provisioning-worker.ts`) already calls
* `configureAppsDeployBackend()` on boot, but it only actually provisions app
* containers once the apps env is present in `/opt/eliza/cloud/.env.local`
* (`appsContainersEnabled()` gates on CONTAINERS_DOCKER_NODES + an SSH key,
* and the deploy runner needs the tenant admin DSN + ingress URL). This script
* UPSERTS exactly that block into the env file (each key set-or-replaced, never
* duplicated, every other line untouched), then restarts the daemon — so it is
* safe to re-run and leaves the agent/coding fleet config alone.
*
* IaC, not manual: the values come from flags / the apps-shared + per-env
* terraform outputs, so re-running after an infra change re-converges the box.
*
* Usage:
* node packages/scripts/cloud/admin/arm-apps-daemon.mjs \
* --host <control-plane-ip> \
* --ssh-key <path-to-key-for-the-cp-deploy-user> \
* --app-node <id:ip:capacity> # e.g. apps-node-1:167.233.112.155:20
* --base-domain apps-staging.elizacloud.ai \
* --caddy-admin http://167.233.112.155:2019 \
* --tenant-admin-dsn 'postgresql://postgres:***@10.30.1.10:5432/postgres?sslmode=require' \
* --node-ssh-key-path /home/deploy/.ssh/apps-node \ # key the APP NODE's deploy user accepts
* # or --node-ssh-key-base64 <base64-private-key> # writes CONTAINERS_SSH_KEY
* [--node-ssh-user deploy] [--egress-proxy http://127.0.0.1:3128] [--dry-run]
* [--target cp-daemon|apps-worker] # which unit to restart (default cp-daemon)
*
* Re-run with the same args = no-op (env already converged) + a daemon restart.
*/
import { spawnSync } from "node:child_process";
const ENV_PATH = "/opt/eliza/cloud/.env.local";
function parseArgs(argv) {
const out = {};
for (let i = 0; i < argv.length; i += 1) {
const a = argv[i];
if (!a.startsWith("--")) continue;
const key = a.slice(2);
const next = argv[i + 1];
if (next === undefined || next.startsWith("--")) {
out[key] = true;
} else {
out[key] = next;
i += 1;
}
}
return out;
}
function die(msg) {
console.error(`error: ${msg}`);
process.exit(1);
}
const args = parseArgs(process.argv.slice(2));
const host = args.host;
const sshKey = args["ssh-key"];
if (!host) die("--host <control-plane-ip> is required");
if (!sshKey)
die("--ssh-key <path> is required (a key the CP's deploy user accepts)");
// Which daemon to restart. Default = the shared control-plane provisioning
// worker; `--target apps-worker` arms the dedicated apps-control daemon's unit
// instead (mirrors the `target` input in arm-apps-daemon.yml). The host/key
// still come from --host/--ssh-key, so no secret-selection is needed here.
const target = args.target || "cp-daemon";
if (target !== "cp-daemon" && target !== "apps-worker") {
die(`--target must be 'cp-daemon' or 'apps-worker' (got '${target}')`);
}
const SYSTEMD_UNIT =
target === "apps-worker"
? "eliza-apps-worker.service"
: "eliza-provisioning-worker.service";
// The apps env block the daemon needs. Only keys with a value are written;
// CONTAINERS_DOCKER_NODES is intentionally NOT overwritten if --app-node is
// omitted, so an operator can layer this onto a box that already has it set
// (and we never clobber an agent-fleet value by accident — see the guard).
const desired = {
// Arms the APP_DEPLOY runner (DefaultAppDeployRunner). Without this the daemon's
// armAppsDeployBackendIfEnabled() early-returns (provisioning-worker.ts:652) and
// APP_DEPLOY jobs are enqueued but never claimed — apps stay stuck "building".
APPS_DEPLOY_ENABLED: "1",
// Arms the container executor backend (AppContainerProvider over docker-over-SSH).
APPS_CONTAINERS_ENABLED: "1",
CONTAINERS_DOCKER_NODES: args["app-node"],
CONTAINERS_SSH_USER: args["node-ssh-user"] || "deploy",
CONTAINERS_SSH_KEY: args["node-ssh-key-base64"],
CONTAINERS_SSH_KEY_PATH: args["node-ssh-key-path"],
APPS_CADDY_ADMIN_URL: args["caddy-admin"],
CONTAINERS_PUBLIC_BASE_DOMAIN: args["base-domain"],
APPS_TENANT_ADMIN_DSN: args["tenant-admin-dsn"],
CONTAINERS_EGRESS_PROXY_URL: args["egress-proxy"],
};
const entries = Object.entries(desired).filter(
([, v]) => typeof v === "string" && v.length > 0,
);
if (!entries.find(([k]) => k === "CONTAINERS_DOCKER_NODES")) {
console.warn(
"warning: no --app-node given; CONTAINERS_DOCKER_NODES will be left as-is on the box.\n" +
" (provide it unless the box already targets the apps node.)",
);
}
// Build a remote bash script that upserts each key with a here-doc-safe sed,
// preserving every other line. Each value is single-quoted for the env file and
// shell-escaped for the remote command.
function shEscape(s) {
return `'${String(s).replaceAll("'", `'\\''`)}'`;
}
function envValueQuote(v) {
// env.local values may contain '@', '?', '/', etc. (DSN); double-quote them and
// escape embedded double quotes + backslashes so the daemon's dotenv reads them whole.
return `"${String(v).replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
}
const upserts = entries
.map(([k, v]) => {
const line = `${k}=${envValueQuote(v)}`;
// delete any existing definition of the key, then append the fresh one.
return [
`grep -q ${shEscape(`^${k}=`)} "$F" && sed -i ${shEscape(`/^${k}=/d`)} "$F" || true`,
`printf '%s\\n' ${shEscape(line)} >> "$F"`,
].join("\n");
})
.join("\n");
const remote = `
set -euo pipefail
F=${ENV_PATH}
[ -f "$F" ] || { echo "env file $F not found on host"; exit 1; }
cp -n "$F" "$F.bak.arm-apps" 2>/dev/null || true
${upserts}
echo "--- apps env now on the box ---"
grep -E '^(APPS_|CONTAINERS_(DOCKER_NODES|SSH_USER|SSH_KEY|SSH_KEY_PATH|PUBLIC_BASE_DOMAIN|EGRESS_PROXY_URL))' "$F" | sed -E 's/(DSN|KEY)=.*/\\1=<redacted>/'
sudo systemctl restart ${SYSTEMD_UNIT}
sleep 2
echo "--- daemon status ---"
systemctl is-active ${SYSTEMD_UNIT}
journalctl -u ${SYSTEMD_UNIT} -n 8 --no-pager | tail -8 || true
`;
if (args["dry-run"]) {
console.log("# DRY RUN — remote script that WOULD run on", host, ":\n");
console.log(remote);
process.exit(0);
}
console.log(`arming apps daemon on ${host} (idempotent upsert + restart)…`);
const res = spawnSync(
"ssh",
[
"-i",
sshKey,
"-o",
"IdentitiesOnly=yes",
"-o",
"StrictHostKeyChecking=accept-new",
"-o",
"ConnectTimeout=15",
`deploy@${host}`,
"bash -s",
],
{ input: remote, stdio: ["pipe", "inherit", "inherit"] },
);
if (res.status !== 0) {
die(`remote arming failed (exit ${res.status})`);
}
console.log(
"\n✅ apps daemon armed. next: trigger a sample app deploy and watch it serve.",
);