@alfe.ai/openclaw-sync 0.3.13 → 0.3.15

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.
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;AAkEsB;;;;;AAWA;;;;;;;;;;AAqCtB,UAhDU,YAAA,CAgDuB;EAyjB3B,IAAA,CAAA,GAAA,EAiWL,MAAA,CAAA,EAAA,IAAA;EAAA,IAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OA1Ve,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OAyQQ,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;UAl3Bd,oBAAA,CA05Ba;QAAmB,EAz5BhC,MAy5BgC,CAAA,MAAA,EAAA,OAAA,CAAA;cAAgB,CAAA,EAAA,MAAA;EAAA,QAAA,EAAA,MAAA;UAt5BhD;;UAGA,SAAA;UACA;;WAEC;wDAC6C;;;iBAGvC,gCAAgC;iBAChC,gCAAgC;;;UA0BhC,gBAAA;;;;;;;;;;;;cAyjBX;;;;;gBAOU;kBAyQQ,YAAS;iBAwCV,mBAAmB,mBAAgB"}
1
+ {"version":3,"file":"plugin.d.cts","names":[],"sources":["../src/plugin.ts"],"mappings":";;AAkEA;AAuCA;AAaC;AA8CqB;;;;;AAWA;;;;;;;;AAW2B,iBAxHjC,mBAAA,CAwHiC,KAAA,EAAA;EAAO,MAAA,EAAA,MAAA;EA0BvC,aAAA,CAAA,EAAA,MAAgB;EA6jB3B,cAuWL,CAAA,EAAA,MAAA;EAAA,WAAA,EAAA,OAAA;UAhWe;;AA+QiB,iBA97BjB,wBAAA,CA87BiB,OAAA,EAAA,MAAA,CAAA,EAAA,OAAA;UAn4BvB,YAAA,CA26Ba;MAAmB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;MAAgB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAA,KAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;;UAp6BhD,oBAAA;UACA;;;UAGA;;UAGA,SAAA;UACA;;WAEC;wDAC6C;;;iBAGvC,gCAAgC;iBAChC,gCAAgC;;;UA0BhC,gBAAA;;;;;;;;;;;;cA6jBX;;;;;gBAOU;kBA+QQ,YAAS;iBAwCV,mBAAmB,mBAAgB"}
package/dist/plugin.d.ts CHANGED
@@ -16,6 +16,14 @@
16
16
  *
17
17
  * Registers gateway RPC methods: `sync.now`, `sync.status`.
18
18
  */
