squad-openclaw 2026.2.2002 → 2026.2.2004
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 +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +135 -7
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ These directories are **completely blocked** from all filesystem operations (rea
|
|
|
31
31
|
|
|
32
32
|
| Path | Reason |
|
|
33
33
|
|---|---|
|
|
34
|
-
| `~/.openclaw/squad-ceo-data/squad-relay.json` | Contains ed25519 private key for relay device identity |
|
|
34
|
+
| `~/.openclaw/squad-ceo-data/relay/squad-relay.json` | Contains ed25519 private key for relay device identity |
|
|
35
35
|
| `~/.openclaw/*.bak` | Backup files at the top level contain unredacted config (tokens, keys) that would bypass redaction |
|
|
36
36
|
|
|
37
37
|
### Layer 2: Redacted Files (hardcoded, non-configurable)
|
|
@@ -60,7 +60,7 @@ Operators can customize via the `fs.allowedRoots` config option.
|
|
|
60
60
|
These files/directories cannot be written to, even if they fall within `allowedRoots`:
|
|
61
61
|
|
|
62
62
|
- `~/.openclaw/openclaw.json` — operator configuration (read-only with redaction)
|
|
63
|
-
- `~/.openclaw/squad-ceo-data/squad-relay.json` — relay device private key
|
|
63
|
+
- `~/.openclaw/squad-ceo-data/relay/squad-relay.json` — relay device private key
|
|
64
64
|
- All blocked directories above (credentials, devices, identity)
|
|
65
65
|
- All `.bak` files at `~/.openclaw/` top level
|
|
66
66
|
|
package/dist/index.d.ts
CHANGED
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
* │ REDACTED on read (sensitive fields replaced with "[REDACTED]"): │
|
|
22
22
|
* │ • ~/.openclaw/openclaw.json → channel.*.botToken │
|
|
23
23
|
* │ • ~/.openclaw/openclaw.json → gateway.auth.* │
|
|
24
|
-
* │ • squad-ceo-data/squad-relay.json → deviceKeys.privateKeyPem
|
|
24
|
+
* │ • squad-ceo-data/relay/squad-relay.json → deviceKeys.privateKeyPem │
|
|
25
25
|
* │ │
|
|
26
26
|
* │ WRITE-PROTECTED (no writes, deletes, or renames): │
|
|
27
27
|
* │ • ~/.openclaw/openclaw.json │
|
|
28
|
-
* │ • squad-ceo-data/squad-relay.json
|
|
28
|
+
* │ • squad-ceo-data/relay/squad-relay.json │
|
|
29
29
|
* │ • All blocked directories above │
|
|
30
30
|
* │ │
|
|
31
31
|
* │ The bundle is NOT minified to allow security auditing of the │
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,107 @@
|
|
|
1
|
+
// src/agents.ts
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
function registerAgentMethods(api) {
|
|
4
|
+
api.registerGatewayMethod(
|
|
5
|
+
"squad.agents.add",
|
|
6
|
+
async ({ params, respond }) => {
|
|
7
|
+
const name = params?.name;
|
|
8
|
+
const model = params?.model;
|
|
9
|
+
if (!name || typeof name !== "string" || !name.trim()) {
|
|
10
|
+
respond(false, { error: "Missing or empty 'name' parameter" });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const safeName = name.trim();
|
|
14
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9 _-]*$/.test(safeName)) {
|
|
15
|
+
respond(false, { error: "Agent name must start with a letter/number and contain only letters, numbers, spaces, hyphens, or underscores" });
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
let cmd = `openclaw agents add ${JSON.stringify(safeName)} --non-interactive`;
|
|
20
|
+
if (model) {
|
|
21
|
+
cmd += ` --model ${JSON.stringify(model)}`;
|
|
22
|
+
}
|
|
23
|
+
const output = execSync(cmd, {
|
|
24
|
+
timeout: 3e4,
|
|
25
|
+
encoding: "utf-8",
|
|
26
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
27
|
+
});
|
|
28
|
+
respond(true, { ok: true, output: output.slice(0, 1e3) });
|
|
29
|
+
} catch (err2) {
|
|
30
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
31
|
+
const stderr = err2?.stderr;
|
|
32
|
+
respond(false, {
|
|
33
|
+
error: `Failed to add agent: ${stderr || msg}`.slice(0, 500)
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
api.registerGatewayMethod(
|
|
39
|
+
"squad.agents.delete",
|
|
40
|
+
async ({ params, respond }) => {
|
|
41
|
+
const agentId = params?.agentId;
|
|
42
|
+
if (!agentId || typeof agentId !== "string" || !agentId.trim()) {
|
|
43
|
+
respond(false, { error: "Missing or empty 'agentId' parameter" });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (agentId === "main") {
|
|
47
|
+
respond(false, { error: "Cannot delete the main agent" });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(agentId)) {
|
|
51
|
+
respond(false, { error: "Invalid agent ID format" });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const output = execSync(
|
|
56
|
+
`openclaw agents delete ${JSON.stringify(agentId)} --non-interactive 2>&1`,
|
|
57
|
+
{ timeout: 3e4, encoding: "utf-8" }
|
|
58
|
+
);
|
|
59
|
+
respond(true, { ok: true, output: output.slice(0, 1e3) });
|
|
60
|
+
} catch (err2) {
|
|
61
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
62
|
+
const stderr = err2?.stderr;
|
|
63
|
+
respond(false, {
|
|
64
|
+
error: `Failed to delete agent: ${stderr || msg}`.slice(0, 500)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
api.registerGatewayMethod(
|
|
70
|
+
"squad.agents.set-identity",
|
|
71
|
+
async ({ params, respond }) => {
|
|
72
|
+
const agentId = params?.agentId;
|
|
73
|
+
const name = params?.name;
|
|
74
|
+
const emoji = params?.emoji;
|
|
75
|
+
const theme = params?.theme;
|
|
76
|
+
if (!agentId || typeof agentId !== "string") {
|
|
77
|
+
respond(false, { error: "Missing 'agentId' parameter" });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const args = [`--agent`, JSON.stringify(agentId)];
|
|
81
|
+
if (name) args.push(`--name`, JSON.stringify(name));
|
|
82
|
+
if (emoji) args.push(`--emoji`, JSON.stringify(emoji));
|
|
83
|
+
if (theme) args.push(`--theme`, JSON.stringify(theme));
|
|
84
|
+
if (args.length <= 2) {
|
|
85
|
+
respond(false, { error: "No identity fields provided (name, emoji, or theme)" });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const output = execSync(
|
|
90
|
+
`openclaw agents set-identity ${args.join(" ")} 2>&1`,
|
|
91
|
+
{ timeout: 15e3, encoding: "utf-8" }
|
|
92
|
+
);
|
|
93
|
+
respond(true, { ok: true, output: output.slice(0, 1e3) });
|
|
94
|
+
} catch (err2) {
|
|
95
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
96
|
+
const stderr = err2?.stderr;
|
|
97
|
+
respond(false, {
|
|
98
|
+
error: `Failed to set identity: ${stderr || msg}`.slice(0, 500)
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
1
105
|
// src/entities.ts
|
|
2
106
|
import { Type as T } from "@sinclair/typebox";
|
|
3
107
|
import path3 from "path";
|
|
@@ -266,7 +370,7 @@ var SENSITIVE_BLOCKED_DIRS = [
|
|
|
266
370
|
path2.join(OPENCLAW_DIR, "identity")
|
|
267
371
|
];
|
|
268
372
|
var SENSITIVE_BLOCKED_FILES = [
|
|
269
|
-
path2.join(OPENCLAW_DIR, "squad-ceo-data", "squad-relay.json")
|
|
373
|
+
path2.join(OPENCLAW_DIR, "squad-ceo-data", "relay", "squad-relay.json")
|
|
270
374
|
];
|
|
271
375
|
function isSensitivePath(resolvedPath) {
|
|
272
376
|
for (const blocked of SENSITIVE_BLOCKED_DIRS) {
|
|
@@ -1111,11 +1215,16 @@ function registerSqlTools(api) {
|
|
|
1111
1215
|
}
|
|
1112
1216
|
|
|
1113
1217
|
// src/version.ts
|
|
1114
|
-
import { execSync } from "child_process";
|
|
1218
|
+
import { execSync as execSync2 } from "child_process";
|
|
1115
1219
|
import fs5 from "fs";
|
|
1116
1220
|
import path5 from "path";
|
|
1117
1221
|
import { fileURLToPath } from "url";
|
|
1118
1222
|
var PACKAGE_NAME = "squad-openclaw";
|
|
1223
|
+
var CONFIG_PATH = path5.join(
|
|
1224
|
+
process.env.HOME ?? "/root",
|
|
1225
|
+
".openclaw",
|
|
1226
|
+
"openclaw.json"
|
|
1227
|
+
);
|
|
1119
1228
|
function getCurrentVersion() {
|
|
1120
1229
|
const thisFile = fileURLToPath(import.meta.url);
|
|
1121
1230
|
const pkgPath = path5.resolve(path5.dirname(thisFile), "..", "package.json");
|
|
@@ -1175,18 +1284,36 @@ function registerVersionMethods(api) {
|
|
|
1175
1284
|
try {
|
|
1176
1285
|
const before = getCurrentVersion();
|
|
1177
1286
|
let updateOutput = "";
|
|
1287
|
+
let configBackup = null;
|
|
1288
|
+
try {
|
|
1289
|
+
configBackup = fs5.readFileSync(CONFIG_PATH, "utf-8");
|
|
1290
|
+
} catch {
|
|
1291
|
+
}
|
|
1292
|
+
try {
|
|
1293
|
+
execSync2("openclaw doctor --fix 2>&1", {
|
|
1294
|
+
timeout: 3e4,
|
|
1295
|
+
encoding: "utf-8"
|
|
1296
|
+
});
|
|
1297
|
+
} catch {
|
|
1298
|
+
}
|
|
1178
1299
|
try {
|
|
1179
|
-
updateOutput =
|
|
1300
|
+
updateOutput = execSync2(
|
|
1180
1301
|
`openclaw plugins update ${PACKAGE_NAME} 2>&1`,
|
|
1181
1302
|
{ timeout: 12e4, encoding: "utf-8" }
|
|
1182
1303
|
);
|
|
1183
1304
|
} catch {
|
|
1184
1305
|
try {
|
|
1185
|
-
updateOutput =
|
|
1306
|
+
updateOutput = execSync2(
|
|
1186
1307
|
`npm install -g ${PACKAGE_NAME}@latest 2>&1`,
|
|
1187
1308
|
{ timeout: 12e4, encoding: "utf-8" }
|
|
1188
1309
|
);
|
|
1189
1310
|
} catch (npmErr) {
|
|
1311
|
+
if (configBackup) {
|
|
1312
|
+
try {
|
|
1313
|
+
fs5.writeFileSync(CONFIG_PATH, configBackup, "utf-8");
|
|
1314
|
+
} catch {
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1190
1317
|
const msg = npmErr instanceof Error ? npmErr.message : String(npmErr);
|
|
1191
1318
|
respond(false, {
|
|
1192
1319
|
error: `Update failed: ${msg}`,
|
|
@@ -1208,7 +1335,7 @@ function registerVersionMethods(api) {
|
|
|
1208
1335
|
);
|
|
1209
1336
|
setTimeout(() => {
|
|
1210
1337
|
try {
|
|
1211
|
-
|
|
1338
|
+
execSync2("openclaw gateway restart 2>&1", {
|
|
1212
1339
|
timeout: 3e4,
|
|
1213
1340
|
encoding: "utf-8"
|
|
1214
1341
|
});
|
|
@@ -1319,7 +1446,7 @@ function readOperatorToken() {
|
|
|
1319
1446
|
return null;
|
|
1320
1447
|
}
|
|
1321
1448
|
}
|
|
1322
|
-
var RELAY_DATA_DIR = path6.join(os.homedir(), ".openclaw", "squad-ceo-data");
|
|
1449
|
+
var RELAY_DATA_DIR = path6.join(os.homedir(), ".openclaw", "squad-ceo-data", "relay");
|
|
1323
1450
|
var RELAY_STATE_PATH = path6.join(RELAY_DATA_DIR, "squad-relay.json");
|
|
1324
1451
|
function readRelayState() {
|
|
1325
1452
|
try {
|
|
@@ -1356,7 +1483,7 @@ function loadOrCreateRelayDeviceKeys() {
|
|
|
1356
1483
|
}
|
|
1357
1484
|
function writeDeviceInfoFile(keys) {
|
|
1358
1485
|
const stateDir = process.env.OPENCLAW_STATE_DIR || path6.join(os.homedir(), ".openclaw");
|
|
1359
|
-
const infoPath = path6.join(stateDir, "squad-ceo-data", "relay-device-info.json");
|
|
1486
|
+
const infoPath = path6.join(stateDir, "squad-ceo-data", "relay", "relay-device-info.json");
|
|
1360
1487
|
const info = {
|
|
1361
1488
|
deviceId: keys.deviceId,
|
|
1362
1489
|
publicKey: keys.publicKey,
|
|
@@ -1895,6 +2022,7 @@ function squadAppPlugin(api) {
|
|
|
1895
2022
|
registerFilesystemTools(api);
|
|
1896
2023
|
registerSqlTools(api);
|
|
1897
2024
|
registerVersionMethods(api);
|
|
2025
|
+
registerAgentMethods(api);
|
|
1898
2026
|
api.registerGatewayMethod(
|
|
1899
2027
|
"tools.invoke",
|
|
1900
2028
|
async ({ params, respond }) => {
|
package/openclaw.plugin.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"type": "array",
|
|
11
11
|
"items": { "type": "string" },
|
|
12
12
|
"default": ["~/.openclaw"],
|
|
13
|
-
"description": "Restrict filesystem operations to these directories. Defaults to [\"~/.openclaw\"]. Hardcoded blocks on credentials/, devices/, identity/, squad-relay.json, and .bak files always apply."
|
|
13
|
+
"description": "Restrict filesystem operations to these directories. Defaults to [\"~/.openclaw\"]. Hardcoded blocks on credentials/, devices/, identity/, relay/squad-relay.json, and .bak files always apply."
|
|
14
14
|
},
|
|
15
15
|
"relay.enabled": {
|
|
16
16
|
"type": "boolean",
|
package/package.json
CHANGED