squad-openclaw 2026.2.2018 → 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 +203 -180
- 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,30 +360,129 @@ 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";
|
|
368
368
|
import os from "os";
|
|
369
|
+
import fs2 from "fs";
|
|
369
370
|
function getOpenclawStateDir() {
|
|
370
|
-
|
|
371
|
+
if (process.env.OPENCLAW_STATE_DIR) {
|
|
372
|
+
return process.env.OPENCLAW_STATE_DIR;
|
|
373
|
+
}
|
|
374
|
+
if (process.env.OPENCLAW_CONFIG_PATH) {
|
|
375
|
+
return path2.dirname(process.env.OPENCLAW_CONFIG_PATH);
|
|
376
|
+
}
|
|
377
|
+
const legacyDir = process.env.OPENCLAW_DIR;
|
|
378
|
+
if (legacyDir) {
|
|
379
|
+
const resolvedLegacyDir = path2.resolve(legacyDir);
|
|
380
|
+
const configPath = path2.join(resolvedLegacyDir, "openclaw.json");
|
|
381
|
+
const hasStateMarkers = fs2.existsSync(configPath) || fs2.existsSync(path2.join(resolvedLegacyDir, "agents")) || fs2.existsSync(path2.join(resolvedLegacyDir, "workspace"));
|
|
382
|
+
const looksLikeStateDir = resolvedLegacyDir.endsWith(`${path2.sep}.openclaw`);
|
|
383
|
+
if (hasStateMarkers || looksLikeStateDir) {
|
|
384
|
+
return resolvedLegacyDir;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return path2.join(os.homedir(), ".openclaw");
|
|
388
|
+
}
|
|
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
|
+
};
|
|
371
470
|
}
|
|
372
471
|
|
|
373
472
|
// src/filesystem.ts
|
|
374
473
|
var HOME_DIR = process.env.HOME ?? "/root";
|
|
375
474
|
var OPENCLAW_DIR = getOpenclawStateDir();
|
|
376
475
|
var SENSITIVE_BLOCKED_DIRS = [
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
476
|
+
path4.join(OPENCLAW_DIR, "credentials"),
|
|
477
|
+
path4.join(OPENCLAW_DIR, "devices"),
|
|
478
|
+
path4.join(OPENCLAW_DIR, "identity")
|
|
380
479
|
];
|
|
381
480
|
var SENSITIVE_BLOCKED_FILES = [
|
|
382
|
-
|
|
481
|
+
path4.join(OPENCLAW_DIR, "squad-ceo-data", "relay", "squad-relay.json")
|
|
383
482
|
];
|
|
384
483
|
function isSensitivePath(resolvedPath) {
|
|
385
484
|
for (const blocked of SENSITIVE_BLOCKED_DIRS) {
|
|
386
|
-
if (resolvedPath === blocked || resolvedPath.startsWith(blocked +
|
|
485
|
+
if (resolvedPath === blocked || resolvedPath.startsWith(blocked + path4.sep)) {
|
|
387
486
|
return true;
|
|
388
487
|
}
|
|
389
488
|
}
|
|
@@ -392,7 +491,7 @@ function isSensitivePath(resolvedPath) {
|
|
|
392
491
|
return true;
|
|
393
492
|
}
|
|
394
493
|
}
|
|
395
|
-
if (
|
|
494
|
+
if (path4.dirname(resolvedPath) === OPENCLAW_DIR && resolvedPath.endsWith(".bak")) {
|
|
396
495
|
return true;
|
|
397
496
|
}
|
|
398
497
|
return false;
|
|
@@ -441,20 +540,20 @@ function redactOpenclawJson(rawContent) {
|
|
|
441
540
|
return JSON.stringify(config, null, 2);
|
|
442
541
|
}
|
|
443
542
|
function isOpenclawJson(resolvedPath) {
|
|
444
|
-
return
|
|
543
|
+
return path4.basename(resolvedPath) === OPENCLAW_JSON_FILENAME && resolvedPath.startsWith(OPENCLAW_DIR);
|
|
445
544
|
}
|
|
446
545
|
function expandHome(p) {
|
|
447
546
|
if (p.startsWith("~/") || p === "~") {
|
|
448
|
-
return
|
|
547
|
+
return path4.join(HOME_DIR, p.slice(1));
|
|
449
548
|
}
|
|
450
549
|
return p;
|
|
451
550
|
}
|
|
452
551
|
function validatePath(p, allowedRoots) {
|
|
453
|
-
const resolved =
|
|
552
|
+
const resolved = path4.resolve(expandHome(p));
|
|
454
553
|
if (!allowedRoots || allowedRoots.length === 0) return resolved;
|
|
455
554
|
const allowed = allowedRoots.some((root) => {
|
|
456
|
-
const resolvedRoot =
|
|
457
|
-
return resolved === resolvedRoot || resolved.startsWith(resolvedRoot +
|
|
555
|
+
const resolvedRoot = path4.resolve(expandHome(root));
|
|
556
|
+
return resolved === resolvedRoot || resolved.startsWith(resolvedRoot + path4.sep);
|
|
458
557
|
});
|
|
459
558
|
if (!allowed) {
|
|
460
559
|
throw new Error(`Path "${p}" is outside allowed roots`);
|
|
@@ -491,18 +590,18 @@ function err(message) {
|
|
|
491
590
|
};
|
|
492
591
|
}
|
|
493
592
|
function listDir(dirPath, opts) {
|
|
494
|
-
const dirents =
|
|
593
|
+
const dirents = fs4.readdirSync(dirPath, { withFileTypes: true });
|
|
495
594
|
const results = [];
|
|
496
595
|
for (const dirent of dirents) {
|
|
497
596
|
if (!opts.includeHidden && dirent.name.startsWith(".")) continue;
|
|
498
|
-
const entryPath =
|
|
597
|
+
const entryPath = path4.join(dirPath, dirent.name);
|
|
499
598
|
let type = "other";
|
|
500
599
|
if (dirent.isFile()) type = "file";
|
|
501
600
|
else if (dirent.isDirectory()) type = "directory";
|
|
502
601
|
else if (dirent.isSymbolicLink()) type = "symlink";
|
|
503
602
|
const entry = { name: dirent.name, path: entryPath, type };
|
|
504
603
|
try {
|
|
505
|
-
const stat =
|
|
604
|
+
const stat = fs4.statSync(entryPath);
|
|
506
605
|
entry.size = stat.size;
|
|
507
606
|
entry.modified = stat.mtime.toISOString();
|
|
508
607
|
} catch {
|
|
@@ -526,7 +625,11 @@ function filterSensitiveEntries(entries) {
|
|
|
526
625
|
});
|
|
527
626
|
}
|
|
528
627
|
function registerFilesystemTools(api) {
|
|
529
|
-
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
|
+
]));
|
|
530
633
|
const allowedRoots = api.pluginConfig?.["fs.allowedRoots"] ?? DEFAULT_ALLOWED_ROOTS;
|
|
531
634
|
api.registerTool({
|
|
532
635
|
name: "fs_read",
|
|
@@ -551,8 +654,8 @@ function registerFilesystemTools(api) {
|
|
|
551
654
|
try {
|
|
552
655
|
const filePath = validateAndBlockSensitive(params.path, allowedRoots);
|
|
553
656
|
const encoding = params.encoding ?? "utf-8";
|
|
554
|
-
let content =
|
|
555
|
-
const stat =
|
|
657
|
+
let content = fs4.readFileSync(filePath, encoding);
|
|
658
|
+
const stat = fs4.statSync(filePath);
|
|
556
659
|
if (isOpenclawJson(filePath) && encoding === "utf-8") {
|
|
557
660
|
content = redactOpenclawJson(content);
|
|
558
661
|
}
|
|
@@ -602,10 +705,10 @@ function registerFilesystemTools(api) {
|
|
|
602
705
|
const encoding = params.encoding ?? "utf-8";
|
|
603
706
|
const mkdir = params.mkdir !== false;
|
|
604
707
|
if (mkdir) {
|
|
605
|
-
|
|
708
|
+
fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
|
|
606
709
|
}
|
|
607
|
-
|
|
608
|
-
const stat =
|
|
710
|
+
fs4.writeFileSync(filePath, content, encoding);
|
|
711
|
+
const stat = fs4.statSync(filePath);
|
|
609
712
|
return ok({
|
|
610
713
|
path: filePath,
|
|
611
714
|
size: stat.size,
|
|
@@ -674,7 +777,7 @@ function registerFilesystemTools(api) {
|
|
|
674
777
|
async execute(_id, params) {
|
|
675
778
|
try {
|
|
676
779
|
const targetPath = validateWritePath(params.path, allowedRoots);
|
|
677
|
-
|
|
780
|
+
fs4.mkdirSync(targetPath, { recursive: true });
|
|
678
781
|
return ok({
|
|
679
782
|
path: targetPath,
|
|
680
783
|
created: true
|
|
@@ -707,7 +810,7 @@ function registerFilesystemTools(api) {
|
|
|
707
810
|
try {
|
|
708
811
|
const resolvedOld = validateWritePath(params.oldPath, allowedRoots);
|
|
709
812
|
const resolvedNew = validateWritePath(params.newPath, allowedRoots);
|
|
710
|
-
|
|
813
|
+
fs4.renameSync(resolvedOld, resolvedNew);
|
|
711
814
|
return ok({
|
|
712
815
|
oldPath: resolvedOld,
|
|
713
816
|
newPath: resolvedNew,
|
|
@@ -736,12 +839,12 @@ function registerFilesystemTools(api) {
|
|
|
736
839
|
async execute(_id, params) {
|
|
737
840
|
try {
|
|
738
841
|
const targetPath = validateWritePath(params.path, allowedRoots);
|
|
739
|
-
const stat =
|
|
842
|
+
const stat = fs4.statSync(targetPath);
|
|
740
843
|
const wasDirectory = stat.isDirectory();
|
|
741
844
|
if (wasDirectory) {
|
|
742
|
-
|
|
845
|
+
fs4.rmSync(targetPath, { recursive: true });
|
|
743
846
|
} else {
|
|
744
|
-
|
|
847
|
+
fs4.unlinkSync(targetPath);
|
|
745
848
|
}
|
|
746
849
|
return ok({
|
|
747
850
|
path: targetPath,
|
|
@@ -793,7 +896,7 @@ function scanAgents(configDir) {
|
|
|
793
896
|
const now = Date.now();
|
|
794
897
|
let entries;
|
|
795
898
|
try {
|
|
796
|
-
entries =
|
|
899
|
+
entries = fs5.readdirSync(configDir, { withFileTypes: true });
|
|
797
900
|
} catch {
|
|
798
901
|
return;
|
|
799
902
|
}
|
|
@@ -802,20 +905,20 @@ function scanAgents(configDir) {
|
|
|
802
905
|
);
|
|
803
906
|
for (const dir of workspaceDirs) {
|
|
804
907
|
const agentId = dir.name === "workspace" ? "main" : dir.name.replace("workspace-", "");
|
|
805
|
-
const workspacePath =
|
|
908
|
+
const workspacePath = path5.join(configDir, dir.name);
|
|
806
909
|
let name = agentId;
|
|
807
910
|
const metadata = { workspacePath };
|
|
808
|
-
const identityPath =
|
|
911
|
+
const identityPath = path5.join(workspacePath, "IDENTITY.md");
|
|
809
912
|
try {
|
|
810
|
-
const content =
|
|
913
|
+
const content = fs5.readFileSync(identityPath, "utf-8");
|
|
811
914
|
const parsed = parseIdentityName(content);
|
|
812
915
|
if (parsed) name = parsed;
|
|
813
916
|
} catch {
|
|
814
917
|
}
|
|
815
918
|
if (name === agentId) {
|
|
816
|
-
const agentJsonPath =
|
|
919
|
+
const agentJsonPath = path5.join(workspacePath, "agent.json");
|
|
817
920
|
try {
|
|
818
|
-
const raw =
|
|
921
|
+
const raw = fs5.readFileSync(agentJsonPath, "utf-8");
|
|
819
922
|
const config = JSON.parse(raw);
|
|
820
923
|
if (config.displayName) name = config.displayName;
|
|
821
924
|
if (config.model) metadata.model = config.model;
|
|
@@ -840,11 +943,11 @@ function scanAgents(configDir) {
|
|
|
840
943
|
}
|
|
841
944
|
function scanSkills(configDir) {
|
|
842
945
|
const now = Date.now();
|
|
843
|
-
const globalSkillsDir =
|
|
946
|
+
const globalSkillsDir = path5.join(configDir, "skills");
|
|
844
947
|
scanSkillsDir(globalSkillsDir, "global", now);
|
|
845
948
|
let entries;
|
|
846
949
|
try {
|
|
847
|
-
entries =
|
|
950
|
+
entries = fs5.readdirSync(configDir, { withFileTypes: true });
|
|
848
951
|
} catch {
|
|
849
952
|
return;
|
|
850
953
|
}
|
|
@@ -853,26 +956,26 @@ function scanSkills(configDir) {
|
|
|
853
956
|
continue;
|
|
854
957
|
}
|
|
855
958
|
const agentId = dir.name === "workspace" ? "main" : dir.name.replace("workspace-", "");
|
|
856
|
-
const agentSkillsDir =
|
|
959
|
+
const agentSkillsDir = path5.join(configDir, dir.name, "skills");
|
|
857
960
|
scanSkillsDir(agentSkillsDir, agentId, now);
|
|
858
961
|
}
|
|
859
962
|
}
|
|
860
963
|
function scanSkillsDir(skillsDir, scope, now) {
|
|
861
964
|
let entries;
|
|
862
965
|
try {
|
|
863
|
-
entries =
|
|
966
|
+
entries = fs5.readdirSync(skillsDir, { withFileTypes: true });
|
|
864
967
|
} catch {
|
|
865
968
|
return;
|
|
866
969
|
}
|
|
867
970
|
for (const entry of entries) {
|
|
868
971
|
if (!entry.isDirectory()) continue;
|
|
869
972
|
const skillKey = entry.name;
|
|
870
|
-
const skillPath =
|
|
973
|
+
const skillPath = path5.join(skillsDir, skillKey);
|
|
871
974
|
let name = skillKey;
|
|
872
975
|
for (const manifestName of ["manifest.json", "package.json"]) {
|
|
873
976
|
try {
|
|
874
|
-
const raw =
|
|
875
|
-
|
|
977
|
+
const raw = fs5.readFileSync(
|
|
978
|
+
path5.join(skillPath, manifestName),
|
|
876
979
|
"utf-8"
|
|
877
980
|
);
|
|
878
981
|
const manifest = JSON.parse(raw);
|
|
@@ -899,19 +1002,19 @@ function scanSkillsDir(skillsDir, scope, now) {
|
|
|
899
1002
|
}
|
|
900
1003
|
function scanPlugins2(configDir) {
|
|
901
1004
|
const now = Date.now();
|
|
902
|
-
const extensionsDir =
|
|
1005
|
+
const extensionsDir = path5.join(configDir, "extensions");
|
|
903
1006
|
let entries;
|
|
904
1007
|
try {
|
|
905
|
-
entries =
|
|
1008
|
+
entries = fs5.readdirSync(extensionsDir, { withFileTypes: true });
|
|
906
1009
|
} catch {
|
|
907
1010
|
return;
|
|
908
1011
|
}
|
|
909
1012
|
for (const dir of entries) {
|
|
910
1013
|
if (!dir.isDirectory()) continue;
|
|
911
|
-
const pluginDir =
|
|
912
|
-
const manifestPath =
|
|
1014
|
+
const pluginDir = path5.join(extensionsDir, dir.name);
|
|
1015
|
+
const manifestPath = path5.join(pluginDir, "openclaw.plugin.json");
|
|
913
1016
|
try {
|
|
914
|
-
const raw =
|
|
1017
|
+
const raw = fs5.readFileSync(manifestPath, "utf-8");
|
|
915
1018
|
const manifest = JSON.parse(raw);
|
|
916
1019
|
const pluginId = manifest.id || dir.name;
|
|
917
1020
|
const name = manifest.name || pluginId;
|
|
@@ -934,8 +1037,8 @@ function scanPlugins2(configDir) {
|
|
|
934
1037
|
function scanTools(configDir) {
|
|
935
1038
|
const now = Date.now();
|
|
936
1039
|
try {
|
|
937
|
-
const raw =
|
|
938
|
-
|
|
1040
|
+
const raw = fs5.readFileSync(
|
|
1041
|
+
path5.join(configDir, "openclaw.json"),
|
|
939
1042
|
"utf-8"
|
|
940
1043
|
);
|
|
941
1044
|
const config = JSON.parse(raw);
|
|
@@ -986,24 +1089,24 @@ var MIME_MAP = {
|
|
|
986
1089
|
".gz": "application/gzip"
|
|
987
1090
|
};
|
|
988
1091
|
function getMimeType(filename) {
|
|
989
|
-
const ext =
|
|
1092
|
+
const ext = path5.extname(filename).toLowerCase();
|
|
990
1093
|
return MIME_MAP[ext] ?? "application/octet-stream";
|
|
991
1094
|
}
|
|
992
1095
|
function scanMedia(configDir) {
|
|
993
1096
|
const now = Date.now();
|
|
994
|
-
const mediaDir =
|
|
1097
|
+
const mediaDir = path5.join(configDir, "media");
|
|
995
1098
|
scanMediaDir(mediaDir, now);
|
|
996
1099
|
}
|
|
997
1100
|
function scanMediaDir(dirPath, now) {
|
|
998
1101
|
let entries;
|
|
999
1102
|
try {
|
|
1000
|
-
entries =
|
|
1103
|
+
entries = fs5.readdirSync(dirPath, { withFileTypes: true });
|
|
1001
1104
|
} catch {
|
|
1002
1105
|
return;
|
|
1003
1106
|
}
|
|
1004
1107
|
for (const entry of entries) {
|
|
1005
1108
|
if (entry.name.startsWith(".")) continue;
|
|
1006
|
-
const entryPath =
|
|
1109
|
+
const entryPath = path5.join(dirPath, entry.name);
|
|
1007
1110
|
if (isSensitivePath(entryPath)) continue;
|
|
1008
1111
|
if (entry.isDirectory()) {
|
|
1009
1112
|
registrySet({
|
|
@@ -1024,7 +1127,7 @@ function scanMediaDir(dirPath, now) {
|
|
|
1024
1127
|
let size;
|
|
1025
1128
|
let mtime = now;
|
|
1026
1129
|
try {
|
|
1027
|
-
const stat =
|
|
1130
|
+
const stat = fs5.statSync(entryPath);
|
|
1028
1131
|
size = stat.size;
|
|
1029
1132
|
mtime = stat.mtimeMs;
|
|
1030
1133
|
} catch {
|
|
@@ -1141,24 +1244,24 @@ function registerEntityTools(api, onFsChange) {
|
|
|
1141
1244
|
|
|
1142
1245
|
// src/sql.ts
|
|
1143
1246
|
import { execFile } from "child_process";
|
|
1144
|
-
import
|
|
1145
|
-
import
|
|
1247
|
+
import path6 from "path";
|
|
1248
|
+
import fs6 from "fs";
|
|
1146
1249
|
import { Type as T2 } from "@sinclair/typebox";
|
|
1147
1250
|
var HOME_DIR2 = process.env.HOME ?? "/root";
|
|
1148
|
-
var ALLOWED_DATA_DIR =
|
|
1251
|
+
var ALLOWED_DATA_DIR = path6.join(getOpenclawStateDir(), "squad-ceo-data");
|
|
1149
1252
|
function validateDbPath(dbPath) {
|
|
1150
1253
|
let expanded = dbPath;
|
|
1151
1254
|
if (expanded.startsWith("~/") || expanded === "~") {
|
|
1152
|
-
expanded =
|
|
1255
|
+
expanded = path6.join(HOME_DIR2, expanded.slice(1));
|
|
1153
1256
|
}
|
|
1154
|
-
const resolved =
|
|
1155
|
-
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)) {
|
|
1156
1259
|
throw new Error(
|
|
1157
1260
|
`Access denied: database path must be within ~/.openclaw/squad-ceo-data/`
|
|
1158
1261
|
);
|
|
1159
1262
|
}
|
|
1160
1263
|
try {
|
|
1161
|
-
const stat =
|
|
1264
|
+
const stat = fs6.statSync(resolved);
|
|
1162
1265
|
if (!stat.isFile()) {
|
|
1163
1266
|
throw new Error(`Not a file: ${dbPath}`);
|
|
1164
1267
|
}
|
|
@@ -1225,18 +1328,18 @@ function registerSqlTools(api) {
|
|
|
1225
1328
|
|
|
1226
1329
|
// src/version.ts
|
|
1227
1330
|
import { execSync as execSync2 } from "child_process";
|
|
1228
|
-
import
|
|
1229
|
-
import
|
|
1331
|
+
import fs7 from "fs";
|
|
1332
|
+
import path7 from "path";
|
|
1230
1333
|
import { fileURLToPath } from "url";
|
|
1231
1334
|
var PACKAGE_NAME = "squad-openclaw";
|
|
1232
|
-
var CONFIG_PATH =
|
|
1335
|
+
var CONFIG_PATH = path7.join(getOpenclawStateDir(), "openclaw.json");
|
|
1233
1336
|
var updateInProgress = false;
|
|
1234
1337
|
var VERIFY_TIMEOUT_MS = 2e4;
|
|
1235
1338
|
var VERIFY_INTERVAL_MS = 500;
|
|
1236
1339
|
var RESTART_BUFFER_MS = 5e3;
|
|
1237
1340
|
function readInstalledVersionFromConfig() {
|
|
1238
1341
|
try {
|
|
1239
|
-
const raw =
|
|
1342
|
+
const raw = fs7.readFileSync(CONFIG_PATH, "utf-8");
|
|
1240
1343
|
const cfg = JSON.parse(raw);
|
|
1241
1344
|
const v = cfg?.plugins?.installs?.[PACKAGE_NAME]?.version;
|
|
1242
1345
|
return typeof v === "string" ? v : null;
|
|
@@ -1247,7 +1350,7 @@ function readInstalledVersionFromConfig() {
|
|
|
1247
1350
|
function reconcileInstallMetadata(verification) {
|
|
1248
1351
|
if (!verification.installPath || !verification.packageVersion) return;
|
|
1249
1352
|
try {
|
|
1250
|
-
const raw =
|
|
1353
|
+
const raw = fs7.readFileSync(CONFIG_PATH, "utf-8");
|
|
1251
1354
|
const config = JSON.parse(raw);
|
|
1252
1355
|
if (!config.plugins || typeof config.plugins !== "object") config.plugins = {};
|
|
1253
1356
|
if (!config.plugins.installs || typeof config.plugins.installs !== "object") {
|
|
@@ -1270,15 +1373,15 @@ function reconcileInstallMetadata(verification) {
|
|
|
1270
1373
|
...entry,
|
|
1271
1374
|
enabled: true
|
|
1272
1375
|
};
|
|
1273
|
-
|
|
1376
|
+
fs7.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
1274
1377
|
} catch {
|
|
1275
1378
|
}
|
|
1276
1379
|
}
|
|
1277
1380
|
function getCurrentVersion() {
|
|
1278
1381
|
const thisFile = fileURLToPath(import.meta.url);
|
|
1279
|
-
const pkgPath =
|
|
1382
|
+
const pkgPath = path7.resolve(path7.dirname(thisFile), "..", "package.json");
|
|
1280
1383
|
try {
|
|
1281
|
-
const pkg = JSON.parse(
|
|
1384
|
+
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
1282
1385
|
return pkg.version ?? "0.0.0";
|
|
1283
1386
|
} catch {
|
|
1284
1387
|
return "0.0.0";
|
|
@@ -1323,7 +1426,7 @@ function compareVersions(a, b) {
|
|
|
1323
1426
|
function verifyInstalledPluginState() {
|
|
1324
1427
|
let configRaw;
|
|
1325
1428
|
try {
|
|
1326
|
-
configRaw =
|
|
1429
|
+
configRaw = fs7.readFileSync(CONFIG_PATH, "utf-8");
|
|
1327
1430
|
} catch (err2) {
|
|
1328
1431
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
1329
1432
|
return {
|
|
@@ -1363,11 +1466,11 @@ function verifyInstalledPluginState() {
|
|
|
1363
1466
|
};
|
|
1364
1467
|
}
|
|
1365
1468
|
const requiredFiles = [
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1469
|
+
path7.join(installPath, "package.json"),
|
|
1470
|
+
path7.join(installPath, "openclaw.plugin.json"),
|
|
1471
|
+
path7.join(installPath, "dist", "index.js")
|
|
1369
1472
|
];
|
|
1370
|
-
const requiredFilesMissing = requiredFiles.filter((p) => !
|
|
1473
|
+
const requiredFilesMissing = requiredFiles.filter((p) => !fs7.existsSync(p));
|
|
1371
1474
|
if (requiredFilesMissing.length > 0) {
|
|
1372
1475
|
return {
|
|
1373
1476
|
ok: false,
|
|
@@ -1381,7 +1484,7 @@ function verifyInstalledPluginState() {
|
|
|
1381
1484
|
let installedPackage;
|
|
1382
1485
|
try {
|
|
1383
1486
|
installedPackage = JSON.parse(
|
|
1384
|
-
|
|
1487
|
+
fs7.readFileSync(path7.join(installPath, "package.json"), "utf-8")
|
|
1385
1488
|
);
|
|
1386
1489
|
} catch (err2) {
|
|
1387
1490
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
@@ -1396,7 +1499,7 @@ function verifyInstalledPluginState() {
|
|
|
1396
1499
|
}
|
|
1397
1500
|
try {
|
|
1398
1501
|
JSON.parse(
|
|
1399
|
-
|
|
1502
|
+
fs7.readFileSync(path7.join(installPath, "openclaw.plugin.json"), "utf-8")
|
|
1400
1503
|
);
|
|
1401
1504
|
} catch (err2) {
|
|
1402
1505
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
@@ -1486,7 +1589,7 @@ function registerVersionMethods(api) {
|
|
|
1486
1589
|
let updateOutput = "";
|
|
1487
1590
|
let configBackup = null;
|
|
1488
1591
|
try {
|
|
1489
|
-
configBackup =
|
|
1592
|
+
configBackup = fs7.readFileSync(CONFIG_PATH, "utf-8");
|
|
1490
1593
|
} catch {
|
|
1491
1594
|
}
|
|
1492
1595
|
runDoctorFixSilently();
|
|
@@ -1505,7 +1608,7 @@ function registerVersionMethods(api) {
|
|
|
1505
1608
|
} catch (installErr) {
|
|
1506
1609
|
if (configBackup) {
|
|
1507
1610
|
try {
|
|
1508
|
-
|
|
1611
|
+
fs7.writeFileSync(CONFIG_PATH, configBackup, "utf-8");
|
|
1509
1612
|
} catch {
|
|
1510
1613
|
}
|
|
1511
1614
|
}
|
|
@@ -1523,7 +1626,7 @@ function registerVersionMethods(api) {
|
|
|
1523
1626
|
if (!verification.ok) {
|
|
1524
1627
|
if (configBackup) {
|
|
1525
1628
|
try {
|
|
1526
|
-
|
|
1629
|
+
fs7.writeFileSync(CONFIG_PATH, configBackup, "utf-8");
|
|
1527
1630
|
} catch {
|
|
1528
1631
|
}
|
|
1529
1632
|
}
|
|
@@ -1581,8 +1684,8 @@ function registerVersionMethods(api) {
|
|
|
1581
1684
|
// src/relay-client.ts
|
|
1582
1685
|
import { WebSocket as NodeWebSocket } from "ws";
|
|
1583
1686
|
import crypto3 from "crypto";
|
|
1584
|
-
import
|
|
1585
|
-
import
|
|
1687
|
+
import fs9 from "fs";
|
|
1688
|
+
import path9 from "path";
|
|
1586
1689
|
|
|
1587
1690
|
// src/e2e-crypto.ts
|
|
1588
1691
|
import crypto from "crypto";
|
|
@@ -1663,24 +1766,24 @@ var E2ECrypto = class {
|
|
|
1663
1766
|
|
|
1664
1767
|
// src/device-keys.ts
|
|
1665
1768
|
import crypto2 from "crypto";
|
|
1666
|
-
import
|
|
1667
|
-
import
|
|
1668
|
-
var RELAY_DATA_DIR =
|
|
1669
|
-
var RELAY_STATE_PATH =
|
|
1670
|
-
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");
|
|
1671
1774
|
function readRelayState() {
|
|
1672
1775
|
try {
|
|
1673
|
-
const raw =
|
|
1776
|
+
const raw = fs8.readFileSync(RELAY_STATE_PATH, "utf-8");
|
|
1674
1777
|
return JSON.parse(raw);
|
|
1675
1778
|
} catch {
|
|
1676
1779
|
return {};
|
|
1677
1780
|
}
|
|
1678
1781
|
}
|
|
1679
1782
|
function writeRelayState(state) {
|
|
1680
|
-
if (!
|
|
1681
|
-
|
|
1783
|
+
if (!fs8.existsSync(RELAY_DATA_DIR)) {
|
|
1784
|
+
fs8.mkdirSync(RELAY_DATA_DIR, { recursive: true });
|
|
1682
1785
|
}
|
|
1683
|
-
|
|
1786
|
+
fs8.writeFileSync(RELAY_STATE_PATH, JSON.stringify(state, null, 2), { mode: 384 });
|
|
1684
1787
|
}
|
|
1685
1788
|
function toBase64Url(buf) {
|
|
1686
1789
|
return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
@@ -1703,7 +1806,7 @@ function loadOrCreateRelayDeviceKeys() {
|
|
|
1703
1806
|
}
|
|
1704
1807
|
function writeDeviceInfoFile(keys) {
|
|
1705
1808
|
const stateDir = getOpenclawStateDir();
|
|
1706
|
-
const infoPath =
|
|
1809
|
+
const infoPath = path8.join(stateDir, "squad-ceo-data", "relay", "relay-device-info.json");
|
|
1707
1810
|
const info = {
|
|
1708
1811
|
deviceId: keys.deviceId,
|
|
1709
1812
|
publicKey: keys.publicKey,
|
|
@@ -1712,7 +1815,7 @@ function writeDeviceInfoFile(keys) {
|
|
|
1712
1815
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1713
1816
|
};
|
|
1714
1817
|
try {
|
|
1715
|
-
|
|
1818
|
+
fs8.writeFileSync(infoPath, JSON.stringify(info, null, 2));
|
|
1716
1819
|
} catch (err2) {
|
|
1717
1820
|
console.error("[device-keys] Failed to write relay-device-info.json:", err2);
|
|
1718
1821
|
}
|
|
@@ -1721,9 +1824,9 @@ function writeDeviceInfoFile(keys) {
|
|
|
1721
1824
|
// src/relay-client.ts
|
|
1722
1825
|
function readOperatorToken() {
|
|
1723
1826
|
const stateDir = getOpenclawStateDir();
|
|
1724
|
-
const configPath =
|
|
1827
|
+
const configPath = path9.join(stateDir, "openclaw.json");
|
|
1725
1828
|
try {
|
|
1726
|
-
const raw =
|
|
1829
|
+
const raw = fs9.readFileSync(configPath, "utf-8");
|
|
1727
1830
|
const config = JSON.parse(raw);
|
|
1728
1831
|
return config?.gateway?.auth?.token ?? config?.gateway?.remote?.token ?? config?.gateway?.token ?? null;
|
|
1729
1832
|
} catch {
|
|
@@ -1737,9 +1840,9 @@ function readGatewayLocalWsConfig() {
|
|
|
1737
1840
|
hosts: ["127.0.0.1", "localhost", "[::1]"]
|
|
1738
1841
|
};
|
|
1739
1842
|
const stateDir = getOpenclawStateDir();
|
|
1740
|
-
const configPath =
|
|
1843
|
+
const configPath = path9.join(stateDir, "openclaw.json");
|
|
1741
1844
|
try {
|
|
1742
|
-
const raw =
|
|
1845
|
+
const raw = fs9.readFileSync(configPath, "utf-8");
|
|
1743
1846
|
const config = JSON.parse(raw);
|
|
1744
1847
|
const parsedPort = Number(config?.gateway?.port);
|
|
1745
1848
|
if (Number.isFinite(parsedPort) && parsedPort > 0) {
|
|
@@ -2285,88 +2388,6 @@ function broadcastToUsers(event, payload) {
|
|
|
2285
2388
|
relayClient?.broadcastToUsers(event, payload);
|
|
2286
2389
|
}
|
|
2287
2390
|
|
|
2288
|
-
// src/layout.ts
|
|
2289
|
-
import fs8 from "fs";
|
|
2290
|
-
import path9 from "path";
|
|
2291
|
-
function resolveMaybeRelativePath(stateDir, p) {
|
|
2292
|
-
if (path9.isAbsolute(p)) return path9.resolve(p);
|
|
2293
|
-
return path9.resolve(stateDir, p);
|
|
2294
|
-
}
|
|
2295
|
-
function listWorkspaceFallbacks(stateDir) {
|
|
2296
|
-
let entries;
|
|
2297
|
-
try {
|
|
2298
|
-
entries = fs8.readdirSync(stateDir, { withFileTypes: true });
|
|
2299
|
-
} catch {
|
|
2300
|
-
return [];
|
|
2301
|
-
}
|
|
2302
|
-
return entries.filter((entry) => entry.isDirectory() && (entry.name === "workspace" || entry.name.startsWith("workspace-"))).map((entry) => {
|
|
2303
|
-
const agentId = entry.name === "workspace" ? "main" : entry.name.replace("workspace-", "");
|
|
2304
|
-
const workspacePath = path9.join(stateDir, entry.name);
|
|
2305
|
-
return {
|
|
2306
|
-
agentId,
|
|
2307
|
-
path: workspacePath,
|
|
2308
|
-
source: "filesystem",
|
|
2309
|
-
exists: true
|
|
2310
|
-
};
|
|
2311
|
-
});
|
|
2312
|
-
}
|
|
2313
|
-
function readOpenclawConfig(configPath) {
|
|
2314
|
-
try {
|
|
2315
|
-
const raw = fs8.readFileSync(configPath, "utf-8");
|
|
2316
|
-
return JSON.parse(raw);
|
|
2317
|
-
} catch {
|
|
2318
|
-
return null;
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
function resolveGatewayLayout() {
|
|
2322
|
-
const stateDir = getOpenclawStateDir();
|
|
2323
|
-
const configPath = path9.join(stateDir, "openclaw.json");
|
|
2324
|
-
const config = readOpenclawConfig(configPath);
|
|
2325
|
-
const workspaces = [];
|
|
2326
|
-
if (config?.agents?.main?.workspace || config?.agents?.main?.workspacePath) {
|
|
2327
|
-
const rawPath = config.agents.main.workspace ?? config.agents.main.workspacePath;
|
|
2328
|
-
if (rawPath) {
|
|
2329
|
-
const resolvedPath = resolveMaybeRelativePath(stateDir, rawPath);
|
|
2330
|
-
workspaces.push({
|
|
2331
|
-
agentId: "main",
|
|
2332
|
-
path: resolvedPath,
|
|
2333
|
-
source: "config",
|
|
2334
|
-
exists: fs8.existsSync(resolvedPath)
|
|
2335
|
-
});
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
for (const agent of config?.agents?.list ?? []) {
|
|
2339
|
-
const agentId = typeof agent.id === "string" && agent.id.trim() ? agent.id : null;
|
|
2340
|
-
const rawPath = agent.workspace ?? agent.workspacePath;
|
|
2341
|
-
if (!agentId || !rawPath) continue;
|
|
2342
|
-
const resolvedPath = resolveMaybeRelativePath(stateDir, rawPath);
|
|
2343
|
-
workspaces.push({
|
|
2344
|
-
agentId,
|
|
2345
|
-
path: resolvedPath,
|
|
2346
|
-
source: "config",
|
|
2347
|
-
exists: fs8.existsSync(resolvedPath)
|
|
2348
|
-
});
|
|
2349
|
-
}
|
|
2350
|
-
const deduped = /* @__PURE__ */ new Map();
|
|
2351
|
-
for (const ws of [...workspaces, ...listWorkspaceFallbacks(stateDir)]) {
|
|
2352
|
-
if (!deduped.has(ws.agentId)) {
|
|
2353
|
-
deduped.set(ws.agentId, ws);
|
|
2354
|
-
}
|
|
2355
|
-
}
|
|
2356
|
-
const resolvedWorkspaces = Array.from(deduped.values());
|
|
2357
|
-
const mainWorkspace = resolvedWorkspaces.find((ws) => ws.agentId === "main");
|
|
2358
|
-
const defaultFileBrowserRoot = mainWorkspace?.path ?? stateDir;
|
|
2359
|
-
return {
|
|
2360
|
-
stateDir,
|
|
2361
|
-
configPath,
|
|
2362
|
-
mediaDir: path9.join(stateDir, "media"),
|
|
2363
|
-
skillsDir: path9.join(stateDir, "skills"),
|
|
2364
|
-
extensionsDir: path9.join(stateDir, "extensions"),
|
|
2365
|
-
defaultFileBrowserRoot,
|
|
2366
|
-
workspaces: resolvedWorkspaces
|
|
2367
|
-
};
|
|
2368
|
-
}
|
|
2369
|
-
|
|
2370
2391
|
// src/index.ts
|
|
2371
2392
|
function squadAppPlugin(api) {
|
|
2372
2393
|
const toolExecutors = /* @__PURE__ */ new Map();
|
|
@@ -2453,7 +2474,9 @@ function squadAppPlugin(api) {
|
|
|
2453
2474
|
"squad.layout.get",
|
|
2454
2475
|
async ({ respond }) => {
|
|
2455
2476
|
try {
|
|
2456
|
-
|
|
2477
|
+
const layout = resolveGatewayLayout();
|
|
2478
|
+
console.log("[squad-openclaw] squad.layout.get", JSON.stringify(layout));
|
|
2479
|
+
respond(true, layout);
|
|
2457
2480
|
} catch (err2) {
|
|
2458
2481
|
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
2459
2482
|
respond(false, { errorMessage: msg });
|
package/package.json
CHANGED