19
+ declare function resolveSyncRelayUrl(input: {
20
+ apiUrl: string;
21
+ configuredUrl?: string;
22
+ environmentUrl?: string;
23
+ privateMode: boolean;
24
+ }): string;
25
+ /** Only RFC1918 IPv4 and unique-local IPv6 addresses are valid VPC relay targets. */
26
+ declare function isVpcPrivateRelayAddress(address: string): boolean;
19
27
  interface PluginLogger {
20
28
  info(msg: string): void;
21
29
  warn(msg: string): void;
@@ -61,5 +69,5 @@ declare const plugin: {
61
69
  configure(api: PluginApi, config: SyncPluginConfig): Promise<void>;
62
70
  };
63
71
  //#endregion
64
- export { SyncPluginConfig, plugin as default };
72
+ export { SyncPluginConfig, plugin as default, isVpcPrivateRelayAddress, resolveSyncRelayUrl };
65
73
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;AAkEsB;;;;;AAWA;;;;;;;;;;AAqCtB,UAhDU,YAAA,CAgDuB;EAyjB3B,IAAA,CAAA,GAAA,EAiWL,MAAA,CAAA,EAAA,IAAA;EAAA,IAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OA1Ve,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;OAyQQ,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;UAl3Bd,oBAAA,CA05Ba;QAAmB,EAz5BhC,MAy5BgC,CAAA,MAAA,EAAA,OAAA,CAAA;cAAgB,CAAA,EAAA,MAAA;EAAA,QAAA,EAAA,MAAA;UAt5BhD;;UAGA,SAAA;UACA;;WAEC;wDAC6C;;;iBAGvC,gCAAgC;iBAChC,gCAAgC;;;UA0BhC,gBAAA;;;;;;;;;;;;cAyjBX;;;;;gBAOU;kBAyQQ,YAAS;iBAwCV,mBAAmB,mBAAgB"}
1
+ {"version":3,"file":"plugin.d.ts","names":[],"sources":["../src/plugin.ts"],"mappings":";;AAkEA;AAuCA;AAaC;AA8CqB;;;;;AAWA;;;;;;;;AAW2B,iBAxHjC,mBAAA,CAwHiC,KAAA,EAAA;EAAO,MAAA,EAAA,MAAA;EA0BvC,aAAA,CAAA,EAAA,MAAgB;EA6jB3B,cAuWL,CAAA,EAAA,MAAA;EAAA,WAAA,EAAA,OAAA;UAhWe;;AA+QiB,iBA97BjB,wBAAA,CA87BiB,OAAA,EAAA,MAAA,CAAA,EAAA,OAAA;UAn4BvB,YAAA,CA26Ba;MAAmB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;MAAgB,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAA,KAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,IAAA;;;UAp6BhD,oBAAA;UACA;;;UAGA;;UAGA,SAAA;UACA;;WAEC;wDAC6C;;;iBAGvC,gCAAgC;iBAChC,gCAAgC;;;UA0BhC,gBAAA;;;;;;;;;;;;cA6jBX;;;;;gBAOU;kBA+QQ,YAAS;iBAwCV,mBAAmB,mBAAgB"}
package/dist/plugin.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as plugin } from "./plugin2.js";
2
- export { plugin as default };
1
+ import { n as plugin, r as resolveSyncRelayUrl, t as isVpcPrivateRelayAddress } from "./plugin2.js";
2
+ export { plugin as default, isVpcPrivateRelayAddress, resolveSyncRelayUrl };
package/dist/plugin2.cjs CHANGED
@@ -8,6 +8,8 @@ let node_module = require("node:module");
8
8
  let _alfe_ai_config = require("@alfe.ai/config");
9
9
  let _alfe_ai_openclaw_plugin_kit = require("@alfe.ai/openclaw-plugin-kit");
10
10
  let _alfe_ai_agent_api_client = require("@alfe.ai/agent-api-client");
11
+ let node_dns = require("node:dns");
12
+ let node_net = require("node:net");
11
13
  //#region src/watcher.ts
