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.
Files changed (2) hide show
  1. package/dist/index.js +185 -179
  2. 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 path4 from "path";
108
- import fs4 from "fs";
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 fs3 from "fs";
364
- import path3 from "path";
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
- path3.join(OPENCLAW_DIR, "credentials"),
395
- path3.join(OPENCLAW_DIR, "devices"),
396
- path3.join(OPENCLAW_DIR, "identity")
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
- path3.join(OPENCLAW_DIR, "squad-ceo-data", "relay", "squad-relay.json")
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 + path3.sep)) {
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 (path3.dirname(resolvedPath) === OPENCLAW_DIR && resolvedPath.endsWith(".bak")) {
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 path3.basename(resolvedPath) === OPENCLAW_JSON_FILENAME && resolvedPath.startsWith(OPENCLAW_DIR);
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 path3.join(HOME_DIR, p.slice(1));
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 = path3.resolve(expandHome(p));
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 = path3.resolve(expandHome(root));
474
- return resolved === resolvedRoot || resolved.startsWith(resolvedRoot + path3.sep);
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 = fs3.readdirSync(dirPath, { withFileTypes: true });
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 = path3.join(dirPath, dirent.name);
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 = fs3.statSync(entryPath);
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 DEFAULT_ALLOWED_ROOTS = [OPENCLAW_DIR];
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 = fs3.readFileSync(filePath, encoding);
572
- const stat = fs3.statSync(filePath);
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
- fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
708
+ fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
623
709
  }
624
- fs3.writeFileSync(filePath, content, encoding);
625
- const stat = fs3.statSync(filePath);
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
- fs3.mkdirSync(targetPath, { recursive: true });
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
- fs3.renameSync(resolvedOld, resolvedNew);
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 = fs3.statSync(targetPath);
842
+ const stat = fs4.statSync(targetPath);
757
843
  const wasDirectory = stat.isDirectory();
758
844
  if (wasDirectory) {
759
- fs3.rmSync(targetPath, { recursive: true });
845
+ fs4.rmSync(targetPath, { recursive: true });
760
846
  } else {
761
- fs3.unlinkSync(targetPath);
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 = fs4.readdirSync(configDir, { withFileTypes: true });
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 = path4.join(configDir, dir.name);
908
+ const workspacePath = path5.join(configDir, dir.name);
823
909
  let name = agentId;
824
910
  const metadata = { workspacePath };
825
- const identityPath = path4.join(workspacePath, "IDENTITY.md");
911
+ const identityPath = path5.join(workspacePath, "IDENTITY.md");
826
912
  try {
827
- const content = fs4.readFileSync(identityPath, "utf-8");
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 = path4.join(workspacePath, "agent.json");
919
+ const agentJsonPath = path5.join(workspacePath, "agent.json");
834
920
  try {
835
- const raw = fs4.readFileSync(agentJsonPath, "utf-8");
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 = path4.join(configDir, "skills");
946
+ const globalSkillsDir = path5.join(configDir, "skills");
861
947
  scanSkillsDir(globalSkillsDir, "global", now);
862
948
  let entries;
863
949
  try {
864
- entries = fs4.readdirSync(configDir, { withFileTypes: true });
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 = path4.join(configDir, dir.name, "skills");
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 = fs4.readdirSync(skillsDir, { withFileTypes: true });
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 = path4.join(skillsDir, skillKey);
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 = fs4.readFileSync(
892
- path4.join(skillPath, manifestName),
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 = path4.join(configDir, "extensions");
1005
+ const extensionsDir = path5.join(configDir, "extensions");
920
1006
  let entries;
921
1007
  try {
922
- entries = fs4.readdirSync(extensionsDir, { withFileTypes: true });
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 = path4.join(extensionsDir, dir.name);
929
- const manifestPath = path4.join(pluginDir, "openclaw.plugin.json");
1014
+ const pluginDir = path5.join(extensionsDir, dir.name);
1015
+ const manifestPath = path5.join(pluginDir, "openclaw.plugin.json");
930
1016
  try {
931
- const raw = fs4.readFileSync(manifestPath, "utf-8");
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 = fs4.readFileSync(
955
- path4.join(configDir, "openclaw.json"),
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 = path4.extname(filename).toLowerCase();
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 = path4.join(configDir, "media");
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 = fs4.readdirSync(dirPath, { withFileTypes: true });
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 = path4.join(dirPath, entry.name);
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 = fs4.statSync(entryPath);
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 path5 from "path";
1162
- import fs5 from "fs";
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 = path5.join(getOpenclawStateDir(), "squad-ceo-data");
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 = path5.join(HOME_DIR2, expanded.slice(1));
1255
+ expanded = path6.join(HOME_DIR2, expanded.slice(1));
1170
1256
  }
1171
- const resolved = path5.resolve(expanded);
1172
- if (resolved !== ALLOWED_DATA_DIR && !resolved.startsWith(ALLOWED_DATA_DIR + path5.sep)) {
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 = fs5.statSync(resolved);
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 fs6 from "fs";
1246
- import path6 from "path";
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 = path6.join(getOpenclawStateDir(), "openclaw.json");
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 = fs6.readFileSync(CONFIG_PATH, "utf-8");
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 = fs6.readFileSync(CONFIG_PATH, "utf-8");
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
- fs6.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
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 = path6.resolve(path6.dirname(thisFile), "..", "package.json");
1382
+ const pkgPath = path7.resolve(path7.dirname(thisFile), "..", "package.json");
1297
1383
  try {
1298
- const pkg = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
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 = fs6.readFileSync(CONFIG_PATH, "utf-8");
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
- path6.join(installPath, "package.json"),
1384
- path6.join(installPath, "openclaw.plugin.json"),
1385
- path6.join(installPath, "dist", "index.js")
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) => !fs6.existsSync(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
- fs6.readFileSync(path6.join(installPath, "package.json"), "utf-8")
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
- fs6.readFileSync(path6.join(installPath, "openclaw.plugin.json"), "utf-8")
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 = fs6.readFileSync(CONFIG_PATH, "utf-8");
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
- fs6.writeFileSync(CONFIG_PATH, configBackup, "utf-8");
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
- fs6.writeFileSync(CONFIG_PATH, configBackup, "utf-8");
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 fs8 from "fs";
1602
- import path8 from "path";
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 fs7 from "fs";
1684
- import path7 from "path";
1685
- var RELAY_DATA_DIR = path7.join(getOpenclawStateDir(), "squad-ceo-data", "relay");
1686
- var RELAY_STATE_PATH = path7.join(RELAY_DATA_DIR, "squad-relay.json");
1687
- var PENDING_APPROVAL_PATH = path7.join(RELAY_DATA_DIR, "pending-approval.json");
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 = fs7.readFileSync(RELAY_STATE_PATH, "utf-8");
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 (!fs7.existsSync(RELAY_DATA_DIR)) {
1698
- fs7.mkdirSync(RELAY_DATA_DIR, { recursive: true });
1783
+ if (!fs8.existsSync(RELAY_DATA_DIR)) {
1784
+ fs8.mkdirSync(RELAY_DATA_DIR, { recursive: true });
1699
1785
  }
1700
- fs7.writeFileSync(RELAY_STATE_PATH, JSON.stringify(state, null, 2), { mode: 384 });
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 = path7.join(stateDir, "squad-ceo-data", "relay", "relay-device-info.json");
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
- fs7.writeFileSync(infoPath, JSON.stringify(info, null, 2));
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 = path8.join(stateDir, "openclaw.json");
1827
+ const configPath = path9.join(stateDir, "openclaw.json");
1742
1828
  try {
1743
- const raw = fs8.readFileSync(configPath, "utf-8");
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 = path8.join(stateDir, "openclaw.json");
1843
+ const configPath = path9.join(stateDir, "openclaw.json");
1758
1844
  try {
1759
- const raw = fs8.readFileSync(configPath, "utf-8");
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
- respond(true, resolveGatewayLayout());
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squad-openclaw",
3
- "version": "2026.2.2019",
3
+ "version": "2026.2.2020",
4
4
  "description": "Entity registry, filesystem tools, and version management plugin for OpenClaw gateway",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",