@synap-core/cli 0.9.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/README.md +250 -0
- package/dist/commands/connect.d.ts +11 -0
- package/dist/commands/connect.js +96 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/finish.d.ts +16 -0
- package/dist/commands/finish.js +82 -0
- package/dist/commands/finish.js.map +1 -0
- package/dist/commands/init.d.ts +21 -0
- package/dist/commands/init.js +865 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/security-audit.d.ts +12 -0
- package/dist/commands/security-audit.js +100 -0
- package/dist/commands/security-audit.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.js +216 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +6 -0
- package/dist/commands/update.js +34 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/auth.d.ts +57 -0
- package/dist/lib/auth.js +322 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/hardening.d.ts +18 -0
- package/dist/lib/hardening.js +203 -0
- package/dist/lib/hardening.js.map +1 -0
- package/dist/lib/openclaw.d.ts +28 -0
- package/dist/lib/openclaw.js +106 -0
- package/dist/lib/openclaw.js.map +1 -0
- package/dist/lib/pod.d.ts +91 -0
- package/dist/lib/pod.js +305 -0
- package/dist/lib/pod.js.map +1 -0
- package/dist/lib/seed.d.ts +13 -0
- package/dist/lib/seed.js +135 -0
- package/dist/lib/seed.js.map +1 -0
- package/dist/lib/templates.d.ts +11 -0
- package/dist/lib/templates.js +13 -0
- package/dist/lib/templates.js.map +1 -0
- package/dist/templates/agent-os.json +3090 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.js +31 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Authentication Module
|
|
3
|
+
*
|
|
4
|
+
* Browser-based OAuth flow:
|
|
5
|
+
* 1. Start temporary HTTP server on localhost (random port)
|
|
6
|
+
* 2. Open browser to synap.live/auth/cli?callback=http://localhost:PORT/callback
|
|
7
|
+
* 3. User logs in via Better Auth (email/password, Google, GitHub)
|
|
8
|
+
* 4. synap.live redirects to localhost callback with session token
|
|
9
|
+
* 5. CLI stores token in ~/.synap/credentials.json
|
|
10
|
+
*/
|
|
11
|
+
export interface StoredCredentials {
|
|
12
|
+
token: string;
|
|
13
|
+
expiresAt: string;
|
|
14
|
+
userId: string;
|
|
15
|
+
email: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function getStoredToken(): StoredCredentials | null;
|
|
18
|
+
/** True if local expiry timestamp has passed (does NOT call the server). */
|
|
19
|
+
export declare function isTokenLocallyExpired(creds: StoredCredentials): boolean;
|
|
20
|
+
export declare function logout(): void;
|
|
21
|
+
export declare function isLoggedIn(): Promise<{
|
|
22
|
+
valid: boolean;
|
|
23
|
+
email?: string;
|
|
24
|
+
userId?: string;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Store a manually provided API token (Better Auth session token).
|
|
28
|
+
* Validates against the CP before saving.
|
|
29
|
+
*
|
|
30
|
+
* Usage: synap login --token <token>
|
|
31
|
+
* Get a token from: https://synap.live/account/tokens
|
|
32
|
+
*/
|
|
33
|
+
export declare function loginWithToken(token: string): Promise<StoredCredentials | null>;
|
|
34
|
+
export declare function login(): Promise<StoredCredentials | null>;
|
|
35
|
+
export interface PodCallbackResult {
|
|
36
|
+
podUrl: string;
|
|
37
|
+
workspaceId?: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function waitForPodCallback(timeoutMs?: number): Promise<PodCallbackResult | null>;
|
|
40
|
+
export declare function getCpUrl(): string;
|
|
41
|
+
export interface Pod {
|
|
42
|
+
id: string;
|
|
43
|
+
subdomain: string;
|
|
44
|
+
customDomain: string | null;
|
|
45
|
+
status: string;
|
|
46
|
+
region: string;
|
|
47
|
+
url: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function listPods(token: string): Promise<Pod[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Get the remote OpenClaw provisioning status from the CP.
|
|
52
|
+
* Returns null if not provisioned or on network error.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getOpenClawRemoteStatus(cpToken: string, podId: string): Promise<{
|
|
55
|
+
status: "not_provisioned" | "provisioning" | "running" | "error";
|
|
56
|
+
url: string | null;
|
|
57
|
+
} | null>;
|
package/dist/lib/auth.js
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Authentication Module
|
|
3
|
+
*
|
|
4
|
+
* Browser-based OAuth flow:
|
|
5
|
+
* 1. Start temporary HTTP server on localhost (random port)
|
|
6
|
+
* 2. Open browser to synap.live/auth/cli?callback=http://localhost:PORT/callback
|
|
7
|
+
* 3. User logs in via Better Auth (email/password, Google, GitHub)
|
|
8
|
+
* 4. synap.live redirects to localhost callback with session token
|
|
9
|
+
* 5. CLI stores token in ~/.synap/credentials.json
|
|
10
|
+
*/
|
|
11
|
+
import http from "node:http";
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import os from "node:os";
|
|
16
|
+
const CP_URL = process.env.SYNAP_CP_URL || "https://api.synap.live";
|
|
17
|
+
const LANDING_URL = process.env.SYNAP_LANDING_URL || "https://synap.live";
|
|
18
|
+
const CREDENTIALS_DIR = path.join(os.homedir(), ".synap");
|
|
19
|
+
const CREDENTIALS_FILE = path.join(CREDENTIALS_DIR, "credentials.json");
|
|
20
|
+
// ─── Credential Storage ────────────────────────────────────────────────────
|
|
21
|
+
function ensureCredentialsDir() {
|
|
22
|
+
if (!fs.existsSync(CREDENTIALS_DIR)) {
|
|
23
|
+
fs.mkdirSync(CREDENTIALS_DIR, { recursive: true, mode: 0o700 });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function writeCredentials(creds) {
|
|
27
|
+
ensureCredentialsDir();
|
|
28
|
+
fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), {
|
|
29
|
+
mode: 0o600,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function getStoredToken() {
|
|
33
|
+
try {
|
|
34
|
+
if (!fs.existsSync(CREDENTIALS_FILE))
|
|
35
|
+
return null;
|
|
36
|
+
const raw = fs.readFileSync(CREDENTIALS_FILE, "utf-8");
|
|
37
|
+
return JSON.parse(raw);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** True if local expiry timestamp has passed (does NOT call the server). */
|
|
44
|
+
export function isTokenLocallyExpired(creds) {
|
|
45
|
+
return new Date(creds.expiresAt) < new Date();
|
|
46
|
+
}
|
|
47
|
+
export function logout() {
|
|
48
|
+
try {
|
|
49
|
+
if (fs.existsSync(CREDENTIALS_FILE)) {
|
|
50
|
+
fs.unlinkSync(CREDENTIALS_FILE);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// ignore
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// ─── Session Validation ────────────────────────────────────────────────────
|
|
58
|
+
export async function isLoggedIn() {
|
|
59
|
+
const creds = getStoredToken();
|
|
60
|
+
if (!creds)
|
|
61
|
+
return { valid: false };
|
|
62
|
+
try {
|
|
63
|
+
const res = await fetch(`${CP_URL}/auth/me`, {
|
|
64
|
+
headers: { Authorization: `Bearer ${creds.token}` },
|
|
65
|
+
signal: AbortSignal.timeout(5000),
|
|
66
|
+
});
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
// Server explicitly rejects token — clean up
|
|
69
|
+
logout();
|
|
70
|
+
return { valid: false };
|
|
71
|
+
}
|
|
72
|
+
const data = (await res.json());
|
|
73
|
+
// Extend local expiry by 7 days from now — keeps the token alive as long
|
|
74
|
+
// as the server is still accepting it (avoids false "expired" deletions)
|
|
75
|
+
writeCredentials({
|
|
76
|
+
...creds,
|
|
77
|
+
email: data.user.email,
|
|
78
|
+
userId: data.user.id,
|
|
79
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
80
|
+
});
|
|
81
|
+
return { valid: true, email: data.user.email, userId: data.user.id };
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Network error — keep token, report as invalid for this call only
|
|
85
|
+
return { valid: false };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// ─── Token-based Login (headless / server) ─────────────────────────────────
|
|
89
|
+
/**
|
|
90
|
+
* Store a manually provided API token (Better Auth session token).
|
|
91
|
+
* Validates against the CP before saving.
|
|
92
|
+
*
|
|
93
|
+
* Usage: synap login --token <token>
|
|
94
|
+
* Get a token from: https://synap.live/account/tokens
|
|
95
|
+
*/
|
|
96
|
+
export async function loginWithToken(token) {
|
|
97
|
+
const res = await fetch(`${CP_URL}/auth/me`, {
|
|
98
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
99
|
+
signal: AbortSignal.timeout(8000),
|
|
100
|
+
});
|
|
101
|
+
if (!res.ok)
|
|
102
|
+
return null;
|
|
103
|
+
const data = (await res.json());
|
|
104
|
+
const creds = {
|
|
105
|
+
token,
|
|
106
|
+
email: data.user.email,
|
|
107
|
+
userId: data.user.id,
|
|
108
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
109
|
+
};
|
|
110
|
+
writeCredentials(creds);
|
|
111
|
+
return creds;
|
|
112
|
+
}
|
|
113
|
+
// ─── Browser Login Flow ────────────────────────────────────────────────────
|
|
114
|
+
function openBrowser(url) {
|
|
115
|
+
try {
|
|
116
|
+
switch (process.platform) {
|
|
117
|
+
case "darwin":
|
|
118
|
+
execSync(`open "${url}"`);
|
|
119
|
+
break;
|
|
120
|
+
case "linux":
|
|
121
|
+
execSync(`xdg-open "${url}"`);
|
|
122
|
+
break;
|
|
123
|
+
case "win32":
|
|
124
|
+
execSync(`start "" "${url}"`);
|
|
125
|
+
break;
|
|
126
|
+
default:
|
|
127
|
+
// Can't open browser — caller handles fallback
|
|
128
|
+
throw new Error("Unsupported platform");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
throw new Error("Could not open browser");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export async function login() {
|
|
136
|
+
return new Promise((resolve) => {
|
|
137
|
+
const server = http.createServer((req, res) => {
|
|
138
|
+
const url = new URL(req.url ?? "/", `http://localhost`);
|
|
139
|
+
if (url.pathname === "/callback") {
|
|
140
|
+
const token = url.searchParams.get("token");
|
|
141
|
+
const email = url.searchParams.get("email");
|
|
142
|
+
const userId = url.searchParams.get("userId");
|
|
143
|
+
const expiresAt = url.searchParams.get("expiresAt");
|
|
144
|
+
if (token && email && userId) {
|
|
145
|
+
const creds = {
|
|
146
|
+
token,
|
|
147
|
+
email,
|
|
148
|
+
userId,
|
|
149
|
+
expiresAt: expiresAt ||
|
|
150
|
+
new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
151
|
+
};
|
|
152
|
+
writeCredentials(creds);
|
|
153
|
+
// Send success page
|
|
154
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
155
|
+
res.end(`<!DOCTYPE html>
|
|
156
|
+
<html>
|
|
157
|
+
<head><title>Synap CLI</title></head>
|
|
158
|
+
<body style="font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #0a0a0a; color: #e5e5e5;">
|
|
159
|
+
<div style="text-align: center;">
|
|
160
|
+
<h1 style="font-size: 24px; margin-bottom: 8px;">Authenticated</h1>
|
|
161
|
+
<p style="color: #888;">You can close this window and return to the terminal.</p>
|
|
162
|
+
</div>
|
|
163
|
+
</body>
|
|
164
|
+
</html>`);
|
|
165
|
+
cleanup();
|
|
166
|
+
resolve(creds);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
170
|
+
res.end(`<!DOCTYPE html>
|
|
171
|
+
<html>
|
|
172
|
+
<head><title>Synap CLI</title></head>
|
|
173
|
+
<body style="font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #0a0a0a; color: #e5e5e5;">
|
|
174
|
+
<div style="text-align: center;">
|
|
175
|
+
<h1 style="font-size: 24px; color: #ef4444; margin-bottom: 8px;">Authentication Failed</h1>
|
|
176
|
+
<p style="color: #888;">Missing token. Please try again.</p>
|
|
177
|
+
</div>
|
|
178
|
+
</body>
|
|
179
|
+
</html>`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
res.writeHead(404);
|
|
184
|
+
res.end();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
// Listen on random port
|
|
188
|
+
server.listen(0, "127.0.0.1", () => {
|
|
189
|
+
const addr = server.address();
|
|
190
|
+
if (!addr || typeof addr === "string") {
|
|
191
|
+
cleanup();
|
|
192
|
+
resolve(null);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const port = addr.port;
|
|
196
|
+
const callbackUrl = `http://localhost:${port}/callback`;
|
|
197
|
+
const loginUrl = `${LANDING_URL}/cli?callback=${encodeURIComponent(callbackUrl)}`;
|
|
198
|
+
try {
|
|
199
|
+
openBrowser(loginUrl);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// Browser didn't open — show manual URL
|
|
203
|
+
console.log(`\n Open this URL in your browser:\n`);
|
|
204
|
+
console.log(` ${loginUrl}\n`);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
// Timeout after 120 seconds
|
|
208
|
+
const timeout = setTimeout(() => {
|
|
209
|
+
cleanup();
|
|
210
|
+
resolve(null);
|
|
211
|
+
}, 120_000);
|
|
212
|
+
function cleanup() {
|
|
213
|
+
clearTimeout(timeout);
|
|
214
|
+
try {
|
|
215
|
+
server.close();
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// ignore
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
export async function waitForPodCallback(timeoutMs = 300_000 // 5-minute window
|
|
224
|
+
) {
|
|
225
|
+
return new Promise((resolve) => {
|
|
226
|
+
const server = http.createServer((req, res) => {
|
|
227
|
+
const url = new URL(req.url ?? "/", `http://localhost`);
|
|
228
|
+
if (url.pathname === "/callback") {
|
|
229
|
+
const podUrl = url.searchParams.get("podUrl");
|
|
230
|
+
const workspaceId = url.searchParams.get("workspaceId") ?? undefined;
|
|
231
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
232
|
+
res.end(`<!DOCTYPE html>
|
|
233
|
+
<html>
|
|
234
|
+
<head><title>Synap CLI</title></head>
|
|
235
|
+
<body style="font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #0a0a0a; color: #e5e5e5;">
|
|
236
|
+
<div style="text-align: center;">
|
|
237
|
+
<h1 style="font-size: 24px; margin-bottom: 8px;">Pod connected</h1>
|
|
238
|
+
<p style="color: #888;">You can close this window and return to the terminal.</p>
|
|
239
|
+
</div>
|
|
240
|
+
</body>
|
|
241
|
+
</html>`);
|
|
242
|
+
cleanup();
|
|
243
|
+
if (podUrl) {
|
|
244
|
+
resolve({ podUrl, workspaceId });
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
resolve(null);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
res.writeHead(404);
|
|
252
|
+
res.end();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
server.listen(0, "127.0.0.1", () => {
|
|
256
|
+
const addr = server.address();
|
|
257
|
+
if (!addr || typeof addr === "string") {
|
|
258
|
+
cleanup();
|
|
259
|
+
resolve(null);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const port = addr.port;
|
|
263
|
+
const callbackUrl = `http://localhost:${port}/callback`;
|
|
264
|
+
const provisionUrl = `${LANDING_URL}/account/pod/provision?cli_callback=${encodeURIComponent(callbackUrl)}`;
|
|
265
|
+
try {
|
|
266
|
+
openBrowser(provisionUrl);
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
console.log(`\n Open this URL in your browser to provision your pod:\n`);
|
|
270
|
+
console.log(` ${provisionUrl}\n`);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
const timeout = setTimeout(() => {
|
|
274
|
+
cleanup();
|
|
275
|
+
resolve(null);
|
|
276
|
+
}, timeoutMs);
|
|
277
|
+
function cleanup() {
|
|
278
|
+
clearTimeout(timeout);
|
|
279
|
+
try {
|
|
280
|
+
server.close();
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// ignore
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
// ─── CP API Helpers ────────────────────────────────────────────────────────
|
|
289
|
+
export function getCpUrl() {
|
|
290
|
+
return CP_URL;
|
|
291
|
+
}
|
|
292
|
+
export async function listPods(token) {
|
|
293
|
+
const res = await fetch(`${CP_URL}/pods`, {
|
|
294
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
295
|
+
signal: AbortSignal.timeout(10000),
|
|
296
|
+
});
|
|
297
|
+
if (!res.ok) {
|
|
298
|
+
throw new Error(`Failed to list pods (HTTP ${res.status})`);
|
|
299
|
+
}
|
|
300
|
+
const data = (await res.json());
|
|
301
|
+
// Only show active/provisioning pods — not deleted/archived
|
|
302
|
+
return (data.pods ?? []).filter((p) => p.status === "active" || p.status === "provisioning" || p.status === "syncing_intelligence");
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Get the remote OpenClaw provisioning status from the CP.
|
|
306
|
+
* Returns null if not provisioned or on network error.
|
|
307
|
+
*/
|
|
308
|
+
export async function getOpenClawRemoteStatus(cpToken, podId) {
|
|
309
|
+
try {
|
|
310
|
+
const res = await fetch(`${getCpUrl()}/openclaw/status?podId=${encodeURIComponent(podId)}`, {
|
|
311
|
+
headers: { Authorization: `Bearer ${cpToken}` },
|
|
312
|
+
signal: AbortSignal.timeout(8000),
|
|
313
|
+
});
|
|
314
|
+
if (!res.ok)
|
|
315
|
+
return null;
|
|
316
|
+
return (await res.json());
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security hardening checks and fixes for OpenClaw.
|
|
3
|
+
* Based on CVE research and official OpenClaw security docs.
|
|
4
|
+
*/
|
|
5
|
+
export interface SecurityCheck {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
severity: "critical" | "high" | "medium" | "low";
|
|
9
|
+
passed: boolean;
|
|
10
|
+
message: string;
|
|
11
|
+
fixable: boolean;
|
|
12
|
+
fix?: () => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Run all 9 security checks against the OpenClaw configuration.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runSecurityChecks(version?: string): SecurityCheck[];
|
|
18
|
+
export declare function computeScore(checks: SecurityCheck[]): string;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security hardening checks and fixes for OpenClaw.
|
|
3
|
+
* Based on CVE research and official OpenClaw security docs.
|
|
4
|
+
*/
|
|
5
|
+
import { chmodSync } from "fs";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { readOpenClawConfig, writeOpenClawConfig, getConfigValue, setConfigValue, getConfigPermissions, } from "./openclaw.js";
|
|
9
|
+
/**
|
|
10
|
+
* Run all 9 security checks against the OpenClaw configuration.
|
|
11
|
+
*/
|
|
12
|
+
export function runSecurityChecks(version) {
|
|
13
|
+
const config = readOpenClawConfig();
|
|
14
|
+
const checks = [];
|
|
15
|
+
// 1. Gateway bound to loopback
|
|
16
|
+
const bind = config ? getConfigValue(config, "gateway.bind") : undefined;
|
|
17
|
+
checks.push({
|
|
18
|
+
id: "gateway-bind",
|
|
19
|
+
name: "Gateway bound to loopback",
|
|
20
|
+
severity: "critical",
|
|
21
|
+
passed: !bind || bind === "loopback" || bind === "127.0.0.1",
|
|
22
|
+
message: !bind || bind === "loopback" || bind === "127.0.0.1"
|
|
23
|
+
? "Gateway only accepts local connections"
|
|
24
|
+
: `Gateway bound to "${bind}" — exposed to network`,
|
|
25
|
+
fixable: true,
|
|
26
|
+
fix: config
|
|
27
|
+
? () => {
|
|
28
|
+
setConfigValue(config, "gateway.bind", "loopback");
|
|
29
|
+
writeOpenClawConfig(config);
|
|
30
|
+
}
|
|
31
|
+
: undefined,
|
|
32
|
+
});
|
|
33
|
+
// 2. Token auth enabled
|
|
34
|
+
const token = config ? getConfigValue(config, "gateway.token") : undefined;
|
|
35
|
+
checks.push({
|
|
36
|
+
id: "gateway-token",
|
|
37
|
+
name: "Token authentication enabled",
|
|
38
|
+
severity: "critical",
|
|
39
|
+
passed: !!token,
|
|
40
|
+
message: token
|
|
41
|
+
? "Gateway requires token for access"
|
|
42
|
+
: "No gateway token set — anyone can connect",
|
|
43
|
+
fixable: false, // user must set their own token
|
|
44
|
+
});
|
|
45
|
+
// 3. OpenClaw version >= 2026.3.12
|
|
46
|
+
const minVersion = "2026.3.12";
|
|
47
|
+
const versionOk = version ? compareVersions(version, minVersion) >= 0 : true;
|
|
48
|
+
checks.push({
|
|
49
|
+
id: "version",
|
|
50
|
+
name: `OpenClaw version >= ${minVersion}`,
|
|
51
|
+
severity: "critical",
|
|
52
|
+
passed: versionOk,
|
|
53
|
+
message: version
|
|
54
|
+
? versionOk
|
|
55
|
+
? `Running v${version}`
|
|
56
|
+
: `Running v${version} — has 9.9 CVSS vulnerability (CVE-2026-24763)`
|
|
57
|
+
: "Could not detect version",
|
|
58
|
+
fixable: false,
|
|
59
|
+
});
|
|
60
|
+
// 4. No plaintext credentials in config
|
|
61
|
+
const hasPlaintext = config
|
|
62
|
+
? containsPlaintextCredentials(JSON.stringify(config))
|
|
63
|
+
: false;
|
|
64
|
+
checks.push({
|
|
65
|
+
id: "plaintext-creds",
|
|
66
|
+
name: "No plaintext credentials in config",
|
|
67
|
+
severity: "high",
|
|
68
|
+
passed: !hasPlaintext,
|
|
69
|
+
message: hasPlaintext
|
|
70
|
+
? "Config contains potential plaintext credentials"
|
|
71
|
+
: "No obvious plaintext credentials found",
|
|
72
|
+
fixable: false,
|
|
73
|
+
});
|
|
74
|
+
// 5. File permissions on ~/.openclaw
|
|
75
|
+
const perms = getConfigPermissions();
|
|
76
|
+
checks.push({
|
|
77
|
+
id: "file-permissions",
|
|
78
|
+
name: "Config directory permissions",
|
|
79
|
+
severity: "high",
|
|
80
|
+
passed: perms.safe,
|
|
81
|
+
message: perms.safe
|
|
82
|
+
? `~/.openclaw mode ${perms.mode} (owner-only)`
|
|
83
|
+
: `~/.openclaw mode ${perms.mode} — readable by others`,
|
|
84
|
+
fixable: true,
|
|
85
|
+
fix: () => {
|
|
86
|
+
chmodSync(join(homedir(), ".openclaw"), 0o700);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
// 6. WebSocket origin validation
|
|
90
|
+
const wsOrigin = config
|
|
91
|
+
? getConfigValue(config, "gateway.websocket.validateOrigin")
|
|
92
|
+
: undefined;
|
|
93
|
+
checks.push({
|
|
94
|
+
id: "ws-origin",
|
|
95
|
+
name: "WebSocket origin validation",
|
|
96
|
+
severity: "medium",
|
|
97
|
+
passed: wsOrigin !== false,
|
|
98
|
+
message: wsOrigin === false
|
|
99
|
+
? "WebSocket origin validation disabled"
|
|
100
|
+
: "WebSocket origin validation active",
|
|
101
|
+
fixable: true,
|
|
102
|
+
fix: config
|
|
103
|
+
? () => {
|
|
104
|
+
setConfigValue(config, "gateway.websocket.validateOrigin", true);
|
|
105
|
+
writeOpenClawConfig(config);
|
|
106
|
+
}
|
|
107
|
+
: undefined,
|
|
108
|
+
});
|
|
109
|
+
// 7. Dangerous skill scanner
|
|
110
|
+
const scanner = config
|
|
111
|
+
? getConfigValue(config, "skills.dangerousCodeScanner")
|
|
112
|
+
: undefined;
|
|
113
|
+
checks.push({
|
|
114
|
+
id: "skill-scanner",
|
|
115
|
+
name: "Dangerous skill scanner enabled",
|
|
116
|
+
severity: "medium",
|
|
117
|
+
passed: scanner !== false,
|
|
118
|
+
message: scanner === false
|
|
119
|
+
? "Skill scanner disabled — malicious skills can run unchecked"
|
|
120
|
+
: "Skill scanner active",
|
|
121
|
+
fixable: true,
|
|
122
|
+
fix: config
|
|
123
|
+
? () => {
|
|
124
|
+
setConfigValue(config, "skills.dangerousCodeScanner", true);
|
|
125
|
+
writeOpenClawConfig(config);
|
|
126
|
+
}
|
|
127
|
+
: undefined,
|
|
128
|
+
});
|
|
129
|
+
// 8. Workspace-only filesystem
|
|
130
|
+
const fsAccess = config
|
|
131
|
+
? getConfigValue(config, "tools.filesystem.scope")
|
|
132
|
+
: undefined;
|
|
133
|
+
checks.push({
|
|
134
|
+
id: "fs-scope",
|
|
135
|
+
name: "Workspace-only filesystem access",
|
|
136
|
+
severity: "medium",
|
|
137
|
+
passed: fsAccess === "workspace" || fsAccess === undefined,
|
|
138
|
+
message: fsAccess && fsAccess !== "workspace"
|
|
139
|
+
? `Filesystem scope: "${fsAccess}" — broader than needed`
|
|
140
|
+
: "Filesystem access scoped to workspace",
|
|
141
|
+
fixable: true,
|
|
142
|
+
fix: config
|
|
143
|
+
? () => {
|
|
144
|
+
setConfigValue(config, "tools.filesystem.scope", "workspace");
|
|
145
|
+
writeOpenClawConfig(config);
|
|
146
|
+
}
|
|
147
|
+
: undefined,
|
|
148
|
+
});
|
|
149
|
+
// 9. Exec approval gates
|
|
150
|
+
const execApproval = config
|
|
151
|
+
? getConfigValue(config, "tools.exec.requireApproval")
|
|
152
|
+
: undefined;
|
|
153
|
+
checks.push({
|
|
154
|
+
id: "exec-approval",
|
|
155
|
+
name: "Exec approval gates enabled",
|
|
156
|
+
severity: "low",
|
|
157
|
+
passed: execApproval !== false,
|
|
158
|
+
message: execApproval === false
|
|
159
|
+
? "Shell commands execute without approval"
|
|
160
|
+
: "Exec commands require approval",
|
|
161
|
+
fixable: true,
|
|
162
|
+
fix: config
|
|
163
|
+
? () => {
|
|
164
|
+
setConfigValue(config, "tools.exec.requireApproval", true);
|
|
165
|
+
writeOpenClawConfig(config);
|
|
166
|
+
}
|
|
167
|
+
: undefined,
|
|
168
|
+
});
|
|
169
|
+
return checks;
|
|
170
|
+
}
|
|
171
|
+
export function computeScore(checks) {
|
|
172
|
+
const criticalFail = checks.some((c) => !c.passed && c.severity === "critical");
|
|
173
|
+
const highFail = checks.some((c) => !c.passed && c.severity === "high");
|
|
174
|
+
const medFail = checks.some((c) => !c.passed && c.severity === "medium");
|
|
175
|
+
const failCount = checks.filter((c) => !c.passed).length;
|
|
176
|
+
if (criticalFail)
|
|
177
|
+
return "D";
|
|
178
|
+
if (highFail)
|
|
179
|
+
return "C";
|
|
180
|
+
if (medFail || failCount > 1)
|
|
181
|
+
return "B";
|
|
182
|
+
return "A";
|
|
183
|
+
}
|
|
184
|
+
function compareVersions(a, b) {
|
|
185
|
+
const pa = a.split(".").map(Number);
|
|
186
|
+
const pb = b.split(".").map(Number);
|
|
187
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
188
|
+
const na = pa[i] ?? 0;
|
|
189
|
+
const nb = pb[i] ?? 0;
|
|
190
|
+
if (na !== nb)
|
|
191
|
+
return na - nb;
|
|
192
|
+
}
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
function containsPlaintextCredentials(text) {
|
|
196
|
+
const patterns = [
|
|
197
|
+
/sk-[a-zA-Z0-9]{20,}/,
|
|
198
|
+
/sk_live_[a-zA-Z0-9]{20,}/,
|
|
199
|
+
/password["']\s*:\s*["'][^"']+["']/i,
|
|
200
|
+
];
|
|
201
|
+
return patterns.some((p) => p.test(text));
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=hardening.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hardening.js","sourceRoot":"","sources":["../../src/lib/hardening.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAYvB;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAgB;IAChD,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,+BAA+B;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACzE,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,cAAc;QAClB,IAAI,EAAE,2BAA2B;QACjC,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,CAAC,IAAI,IAAI,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,WAAW;QAC5D,OAAO,EACL,CAAC,IAAI,IAAI,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,WAAW;YAClD,CAAC,CAAC,wCAAwC;YAC1C,CAAC,CAAC,qBAAqB,IAAI,wBAAwB;QACvD,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,MAAM;YACT,CAAC,CAAC,GAAG,EAAE;gBACH,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;gBACnD,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;YACH,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;IAEH,wBAAwB;IACxB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,8BAA8B;QACpC,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,CAAC,CAAC,KAAK;QACf,OAAO,EAAE,KAAK;YACZ,CAAC,CAAC,mCAAmC;YACrC,CAAC,CAAC,2CAA2C;QAC/C,OAAO,EAAE,KAAK,EAAE,gCAAgC;KACjD,CAAC,CAAC;IAEH,mCAAmC;IACnC,MAAM,UAAU,GAAG,WAAW,CAAC;IAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,SAAS;QACb,IAAI,EAAE,uBAAuB,UAAU,EAAE;QACzC,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,OAAO;YACd,CAAC,CAAC,SAAS;gBACT,CAAC,CAAC,YAAY,OAAO,EAAE;gBACvB,CAAC,CAAC,YAAY,OAAO,gDAAgD;YACvE,CAAC,CAAC,0BAA0B;QAC9B,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;IAEH,wCAAwC;IACxC,MAAM,YAAY,GAAG,MAAM;QACzB,CAAC,CAAC,4BAA4B,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACtD,CAAC,CAAC,KAAK,CAAC;IACV,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,iBAAiB;QACrB,IAAI,EAAE,oCAAoC;QAC1C,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,CAAC,YAAY;QACrB,OAAO,EAAE,YAAY;YACnB,CAAC,CAAC,iDAAiD;YACnD,CAAC,CAAC,wCAAwC;QAC5C,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;IAEH,qCAAqC;IACrC,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACrC,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,kBAAkB;QACtB,IAAI,EAAE,8BAA8B;QACpC,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,KAAK,CAAC,IAAI;QAClB,OAAO,EAAE,KAAK,CAAC,IAAI;YACjB,CAAC,CAAC,oBAAoB,KAAK,CAAC,IAAI,eAAe;YAC/C,CAAC,CAAC,oBAAoB,KAAK,CAAC,IAAI,uBAAuB;QACzD,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,EAAE;YACR,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,CAAC,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;KACF,CAAC,CAAC;IAEH,iCAAiC;IACjC,MAAM,QAAQ,GAAG,MAAM;QACrB,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,kCAAkC,CAAC;QAC5D,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,WAAW;QACf,IAAI,EAAE,6BAA6B;QACnC,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,QAAQ,KAAK,KAAK;QAC1B,OAAO,EACL,QAAQ,KAAK,KAAK;YAChB,CAAC,CAAC,sCAAsC;YACxC,CAAC,CAAC,oCAAoC;QAC1C,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,MAAM;YACT,CAAC,CAAC,GAAG,EAAE;gBACH,cAAc,CAAC,MAAM,EAAE,kCAAkC,EAAE,IAAI,CAAC,CAAC;gBACjE,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;YACH,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;IAEH,6BAA6B;IAC7B,MAAM,OAAO,GAAG,MAAM;QACpB,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,6BAA6B,CAAC;QACvD,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,iCAAiC;QACvC,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,OAAO,KAAK,KAAK;QACzB,OAAO,EACL,OAAO,KAAK,KAAK;YACf,CAAC,CAAC,6DAA6D;YAC/D,CAAC,CAAC,sBAAsB;QAC5B,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,MAAM;YACT,CAAC,CAAC,GAAG,EAAE;gBACH,cAAc,CAAC,MAAM,EAAE,6BAA6B,EAAE,IAAI,CAAC,CAAC;gBAC5D,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;YACH,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;IAEH,+BAA+B;IAC/B,MAAM,QAAQ,GAAG,MAAM;QACrB,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,wBAAwB,CAAC;QAClD,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,UAAU;QACd,IAAI,EAAE,kCAAkC;QACxC,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,SAAS;QAC1D,OAAO,EACL,QAAQ,IAAI,QAAQ,KAAK,WAAW;YAClC,CAAC,CAAC,sBAAsB,QAAQ,yBAAyB;YACzD,CAAC,CAAC,uCAAuC;QAC7C,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,MAAM;YACT,CAAC,CAAC,GAAG,EAAE;gBACH,cAAc,CAAC,MAAM,EAAE,wBAAwB,EAAE,WAAW,CAAC,CAAC;gBAC9D,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;YACH,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;IAEH,yBAAyB;IACzB,MAAM,YAAY,GAAG,MAAM;QACzB,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,4BAA4B,CAAC;QACtD,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,CAAC,IAAI,CAAC;QACV,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,6BAA6B;QACnC,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,YAAY,KAAK,KAAK;QAC9B,OAAO,EACL,YAAY,KAAK,KAAK;YACpB,CAAC,CAAC,yCAAyC;YAC3C,CAAC,CAAC,gCAAgC;QACtC,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,MAAM;YACT,CAAC,CAAC,GAAG,EAAE;gBACH,cAAc,CAAC,MAAM,EAAE,4BAA4B,EAAE,IAAI,CAAC,CAAC;gBAC3D,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC9B,CAAC;YACH,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAuB;IAClD,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAC9B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,CAC9C,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;IAEzD,IAAI,YAAY;QAAE,OAAO,GAAG,CAAC;IAC7B,IAAI,QAAQ;QAAE,OAAO,GAAG,CAAC;IACzB,IAAI,OAAO,IAAI,SAAS,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACzC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,eAAe,CAAC,CAAS,EAAE,CAAS;IAC3C,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACxD,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtB,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,EAAE,GAAG,EAAE,CAAC;IAChC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,4BAA4B,CAAC,IAAY;IAChD,MAAM,QAAQ,GAAG;QACf,qBAAqB;QACrB,0BAA0B;QAC1B,oCAAoC;KACrC,CAAC;IACF,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw detection and configuration utilities.
|
|
3
|
+
* Reads/writes OpenClaw config without modifying unrelated settings.
|
|
4
|
+
*/
|
|
5
|
+
export interface OpenClawInfo {
|
|
6
|
+
found: boolean;
|
|
7
|
+
version?: string;
|
|
8
|
+
configPath?: string;
|
|
9
|
+
config?: Record<string, unknown>;
|
|
10
|
+
gatewayRunning?: boolean;
|
|
11
|
+
gatewayPort?: number;
|
|
12
|
+
skillsDir?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function detectOpenClaw(): OpenClawInfo;
|
|
15
|
+
export declare function readOpenClawConfig(): Record<string, unknown> | null;
|
|
16
|
+
export declare function writeOpenClawConfig(config: Record<string, unknown>): void;
|
|
17
|
+
export declare function getConfigPermissions(): {
|
|
18
|
+
mode: string;
|
|
19
|
+
safe: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Get a nested config value safely.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getConfigValue(config: Record<string, unknown>, path: string): unknown;
|
|
25
|
+
/**
|
|
26
|
+
* Set a nested config value.
|
|
27
|
+
*/
|
|
28
|
+
export declare function setConfigValue(config: Record<string, unknown>, path: string, value: unknown): void;
|