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.
Files changed (2) hide show
  1. package/dist/index.js +203 -180
  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 fs3 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,30 +360,129 @@ function startWatcher(configDir, onFsChange) {
360
360
  }
361
361
 
362
362
  // src/filesystem.ts
363
- import fs2 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";
368
368
  import os from "os";
369
+ import fs2 from "fs";
369
370
  function getOpenclawStateDir() {
370
- return process.env.OPENCLAW_STATE_DIR || process.env.OPENCLAW_DIR || path2.join(os.homedir(), ".openclaw");
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
- path3.join(OPENCLAW_DIR, "credentials"),
378
- path3.join(OPENCLAW_DIR, "devices"),
379
- path3.join(OPENCLAW_DIR, "identity")
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
- path3.join(OPENCLAW_DIR, "squad-ceo-data", "relay", "squad-relay.json")
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 + path3.sep)) {
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 (path3.dirname(resolvedPath) === OPENCLAW_DIR && resolvedPath.endsWith(".bak")) {
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 path3.basename(resolvedPath) === OPENCLAW_JSON_FILENAME && resolvedPath.startsWith(OPENCLAW_DIR);
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 path3.join(HOME_DIR, p.slice(1));
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 = path3.resolve(expandHome(p));
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 = path3.resolve(expandHome(root));
457
- return resolved === resolvedRoot || resolved.startsWith(resolvedRoot + path3.sep);
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 = fs2.readdirSync(dirPath, { withFileTypes: true });
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 = path3.join(dirPath, dirent.name);
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 = fs2.statSync(entryPath);
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 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
+ ]));
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 = fs2.readFileSync(filePath, encoding);
555
- const stat = fs2.statSync(filePath);
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
- fs2.mkdirSync(path3.dirname(filePath), { recursive: true });
708
+ fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
606
709
  }
607
- fs2.writeFileSync(filePath, content, encoding);
608
- const stat = fs2.statSync(filePath);
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
- fs2.mkdirSync(targetPath, { recursive: true });
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
- fs2.renameSync(resolvedOld, resolvedNew);
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 = fs2.statSync(targetPath);
842
+ const stat = fs4.statSync(targetPath);
740
843
  const wasDirectory = stat.isDirectory();
