@vellumai/cli 0.1.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.
@@ -0,0 +1,189 @@
1
+ import crypto from "node:crypto";
2
+ import { execSync, spawn } from "node:child_process";
3
+ import http from "node:http";
4
+
5
+ const PORT = parseInt(process.env.PORT || "7830", 10);
6
+
7
+ interface StoredMessage {
8
+ id: string;
9
+ role: "user" | "assistant";
10
+ content: string;
11
+ timestamp: string;
12
+ }
13
+
14
+ const messages: Record<string, StoredMessage[]> = {};
15
+
16
+ function parseBody(req: http.IncomingMessage): Promise<Record<string, unknown>> {
17
+ return new Promise((resolve, reject) => {
18
+ let body = "";
19
+ req.on("data", (chunk: Buffer) => (body += chunk.toString()));
20
+ req.on("end", () => {
21
+ try {
22
+ resolve(JSON.parse(body));
23
+ } catch (e) {
24
+ reject(e);
25
+ }
26
+ });
27
+ req.on("error", reject);
28
+ });
29
+ }
30
+
31
+ function log(method: string, path: string, status: number): void {
32
+ const ts = new Date().toISOString();
33
+ console.log(`[${ts}] ${method} ${path} -> ${status}`);
34
+ }
35
+
36
+ const server = http.createServer(async (req, res) => {
37
+ const url = new URL(req.url ?? "/", `http://localhost:${PORT}`);
38
+ const method = req.method ?? "UNKNOWN";
39
+
40
+ res.setHeader("Content-Type", "application/json");
41
+
42
+ if (req.method === "POST" && url.pathname === "/upgrade") {
43
+ try {
44
+ const npmEnv = {
45
+ ...process.env,
46
+ PATH: `${process.env.HOME || "/root"}/.npm-global/bin:${process.env.HOME || "/root"}/.local/bin:/usr/local/bin:${process.env.PATH}`,
47
+ };
48
+ execSync("npm install -g @vellum/openclaw-adapter@latest", {
49
+ encoding: "utf-8",
50
+ timeout: 120000,
51
+ env: npmEnv,
52
+ });
53
+ const child = spawn("vellum-openclaw-adapter", [], {
54
+ detached: true,
55
+ stdio: "ignore",
56
+ env: npmEnv,
57
+ });
58
+ child.unref();
59
+ const responseBody = JSON.stringify({
60
+ status: "success",
61
+ message: "HTTPS adapter installed and started. HTTP adapter shutting down.",
62
+ });
63
+ res.writeHead(200);
64
+ res.end(responseBody, () => {
65
+ log(method, url.pathname, 200);
66
+ server.close(() => process.exit(0));
67
+ });
68
+ } catch (e) {
69
+ const responseBody = JSON.stringify({
70
+ status: "error",
71
+ message: e instanceof Error ? e.message : String(e),
72
+ });
73
+ res.writeHead(500);
74
+ res.end(responseBody);
75
+ log(method, url.pathname, 500);
76
+ }
77
+ return;
78
+ }
79
+
80
+ if (req.method === "GET" && url.pathname === "/healthz") {
81
+ const execEnv = {
82
+ ...process.env,
83
+ PATH: `${process.env.HOME || "/root"}/.npm-global/bin:${process.env.HOME || "/root"}/.local/bin:/usr/local/bin:${process.env.PATH}`,
84
+ };
85
+ let responseBody: string;
86
+ try {
87
+ const output = execSync("openclaw health --json", {
88
+ encoding: "utf-8",
89
+ timeout: 10000,
90
+ env: execEnv,
91
+ });
92
+ const health = JSON.parse(output.trim()) as Record<string, unknown>;
93
+ const result: Record<string, unknown> = {
94
+ status: health.status ?? "healthy",
95
+ message: health.message,
96
+ };
97
+
98
+ const healthStr = JSON.stringify(health);
99
+ if (healthStr.includes("1006") || healthStr.includes("abnormal closure")) {
100
+ try {
101
+ const gatewayOutput = execSync("openclaw gateway status", {
102
+ encoding: "utf-8",
103
+ timeout: 10000,
104
+ env: execEnv,
105
+ });
106
+ result.message = `${result.message}\n\nGateway Status:\n${gatewayOutput.trim()}`;
107
+ } catch (gatewayErr) {
108
+ const gatewayErrMsg =
109
+ gatewayErr instanceof Error ? gatewayErr.message : String(gatewayErr);
110
+ result.message = `${result.message}\n\nGateway Status Error:\n${gatewayErrMsg}`;
111
+ }
112
+ }
113
+
114
+ responseBody = JSON.stringify(result);
115
+ res.writeHead(200);
116
+ res.end(responseBody);
117
+ } catch (e) {
118
+ const errorMessage = e instanceof Error ? e.message : String(e);
119
+ const result: Record<string, unknown> = {
120
+ status: "unhealthy",
121
+ message: errorMessage,
122
+ };
123
+
124
+ if (errorMessage.includes("1006") || errorMessage.includes("abnormal closure")) {
125
+ try {
126
+ const gatewayOutput = execSync("openclaw gateway status", {
127
+ encoding: "utf-8",
128
+ timeout: 10000,
129
+ env: execEnv,
130
+ });
131
+ result.message = `${result.message}\n\nGateway Status:\n${gatewayOutput.trim()}`;
132
+ } catch (gatewayErr) {
133
+ const gatewayErrMsg =
134
+ gatewayErr instanceof Error ? gatewayErr.message : String(gatewayErr);
135
+ result.message = `${result.message}\n\nGateway Status Error:\n${gatewayErrMsg}`;
136
+ }
137
+ }
138
+
139
+ responseBody = JSON.stringify(result);
140
+ res.writeHead(200);
141
+ res.end(responseBody);
142
+ }
143
+ log(method, url.pathname, 200);
144
+ return;
145
+ }
146
+
147
+ const messagesMatch = url.pathname.match(/^\/v1\/assistants\/([^/]+)\/messages$/);
148
+ if (messagesMatch) {
149
+ const assistantId = messagesMatch[1];
150
+
151
+ if (req.method === "GET") {
152
+ const key = url.searchParams.get("conversationKey") ?? assistantId;
153
+ const msgs = messages[key] ?? [];
154
+ res.writeHead(200);
155
+ res.end(JSON.stringify({ messages: msgs }));
156
+ return;
157
+ }
158
+
159
+ if (req.method === "POST") {
160
+ try {
161
+ const parsed = await parseBody(req);
162
+ const key = (parsed.conversationKey as string) || assistantId;
163
+ if (!messages[key]) messages[key] = [];
164
+ const messageId = crypto.randomUUID();
165
+ messages[key].push({
166
+ id: messageId,
167
+ role: "user",
168
+ content: parsed.content as string,
169
+ timestamp: new Date().toISOString(),
170
+ });
171
+ res.writeHead(200);
172
+ res.end(JSON.stringify({ accepted: true, messageId }));
173
+ } catch {
174
+ res.writeHead(400);
175
+ res.end(JSON.stringify({ error: "Invalid request body" }));
176
+ }
177
+ return;
178
+ }
179
+ }
180
+
181
+ const notFoundBody = JSON.stringify({ error: "Not found" });
182
+ res.writeHead(404);
183
+ res.end(notFoundBody);
184
+ log(method, url.pathname, 404);
185
+ });
186
+
187
+ server.listen(PORT, "0.0.0.0", () => {
188
+ console.log(`OpenClaw runtime server listening on port ${PORT}`);
189
+ });
@@ -0,0 +1,118 @@
1
+ import { GATEWAY_PORT } from "../lib/constants";
2
+ import { buildOpenclawRuntimeServer } from "../lib/openclaw-runtime-server";
3
+
4
+ export function buildOpenclawStartupScript(
5
+ bearerToken: string,
6
+ sshUser: string,
7
+ anthropicApiKey: string,
8
+ timestampRedirect: string,
9
+ userSetup: string,
10
+ ownershipFixup: string,
11
+ ): string {
12
+ const runtimeServer = buildOpenclawRuntimeServer();
13
+
14
+ return `#!/bin/bash
15
+ set -e
16
+
17
+ ${timestampRedirect}
18
+
19
+ trap 'EXIT_CODE=\$?; if [ \$EXIT_CODE -ne 0 ]; then echo "Startup script failed with exit code \$EXIT_CODE" > /var/log/startup-error; fi' EXIT
20
+ ${userSetup}
21
+
22
+ export OPENCLAW_NPM_LOGLEVEL=verbose
23
+ export OPENCLAW_NO_ONBOARD=1
24
+ export OPENCLAW_NO_PROMPT=1
25
+
26
+ echo "=== Pre-install diagnostics ==="
27
+ echo "Date: $(date -u)"
28
+ echo "Disk:" && df -h / 2>&1 || true
29
+ echo "Memory:" && free -m 2>&1 || true
30
+ echo "DNS:" && nslookup registry.npmjs.org 2>&1 || true
31
+ echo "Registry ping:" && curl -sSf --max-time 10 https://registry.npmjs.org/-/ping 2>&1 || echo "WARN: npm registry unreachable"
32
+ echo "=== End pre-install diagnostics ==="
33
+
34
+ echo "=== Installing build dependencies ==="
35
+ apt-get update -y
36
+ apt-get install -y build-essential python3 python3-pip git
37
+ pip3 install cmake
38
+ echo "cmake version: $(cmake --version | head -1)"
39
+ echo "=== Build dependencies installed ==="
40
+
41
+ curl -fsSL https://openclaw.ai/install.sh -o /tmp/openclaw-install.sh
42
+ chmod +x /tmp/openclaw-install.sh
43
+
44
+ set +e
45
+ bash /tmp/openclaw-install.sh
46
+ INSTALL_EXIT_CODE=\$?
47
+ set -e
48
+
49
+ if [ \$INSTALL_EXIT_CODE -ne 0 ]; then
50
+ echo "=== OpenClaw install failed (exit code: \$INSTALL_EXIT_CODE) ==="
51
+ echo "=== npm debug logs ==="
52
+ find \$HOME/.npm/_logs -name '*.log' -type f 2>/dev/null | sort | while read -r logfile; do
53
+ echo "--- \$logfile ---"
54
+ tail -n 200 "\$logfile" 2>/dev/null || true
55
+ done
56
+ echo "=== Post-failure diagnostics ==="
57
+ echo "Disk:" && df -h / 2>&1 || true
58
+ echo "Memory:" && free -m 2>&1 || true
59
+ echo "node version:" && node --version 2>&1 || echo "node not found"
60
+ echo "npm version:" && npm --version 2>&1 || echo "npm not found"
61
+ echo "npm config:" && npm config list 2>&1 || true
62
+ echo "cmake version:" && cmake --version 2>&1 || echo "cmake not found"
63
+ echo "PATH: \$PATH"
64
+ echo "=== End diagnostics ==="
65
+ exit \$INSTALL_EXIT_CODE
66
+ fi
67
+
68
+ export PATH="\$HOME/.npm-global/bin:\$HOME/.local/bin:/usr/local/bin:\$PATH"
69
+
70
+ if ! command -v openclaw >/dev/null 2>&1; then
71
+ echo "ERROR: openclaw CLI installation failed. The 'openclaw' command is not available."
72
+ echo "PATH: \$PATH"
73
+ echo "which openclaw:" && which openclaw 2>&1 || true
74
+ echo "npm global bin:" && npm bin -g 2>&1 || true
75
+ echo "npm global list:" && npm list -g --depth=0 2>&1 || true
76
+ exit 1
77
+ fi
78
+
79
+ export XDG_RUNTIME_DIR="/run/user/\$(id -u)"
80
+ export DBUS_SESSION_BUS_ADDRESS="unix:path=\$XDG_RUNTIME_DIR/bus"
81
+ mkdir -p "\$XDG_RUNTIME_DIR"
82
+ loginctl enable-linger root 2>/dev/null || true
83
+ systemctl --user daemon-reexec 2>/dev/null || true
84
+
85
+ if ! command -v bun >/dev/null 2>&1; then
86
+ echo "=== Installing bun ==="
87
+ if ! command -v unzip >/dev/null 2>&1; then
88
+ echo "Installing unzip (required by bun)..."
89
+ apt-get install -y unzip
90
+ fi
91
+ curl -fsSL https://bun.sh/install | bash
92
+ export BUN_INSTALL="\$HOME/.bun"
93
+ export PATH="\$BUN_INSTALL/bin:\$PATH"
94
+ echo "bun version: $(bun --version)"
95
+ echo "=== Bun installed ==="
96
+ else
97
+ echo "bun already installed: $(bun --version)"
98
+ fi
99
+
100
+ openclaw gateway install --token ${bearerToken}
101
+
102
+ mkdir -p /root/.openclaw
103
+ openclaw config set env.ANTHROPIC_API_KEY "${anthropicApiKey}"
104
+ openclaw config set agents.defaults.model.primary "anthropic/claude-opus-4-6"
105
+ openclaw config set gateway.auth.token "${bearerToken}"
106
+
107
+ echo "=== Starting openclaw gateway at user level ==="
108
+ systemctl --user daemon-reload
109
+ systemctl --user enable --now openclaw-gateway.service
110
+
111
+ export PORT=${GATEWAY_PORT}
112
+
113
+ echo "=== Starting OpenClaw runtime server ==="
114
+ ${runtimeServer}
115
+ echo "=== OpenClaw runtime server started ==="
116
+ ${ownershipFixup}
117
+ `;
118
+ }