squad-openclaw 2026.2.1816 → 2026.2.1901

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.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Provides:
5
5
  * - In-memory entity registry with filesystem watching (entity_list, entity_search, entity_sync)
6
- * - Filesystem tools for remote clients (fs_read, fs_write, fs_list)
6
+ * - Filesystem tools for remote clients (fs_read, fs_write, fs_list, fs_delete)
7
7
  * - Version check and self-update gateway methods (squad.version.*)
8
8
  * - Cloud relay client for remote browser access (relay-client)
9
9
  */
package/dist/index.js CHANGED
@@ -20,6 +20,20 @@ function debounced(key, fn) {
20
20
  }, DEBOUNCE_MS)
21
21
  );
22
22
  }
23
+ var fsDebounceTimers = /* @__PURE__ */ new Map();
24
+ var FS_DEBOUNCE_MS = 300;
25
+ function debouncedFs(relPath, action, fn) {
26
+ const key = `fs:${action}:${relPath}`;
27
+ const existing = fsDebounceTimers.get(key);
28
+ if (existing) clearTimeout(existing);
29
+ fsDebounceTimers.set(
30
+ key,
31
+ setTimeout(() => {
32
+ fsDebounceTimers.delete(key);
33
+ fn();
34
+ }, FS_DEBOUNCE_MS)
35
+ );
36
+ }
23
37
  function isWorkspaceIdentity(filePath, configDir) {
24
38
  const rel = path.relative(configDir, filePath);
25
39
  const match = rel.match(/^(workspace(?:-([^/]+))?)\/IDENTITY\.md$/);
@@ -126,7 +140,7 @@ function updatePlugin(pluginDirName, configDir) {
126
140
  registryDelete(`plugin:${pluginDirName}`);
127
141
  }
128
142
  }
