squad-openclaw 2026.2.2021 → 2026.2.2022
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/dist/index.js +139 -67
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -731,6 +731,11 @@ function broadcastToUsers(event, payload) {
|
|
|
731
731
|
|
|
732
732
|
// src/agents.ts
|
|
733
733
|
import { execSync } from "child_process";
|
|
734
|
+
import path4 from "path";
|
|
735
|
+
function deriveAgentIdFromName(name) {
|
|
736
|
+
const normalized = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
737
|
+
return normalized || "agent";
|
|
738
|
+
}
|
|
734
739
|
function registerAgentMethods(api) {
|
|
735
740
|
const callGateway = async (ctx, method, params = {}) => {
|
|
736
741
|
const ctxRequest = ctx.request;
|
|
@@ -745,6 +750,8 @@ function registerAgentMethods(api) {
|
|
|
745
750
|
"squad.agents.add",
|
|
746
751
|
async ({ params, respond }) => {
|
|
747
752
|
const name = params?.name;
|
|
753
|
+
const agentId = params?.agentId;
|
|
754
|
+
const workspace = params?.workspace;
|
|
748
755
|
const model = params?.model;
|
|
749
756
|
if (!name || typeof name !== "string" || !name.trim()) {
|
|
750
757
|
respond(false, { error: "Missing or empty 'name' parameter" });
|
|
@@ -755,8 +762,19 @@ function registerAgentMethods(api) {
|
|
|
755
762
|
respond(false, { error: "Agent name must start with a letter/number and contain only letters, numbers, spaces, hyphens, or underscores" });
|
|
756
763
|
return;
|
|
757
764
|
}
|
|
765
|
+
const providedAgentId = typeof agentId === "string" ? agentId.trim() : "";
|
|
766
|
+
if (providedAgentId && !/^[a-z0-9][a-z0-9-]*$/.test(providedAgentId)) {
|
|
767
|
+
respond(false, { error: "Invalid agentId format" });
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
const effectiveAgentId = providedAgentId || deriveAgentIdFromName(safeName);
|
|
771
|
+
const defaultWorkspace = path4.join(
|
|
772
|
+
getOpenclawStateDir(),
|
|
773
|
+
effectiveAgentId === "main" ? "workspace" : `workspace-${effectiveAgentId}`
|
|
774
|
+
);
|
|
775
|
+
const workspacePath = typeof workspace === "string" && workspace.trim() ? workspace.trim() : defaultWorkspace;
|
|
758
776
|
try {
|
|
759
|
-
let cmd = `openclaw agents add ${JSON.stringify(safeName)} --non-interactive`;
|
|
777
|
+
let cmd = `openclaw agents add ${JSON.stringify(safeName)} --non-interactive --workspace ${JSON.stringify(workspacePath)}`;
|
|
760
778
|
if (model) {
|
|
761
779
|
cmd += ` --model ${JSON.stringify(model)}`;
|
|
762
780
|
}
|
|
@@ -910,11 +928,11 @@ function registerAgentMethods(api) {
|
|
|
910
928
|
|
|
911
929
|
// src/entities.ts
|
|
912
930
|
import { Type as T } from "@sinclair/typebox";
|
|
913
|
-
import
|
|
931
|
+
import path8 from "path";
|
|
914
932
|
import fs7 from "fs";
|
|
915
933
|
|
|
916
934
|
// src/watcher.ts
|
|
917
|
-
import
|
|
935
|
+
import path5 from "path";
|
|
918
936
|
import fs4 from "fs";
|
|
919
937
|
import chokidar from "chokidar";
|
|
920
938
|
var debounceTimers = /* @__PURE__ */ new Map();
|
|
@@ -945,29 +963,29 @@ function debouncedFs(relPath, action, fn) {
|
|
|
945
963
|
);
|
|
946
964
|
}
|
|
947
965
|
function isWorkspaceIdentity(filePath, configDir) {
|
|
948
|
-
const rel =
|
|
966
|
+
const rel = path5.relative(configDir, filePath);
|
|
949
967
|
const match = rel.match(/^(workspace(?:-([^/]+))?)\/IDENTITY\.md$/);
|
|
950
968
|
if (!match) return null;
|
|
951
969
|
const dirName = match[1];
|
|
952
970
|
const agentId = match[2] ?? "main";
|
|
953
|
-
return { agentId, workspacePath:
|
|
971
|
+
return { agentId, workspacePath: path5.join(configDir, dirName) };
|
|
954
972
|
}
|
|
955
973
|
function isWorkspaceAgentJson(filePath, configDir) {
|
|
956
|
-
const rel =
|
|
974
|
+
const rel = path5.relative(configDir, filePath);
|
|
957
975
|
const match = rel.match(/^(workspace(?:-([^/]+))?)\/agent\.json$/);
|
|
958
976
|
if (!match) return null;
|
|
959
977
|
const dirName = match[1];
|
|
960
978
|
const agentId = match[2] ?? "main";
|
|
961
|
-
return { agentId, workspacePath:
|
|
979
|
+
return { agentId, workspacePath: path5.join(configDir, dirName) };
|
|
962
980
|
}
|
|
963
981
|
function isGlobalSkillDir(filePath, configDir) {
|
|
964
|
-
const rel =
|
|
982
|
+
const rel = path5.relative(configDir, filePath);
|
|
965
983
|
const match = rel.match(/^skills\/([^/]+)\/?$/);
|
|
966
984
|
if (!match) return null;
|
|
967
985
|
return { skillKey: match[1] };
|
|
968
986
|
}
|
|
969
987
|
function isWorkspaceSkillDir(filePath, configDir) {
|
|
970
|
-
const rel =
|
|
988
|
+
const rel = path5.relative(configDir, filePath);
|
|
971
989
|
const match = rel.match(
|
|
972
990
|
/^workspace(?:-([^/]+))?\/skills\/([^/]+)\/?$/
|
|
973
991
|
);
|
|
@@ -975,13 +993,13 @@ function isWorkspaceSkillDir(filePath, configDir) {
|
|
|
975
993
|
return { agentId: match[1] ?? "main", skillKey: match[2] };
|
|
976
994
|
}
|
|
977
995
|
function isPluginManifest(filePath, configDir) {
|
|
978
|
-
const rel =
|
|
996
|
+
const rel = path5.relative(configDir, filePath);
|
|
979
997
|
const match = rel.match(/^extensions\/([^/]+)\/openclaw\.plugin\.json$/);
|
|
980
998
|
if (!match) return null;
|
|
981
999
|
return { pluginDirName: match[1] };
|
|
982
1000
|
}
|
|
983
1001
|
function isOpenClawConfig(filePath, configDir) {
|
|
984
|
-
return
|
|
1002
|
+
return path5.relative(configDir, filePath) === "openclaw.json";
|
|
985
1003
|
}
|
|
986
1004
|
function updateAgent(agentId, workspacePath) {
|
|
987
1005
|
const now = Date.now();
|
|
@@ -989,7 +1007,7 @@ function updateAgent(agentId, workspacePath) {
|
|
|
989
1007
|
const metadata = { workspacePath };
|
|
990
1008
|
try {
|
|
991
1009
|
const content = fs4.readFileSync(
|
|
992
|
-
|
|
1010
|
+
path5.join(workspacePath, "IDENTITY.md"),
|
|
993
1011
|
"utf-8"
|
|
994
1012
|
);
|
|
995
1013
|
const parsed = parseIdentityName(content);
|
|
@@ -999,7 +1017,7 @@ function updateAgent(agentId, workspacePath) {
|
|
|
999
1017
|
if (name === agentId) {
|
|
1000
1018
|
try {
|
|
1001
1019
|
const raw = fs4.readFileSync(
|
|
1002
|
-
|
|
1020
|
+
path5.join(workspacePath, "agent.json"),
|
|
1003
1021
|
"utf-8"
|
|
1004
1022
|
);
|
|
1005
1023
|
const config = JSON.parse(raw);
|
|
@@ -1023,7 +1041,7 @@ function updateAgent(agentId, workspacePath) {
|
|
|
1023
1041
|
}
|
|
1024
1042
|
function updatePlugin(pluginDirName, configDir) {
|
|
1025
1043
|
const now = Date.now();
|
|
1026
|
-
const manifestPath =
|
|
1044
|
+
const manifestPath = path5.join(
|
|
1027
1045
|
configDir,
|
|
1028
1046
|
"extensions",
|
|
1029
1047
|
pluginDirName,
|
|
@@ -1040,7 +1058,7 @@ function updatePlugin(pluginDirName, configDir) {
|
|
|
1040
1058
|
name,
|
|
1041
1059
|
title: name,
|
|
1042
1060
|
description: manifest.description || null,
|
|
1043
|
-
metadata: { pluginId, pluginDir:
|
|
1061
|
+
metadata: { pluginId, pluginDir: path5.dirname(manifestPath) },
|
|
1044
1062
|
source: "filesystem",
|
|
1045
1063
|
source_key: manifestPath,
|
|
1046
1064
|
created_at: now,
|
|
@@ -1067,7 +1085,7 @@ function startWatcher(configDir, onFsChange) {
|
|
|
1067
1085
|
});
|
|
1068
1086
|
const emitFsChange = (action, filePath) => {
|
|
1069
1087
|
if (!onFsChange) return;
|
|
1070
|
-
const rel =
|
|
1088
|
+
const rel = path5.relative(configDir, filePath);
|
|
1071
1089
|
debouncedFs(rel, action, () => {
|
|
1072
1090
|
onFsChange({ action, path: rel });
|
|
1073
1091
|
});
|
|
@@ -1121,7 +1139,7 @@ function startWatcher(configDir, onFsChange) {
|
|
|
1121
1139
|
);
|
|
1122
1140
|
return;
|
|
1123
1141
|
}
|
|
1124
|
-
const rel =
|
|
1142
|
+
const rel = path5.relative(configDir, dirPath);
|
|
1125
1143
|
if (/^workspace(-[^/]+)?$/.test(rel)) {
|
|
1126
1144
|
debounced("agents", () => scanAgents(configDir));
|
|
1127
1145
|
return;
|
|
@@ -1129,7 +1147,7 @@ function startWatcher(configDir, onFsChange) {
|
|
|
1129
1147
|
};
|
|
1130
1148
|
const handleUnlinkDir = (dirPath) => {
|
|
1131
1149
|
emitFsChange("unlinkDir", dirPath);
|
|
1132
|
-
const rel =
|
|
1150
|
+
const rel = path5.relative(configDir, dirPath);
|
|
1133
1151
|
const wsMatch = rel.match(/^workspace(?:-([^/]+))?$/);
|
|
1134
1152
|
if (wsMatch) {
|
|
1135
1153
|
const agentId = wsMatch[1] ?? "main";
|
|
@@ -1167,14 +1185,14 @@ function startWatcher(configDir, onFsChange) {
|
|
|
1167
1185
|
|
|
1168
1186
|
// src/filesystem.ts
|
|
1169
1187
|
import fs6 from "fs";
|
|
1170
|
-
import
|
|
1188
|
+
import path7 from "path";
|
|
1171
1189
|
|
|
1172
1190
|
// src/layout.ts
|
|
1173
1191
|
import fs5 from "fs";
|
|
1174
|
-
import
|
|
1192
|
+
import path6 from "path";
|
|
1175
1193
|
function resolveMaybeRelativePath(stateDir, p) {
|
|
1176
|
-
if (
|
|
1177
|
-
return
|
|
1194
|
+
if (path6.isAbsolute(p)) return path6.resolve(p);
|
|
1195
|
+
return path6.resolve(stateDir, p);
|
|
1178
1196
|
}
|
|
1179
1197
|
function listWorkspaceFallbacks(stateDir) {
|
|
1180
1198
|
let entries;
|
|
@@ -1185,7 +1203,7 @@ function listWorkspaceFallbacks(stateDir) {
|
|
|
1185
1203
|
}
|
|
1186
1204
|
return entries.filter((entry) => entry.isDirectory() && (entry.name === "workspace" || entry.name.startsWith("workspace-"))).map((entry) => {
|
|
1187
1205
|
const agentId = entry.name === "workspace" ? "main" : entry.name.replace("workspace-", "");
|
|
1188
|
-
const workspacePath =
|
|
1206
|
+
const workspacePath = path6.join(stateDir, entry.name);
|
|
1189
1207
|
return {
|
|
1190
1208
|
agentId,
|
|
1191
1209
|
path: workspacePath,
|
|
@@ -1204,7 +1222,7 @@ function readOpenclawConfig(configPath) {
|
|
|
1204
1222
|
}
|
|
1205
1223
|
function resolveGatewayLayout() {
|
|
1206
1224
|
const stateDir = getOpenclawStateDir();
|
|
1207
|
-
const configPath =
|
|
1225
|
+
const configPath = path6.join(stateDir, "openclaw.json");
|
|
1208
1226
|
const config = readOpenclawConfig(configPath);
|
|
1209
1227
|
const workspaces = [];
|
|
1210
1228
|
if (config?.agents?.main?.workspace || config?.agents?.main?.workspacePath) {
|
|
@@ -1243,9 +1261,9 @@ function resolveGatewayLayout() {
|
|
|
1243
1261
|
return {
|
|
1244
1262
|
stateDir,
|
|
1245
1263
|
configPath,
|
|
1246
|
-
mediaDir:
|
|
1247
|
-
skillsDir:
|
|
1248
|
-
extensionsDir:
|
|
1264
|
+
mediaDir: path6.join(stateDir, "media"),
|
|
1265
|
+
skillsDir: path6.join(stateDir, "skills"),
|
|
1266
|
+
extensionsDir: path6.join(stateDir, "extensions"),
|
|
1249
1267
|
defaultFileBrowserRoot,
|
|
1250
1268
|
workspaces: resolvedWorkspaces
|
|
1251
1269
|
};
|
|
@@ -1255,16 +1273,16 @@ function resolveGatewayLayout() {
|
|
|
1255
1273
|
var HOME_DIR = process.env.HOME ?? "/root";
|
|
1256
1274
|
var OPENCLAW_DIR = getOpenclawStateDir();
|
|
1257
1275
|
var SENSITIVE_BLOCKED_DIRS = [
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1276
|
+
path7.join(OPENCLAW_DIR, "credentials"),
|
|
1277
|
+
path7.join(OPENCLAW_DIR, "devices"),
|
|
1278
|
+
path7.join(OPENCLAW_DIR, "identity")
|
|
1261
1279
|
];
|
|
1262
1280
|
var SENSITIVE_BLOCKED_FILES = [
|
|
1263
|
-
|
|
1281
|
+
path7.join(OPENCLAW_DIR, "squad-ceo-data", "relay", "squad-relay.json")
|
|
1264
1282
|
];
|
|
1265
1283
|
function isSensitivePath(resolvedPath) {
|
|
1266
1284
|
for (const blocked of SENSITIVE_BLOCKED_DIRS) {
|
|
1267
|
-
if (resolvedPath === blocked || resolvedPath.startsWith(blocked +
|
|
1285
|
+
if (resolvedPath === blocked || resolvedPath.startsWith(blocked + path7.sep)) {
|
|
1268
1286
|
return true;
|
|
1269
1287
|
}
|
|
1270
1288
|
}
|
|
@@ -1273,7 +1291,7 @@ function isSensitivePath(resolvedPath) {
|
|
|
1273
1291
|
return true;
|
|
1274
1292
|
}
|
|
1275
1293
|
}
|
|
1276
|
-
if (
|
|
1294
|
+
if (path7.dirname(resolvedPath) === OPENCLAW_DIR && resolvedPath.endsWith(".bak")) {
|
|
1277
1295
|
return true;
|
|
1278
1296
|
}
|
|
1279
1297
|
return false;
|
|
@@ -1322,20 +1340,20 @@ function redactOpenclawJson(rawContent) {
|
|
|
1322
1340
|
return JSON.stringify(config, null, 2);
|
|
1323
1341
|
}
|
|
1324
1342
|
function isOpenclawJson(resolvedPath) {
|
|
1325
|
-
return
|
|
1343
|
+
return path7.basename(resolvedPath) === OPENCLAW_JSON_FILENAME && resolvedPath.startsWith(OPENCLAW_DIR);
|
|
1326
1344
|
}
|
|
1327
1345
|
function expandHome(p) {
|
|
1328
1346
|
if (p.startsWith("~/") || p === "~") {
|
|
1329
|
-
return
|
|
1347
|
+
return path7.join(HOME_DIR, p.slice(1));
|
|
1330
1348
|
}
|
|
1331
1349
|
return p;
|
|
1332
1350
|
}
|
|
1333
1351
|
function validatePath(p, allowedRoots) {
|
|
1334
|
-
const resolved =
|
|
1352
|
+
const resolved = path7.resolve(expandHome(p));
|
|
1335
1353
|
if (!allowedRoots || allowedRoots.length === 0) return resolved;
|
|
1336
1354
|
const allowed = allowedRoots.some((root) => {
|
|
1337
|
-
const resolvedRoot =
|
|
1338
|
-
return resolved === resolvedRoot || resolved.startsWith(resolvedRoot +
|
|
1355
|
+
const resolvedRoot = path7.resolve(expandHome(root));
|
|
1356
|
+
return resolved === resolvedRoot || resolved.startsWith(resolvedRoot + path7.sep);
|
|
1339
1357
|
});
|
|
1340
1358
|
if (!allowed) {
|
|
1341
1359
|
throw new Error(`Path "${p}" is outside allowed roots`);
|
|
@@ -1376,7 +1394,7 @@ function listDir(dirPath, opts) {
|
|
|
1376
1394
|
const results = [];
|
|
1377
1395
|
for (const dirent of dirents) {
|
|
1378
1396
|
if (!opts.includeHidden && dirent.name.startsWith(".")) continue;
|
|
1379
|
-
const entryPath =
|
|
1397
|
+
const entryPath = path7.join(dirPath, dirent.name);
|
|
1380
1398
|
let type = "other";
|
|
1381
1399
|
if (dirent.isFile()) type = "file";
|
|
1382
1400
|
else if (dirent.isDirectory()) type = "directory";
|
|
@@ -1487,7 +1505,7 @@ function registerFilesystemTools(api) {
|
|
|
1487
1505
|
const encoding = params.encoding ?? "utf-8";
|
|
1488
1506
|
const mkdir = params.mkdir !== false;
|
|
1489
1507
|
if (mkdir) {
|
|
1490
|
-
fs6.mkdirSync(
|
|
1508
|
+
fs6.mkdirSync(path7.dirname(filePath), { recursive: true });
|
|
1491
1509
|
}
|
|
1492
1510
|
fs6.writeFileSync(filePath, content, encoding);
|
|
1493
1511
|
const stat = fs6.statSync(filePath);
|
|
@@ -1687,10 +1705,10 @@ function scanAgents(configDir) {
|
|
|
1687
1705
|
);
|
|
1688
1706
|
for (const dir of workspaceDirs) {
|
|
1689
1707
|
const agentId = dir.name === "workspace" ? "main" : dir.name.replace("workspace-", "");
|
|
1690
|
-
const workspacePath =
|
|
1708
|
+
const workspacePath = path8.join(configDir, dir.name);
|
|
1691
1709
|
let name = agentId;
|
|
1692
1710
|
const metadata = { workspacePath };
|
|
1693
|
-
const identityPath =
|
|
1711
|
+
const identityPath = path8.join(workspacePath, "IDENTITY.md");
|
|
1694
1712
|
try {
|
|
1695
1713
|
const content = fs7.readFileSync(identityPath, "utf-8");
|
|
1696
1714
|
const parsed = parseIdentityName(content);
|
|
@@ -1698,7 +1716,7 @@ function scanAgents(configDir) {
|
|
|
1698
1716
|
} catch {
|
|
1699
1717
|
}
|
|
1700
1718
|
if (name === agentId) {
|
|
1701
|
-
const agentJsonPath =
|
|
1719
|
+
const agentJsonPath = path8.join(workspacePath, "agent.json");
|
|
1702
1720
|
try {
|
|
1703
1721
|
const raw = fs7.readFileSync(agentJsonPath, "utf-8");
|
|
1704
1722
|
const config = JSON.parse(raw);
|
|
@@ -1725,7 +1743,7 @@ function scanAgents(configDir) {
|
|
|
1725
1743
|
}
|
|
1726
1744
|
function scanSkills(configDir) {
|
|
1727
1745
|
const now = Date.now();
|
|
1728
|
-
const globalSkillsDir =
|
|
1746
|
+
const globalSkillsDir = path8.join(configDir, "skills");
|
|
1729
1747
|
scanSkillsDir(globalSkillsDir, "global", now);
|
|
1730
1748
|
let entries;
|
|
1731
1749
|
try {
|
|
@@ -1738,7 +1756,7 @@ function scanSkills(configDir) {
|
|
|
1738
1756
|
continue;
|
|
1739
1757
|
}
|
|
1740
1758
|
const agentId = dir.name === "workspace" ? "main" : dir.name.replace("workspace-", "");
|
|
1741
|
-
const agentSkillsDir =
|
|
1759
|
+
const agentSkillsDir = path8.join(configDir, dir.name, "skills");
|
|
1742
1760
|
scanSkillsDir(agentSkillsDir, agentId, now);
|
|
1743
1761
|
}
|
|
1744
1762
|
}
|
|
@@ -1752,12 +1770,12 @@ function scanSkillsDir(skillsDir, scope, now) {
|
|
|
1752
1770
|
for (const entry of entries) {
|
|
1753
1771
|
if (!entry.isDirectory()) continue;
|
|
1754
1772
|
const skillKey = entry.name;
|
|
1755
|
-
const skillPath =
|
|
1773
|
+
const skillPath = path8.join(skillsDir, skillKey);
|
|
1756
1774
|
let name = skillKey;
|
|
1757
1775
|
for (const manifestName of ["manifest.json", "package.json"]) {
|
|
1758
1776
|
try {
|
|
1759
1777
|
const raw = fs7.readFileSync(
|
|
1760
|
-
|
|
1778
|
+
path8.join(skillPath, manifestName),
|
|
1761
1779
|
"utf-8"
|
|
1762
1780
|
);
|
|
1763
1781
|
const manifest = JSON.parse(raw);
|
|
@@ -1784,7 +1802,7 @@ function scanSkillsDir(skillsDir, scope, now) {
|
|
|
1784
1802
|
}
|
|
1785
1803
|
function scanPlugins2(configDir) {
|
|
1786
1804
|
const now = Date.now();
|
|
1787
|
-
const extensionsDir =
|
|
1805
|
+
const extensionsDir = path8.join(configDir, "extensions");
|
|
1788
1806
|
let entries;
|
|
1789
1807
|
try {
|
|
1790
1808
|
entries = fs7.readdirSync(extensionsDir, { withFileTypes: true });
|
|
@@ -1793,8 +1811,8 @@ function scanPlugins2(configDir) {
|
|
|
1793
1811
|
}
|
|
1794
1812
|
for (const dir of entries) {
|
|
1795
1813
|
if (!dir.isDirectory()) continue;
|
|
1796
|
-
const pluginDir =
|
|
1797
|
-
const manifestPath =
|
|
1814
|
+
const pluginDir = path8.join(extensionsDir, dir.name);
|
|
1815
|
+
const manifestPath = path8.join(pluginDir, "openclaw.plugin.json");
|
|
1798
1816
|
try {
|
|
1799
1817
|
const raw = fs7.readFileSync(manifestPath, "utf-8");
|
|
1800
1818
|
const manifest = JSON.parse(raw);
|
|
@@ -1820,7 +1838,7 @@ function scanTools(configDir) {
|
|
|
1820
1838
|
const now = Date.now();
|
|
1821
1839
|
try {
|
|
1822
1840
|
const raw = fs7.readFileSync(
|
|
1823
|
-
|
|
1841
|
+
path8.join(configDir, "openclaw.json"),
|
|
1824
1842
|
"utf-8"
|
|
1825
1843
|
);
|
|
1826
1844
|
const config = JSON.parse(raw);
|
|
@@ -1871,12 +1889,12 @@ var MIME_MAP = {
|
|
|
1871
1889
|
".gz": "application/gzip"
|
|
1872
1890
|
};
|
|
1873
1891
|
function getMimeType(filename) {
|
|
1874
|
-
const ext =
|
|
1892
|
+
const ext = path8.extname(filename).toLowerCase();
|
|
1875
1893
|
return MIME_MAP[ext] ?? "application/octet-stream";
|
|
1876
1894
|
}
|
|
1877
1895
|
function scanMedia(configDir) {
|
|
1878
1896
|
const now = Date.now();
|
|
1879
|
-
const mediaDir =
|
|
1897
|
+
const mediaDir = path8.join(configDir, "media");
|
|
1880
1898
|
scanMediaDir(mediaDir, now);
|
|
1881
1899
|
}
|
|
1882
1900
|
function scanMediaDir(dirPath, now) {
|
|
@@ -1888,7 +1906,7 @@ function scanMediaDir(dirPath, now) {
|
|
|
1888
1906
|
}
|
|
1889
1907
|
for (const entry of entries) {
|
|
1890
1908
|
if (entry.name.startsWith(".")) continue;
|
|
1891
|
-
const entryPath =
|
|
1909
|
+
const entryPath = path8.join(dirPath, entry.name);
|
|
1892
1910
|
if (isSensitivePath(entryPath)) continue;
|
|
1893
1911
|
if (entry.isDirectory()) {
|
|
1894
1912
|
registrySet({
|
|
@@ -2026,18 +2044,18 @@ function registerEntityTools(api, onFsChange) {
|
|
|
2026
2044
|
|
|
2027
2045
|
// src/sql.ts
|
|
2028
2046
|
import { execFile } from "child_process";
|
|
2029
|
-
import
|
|
2047
|
+
import path9 from "path";
|
|
2030
2048
|
import fs8 from "fs";
|
|
2031
2049
|
import { Type as T2 } from "@sinclair/typebox";
|
|
2032
2050
|
var HOME_DIR2 = process.env.HOME ?? "/root";
|
|
2033
|
-
var ALLOWED_DATA_DIR =
|
|
2051
|
+
var ALLOWED_DATA_DIR = path9.join(getOpenclawStateDir(), "squad-ceo-data");
|
|
2034
2052
|
function validateDbPath(dbPath) {
|
|
2035
2053
|
let expanded = dbPath;
|
|
2036
2054
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
2037
|
-
expanded =
|
|
2055
|
+
expanded = path9.join(HOME_DIR2, expanded.slice(1));
|
|
2038
2056
|
}
|
|
2039
|
-
const resolved =
|
|
2040
|
-
if (resolved !== ALLOWED_DATA_DIR && !resolved.startsWith(ALLOWED_DATA_DIR +
|
|
2057
|
+
const resolved = path9.resolve(expanded);
|
|
2058
|
+
if (resolved !== ALLOWED_DATA_DIR && !resolved.startsWith(ALLOWED_DATA_DIR + path9.sep)) {
|
|
2041
2059
|
throw new Error(
|
|
2042
2060
|
`Access denied: database path must be within ~/.openclaw/squad-ceo-data/`
|
|
2043
2061
|
);
|
|
@@ -2111,10 +2129,10 @@ function registerSqlTools(api) {
|
|
|
2111
2129
|
// src/version.ts
|
|
2112
2130
|
import { execSync as execSync2 } from "child_process";
|
|
2113
2131
|
import fs9 from "fs";
|
|
2114
|
-
import
|
|
2132
|
+
import path10 from "path";
|
|
2115
2133
|
import { fileURLToPath } from "url";
|
|
2116
2134
|
var PACKAGE_NAME = "squad-openclaw";
|
|
2117
|
-
var CONFIG_PATH =
|
|
2135
|
+
var CONFIG_PATH = path10.join(getOpenclawStateDir(), "openclaw.json");
|
|
2118
2136
|
var updateInProgress = false;
|
|
2119
2137
|
var VERIFY_TIMEOUT_MS = 2e4;
|
|
2120
2138
|
var VERIFY_INTERVAL_MS = 500;
|
|
@@ -2161,7 +2179,7 @@ function reconcileInstallMetadata(verification) {
|
|
|
2161
2179
|
}
|
|
2162
2180
|
function getCurrentVersion() {
|
|
2163
2181
|
const thisFile = fileURLToPath(import.meta.url);
|
|
2164
|
-
const pkgPath =
|
|
2182
|
+
const pkgPath = path10.resolve(path10.dirname(thisFile), "..", "package.json");
|
|
2165
2183
|
try {
|
|
2166
2184
|
const pkg = JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
|
|
2167
2185
|
return pkg.version ?? "0.0.0";
|
|
@@ -2248,9 +2266,9 @@ function verifyInstalledPluginState() {
|
|
|
2248
2266
|
};
|
|
2249
2267
|
}
|
|
2250
2268
|
const requiredFiles = [
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2269
|
+
path10.join(installPath, "package.json"),
|
|
2270
|
+
path10.join(installPath, "openclaw.plugin.json"),
|
|
2271
|
+
path10.join(installPath, "dist", "index.js")
|
|
2254
2272
|
];
|
|
2255
2273
|
const requiredFilesMissing = requiredFiles.filter((p) => !fs9.existsSync(p));
|
|
2256
2274
|
if (requiredFilesMissing.length > 0) {
|
|
@@ -2266,7 +2284,7 @@ function verifyInstalledPluginState() {
|
|
|
2266
2284
|
let installedPackage;
|
|
2267
2285
|
try {
|
|
2268
2286
|
installedPackage = JSON.parse(
|
|
2269
|
-
fs9.readFileSync(
|
|
2287
|
+
fs9.readFileSync(path10.join(installPath, "package.json"), "utf-8")
|
|
2270
2288
|
);
|
|
2271
2289
|
} catch (err2) {
|
|
2272
2290
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
@@ -2281,7 +2299,7 @@ function verifyInstalledPluginState() {
|
|
|
2281
2299
|
}
|
|
2282
2300
|
try {
|
|
2283
2301
|
JSON.parse(
|
|
2284
|
-
fs9.readFileSync(
|
|
2302
|
+
fs9.readFileSync(path10.join(installPath, "openclaw.plugin.json"), "utf-8")
|
|
2285
2303
|
);
|
|
2286
2304
|
} catch (err2) {
|
|
2287
2305
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
@@ -2463,6 +2481,59 @@ function registerVersionMethods(api) {
|
|
|
2463
2481
|
);
|
|
2464
2482
|
}
|
|
2465
2483
|
|
|
2484
|
+
// src/questions.ts
|
|
2485
|
+
var MARKER = "[HUMAN_INPUT_REQUIRED]";
|
|
2486
|
+
function normalizeEnvelope(raw) {
|
|
2487
|
+
if (raw.blocking !== true) return null;
|
|
2488
|
+
if (typeof raw.qid !== "string" || !raw.qid.trim()) return null;
|
|
2489
|
+
if (typeof raw.sessionKey !== "string" || !raw.sessionKey.trim()) return null;
|
|
2490
|
+
if (typeof raw.title !== "string" || !raw.title.trim()) return null;
|
|
2491
|
+
if (typeof raw.question !== "string" || !raw.question.trim()) return null;
|
|
2492
|
+
const agentId = typeof raw.agentId === "string" && raw.agentId.trim() ? raw.agentId.trim() : void 0;
|
|
2493
|
+
return {
|
|
2494
|
+
qid: raw.qid.trim(),
|
|
2495
|
+
sessionKey: raw.sessionKey.trim(),
|
|
2496
|
+
agentId,
|
|
2497
|
+
title: raw.title.trim(),
|
|
2498
|
+
question: raw.question.trim(),
|
|
2499
|
+
blocking: true
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
function validateHumanQuestionEnvelope(text) {
|
|
2503
|
+
const markerIndex = text.indexOf(MARKER);
|
|
2504
|
+
if (markerIndex < 0) return { valid: false, markerFound: false };
|
|
2505
|
+
const tail = text.slice(markerIndex + MARKER.length).trimStart();
|
|
2506
|
+
const firstLine = tail.split("\n")[0]?.trim() ?? "";
|
|
2507
|
+
if (!firstLine.startsWith("{")) {
|
|
2508
|
+
return { valid: false, markerFound: true, errorCode: "missing_json_line" };
|
|
2509
|
+
}
|
|
2510
|
+
let parsed = null;
|
|
2511
|
+
try {
|
|
2512
|
+
parsed = JSON.parse(firstLine);
|
|
2513
|
+
} catch {
|
|
2514
|
+
return { valid: false, markerFound: true, errorCode: "invalid_json" };
|
|
2515
|
+
}
|
|
2516
|
+
const normalized = normalizeEnvelope(parsed);
|
|
2517
|
+
if (!normalized) {
|
|
2518
|
+
return { valid: false, markerFound: true, errorCode: "schema_invalid" };
|
|
2519
|
+
}
|
|
2520
|
+
return { valid: true, markerFound: true, normalizedEnvelope: normalized };
|
|
2521
|
+
}
|
|
2522
|
+
function registerQuestionMethods(api) {
|
|
2523
|
+
api.registerGatewayMethod(
|
|
2524
|
+
"squad.questions.validate-envelope",
|
|
2525
|
+
async ({ params, respond }) => {
|
|
2526
|
+
const text = typeof params?.text === "string" ? params.text : "";
|
|
2527
|
+
if (!text) {
|
|
2528
|
+
respond(false, { error: "Missing 'text' parameter" });
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
const result = validateHumanQuestionEnvelope(text);
|
|
2532
|
+
respond(true, result);
|
|
2533
|
+
}
|
|
2534
|
+
);
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2466
2537
|
// src/shared-api.ts
|
|
2467
2538
|
var CORE_TOOLS = [
|
|
2468
2539
|
"exec",
|
|
@@ -2514,6 +2585,7 @@ function registerSquadSharedApi(api, onFsChange) {
|
|
|
2514
2585
|
registerSqlTools(api);
|
|
2515
2586
|
registerVersionMethods(api);
|
|
2516
2587
|
registerAgentMethods(api);
|
|
2588
|
+
registerQuestionMethods(api);
|
|
2517
2589
|
const invokeTool = async (tool, args) => {
|
|
2518
2590
|
const executeFn = toolExecutors.get(tool);
|
|
2519
2591
|
if (!executeFn) {
|
package/package.json
CHANGED