12
14
  /**
13
15
  * AlfeSync watcher — recursive file watcher with debounce and ignore support.
@@ -447,6 +449,64 @@ const SYNC_RELAY_DEBOUNCE_MS = 500;
447
449
  const SYNC_RELAY_MAX_MESSAGE_BYTES = 64 * 1024;
448
450
  const SYNC_RELAY_HANDSHAKE_TIMEOUT_MS = 1e4;
449
451
  const REMOTE_DELETE_SUPPRESSION_MS = 3e4;
452
+ function resolveSyncRelayUrl(input) {
453
+ if (input.privateMode) {
454
+ const regionalUrl = input.environmentUrl?.trim();
455
+ if (!regionalUrl) throw new Error("ALFE_SYNC_RELAY_WS_URL is required in AWS-private mode");
456
+ let parsed;
457
+ try {
458
+ parsed = new URL(regionalUrl);
459
+ } catch {
460
+ throw new Error("AWS-private sync relay URL must be valid");
461
+ }
462
+ if (parsed.protocol !== "ws:" || !parsed.hostname.endsWith(".local") && !parsed.hostname.endsWith(".sst") || parsed.pathname !== "/ws" || parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") throw new Error("AWS-private sync relay URL must be a canonical VPC-local service URL");
463
+ return parsed.toString();
464
+ }
465
+ const configuredUrl = input.configuredUrl?.trim();
466
+ const environmentUrl = input.environmentUrl?.trim();
467
+ if (configuredUrl && configuredUrl.length > 0) return configuredUrl;
468
+ if (environmentUrl && environmentUrl.length > 0) return environmentUrl;
469
+ return (0, _alfe_ai_config.deriveServiceWsUrl)(input.apiUrl, "sync");
470
+ }
471
+ /** Only RFC1918 IPv4 and unique-local IPv6 addresses are valid VPC relay targets. */
472
+ function isVpcPrivateRelayAddress(address) {
473
+ const family = (0, node_net.isIP)(address);
474
+ if (family === 4) {
475
+ const [first, second] = address.split(".").map(Number);
476
+ return first === 10 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168;
477
+ }
478
+ if (family === 6) {
479
+ const firstHextet = Number.parseInt(address.split(":", 1)[0] ?? "", 16);
480
+ return Number.isFinite(firstHextet) && (firstHextet & 65024) === 64512;
481
+ }
482
+ return false;
483
+ }
484
+ const vpcPrivateRelayLookup = (hostname, options, callback) => {
485
+ (0, node_dns.lookup)(hostname, {
486
+ all: true,
487
+ verbatim: true
488
+ }, (error, addresses) => {
489
+ if (error) {
490
+ callback(error, "", 4);
491
+ return;
492
+ }
493
+ if (addresses.length === 0 || addresses.some(({ address }) => !isVpcPrivateRelayAddress(address))) {
494
+ callback(Object.assign(/* @__PURE__ */ new Error("private sync relay resolved outside the VPC address space"), { code: "EACCES" }), "", 4);
495
+ return;
496
+ }
497
+ const family = typeof options.family === "number" ? options.family : 0;
498
+ const selected = family === 0 ? addresses[0] : addresses.find((candidate) => candidate.family === family);
499
+ if (!selected) {
500
+ callback(Object.assign(/* @__PURE__ */ new Error("private sync relay has no address in the requested family"), { code: "EADDRNOTAVAIL" }), "", family || 4);
501
+ return;
502
+ }
503
+ if (options.all) {
504
+ callback(null, addresses);
505
+ return;
506
+ }
507
+ callback(null, selected.address, selected.family);
508
+ });
509
+ };
450
510
  let client = null;
451
511
  let agentId = null;
452
512
  let syncEngine = null;