129
- function startWatcher(configDir) {
143
+ function startWatcher(configDir, onFsChange) {
130
144
  const watcher = chokidar.watch(configDir, {
131
145
  persistent: true,
132
146
  usePolling: false,
@@ -141,7 +155,15 @@ function startWatcher(configDir) {
141
155
  "**/data/**"
142
156
  ]
143
157
  });
144
- const handleChange = (filePath) => {
158
+ const emitFsChange = (action, filePath) => {
159
+ if (!onFsChange) return;
160
+ const rel = path.relative(configDir, filePath);
161
+ debouncedFs(rel, action, () => {
162
+ onFsChange({ action, path: rel });
163
+ });
164
+ };
165
+ const handleChange = (filePath, action) => {
166
+ emitFsChange(action, filePath);
145
167
  const identity = isWorkspaceIdentity(filePath, configDir);
146
168
  if (identity) {
147
169
  debounced(
@@ -172,6 +194,7 @@ function startWatcher(configDir) {
172
194
  }
173
195
  };
174
196
  const handleAddDir = (dirPath) => {
197
+ emitFsChange("addDir", dirPath);
175
198
  const globalSkill = isGlobalSkillDir(dirPath, configDir);
176
199
  if (globalSkill) {
177
200
  debounced(
@@ -195,6 +218,7 @@ function startWatcher(configDir) {
195
218
  }
196
219
  };
197
220
  const handleUnlinkDir = (dirPath) => {
221
+ emitFsChange("unlinkDir", dirPath);
198
222
  const rel = path.relative(configDir, dirPath);
199
223
  const wsMatch = rel.match(/^workspace(?:-([^/]+))?$/);
200
224
  if (wsMatch) {
@@ -213,9 +237,9 @@ function startWatcher(configDir) {
213
237
  return;
214
238
  }
215
239
  };
216
- watcher.on("add", handleChange);
217
- watcher.on("change", handleChange);
218
- watcher.on("unlink", handleChange);
240
+ watcher.on("add", (fp) => handleChange(fp, "add"));
241
+ watcher.on("change", (fp) => handleChange(fp, "change"));
242
+ watcher.on("unlink", (fp) => handleChange(fp, "unlink"));
219
243
  watcher.on("addDir", handleAddDir);
220
244
  watcher.on("unlinkDir", handleUnlinkDir);
221
245
  return () => {
@@ -223,6 +247,10 @@ function startWatcher(configDir) {
223
247
  clearTimeout(timer);
224
248
  }
225
249
  debounceTimers.clear();
250
+ for (const timer of fsDebounceTimers.values()) {
251
+ clearTimeout(timer);
252
+ }
253
+ fsDebounceTimers.clear();
226
254
  watcher.close();
227
255
  };
228
256
  }
@@ -500,7 +528,7 @@ function fullScan(configDir) {
500
528
  scanTools(configDir);
501
529
  scanMedia(configDir);
502
530
  }
503
- function registerEntityTools(api) {
531
+ function registerEntityTools(api, onFsChange) {
504
532
  const configDir = process.env.HOME + "/.openclaw";
505
533
  api.registerTool({
506
534
  name: "entity_list",
@@ -576,7 +604,7 @@ function registerEntityTools(api) {
576
604
  }
577
605
  let stopWatcher = null;
578
606
  try {
579
- stopWatcher = startWatcher(configDir);
607
+ stopWatcher = startWatcher(configDir, onFsChange);
580
608
  } catch (err2) {
581
609
  console.error("[squad-openclaw] Watcher failed to start:", err2);
582
610
  }
@@ -773,6 +801,69 @@ function registerFilesystemTools(api) {
773
801
  }
774
802
  }
775
803
  });
804
+ api.registerTool({
805
+ name: "fs_mkdir",
806
+ label: "Create Directory",
807
+ description: "Create a directory on the server filesystem. Creates parent directories as needed. Supports ~ for home directory expansion.",
808
+ parameters: {
809
+ type: "object",
810
+ properties: {
811
+ path: {
812
+ type: "string",
813
+ description: "Absolute or ~-prefixed path of the directory to create"
814
+ }
815
+ },
816
+ required: ["path"]
817
+ },
818
+ async execute(_id, params) {
819
+ try {
820
+ const targetPath = validatePath(params.path, allowedRoots);
821
+ fs3.mkdirSync(targetPath, { recursive: true });
822
+ return ok({
823
+ path: targetPath,
824
+ created: true
825
+ });
826
+ } catch (e) {
827
+ const msg = e instanceof Error ? e.message : String(e);
828
+ return err(`fs_mkdir failed: ${msg}`);
829
+ }
830
+ }
831
+ });
832
+ api.registerTool({
833
+ name: "fs_delete",
834
+ label: "Delete File or Directory",
835
+ description: "Delete a file or directory from the server filesystem. For directories, removes recursively. Supports ~ for home directory expansion.",
836
+ parameters: {
837
+ type: "object",
838
+ properties: {
839
+ path: {
840
+ type: "string",
841
+ description: "Absolute or ~-prefixed path to the file or directory to delete"
842
+ }
843
+ },
844
+ required: ["path"]
845
+ },
846
+ async execute(_id, params) {
847
+ try {
848
+ const targetPath = validatePath(params.path, allowedRoots);
849
+ const stat = fs3.statSync(targetPath);
850
+ const wasDirectory = stat.isDirectory();
851
+ if (wasDirectory) {
852
+ fs3.rmSync(targetPath, { recursive: true });
853
+ } else {
854
+ fs3.unlinkSync(targetPath);
855
+ }
856
+ return ok({
857
+ path: targetPath,
858
+ deleted: true,
859
+ type: wasDirectory ? "directory" : "file"
860
+ });
861
+ } catch (e) {
862
+ const msg = e instanceof Error ? e.message : String(e);
863
+ return err(`fs_delete failed: ${msg}`);
864
+ }
865
+ }
866
+ });
776
867
  }
777
868
 
778
869
  // src/version.ts
@@ -973,7 +1064,8 @@ function readOperatorToken() {
973
1064
  return null;
974
1065
  }
975
1066
  }
976
- var RELAY_STATE_PATH = path5.join(os.homedir(), ".openclaw", "squad-relay.json");
1067
+ var RELAY_DATA_DIR = path5.join(os.homedir(), ".openclaw", "squad-ceo-data");
1068
+ var RELAY_STATE_PATH = path5.join(RELAY_DATA_DIR, "squad-relay.json");
977
1069
  function readRelayState() {
978
1070
  try {
979
1071
  const raw = fs5.readFileSync(RELAY_STATE_PATH, "utf-8");
@@ -983,9 +1075,8 @@ function readRelayState() {
983
1075
  }
984
1076
  }
985
1077
  function writeRelayState(state) {
986
- const dir = path5.dirname(RELAY_STATE_PATH);
987
- if (!fs5.existsSync(dir)) {
988
- fs5.mkdirSync(dir, { recursive: true });
1078
+ if (!fs5.existsSync(RELAY_DATA_DIR)) {
1079
+ fs5.mkdirSync(RELAY_DATA_DIR, { recursive: true });
989
1080
  }
990
1081
  fs5.writeFileSync(RELAY_STATE_PATH, JSON.stringify(state, null, 2), { mode: 384 });
991
1082
  }
@@ -1485,6 +1576,28 @@ var RelayClient = class {
1485
1576
  } catch {
1486
1577
  }
1487
1578
  }
1579
+ /** Broadcast an event to all connected users, E2E encrypted per-user */
1580
+ broadcastToUsers(event, payload) {
1581
+ const msg = { type: "event", event, payload };
1582
+ for (const [userId, conn] of this.userConnections) {
1583
+ if (!conn.connectHandshakeComplete) continue;
1584
+ let innerMsg = msg;
1585
+ if (conn.e2e) {
1586
+ try {
1587
+ const encrypted = conn.e2e.encrypt(JSON.stringify(msg));
1588
+ innerMsg = { _e2e: true, ...encrypted };
1589
+ } catch (err2) {
1590
+ console.error(`[relay-client] E2E encrypt error for broadcast to ${userId}:`, err2);
1591
+ innerMsg = msg;
1592
+ }
1593
+ }
1594
+ this.sendToRelay({
1595
+ type: "relay.forward",
1596
+ userId,
1597
+ inner: innerMsg
1598
+ });
1599
+ }
1600
+ }
1488
1601
  };
1489
1602
  var relayClient = null;
1490
1603
  function startRelayClient(api, relayUrl) {
@@ -1512,6 +1625,9 @@ function startRelayClient(api, relayUrl) {
1512
1625
  process.on("SIGTERM", cleanup);
1513
1626
  process.on("SIGINT", cleanup);
1514
1627
  }
1628
+ function broadcastToUsers(event, payload) {
1629
+ relayClient?.broadcastToUsers(event, payload);
1630
+ }
1515
1631
 
1516
1632
  // src/index.ts
1517
1633
  function squadAppPlugin(api) {
@@ -1523,7 +1639,8 @@ function squadAppPlugin(api) {
1523
1639
  }
1524
1640
  return origRegisterTool(toolDef);
1525
1641
  };
1526
- registerEntityTools(api);
1642
+ const onFsChange = (evt) => broadcastToUsers("fs.change", evt);
1643
+ registerEntityTools(api, onFsChange);
1527
1644
  registerFilesystemTools(api);
1528
1645
  registerVersionMethods(api);
1529
1646
  api.registerGatewayMethod(
@@ -9,7 +9,7 @@
9
9
  "fs.allowedRoots": {
10
10
  "type": "array",
11
11
  "items": { "type": "string" },
12
- "description": "Restrict fs_read/fs_write/fs_list to these directories. Empty or omitted = allow all."
12
+ "description": "Restrict fs_read/fs_write/fs_list/fs_delete to these directories. Empty or omitted = allow all."
13
13
  },
14
14
  "relay.enabled": {
15
15
  "type": "boolean",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squad-openclaw",
3
- "version": "2026.2.1816",
3
+ "version": "2026.2.1901",
4
4
  "description": "Entity registry, filesystem tools, and version management plugin for OpenClaw gateway",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",