741
844
  if (wasDirectory) {
742
- fs2.rmSync(targetPath, { recursive: true });
845
+ fs4.rmSync(targetPath, { recursive: true });
743
846
  } else {
744
- fs2.unlinkSync(targetPath);
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 = fs3.readdirSync(configDir, { withFileTypes: true });
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 = path4.join(configDir, dir.name);
908
+ const workspacePath = path5.join(configDir, dir.name);
806
909
  let name = agentId;
807
910
  const metadata = { workspacePath };
808
- const identityPath = path4.join(workspacePath, "IDENTITY.md");
911
+ const identityPath = path5.join(workspacePath, "IDENTITY.md");
809
912
  try {
810
- const content = fs3.readFileSync(identityPath, "utf-8");
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 = path4.join(workspacePath, "agent.json");
919
+ const agentJsonPath = path5.join(workspacePath, "agent.json");
817
920
  try {
818
- const raw = fs3.readFileSync(agentJsonPath, "utf-8");
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 = path4.join(configDir, "skills");
946
+ const globalSkillsDir = path5.join(configDir, "skills");
844
947
  scanSkillsDir(globalSkillsDir, "global", now);
845
948
  let entries;
846
949
  try {
847
- entries = fs3.readdirSync(configDir, { withFileTypes: true });
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 = path4.join(configDir, dir.name, "skills");
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 = fs3.readdirSync(skillsDir, { withFileTypes: true });
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 = path4.join(skillsDir, skillKey);
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 = fs3.readFileSync(
875
- path4.join(skillPath, manifestName),
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 = path4.join(configDir, "extensions");
1005
+ const extensionsDir = path5.join(configDir, "extensions");
903
1006
  let entries;
904
1007
  try {
905
- entries = fs3.readdirSync(extensionsDir, { withFileTypes: true });
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 = path4.join(extensionsDir, dir.name);
912
- 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");
913
1016
  try {
914
- const raw = fs3.readFileSync(manifestPath, "utf-8");
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 = fs3.readFileSync(
938
- path4.join(configDir, "openclaw.json"),
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 = path4.extname(filename).toLowerCase();
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 = path4.join(configDir, "media");
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 = fs3.readdirSync(dirPath, { withFileTypes: true });
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 = path4.join(dirPath, entry.name);
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 = fs3.statSync(entryPath);
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 path5 from "path";
1145
- import fs4 from "fs";
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 = path5.join(getOpenclawStateDir(), "squad-ceo-data");
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 = path5.join(HOME_DIR2, expanded.slice(1));
1255
+ expanded = path6.join(HOME_DIR2, expanded.slice(1));
1153
1256
  }
1154
- const resolved = path5.resolve(expanded);
1155
- 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)) {
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 = fs4.statSync(resolved);
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 fs5 from "fs";
1229
- import path6 from "path";
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 = path6.join(getOpenclawStateDir(), "openclaw.json");
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 = fs5.readFileSync(CONFIG_PATH, "utf-8");
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 = fs5.readFileSync(CONFIG_PATH, "utf-8");
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
- fs5.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
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 = path6.resolve(path6.dirname(thisFile), "..", "package.json");
1382
+ const pkgPath = path7.resolve(path7.dirname(thisFile), "..", "package.json");
1280
1383
  try {
1281
- const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
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 = fs5.readFileSync(CONFIG_PATH, "utf-8");
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
- path6.join(installPath, "package.json"),
1367
- path6.join(installPath, "openclaw.plugin.json"),
1368
- 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")
1369
1472
  ];
1370
- const requiredFilesMissing = requiredFiles.filter((p) => !fs5.existsSync(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
- fs5.readFileSync(path6.join(installPath, "package.json"), "utf-8")
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
- fs5.readFileSync(path6.join(installPath, "openclaw.plugin.json"), "utf-8")
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 = fs5.readFileSync(CONFIG_PATH, "utf-8");
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
- fs5.writeFileSync(CONFIG_PATH, configBackup, "utf-8");
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
- fs5.writeFileSync(CONFIG_PATH, configBackup, "utf-8");
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 fs7 from "fs";
1585
- import path8 from "path";
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 fs6 from "fs";
1667
- import path7 from "path";
1668
- var RELAY_DATA_DIR = path7.join(getOpenclawStateDir(), "squad-ceo-data", "relay");
1669
- var RELAY_STATE_PATH = path7.join(RELAY_DATA_DIR, "squad-relay.json");
1670
- 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");
1671
1774
  function readRelayState() {
1672
1775
  try {
1673
- const raw = fs6.readFileSync(RELAY_STATE_PATH, "utf-8");
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 (!fs6.existsSync(RELAY_DATA_DIR)) {
1681
- fs6.mkdirSync(RELAY_DATA_DIR, { recursive: true });
1783
+ if (!fs8.existsSync(RELAY_DATA_DIR)) {
1784
+ fs8.mkdirSync(RELAY_DATA_DIR, { recursive: true });
1682
1785
  }
1683
- fs6.writeFileSync(RELAY_STATE_PATH, JSON.stringify(state, null, 2), { mode: 384 });
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 = 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");
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
- fs6.writeFileSync(infoPath, JSON.stringify(info, null, 2));
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 = path8.join(stateDir, "openclaw.json");
1827
+ const configPath = path9.join(stateDir, "openclaw.json");
1725
1828
  try {
1726
- const raw = fs7.readFileSync(configPath, "utf-8");
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 = path8.join(stateDir, "openclaw.json");
1843
+ const configPath = path9.join(stateDir, "openclaw.json");
1741
1844
  try {
1742
- const raw = fs7.readFileSync(configPath, "utf-8");
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
- respond(true, resolveGatewayLayout());
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squad-openclaw",
3
- "version": "2026.2.2018",
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",