squad-openclaw 2026.2.2019 → 2026.2.2020
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 +185 -179
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -104,8 +104,8 @@ function registerAgentMethods(api) {
|
|
|
104
104
|
|
|
105
105
|
// src/entities.ts
|
|
106
106
|
import { Type as T } from "@sinclair/typebox";
|
|
107
|
-
import
|
|
108
|
-
import
|
|
107
|
+
import path5 from "path";
|
|
108
|
+
import fs5 from "fs";
|
|
109
109
|
|
|
110
110
|
// src/watcher.ts
|
|
111
111
|
import path from "path";
|
|
@@ -360,8 +360,8 @@ function startWatcher(configDir, onFsChange) {
|
|
|
360
360
|
}
|
|
361
361
|
|
|
362
362
|
// src/filesystem.ts
|
|
363
|
-
import
|
|
364
|
-
import
|
|
363
|
+
import fs4 from "fs";
|
|
364
|
+
import path4 from "path";
|
|
365
365
|
|
|
366
366
|
// src/paths.ts
|
|
367
367
|
import path2 from "path";
|
|
@@ -387,20 +387,102 @@ function getOpenclawStateDir() {
|
|
|
387
387
|
return path2.join(os.homedir(), ".openclaw");
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
+
// src/layout.ts
|
|
391
|
+
import fs3 from "fs";
|
|
392
|
+
import path3 from "path";
|
|
393
|
+
function resolveMaybeRelativePath(stateDir, p) {
|
|
394
|
+
if (path3.isAbsolute(p)) return path3.resolve(p);
|
|
395
|
+
return path3.resolve(stateDir, p);
|
|
396
|
+
}
|
|
397
|
+
function listWorkspaceFallbacks(stateDir) {
|
|
398
|
+
let entries;
|
|
399
|
+
try {
|
|
400
|
+
entries = fs3.readdirSync(stateDir, { withFileTypes: true });
|
|
401
|
+
} catch {
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
return entries.filter((entry) => entry.isDirectory() && (entry.name === "workspace" || entry.name.startsWith("workspace-"))).map((entry) => {
|
|
405
|
+
const agentId = entry.name === "workspace" ? "main" : entry.name.replace("workspace-", "");
|
|
406
|
+
const workspacePath = path3.join(stateDir, entry.name);
|
|
407
|
+
return {
|
|
408
|
+
agentId,
|
|
409
|
+
path: workspacePath,
|
|
410
|
+
source: "filesystem",
|
|
411
|
+
exists: true
|
|
412
|
+
};
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
function readOpenclawConfig(configPath) {
|
|
416
|
+
try {
|
|
417
|
+
const raw = fs3.readFileSync(configPath, "utf-8");
|
|
418
|
+
return JSON.parse(raw);
|
|
419
|
+
} catch {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function resolveGatewayLayout() {
|
|
424
|
+
const stateDir = getOpenclawStateDir();
|
|
425
|
+
const configPath = path3.join(stateDir, "openclaw.json");
|
|
426
|
+
const config = readOpenclawConfig(configPath);
|
|
427
|
+
const workspaces = [];
|
|
428
|
+
if (config?.agents?.main?.workspace || config?.agents?.main?.workspacePath) {
|
|
429
|
+
const rawPath = config.agents.main.workspace ?? config.agents.main.workspacePath;
|
|
430
|
+
if (rawPath) {
|
|
431
|
+
const resolvedPath = resolveMaybeRelativePath(stateDir, rawPath);
|
|
432
|
+
workspaces.push({
|
|
433
|
+
agentId: "main",
|
|
434
|
+
path: resolvedPath,
|
|
435
|
+
source: "config",
|
|
436
|
+
exists: fs3.existsSync(resolvedPath)
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
for (const agent of config?.agents?.list ?? []) {
|
|
441
|
+
const agentId = typeof agent.id === "string" && agent.id.trim() ? agent.id : null;
|
|
442
|
+
const rawPath = agent.workspace ?? agent.workspacePath;
|
|
443
|
+
if (!agentId || !rawPath) continue;
|
|
444
|
+
const resolvedPath = resolveMaybeRelativePath(stateDir, rawPath);
|
|
445
|
+
workspaces.push({
|
|
446
|
+
agentId,
|
|
447
|
+
path: resolvedPath,
|
|
448
|
+
source: "config",
|
|
449
|
+
exists: fs3.existsSync(resolvedPath)
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
453
|
+
for (const ws of [...workspaces, ...listWorkspaceFallbacks(stateDir)]) {
|
|
454
|
+
if (!deduped.has(ws.agentId)) {
|
|
455
|
+
deduped.set(ws.agentId, ws);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const resolvedWorkspaces = Array.from(deduped.values());
|
|
459
|
+
const mainWorkspace = resolvedWorkspaces.find((ws) => ws.agentId === "main");
|
|
460
|
+
const defaultFileBrowserRoot = mainWorkspace?.path ?? stateDir;
|
|
461
|
+
return {
|
|
462
|
+
stateDir,
|
|
463
|
+
configPath,
|
|
464
|
+
mediaDir: path3.join(stateDir, "media"),
|
|
465
|
+
skillsDir: path3.join(stateDir, "skills"),
|
|
466
|
+
extensionsDir: path3.join(stateDir, "extensions"),
|
|
467
|
+
defaultFileBrowserRoot,
|
|
468
|
+
workspaces: resolvedWorkspaces
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
390
472
|
// src/filesystem.ts
|
|
391
473
|
var HOME_DIR = process.env.HOME ?? "/root";
|
|
392
474
|
var OPENCLAW_DIR = getOpenclawStateDir();
|
|
393
475
|
var SENSITIVE_BLOCKED_DIRS = [
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
476
|
+
path4.join(OPENCLAW_DIR, "credentials"),
|
|
477
|
+
path4.join(OPENCLAW_DIR, "devices"),
|
|
478
|
+
path4.join(OPENCLAW_DIR, "identity")
|
|
397
479
|
];
|
|
398
480
|
var SENSITIVE_BLOCKED_FILES = [
|
|
399
|
-
|
|
481
|
+
path4.join(OPENCLAW_DIR, "squad-ceo-data", "relay", "squad-relay.json")
|
|
400
482
|
];
|
|
401
483
|
function isSensitivePath(resolvedPath) {
|
|
402
484
|
for (const blocked of SENSITIVE_BLOCKED_DIRS) {
|
|
403
|
-
if (resolvedPath === blocked || resolvedPath.startsWith(blocked +
|
|
485
|
+
if (resolvedPath === blocked || resolvedPath.startsWith(blocked + path4.sep)) {
|
|
404
486
|
return true;
|
|
405
487
|
}
|
|
406
488
|
}
|
|
@@ -409,7 +491,7 @@ function isSensitivePath(resolvedPath) {
|
|
|
409
491
|
return true;
|
|
410
492
|
}
|
|
411
493
|
}
|
|
412
|
-
if (
|
|
494
|
+
if (path4.dirname(resolvedPath) === OPENCLAW_DIR && resolvedPath.endsWith(".bak")) {
|
|
413
495
|
return true;
|
|
414
496
|
}
|
|
415
497
|
return false;
|
|
@@ -458,20 +540,20 @@ function redactOpenclawJson(rawContent) {
|
|
|
458
540
|
return JSON.stringify(config, null, 2);
|
|
459
541
|
}
|
|
460
542
|
function isOpenclawJson(resolvedPath) {
|
|
461
|
-
return
|
|
543
|
+
return path4.basename(resolvedPath) === OPENCLAW_JSON_FILENAME && resolvedPath.startsWith(OPENCLAW_DIR);
|
|
462
544
|
}
|
|
463
545
|
function expandHome(p) {
|
|
464
546
|
if (p.startsWith("~/") || p === "~") {
|
|
465
|
-
return
|
|
547
|
+
return path4.join(HOME_DIR, p.slice(1));
|
|
466
548
|
}
|
|
467
549
|
return p;
|
|
468
550
|
}
|
|
469
551
|
function validatePath(p, allowedRoots) {
|
|
470
|
-
const resolved =
|
|
552
|
+
const resolved = path4.resolve(expandHome(p));
|
|
471
553
|
if (!allowedRoots || allowedRoots.length === 0) return resolved;
|
|
472
554
|
const allowed = allowedRoots.some((root) => {
|
|
473
|
-
const resolvedRoot =
|
|
474
|
-
return resolved === resolvedRoot || resolved.startsWith(resolvedRoot +
|
|
555
|
+
const resolvedRoot = path4.resolve(expandHome(root));
|
|
556
|
+
return resolved === resolvedRoot || resolved.startsWith(resolvedRoot + path4.sep);
|
|
475
557
|
});
|
|
476
558
|
if (!allowed) {
|
|
477
559
|
throw new Error(`Path "${p}" is outside allowed roots`);
|
|
@@ -508,18 +590,18 @@ function err(message) {
|
|
|
508
590
|
};
|
|
509
591
|
}
|
|
510
592
|
function listDir(dirPath, opts) {
|
|
511
|
-
const dirents =
|
|
593
|
+
const dirents = fs4.readdirSync(dirPath, { withFileTypes: true });
|
|
512
594
|
const results = [];
|
|
513
595
|
for (const dirent of dirents) {
|
|
514
596
|
if (!opts.includeHidden && dirent.name.startsWith(".")) continue;
|
|
515
|
-
const entryPath =
|
|
597
|
+
const entryPath = path4.join(dirPath, dirent.name);
|
|
516
598
|
let type = "other";
|
|
517
599
|
if (dirent.isFile()) type = "file";
|
|
518
600
|
else if (dirent.isDirectory()) type = "directory";
|
|
519
601
|
else if (dirent.isSymbolicLink()) type = "symlink";
|
|
520
602
|
const entry = { name: dirent.name, path: entryPath, type };
|
|
521
603
|
try {
|
|
522
|
-
const stat =
|
|
604
|
+
const stat = fs4.statSync(entryPath);
|
|
523
605
|
entry.size = stat.size;
|
|
524
606
|
entry.modified = stat.mtime.toISOString();
|
|
525
607
|
} catch {
|
|
@@ -543,7 +625,11 @@ function filterSensitiveEntries(entries) {
|
|
|
543
625
|
});
|
|
544
626
|
}
|
|
545
627
|
function registerFilesystemTools(api) {
|
|
546
|
-
const
|
|
628
|
+
const layout = resolveGatewayLayout();
|
|
629
|
+
const DEFAULT_ALLOWED_ROOTS = Array.from(/* @__PURE__ */ new Set([
|
|
630
|
+
OPENCLAW_DIR,
|
|
631
|
+
...layout.workspaces.map((ws) => ws.path)
|
|
632
|
+
]));
|
|
547
633
|
const allowedRoots = api.pluginConfig?.["fs.allowedRoots"] ?? DEFAULT_ALLOWED_ROOTS;
|
|
548
634
|
api.registerTool({
|
|
549
635
|
name: "fs_read",
|
|
@@ -568,8 +654,8 @@ function registerFilesystemTools(api) {
|
|
|
568
654
|
try {
|
|
569
655
|
const filePath = validateAndBlockSensitive(params.path, allowedRoots);
|
|
570
656
|
const encoding = params.encoding ?? "utf-8";
|
|
571
|
-
let content =
|
|
572
|
-
const stat =
|
|
657
|
+
let content = fs4.readFileSync(filePath, encoding);
|
|
658
|
+
const stat = fs4.statSync(filePath);
|
|
573
659
|
if (isOpenclawJson(filePath) && encoding === "utf-8") {
|
|
574
660
|
content = redactOpenclawJson(content);
|
|
575
661
|
}
|
|
@@ -619,10 +705,10 @@ function registerFilesystemTools(api) {
|
|
|
619
705
|
const encoding = params.encoding ?? "utf-8";
|
|
620
706
|
const mkdir = params.mkdir !== false;
|
|
621
707
|
if (mkdir) {
|
|
622
|
-
|
|
708
|
+
fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
|
|
623
709
|
}
|
|
624
|
-
|
|
625
|
-
const stat =
|
|
710
|
+
fs4.writeFileSync(filePath, content, encoding);
|
|
711
|
+
const stat = fs4.statSync(filePath);
|
|
626
712
|
return ok({
|
|
627
713
|
path: filePath,
|
|
628
714
|
size: stat.size,
|
|
@@ -691,7 +777,7 @@ function registerFilesystemTools(api) {
|
|
|
691
777
|
async execute(_id, params) {
|
|
692
778
|
try {
|
|
693
779
|
const targetPath = validateWritePath(params.path, allowedRoots);
|
|
694
|
-
|
|
780
|
+
fs4.mkdirSync(targetPath, { recursive: true });
|
|
695
781
|
return ok({
|
|
696
782
|
path: targetPath,
|
|
697
783
|
created: true
|
|
@@ -724,7 +810,7 @@ function registerFilesystemTools(api) {
|
|
|
724
810
|
try {
|
|
725
811
|
const resolvedOld = validateWritePath(params.oldPath, allowedRoots);
|
|
726
812
|
const resolvedNew = validateWritePath(params.newPath, allowedRoots);
|
|
727
|
-
|
|
813
|
+
fs4.renameSync(resolvedOld, resolvedNew);
|
|
728
814
|
return ok({
|
|
729
815
|
oldPath: resolvedOld,
|
|
730
816
|
newPath: resolvedNew,
|
|
@@ -753,12 +839,12 @@ function registerFilesystemTools(api) {
|
|
|
753
839
|
async execute(_id, params) {
|
|
754
840
|
try {
|
|
755
841
|
const targetPath = validateWritePath(params.path, allowedRoots);
|
|
756
|
-
const stat =
|
|
842
|
+
const stat = fs4.statSync(targetPath);
|
|
757
843
|
const wasDirectory = stat.isDirectory();
|
|
758
844
|
if (wasDirectory) {
|
|
759
|
-
|
|
845
|
+
fs4.rmSync(targetPath, { recursive: true });
|
|
760
846
|
} else {
|
|
761
|
-
|
|
847
|
+
fs4.unlinkSync(targetPath);
|
|
762
848
|
}
|
|
763
849
|
return ok({
|
|
764
850
|
path: targetPath,
|
|
@@ -810,7 +896,7 @@ function scanAgents(configDir) {
|
|
|
810
896
|
const now = Date.now();
|
|
811
897
|
let entries;
|
|
812
898
|
try {
|
|
813
|
-
entries =
|
|
899
|
+
entries = fs5.readdirSync(configDir, { withFileTypes: true });
|
|
814
900
|
} catch {
|
|
815
901
|
return;
|
|
816
902
|
}
|
|
@@ -819,20 +905,20 @@ function scanAgents(configDir) {
|
|
|
819
905
|
);
|
|
820
906
|
for (const dir of workspaceDirs) {
|
|
821
907
|
const agentId = dir.name === "workspace" ? "main" : dir.name.replace("workspace-", "");
|
|
822
|
-
const workspacePath =
|
|
908
|
+
const workspacePath = path5.join(configDir, dir.name);
|
|
823
909
|
let name = agentId;
|
|
824
910
|
const metadata = { workspacePath };
|
|
825
|
-
const identityPath =
|
|
911
|
+
const identityPath = path5.join(workspacePath, "IDENTITY.md");
|
|
826
912
|
try {
|
|
827
|
-
const content =
|
|
913
|
+
const content = fs5.readFileSync(identityPath, "utf-8");
|
|
828
914
|
const parsed = parseIdentityName(content);
|
|
829
915
|
if (parsed) name = parsed;
|
|
830
916
|
} catch {
|
|
831
917
|
}
|
|
832
918
|
if (name === agentId) {
|
|
833
|
-
const agentJsonPath =
|
|
919
|
+
const agentJsonPath = path5.join(workspacePath, "agent.json");
|
|
834
920
|
try {
|
|
835
|
-
const raw =
|
|
921
|
+
const raw = fs5.readFileSync(agentJsonPath, "utf-8");
|
|
836
922
|
const config = JSON.parse(raw);
|
|
837
923
|
if (config.displayName) name = config.displayName;
|
|
838
924
|
if (config.model) metadata.model = config.model;
|
|
@@ -857,11 +943,11 @@ function scanAgents(configDir) {
|
|
|
857
943
|
}
|
|
858
944
|
function scanSkills(configDir) {
|
|
859
945
|
const now = Date.now();
|
|
860
|
-
const globalSkillsDir =
|
|
946
|
+
const globalSkillsDir = path5.join(configDir, "skills");
|
|
861
947
|
scanSkillsDir(globalSkillsDir, "global", now);
|
|
862
948
|
let entries;
|
|
863
949
|
try {
|
|
864
|
-
entries =
|
|
950
|
+
entries = fs5.readdirSync(configDir, { withFileTypes: true });
|
|
865
951
|
} catch {
|
|
866
952
|
return;
|
|
867
953
|
}
|
|
@@ -870,26 +956,26 @@ function scanSkills(configDir) {
|
|
|
870
956
|
continue;
|
|
871
957
|
}
|
|
872
958
|
const agentId = dir.name === "workspace" ? "main" : dir.name.replace("workspace-", "");
|
|
873
|
-
const agentSkillsDir =
|
|
959
|
+
const agentSkillsDir = path5.join(configDir, dir.name, "skills");
|
|
874
960
|
scanSkillsDir(agentSkillsDir, agentId, now);
|
|
875
961
|
}
|
|
876
962
|
}
|
|
877
963
|
function scanSkillsDir(skillsDir, scope, now) {
|
|
878
964
|
let entries;
|
|
879
965
|
try {
|
|
880
|
-
entries =
|
|
966
|
+
entries = fs5.readdirSync(skillsDir, { withFileTypes: true });
|
|
881
967
|
} catch {
|
|
882
968
|
return;
|
|
883
969
|
}
|
|
884
970
|
for (const entry of entries) {
|
|
885
971
|
if (!entry.isDirectory()) continue;
|
|
886
972
|
const skillKey = entry.name;
|
|
887
|
-
const skillPath =
|
|
973
|
+
const skillPath = path5.join(skillsDir, skillKey);
|
|
888
974
|
let name = skillKey;
|
|
889
975
|
for (const manifestName of ["manifest.json", "package.json"]) {
|
|
890
976
|
try {
|
|
891
|
-
const raw =
|
|
892
|
-
|
|
977
|
+
const raw = fs5.readFileSync(
|
|
978
|
+
path5.join(skillPath, manifestName),
|
|
893
979
|
"utf-8"
|
|
894
980
|
);
|
|
895
981
|
const manifest = JSON.parse(raw);
|
|
@@ -916,19 +1002,19 @@ function scanSkillsDir(skillsDir, scope, now) {
|
|
|
916
1002
|
}
|
|
917
1003
|
function scanPlugins2(configDir) {
|
|
918
1004
|
const now = Date.now();
|
|
919
|
-
const extensionsDir =
|
|
1005
|
+
const extensionsDir = path5.join(configDir, "extensions");
|
|
920
1006
|
let entries;
|
|
921
1007
|
try {
|
|
922
|
-
entries =
|
|
1008
|
+
entries = fs5.readdirSync(extensionsDir, { withFileTypes: true });
|
|
923
1009
|
} catch {
|
|
924
1010
|
return;
|
|
925
1011
|
}
|
|
926
1012
|
for (const dir of entries) {
|
|
927
1013
|
if (!dir.isDirectory()) continue;
|
|
928
|
-
const pluginDir =
|
|
929
|
-
const manifestPath =
|
|
1014
|
+
const pluginDir = path5.join(extensionsDir, dir.name);
|
|
1015
|
+
const manifestPath = path5.join(pluginDir, "openclaw.plugin.json");
|
|
930
1016
|
try {
|
|
931
|
-
const raw =
|
|
1017
|
+
const raw = fs5.readFileSync(manifestPath, "utf-8");
|
|
932
1018
|
const manifest = JSON.parse(raw);
|
|
933
1019
|
const pluginId = manifest.id || dir.name;
|
|
934
1020
|
const name = manifest.name || pluginId;
|
|
@@ -951,8 +1037,8 @@ function scanPlugins2(configDir) {
|
|
|
951
1037
|
function scanTools(configDir) {
|
|
952
1038
|
const now = Date.now();
|
|
953
1039
|
try {
|
|
954
|
-
const raw =
|
|
955
|
-
|
|
1040
|
+
const raw = fs5.readFileSync(
|
|
1041
|
+
path5.join(configDir, "openclaw.json"),
|
|
956
1042
|
"utf-8"
|
|
957
1043
|
);
|
|
958
1044
|
const config = JSON.parse(raw);
|
|
@@ -1003,24 +1089,24 @@ var MIME_MAP = {
|
|
|
1003
1089
|
".gz": "application/gzip"
|
|
1004
1090
|
};
|
|
1005
1091
|
function getMimeType(filename) {
|
|
1006
|
-
const ext =
|
|
1092
|
+
const ext = path5.extname(filename).toLowerCase();
|
|
1007
1093
|
return MIME_MAP[ext] ?? "application/octet-stream";
|
|
1008
1094
|
}
|
|
1009
1095
|
function scanMedia(configDir) {
|
|
1010
1096
|
const now = Date.now();
|
|
1011
|
-
const mediaDir =
|
|
1097
|
+
const mediaDir = path5.join(configDir, "media");
|
|
1012
1098
|
scanMediaDir(mediaDir, now);
|
|
1013
1099
|
}
|
|
1014
1100
|
function scanMediaDir(dirPath, now) {
|
|
1015
1101
|
let entries;
|
|
1016
1102
|
try {
|
|
1017
|
-
entries =
|
|
1103
|
+
entries = fs5.readdirSync(dirPath, { withFileTypes: true });
|
|
1018
1104
|
} catch {
|
|
1019
1105
|
return;
|
|
1020
1106
|
}
|
|
1021
1107
|
for (const entry of entries) {
|
|
1022
1108
|
if (entry.name.startsWith(".")) continue;
|
|
1023
|
-
const entryPath =
|
|
1109
|
+
const entryPath = path5.join(dirPath, entry.name);
|
|
1024
1110
|
if (isSensitivePath(entryPath)) continue;
|
|
1025
1111
|
if (entry.isDirectory()) {
|
|
1026
1112
|
registrySet({
|
|
@@ -1041,7 +1127,7 @@ function scanMediaDir(dirPath, now) {
|
|
|
1041
1127
|
let size;
|
|
1042
1128
|
let mtime = now;
|
|
1043
1129
|
try {
|
|
1044
|
-
const stat =
|
|
1130
|
+
const stat = fs5.statSync(entryPath);
|
|
1045
1131
|
size = stat.size;
|
|
1046
1132
|
mtime = stat.mtimeMs;
|
|
1047
1133
|
} catch {
|
|
@@ -1158,24 +1244,24 @@ function registerEntityTools(api, onFsChange) {
|
|
|
1158
1244
|
|
|
1159
1245
|
// src/sql.ts
|
|
1160
1246
|
import { execFile } from "child_process";
|
|
1161
|
-
import
|
|
1162
|
-
import
|
|
1247
|
+
import path6 from "path";
|
|
1248
|
+
import fs6 from "fs";
|
|
1163
1249
|
import { Type as T2 } from "@sinclair/typebox";
|
|
1164
1250
|
var HOME_DIR2 = process.env.HOME ?? "/root";
|
|
1165
|
-
var ALLOWED_DATA_DIR =
|
|
1251
|
+
var ALLOWED_DATA_DIR = path6.join(getOpenclawStateDir(), "squad-ceo-data");
|
|
1166
1252
|
function validateDbPath(dbPath) {
|
|
1167
1253
|
let expanded = dbPath;
|
|
1168
1254
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
1169
|
-
expanded =
|
|
1255
|
+
expanded = path6.join(HOME_DIR2, expanded.slice(1));
|
|
1170
1256
|
}
|
|
1171
|
-
const resolved =
|
|
1172
|
-
if (resolved !== ALLOWED_DATA_DIR && !resolved.startsWith(ALLOWED_DATA_DIR +
|
|
1257
|
+
const resolved = path6.resolve(expanded);
|
|
1258
|
+
if (resolved !== ALLOWED_DATA_DIR && !resolved.startsWith(ALLOWED_DATA_DIR + path6.sep)) {
|
|
1173
1259
|
throw new Error(
|
|
1174
1260
|
`Access denied: database path must be within ~/.openclaw/squad-ceo-data/`
|
|
1175
1261
|
);
|
|
1176
1262
|
}
|
|
1177
1263
|
try {
|
|
1178
|
-
const stat =
|
|
1264
|
+
const stat = fs6.statSync(resolved);
|
|
1179
1265
|
if (!stat.isFile()) {
|
|
1180
1266
|
throw new Error(`Not a file: ${dbPath}`);
|
|
1181
1267
|
}
|
|
@@ -1242,18 +1328,18 @@ function registerSqlTools(api) {
|
|
|
1242
1328
|
|
|
1243
1329
|
// src/version.ts
|
|
1244
1330
|
import { execSync as execSync2 } from "child_process";
|
|
1245
|
-
import
|
|
1246
|
-
import
|
|
1331
|
+
import fs7 from "fs";
|
|
1332
|
+
import path7 from "path";
|
|
1247
1333
|
import { fileURLToPath } from "url";
|
|
1248
1334
|
var PACKAGE_NAME = "squad-openclaw";
|
|
1249
|
-
var CONFIG_PATH =
|
|
1335
|
+
var CONFIG_PATH = path7.join(getOpenclawStateDir(), "openclaw.json");
|
|
1250
1336
|
var updateInProgress = false;
|
|
1251
1337
|
var VERIFY_TIMEOUT_MS = 2e4;
|
|
1252
1338
|
var VERIFY_INTERVAL_MS = 500;
|
|
1253
1339
|
var RESTART_BUFFER_MS = 5e3;
|
|
1254
1340
|
function readInstalledVersionFromConfig() {
|
|
1255
1341
|
try {
|
|
1256
|
-
const raw =
|
|
1342
|
+
const raw = fs7.readFileSync(CONFIG_PATH, "utf-8");
|
|
1257
1343
|
const cfg = JSON.parse(raw);
|
|
1258
1344
|
const v = cfg?.plugins?.installs?.[PACKAGE_NAME]?.version;
|
|
1259
1345
|
return typeof v === "string" ? v : null;
|
|
@@ -1264,7 +1350,7 @@ function readInstalledVersionFromConfig() {
|
|
|
1264
1350
|
function reconcileInstallMetadata(verification) {
|
|
1265
1351
|
if (!verification.installPath || !verification.packageVersion) return;
|
|
1266
1352
|
try {
|
|
1267
|
-
const raw =
|
|
1353
|
+
const raw = fs7.readFileSync(CONFIG_PATH, "utf-8");
|
|
1268
1354
|
const config = JSON.parse(raw);
|
|
1269
1355
|
if (!config.plugins || typeof config.plugins !== "object") config.plugins = {};
|
|
1270
1356
|
if (!config.plugins.installs || typeof config.plugins.installs !== "object") {
|
|
@@ -1287,15 +1373,15 @@ function reconcileInstallMetadata(verification) {
|
|
|
1287
1373
|
...entry,
|
|
1288
1374
|
enabled: true
|
|
1289
1375
|
};
|
|
1290
|
-
|
|
1376
|
+
fs7.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
1291
1377
|
} catch {
|
|
1292
1378
|
}
|
|
1293
1379
|
}
|
|
1294
1380
|
function getCurrentVersion() {
|
|
1295
1381
|
const thisFile = fileURLToPath(import.meta.url);
|
|
1296
|
-
const pkgPath =
|
|
1382
|
+
const pkgPath = path7.resolve(path7.dirname(thisFile), "..", "package.json");
|
|
1297
1383
|
try {
|
|
1298
|
-
const pkg = JSON.parse(
|
|
1384
|
+
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
1299
1385
|
return pkg.version ?? "0.0.0";
|
|
1300
1386
|
} catch {
|
|
1301
1387
|
return "0.0.0";
|
|
@@ -1340,7 +1426,7 @@ function compareVersions(a, b) {
|
|
|
1340
1426
|
function verifyInstalledPluginState() {
|
|
1341
1427
|
let configRaw;
|
|
1342
1428
|
try {
|
|
1343
|
-
configRaw =
|
|
1429
|
+
configRaw = fs7.readFileSync(CONFIG_PATH, "utf-8");
|
|
1344
1430
|
} catch (err2) {
|
|
1345
1431
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
1346
1432
|
return {
|
|
@@ -1380,11 +1466,11 @@ function verifyInstalledPluginState() {
|
|
|
1380
1466
|
};
|
|
1381
1467
|
}
|
|
1382
1468
|
const requiredFiles = [
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1469
|
+
path7.join(installPath, "package.json"),
|
|
1470
|
+
path7.join(installPath, "openclaw.plugin.json"),
|
|
1471
|
+
path7.join(installPath, "dist", "index.js")
|
|
1386
1472
|
];
|
|
1387
|
-
const requiredFilesMissing = requiredFiles.filter((p) => !
|
|
1473
|
+
const requiredFilesMissing = requiredFiles.filter((p) => !fs7.existsSync(p));
|
|
1388
1474
|
if (requiredFilesMissing.length > 0) {
|
|
1389
1475
|
return {
|
|
1390
1476
|
ok: false,
|
|
@@ -1398,7 +1484,7 @@ function verifyInstalledPluginState() {
|
|
|
1398
1484
|
let installedPackage;
|
|
1399
1485
|
try {
|
|
1400
1486
|
installedPackage = JSON.parse(
|
|
1401
|
-
|
|
1487
|
+
fs7.readFileSync(path7.join(installPath, "package.json"), "utf-8")
|
|
1402
1488
|
);
|
|
1403
1489
|
} catch (err2) {
|
|
1404
1490
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
@@ -1413,7 +1499,7 @@ function verifyInstalledPluginState() {
|
|
|
1413
1499
|
}
|
|
1414
1500
|
try {
|
|
1415
1501
|
JSON.parse(
|
|
1416
|
-
|
|
1502
|
+
fs7.readFileSync(path7.join(installPath, "openclaw.plugin.json"), "utf-8")
|
|
1417
1503
|
);
|
|
1418
1504
|
} catch (err2) {
|
|
1419
1505
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
@@ -1503,7 +1589,7 @@ function registerVersionMethods(api) {
|
|
|
1503
1589
|
let updateOutput = "";
|
|
1504
1590
|
let configBackup = null;
|
|
1505
1591
|
try {
|
|
1506
|
-
configBackup =
|
|
1592
|
+
configBackup = fs7.readFileSync(CONFIG_PATH, "utf-8");
|
|
1507
1593
|
} catch {
|
|
1508
1594
|
}
|
|
1509
1595
|
runDoctorFixSilently();
|
|
@@ -1522,7 +1608,7 @@ function registerVersionMethods(api) {
|
|
|
1522
1608
|
} catch (installErr) {
|
|
1523
1609
|
if (configBackup) {
|
|
1524
1610
|
try {
|
|
1525
|
-
|
|
1611
|
+
fs7.writeFileSync(CONFIG_PATH, configBackup, "utf-8");
|
|
1526
1612
|
} catch {
|
|
1527
1613
|
}
|
|
1528
1614
|
}
|
|
@@ -1540,7 +1626,7 @@ function registerVersionMethods(api) {
|
|
|
1540
1626
|
if (!verification.ok) {
|
|
1541
1627
|
if (configBackup) {
|
|
1542
1628
|
try {
|
|
1543
|
-
|
|
1629
|
+
fs7.writeFileSync(CONFIG_PATH, configBackup, "utf-8");
|
|
1544
1630
|
} catch {
|
|
1545
1631
|
}
|
|
1546
1632
|
}
|
|
@@ -1598,8 +1684,8 @@ function registerVersionMethods(api) {
|
|
|
1598
1684
|
// src/relay-client.ts
|
|
1599
1685
|
import { WebSocket as NodeWebSocket } from "ws";
|
|
1600
1686
|
import crypto3 from "crypto";
|
|
1601
|
-
import
|
|
1602
|
-
import
|
|
1687
|
+
import fs9 from "fs";
|
|
1688
|
+
import path9 from "path";
|
|
1603
1689
|
|
|
1604
1690
|
// src/e2e-crypto.ts
|
|
1605
1691
|
import crypto from "crypto";
|
|
@@ -1680,24 +1766,24 @@ var E2ECrypto = class {
|
|
|
1680
1766
|
|
|
1681
1767
|
// src/device-keys.ts
|
|
1682
1768
|
import crypto2 from "crypto";
|
|
1683
|
-
import
|
|
1684
|
-
import
|
|
1685
|
-
var RELAY_DATA_DIR =
|
|
1686
|
-
var RELAY_STATE_PATH =
|
|
1687
|
-
var PENDING_APPROVAL_PATH =
|
|
1769
|
+
import fs8 from "fs";
|
|
1770
|
+
import path8 from "path";
|
|
1771
|
+
var RELAY_DATA_DIR = path8.join(getOpenclawStateDir(), "squad-ceo-data", "relay");
|
|
1772
|
+
var RELAY_STATE_PATH = path8.join(RELAY_DATA_DIR, "squad-relay.json");
|
|
1773
|
+
var PENDING_APPROVAL_PATH = path8.join(RELAY_DATA_DIR, "pending-approval.json");
|
|
1688
1774
|
function readRelayState() {
|
|
1689
1775
|
try {
|
|
1690
|
-
const raw =
|
|
1776
|
+
const raw = fs8.readFileSync(RELAY_STATE_PATH, "utf-8");
|
|
1691
1777
|
return JSON.parse(raw);
|
|
1692
1778
|
} catch {
|
|
1693
1779
|
return {};
|
|
1694
1780
|
}
|
|
1695
1781
|
}
|
|
1696
1782
|
function writeRelayState(state) {
|
|
1697
|
-
if (!
|
|
1698
|
-
|
|
1783
|
+
if (!fs8.existsSync(RELAY_DATA_DIR)) {
|
|
1784
|
+
fs8.mkdirSync(RELAY_DATA_DIR, { recursive: true });
|
|
1699
1785
|
}
|
|
1700
|
-
|
|
1786
|
+
fs8.writeFileSync(RELAY_STATE_PATH, JSON.stringify(state, null, 2), { mode: 384 });
|
|
1701
1787
|
}
|
|
1702
1788
|
function toBase64Url(buf) {
|
|
1703
1789
|
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
@@ -1720,7 +1806,7 @@ function loadOrCreateRelayDeviceKeys() {
|
|
|
1720
1806
|
}
|
|
1721
1807
|
function writeDeviceInfoFile(keys) {
|
|
1722
1808
|
const stateDir = getOpenclawStateDir();
|
|
1723
|
-
const infoPath =
|
|
1809
|
+
const infoPath = path8.join(stateDir, "squad-ceo-data", "relay", "relay-device-info.json");
|
|
1724
1810
|
const info = {
|
|
1725
1811
|
deviceId: keys.deviceId,
|
|
1726
1812
|
publicKey: keys.publicKey,
|
|
@@ -1729,7 +1815,7 @@ function writeDeviceInfoFile(keys) {
|
|
|
1729
1815
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1730
1816
|
};
|
|
1731
1817
|
try {
|
|
1732
|
-
|
|
1818
|
+
fs8.writeFileSync(infoPath, JSON.stringify(info, null, 2));
|
|
1733
1819
|
} catch (err2) {
|
|
1734
1820
|
console.error("[device-keys] Failed to write relay-device-info.json:", err2);
|
|
1735
1821
|
}
|
|
@@ -1738,9 +1824,9 @@ function writeDeviceInfoFile(keys) {
|
|
|
1738
1824
|
// src/relay-client.ts
|
|
1739
1825
|
function readOperatorToken() {
|
|
1740
1826
|
const stateDir = getOpenclawStateDir();
|
|
1741
|
-
const configPath =
|
|
1827
|
+
const configPath = path9.join(stateDir, "openclaw.json");
|
|
1742
1828
|
try {
|
|
1743
|
-
const raw =
|
|
1829
|
+
const raw = fs9.readFileSync(configPath, "utf-8");
|
|
1744
1830
|
const config = JSON.parse(raw);
|
|
1745
1831
|
return config?.gateway?.auth?.token ?? config?.gateway?.remote?.token ?? config?.gateway?.token ?? null;
|
|
1746
1832
|
} catch {
|
|
@@ -1754,9 +1840,9 @@ function readGatewayLocalWsConfig() {
|
|
|
1754
1840
|
hosts: ["127.0.0.1", "localhost", "[::1]"]
|
|
1755
1841
|
};
|
|
1756
1842
|
const stateDir = getOpenclawStateDir();
|
|
1757
|
-
const configPath =
|
|
1843
|
+
const configPath = path9.join(stateDir, "openclaw.json");
|
|
1758
1844
|
try {
|
|
1759
|
-
const raw =
|
|
1845
|
+
const raw = fs9.readFileSync(configPath, "utf-8");
|
|
1760
1846
|
const config = JSON.parse(raw);
|
|
1761
1847
|
const parsedPort = Number(config?.gateway?.port);
|
|
1762
1848
|
if (Number.isFinite(parsedPort) && parsedPort > 0) {
|
|
@@ -2302,88 +2388,6 @@ function broadcastToUsers(event, payload) {
|
|
|
2302
2388
|
relayClient?.broadcastToUsers(event, payload);
|
|
2303
2389
|
}
|
|
2304
2390
|
|
|
2305
|
-
// src/layout.ts
|
|
2306
|
-
import fs9 from "fs";
|
|
2307
|
-
import path9 from "path";
|
|
2308
|
-
function resolveMaybeRelativePath(stateDir, p) {
|
|
2309
|
-
if (path9.isAbsolute(p)) return path9.resolve(p);
|
|
2310
|
-
return path9.resolve(stateDir, p);
|
|
2311
|
-
}
|
|
2312
|
-
function listWorkspaceFallbacks(stateDir) {
|
|
2313
|
-
let entries;
|
|
2314
|
-
try {
|
|
2315
|
-
entries = fs9.readdirSync(stateDir, { withFileTypes: true });
|
|
2316
|
-
} catch {
|
|
2317
|
-
return [];
|
|
2318
|
-
}
|
|
2319
|
-
return entries.filter((entry) => entry.isDirectory() && (entry.name === "workspace" || entry.name.startsWith("workspace-"))).map((entry) => {
|
|
2320
|
-
const agentId = entry.name === "workspace" ? "main" : entry.name.replace("workspace-", "");
|
|
2321
|
-
const workspacePath = path9.join(stateDir, entry.name);
|
|
2322
|
-
return {
|
|
2323
|
-
agentId,
|
|
2324
|
-
path: workspacePath,
|
|
2325
|
-
source: "filesystem",
|
|
2326
|
-
exists: true
|
|
2327
|
-
};
|
|
2328
|
-
});
|
|
2329
|
-
}
|
|
2330
|
-
function readOpenclawConfig(configPath) {
|
|
2331
|
-
try {
|
|
2332
|
-
const raw = fs9.readFileSync(configPath, "utf-8");
|
|
2333
|
-
return JSON.parse(raw);
|
|
2334
|
-
} catch {
|
|
2335
|
-
return null;
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
function resolveGatewayLayout() {
|
|
2339
|
-
const stateDir = getOpenclawStateDir();
|
|
2340
|
-
const configPath = path9.join(stateDir, "openclaw.json");
|
|
2341
|
-
const config = readOpenclawConfig(configPath);
|
|
2342
|
-
const workspaces = [];
|
|
2343
|
-
if (config?.agents?.main?.workspace || config?.agents?.main?.workspacePath) {
|
|
2344
|
-
const rawPath = config.agents.main.workspace ?? config.agents.main.workspacePath;
|
|
2345
|
-
if (rawPath) {
|
|
2346
|
-
const resolvedPath = resolveMaybeRelativePath(stateDir, rawPath);
|
|
2347
|
-
workspaces.push({
|
|
2348
|
-
agentId: "main",
|
|
2349
|
-
path: resolvedPath,
|
|
2350
|
-
source: "config",
|
|
2351
|
-
exists: fs9.existsSync(resolvedPath)
|
|
2352
|
-
});
|
|
2353
|
-
}
|
|
2354
|
-
}
|
|
2355
|
-
for (const agent of config?.agents?.list ?? []) {
|
|
2356
|
-
const agentId = typeof agent.id === "string" && agent.id.trim() ? agent.id : null;
|
|
2357
|
-
const rawPath = agent.workspace ?? agent.workspacePath;
|
|
2358
|
-
if (!agentId || !rawPath) continue;
|
|
2359
|
-
const resolvedPath = resolveMaybeRelativePath(stateDir, rawPath);
|
|
2360
|
-
workspaces.push({
|
|
2361
|
-
agentId,
|
|
2362
|
-
path: resolvedPath,
|
|
2363
|
-
source: "config",
|
|
2364
|
-
exists: fs9.existsSync(resolvedPath)
|
|
2365
|
-
});
|
|
2366
|
-
}
|
|
2367
|
-
const deduped = /* @__PURE__ */ new Map();
|
|
2368
|
-
for (const ws of [...workspaces, ...listWorkspaceFallbacks(stateDir)]) {
|
|
2369
|
-
if (!deduped.has(ws.agentId)) {
|
|
2370
|
-
deduped.set(ws.agentId, ws);
|
|
2371
|
-
}
|
|
2372
|
-
}
|
|
2373
|
-
const resolvedWorkspaces = Array.from(deduped.values());
|
|
2374
|
-
const mainWorkspace = resolvedWorkspaces.find((ws) => ws.agentId === "main");
|
|
2375
|
-
const defaultFileBrowserRoot = mainWorkspace?.path ?? stateDir;
|
|
2376
|
-
return {
|
|
2377
|
-
stateDir,
|
|
2378
|
-
configPath,
|
|
2379
|
-
mediaDir: path9.join(stateDir, "media"),
|
|
2380
|
-
skillsDir: path9.join(stateDir, "skills"),
|
|
2381
|
-
extensionsDir: path9.join(stateDir, "extensions"),
|
|
2382
|
-
defaultFileBrowserRoot,
|
|
2383
|
-
workspaces: resolvedWorkspaces
|
|
2384
|
-
};
|
|
2385
|
-
}
|
|
2386
|
-
|
|
2387
2391
|
// src/index.ts
|
|
2388
2392
|
function squadAppPlugin(api) {
|
|
2389
2393
|
const toolExecutors = /* @__PURE__ */ new Map();
|
|
@@ -2470,7 +2474,9 @@ function squadAppPlugin(api) {
|
|
|
2470
2474
|
"squad.layout.get",
|
|
2471
2475
|
async ({ respond }) => {
|
|
2472
2476
|
try {
|
|
2473
|
-
|
|
2477
|
+
const layout = resolveGatewayLayout();
|
|
2478
|
+
console.log("[squad-openclaw] squad.layout.get", JSON.stringify(layout));
|
|
2479
|
+
respond(true, layout);
|
|
2474
2480
|
} catch (err2) {
|
|
2475
2481
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
2476
2482
|
respond(false, { errorMessage: msg });
|
package/package.json
CHANGED