@@ -679,10 +739,13 @@ async function connectToSyncRelay(relayUrl, token, agentIdForSubscribe, log, gen
679
739
  try {
680
740
  const { default: WebSocket } = await import("ws");
681
741
  if (!syncRelayActive || generation !== syncRelayGeneration) return null;
742
+ const relayHost = new URL(relayUrl).hostname;
743
+ const privateRelay = relayHost.endsWith(".local") || relayHost.endsWith(".sst");
682
744
  const ws = new WebSocket(relayUrl, {
683
745
  headers: { Authorization: `Bearer ${token}` },
684
746
  handshakeTimeout: SYNC_RELAY_HANDSHAKE_TIMEOUT_MS,
685
- maxPayload: SYNC_RELAY_MAX_MESSAGE_BYTES
747
+ maxPayload: SYNC_RELAY_MAX_MESSAGE_BYTES,
748
+ ...privateRelay ? { lookup: vpcPrivateRelayLookup } : {}
686
749
  });
687
750
  ws.on("open", () => {
688
751
  if (!syncRelayActive || generation !== syncRelayGeneration) {
@@ -897,7 +960,14 @@ const plugin = {
897
960
  log.debug(`Shared sync engine skipped: ${err instanceof Error ? err.message : String(err)}`);
898
961
  }
899
962
  try {
900
- const relayUrl = pluginConfig.syncRelayUrl ?? (0, _alfe_ai_config.deriveServiceWsUrl)(syncCfg.apiUrl, "sync");
963
+ const configuredRelayUrl = pluginConfig.syncRelayUrl?.trim();
964
+ const environmentRelayUrl = process.env.ALFE_SYNC_RELAY_WS_URL?.trim();
965
+ const relayUrl = resolveSyncRelayUrl({
966
+ apiUrl: syncCfg.apiUrl,
967
+ configuredUrl: configuredRelayUrl,
968
+ environmentUrl: environmentRelayUrl,
969
+ privateMode: process.env.ALFE_PRIVATE_DATA_PLANE === "true"
970
+ });
901
971
  syncRelayActive = true;
902
972
  const relayGeneration = ++syncRelayGeneration;
903
973
  syncRelayWs = await connectToSyncRelay(relayUrl, syncCfg.apiKey, registered.agentId, log, relayGeneration);
@@ -1061,12 +1131,24 @@ Object.defineProperty(exports, "createSharedSyncEngine", {
1061
1131
  return createSharedSyncEngine;
1062
1132
  }
1063
1133
  });
1134
+ Object.defineProperty(exports, "isVpcPrivateRelayAddress", {
1135
+ enumerable: true,
1136
+ get: function() {
1137
+ return isVpcPrivateRelayAddress;
1138
+ }
1139
+ });
1064
1140
  Object.defineProperty(exports, "plugin", {
1065
1141
  enumerable: true,
1066
1142
  get: function() {
1067
1143
  return plugin;
1068
1144
  }
1069
1145
  });
1146
+ Object.defineProperty(exports, "resolveSyncRelayUrl", {
1147
+ enumerable: true,
1148
+ get: function() {
1149
+ return resolveSyncRelayUrl;
1150
+ }
1151
+ });
1070
1152
  Object.defineProperty(exports, "startWatcher", {
1071
1153
  enumerable: true,
1072
1154
  get: function() {
package/dist/plugin2.js CHANGED
@@ -8,6 +8,8 @@ import { watch } from "chokidar";
8
8
  import { DEFAULT_SOCKET_PATH, DEFAULT_WORKSPACE_PATH, configExists, deriveServiceWsUrl, resolveConfig } from "@alfe.ai/config";
9
9
  import { connectToDaemon, getActivationKey, guardedStart, resetActivation } from "@alfe.ai/openclaw-plugin-kit";
10
10
  import { AgentApiClient } from "@alfe.ai/agent-api-client";
11
+ import { lookup } from "node:dns";
12
+ import { isIP } from "node:net";
11
13
  //#region src/watcher.ts
12
14
  /**
13
15
  * AlfeSync watcher — recursive file watcher with debounce and ignore support.
@@ -447,6 +449,64 @@ const SYNC_RELAY_DEBOUNCE_MS = 500;
447
449
  const SYNC_RELAY_MAX_MESSAGE_BYTES = 64 * 1024;
448
450
  const SYNC_RELAY_HANDSHAKE_TIMEOUT_MS = 1e4;
449
451
  const REMOTE_DELETE_SUPPRESSION_MS = 3e4;
452
+ function resolveSyncRelayUrl(input) {
453
+ if (input.privateMode) {
454
+ const regionalUrl = input.environmentUrl?.trim();
455
+ if (!regionalUrl) throw new Error("ALFE_SYNC_RELAY_WS_URL is required in AWS-private mode");
456
+ let parsed;
457
+ try {
458
+ parsed = new URL(regionalUrl);
459
+ } catch {
460
+ throw new Error("AWS-private sync relay URL must be valid");
461
+ }
462
+ if (parsed.protocol !== "ws:" || !parsed.hostname.endsWith(".local") && !parsed.hostname.endsWith(".sst") || parsed.pathname !== "/ws" || parsed.username !== "" || parsed.password !== "" || parsed.search !== "" || parsed.hash !== "") throw new Error("AWS-private sync relay URL must be a canonical VPC-local service URL");
463
+ return parsed.toString();
464
+ }
465
+ const configuredUrl = input.configuredUrl?.trim();
466
+ const environmentUrl = input.environmentUrl?.trim();
467
+ if (configuredUrl && configuredUrl.length > 0) return configuredUrl;
468
+ if (environmentUrl && environmentUrl.length > 0) return environmentUrl;
469
+ return deriveServiceWsUrl(input.apiUrl, "sync");
470
+ }
471
+ /** Only RFC1918 IPv4 and unique-local IPv6 addresses are valid VPC relay targets. */
472
+ function isVpcPrivateRelayAddress(address) {
473
+ const family = isIP(address);
474
+ if (family === 4) {
475
+ const [first, second] = address.split(".").map(Number);
476
+ return first === 10 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168;
477
+ }
478
+ if (family === 6) {
479
+ const firstHextet = Number.parseInt(address.split(":", 1)[0] ?? "", 16);
480
+ return Number.isFinite(firstHextet) && (firstHextet & 65024) === 64512;
481
+ }
482
+ return false;
483
+ }
484
+ const vpcPrivateRelayLookup = (hostname, options, callback) => {
485
+ lookup(hostname, {
486
+ all: true,
487
+ verbatim: true
488
+ }, (error, addresses) => {
489
+ if (error) {
490
+ callback(error, "", 4);
491
+ return;
492
+ }
493
+ if (addresses.length === 0 || addresses.some(({ address }) => !isVpcPrivateRelayAddress(address))) {
494
+ callback(Object.assign(/* @__PURE__ */ new Error("private sync relay resolved outside the VPC address space"), { code: "EACCES" }), "", 4);
495
+ return;
496
+ }
497
+ const family = typeof options.family === "number" ? options.family : 0;
498
+ const selected = family === 0 ? addresses[0] : addresses.find((candidate) => candidate.family === family);
499
+ if (!selected) {
500
+ callback(Object.assign(/* @__PURE__ */ new Error("private sync relay has no address in the requested family"), { code: "EADDRNOTAVAIL" }), "", family || 4);
501
+ return;
502
+ }
503
+ if (options.all) {
504
+ callback(null, addresses);
505
+ return;
506
+ }
507
+ callback(null, selected.address, selected.family);
508
+ });
509
+ };
450
510
  let client = null;
451
511
  let agentId = null;
452
512
  let syncEngine = null;
@@ -679,10 +739,13 @@ async function connectToSyncRelay(relayUrl, token, agentIdForSubscribe, log, gen
679
739
  try {
680
740
  const { default: WebSocket } = await import("ws");
681
741
  if (!syncRelayActive || generation !== syncRelayGeneration) return null;
742
+ const relayHost = new URL(relayUrl).hostname;
743
+ const privateRelay = relayHost.endsWith(".local") || relayHost.endsWith(".sst");
682
744
  const ws = new WebSocket(relayUrl, {
683
745
  headers: { Authorization: `Bearer ${token}` },
684
746
  handshakeTimeout: SYNC_RELAY_HANDSHAKE_TIMEOUT_MS,
685
- maxPayload: SYNC_RELAY_MAX_MESSAGE_BYTES
747
+ maxPayload: SYNC_RELAY_MAX_MESSAGE_BYTES,
748
+ ...privateRelay ? { lookup: vpcPrivateRelayLookup } : {}
686
749
  });
687
750
  ws.on("open", () => {
688
751
  if (!syncRelayActive || generation !== syncRelayGeneration) {
@@ -897,7 +960,14 @@ const plugin = {
897
960
  log.debug(`Shared sync engine skipped: ${err instanceof Error ? err.message : String(err)}`);
898
961
  }
899
962
  try {
900
- const relayUrl = pluginConfig.syncRelayUrl ?? deriveServiceWsUrl(syncCfg.apiUrl, "sync");
963
+ const configuredRelayUrl = pluginConfig.syncRelayUrl?.trim();
964
+ const environmentRelayUrl = process.env.ALFE_SYNC_RELAY_WS_URL?.trim();
965
+ const relayUrl = resolveSyncRelayUrl({
966
+ apiUrl: syncCfg.apiUrl,
967
+ configuredUrl: configuredRelayUrl,
968
+ environmentUrl: environmentRelayUrl,
969
+ privateMode: process.env.ALFE_PRIVATE_DATA_PLANE === "true"
970
+ });
901
971
  syncRelayActive = true;
902
972
  const relayGeneration = ++syncRelayGeneration;
903
973
  syncRelayWs = await connectToSyncRelay(relayUrl, syncCfg.apiKey, registered.agentId, log, relayGeneration);
@@ -1055,6 +1125,6 @@ const plugin = {
1055
1125
  }
1056
1126
  };
1057
1127
  //#endregion
1058
- export { createSharedSyncEngine as n, startWatcher as r, plugin as t };
1128
+ export { startWatcher as a, createSharedSyncEngine as i, plugin as n, resolveSyncRelayUrl as r, isVpcPrivateRelayAddress as t };
1059
1129
 
1060
1130
  //# sourceMappingURL=plugin2.js.map