sealclaw 2026.9.2-beta.13 → 2026.9.2-beta.14

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.
@@ -14,6 +14,22 @@ import { Transform } from "node:stream";
14
14
  import { pipeline } from "node:stream/promises";
15
15
  import { createGunzip } from "node:zlib";
16
16
  import * as tar from "tar";
17
+ //#region src/backup/platform.ts
18
+ function backupPath(platform) {
19
+ if (platform === "win32") return path.win32;
20
+ if (platform === "darwin" || platform === "linux") return path.posix;
21
+ throw new Error("不支持此备份的操作系统");
22
+ }
23
+ function isBackupAbsolute(value, platform) {
24
+ if (value.includes("\0")) return false;
25
+ if (platform === "win32") return /^(?:[a-z]:[\\/]|[\\/]{2}[^\\/]+[\\/][^\\/]+(?:[\\/]|$))/iu.test(value) && !/^[\\/]{2}[?.][\\/]/u.test(value);
26
+ return backupPath(platform).isAbsolute(value);
27
+ }
28
+ function restoredFileMode(mode, sourcePlatform, targetPlatform, prefix) {
29
+ if (sourcePlatform === "win32" && targetPlatform !== "win32") return prefix[0] === 35 && prefix[1] === 33 ? 448 : 384;
30
+ return mode & 448;
31
+ }
32
+ //#endregion
17
33
  //#region src/backup/data.ts
18
34
  const PRESERVED_STATE_NAMES = new Set([
19
35
  "backups",
@@ -148,14 +164,22 @@ const PATH_FIELDS = new Set([
148
164
  "outputRoot",
149
165
  "rootDir"
150
166
  ]);
151
- function remapDataPaths(value, mappings) {
152
- if (Array.isArray(value)) return value.map((entry) => remapDataPaths(entry, mappings));
167
+ function remapDataPaths(value, mappings, sourcePlatform = process.platform, targetPlatform = process.platform) {
168
+ if (Array.isArray(value)) return value.map((entry) => remapDataPaths(entry, mappings, sourcePlatform, targetPlatform));
153
169
  if (!value || typeof value !== "object") return value;
154
170
  const result = Object.create(null);
155
- for (const [key, entry] of Object.entries(value)) if (PATH_FIELDS.has(key) && typeof entry === "string" && path.isAbsolute(entry)) {
156
- const mapping = [...mappings].sort((left, right) => right.source.length - left.source.length).find((item) => inside(item.source, entry));
157
- result[key] = mapping ? path.join(mapping.target, path.relative(mapping.source, entry)) : entry;
158
- } else result[key] = remapDataPaths(entry, mappings);
171
+ for (const [key, entry] of Object.entries(value)) if (PATH_FIELDS.has(key) && typeof entry === "string" && isBackupAbsolute(entry, sourcePlatform)) {
172
+ const sourcePath = backupPath(sourcePlatform);
173
+ const targetPath = backupPath(targetPlatform);
174
+ const mapping = [...mappings].sort((left, right) => right.source.length - left.source.length).find((item) => {
175
+ const relative = sourcePath.relative(item.source, entry);
176
+ return !sourcePath.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${sourcePath.sep}`);
177
+ });
178
+ if (mapping) {
179
+ const relative = sourcePath.relative(mapping.source, entry).split(sourcePath.sep).join("/");
180
+ result[key] = relative ? targetPath.join(mapping.target, ...safeRelative(relative).split("/")) : mapping.target;
181
+ } else result[key] = entry;
182
+ } else result[key] = remapDataPaths(entry, mappings, sourcePlatform, targetPlatform);
159
183
  return result;
160
184
  }
161
185
  //#endregion
@@ -163,13 +187,14 @@ function remapDataPaths(value, mappings) {
163
187
  function validateManifest(raw) {
164
188
  const value = object(raw);
165
189
  if (value.format !== "sealseek-backup" || value.formatVersion !== 2 || value.dataSchemaVersion !== 1) throw new Error("不支持此备份格式或版本");
166
- if (value.platform !== process.platform) throw new Error("目前仅支持同操作系统的备份迁移");
190
+ if (typeof value.platform !== "string") throw new Error("备份操作系统无效");
191
+ backupPath(value.platform);
167
192
  if (typeof value.id !== "string" || !/^[a-f0-9-]{36}$/u.test(value.id) || typeof value.createdAt !== "string" || !Number.isFinite(Date.parse(value.createdAt)) || typeof value.runtimeVersion !== "string") throw new Error("备份标识或时间无效");
168
193
  if (!Array.isArray(value.roots) || value.roots.length > 1e3 || !Array.isArray(value.entries) || value.entries.length > BACKUP_LIMITS.entries) throw new Error("备份数据清单无效或超限");
169
194
  const roots = /* @__PURE__ */ new Set();
170
195
  for (const rawRoot of value.roots) {
171
196
  const root = object(rawRoot);
172
- if (typeof root.id !== "string" || !/^(state|root-\d+)$/u.test(root.id) || roots.has(root.id) || typeof root.source !== "string" || !path.isAbsolute(root.source) || ![
197
+ if (typeof root.id !== "string" || !/^(state|root-\d+)$/u.test(root.id) || roots.has(root.id) || typeof root.source !== "string" || !isBackupAbsolute(root.source, value.platform) || ![
173
198
  "state",
174
199
  "workspace",
175
200
  "knowledge",
@@ -511,7 +536,11 @@ async function prepareRestore(context, source) {
511
536
  createdAt: manifest.createdAt,
512
537
  totalBytes: manifest.totalBytes,
513
538
  targets,
514
- warnings: manifest.warnings
539
+ warnings: [...manifest.warnings, ...manifest.platform !== process.platform ? [
540
+ "这是跨操作系统还原:工作区、会话索引和知识空间中的受支持路径将自动转换为本机路径。",
541
+ "平台专用程序、插件依赖及未包含在备份中的外部路径需要在本机重新安装或配置;历史消息中的路径原文保留。",
542
+ ...manifest.platform === "win32" && process.platform !== "win32" ? ["Windows 文件将按本机私有权限恢复;以 #! 开头的脚本会获得执行权限,脚本解释器与换行格式请按本机环境确认。"] : []
543
+ ] : []]
515
544
  };
516
545
  await writeJson(path.join(directory, "plan.json"), plan);
517
546
  await writeJson(path.join(directory, "manifest.json"), manifest);
@@ -541,7 +570,7 @@ async function transformStaged(manifest, staged, extracted, stateDir, id) {
541
570
  for (const entry of files) {
542
571
  const relative = entry.path.replace(/^payload\/state\//u, "");
543
572
  const target = entry.path.startsWith("payload/state/") ? path.join(staged, relative) : path.join(extracted, entry.path);
544
- const data = object(remapDataPaths(await readJson(target), mappings));
573
+ const data = object(remapDataPaths(await readJson(target), mappings, manifest.platform));
545
574
  if (entry.path === `payload/state/${manifest.configRelativePath}`) {
546
575
  const gateway = data.gateway ? object(data.gateway) : {};
547
576
  delete gateway.auth;
@@ -601,12 +630,14 @@ async function restoreBackup(context, id) {
601
630
  const results = await Promise.allSettled(manifest.entries.slice(offset, offset + 4).map(async (entry) => {
602
631
  if (entry.kind === "file") {
603
632
  const handle = await fs$1.open(path.join(extracted, entry.path), "r+");
633
+ const prefix = Buffer.alloc(2);
604
634
  try {
635
+ if (manifest.platform === "win32" && process.platform !== "win32") await handle.read(prefix, 0, prefix.length, 0);
605
636
  await handle.sync();
606
637
  } finally {
607
638
  await handle.close();
608
639
  }
609
- await fs$1.chmod(path.join(extracted, entry.path), entry.mode & 448);
640
+ await fs$1.chmod(path.join(extracted, entry.path), restoredFileMode(entry.mode, manifest.platform, process.platform, prefix));
610
641
  }
611
642
  completedBytes += entry.size;
612
643
  context.onProgress?.({
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2026.9.2-beta.13",
2
+ "version": "2026.9.2-beta.14",
3
3
  "commit": "d0e6a1d92e0315ac0bd313c988870dd95b448241",
4
- "builtAt": "2026-09-10T04:30:43.867Z"
4
+ "builtAt": "2026-09-10T06:56:30.549Z"
5
5
  }
@@ -17,5 +17,5 @@
17
17
  ],
18
18
  "channelCatalogSignature": "701dd1d3ff65f80e219795873d85a623b7af5361",
19
19
  "rootHelpBundleSignature": "446fa08060c9dee9c878286833666b974b7dadf1",
20
- "rootHelpText": "\n🦞 SealClaw 2026.9.2-beta.13 (d0e6a1d)\n I'm not saying your workflow is chaotic... I'm just bringing a linter and a helmet.\n\nUsage: openclaw [options] [command]\n\nOptions:\n --container <name> Run the CLI inside a running Podman/Docker container\n named <name> (default: env OPENCLAW_CONTAINER)\n --dev Dev profile: isolate state under ~/.openclaw-dev, default\n gateway port 19001, and shift derived ports\n (browser/canvas)\n -h, --help Display help for command\n --log-level <level> Global log level override for file + console\n (silent|fatal|error|warn|info|debug|trace)\n --no-color Disable ANSI colors\n --profile <name> Use a named profile (isolates\n OPENCLAW_STATE_DIR/OPENCLAW_CONFIG_PATH under\n ~/.openclaw-<name>)\n -V, --version output the version number\n\nCommands:\n Hint: commands suffixed with * have subcommands. Run <command> --help for details.\n acp * Agent Control Protocol tools\n agent Run one agent turn via the Gateway\n agents * Manage isolated agents (workspaces, auth, routing)\n approvals * Manage exec approvals (gateway or node host)\n backup * Create and verify local backup archives for OpenClaw\n state\n capability * Run provider-backed inference commands (fallback alias:\n infer)\n channels * Manage connected chat channels (Telegram, Discord, etc.)\n clawbot * Legacy clawbot command aliases\n commitments * List and manage inferred follow-up commitments\n completion Generate shell completion script\n config * Non-interactive config helpers\n (get/set/unset/file/validate). Default: starts guided\n setup.\n configure Interactive configuration for credentials, channels,\n gateway, and agent defaults\n crestodian Run a ring-zero setup or repair request\n cron * Manage cron jobs via the Gateway scheduler\n daemon * Gateway service (legacy alias)\n dashboard Open the Control UI with your current token\n devices * Device pairing + token management\n directory * Lookup contact and group IDs (self, peers, groups) for\n supported chat channels\n dns * DNS helpers for wide-area discovery (Tailscale + CoreDNS)\n docs Search the live OpenClaw docs\n doctor Health checks + quick fixes for the gateway and channels\n exec-policy * Show or synchronize requested exec policy with host\n approvals\n gateway * Run, inspect, and query the WebSocket Gateway\n health Fetch health from the running gateway\n help Display help for command\n hooks * Manage internal agent hooks\n infer * Run provider-backed inference commands\n logs Tail gateway file logs via RPC\n mcp * Manage OpenClaw MCP config and channel bridge\n memory Search, inspect, and reindex memory files\n message * Send, read, and manage messages\n migrate * Import state from another agent system\n models * Discover, scan, and configure models\n node * Run and manage the headless node host service\n nodes * Manage gateway-owned node pairing and node commands\n officecli-cli Install and run OfficeCLI\n onboard Interactive onboarding for gateway, workspace, and skills\n pairing * Secure DM pairing (approve inbound requests)\n plugins * Manage OpenClaw plugins\n proxy * Run the OpenClaw debug proxy and inspect captured traffic\n qr Generate mobile pairing QR/setup code\n reset Reset local config/state (keeps the CLI installed)\n sandbox * Manage sandbox containers for agent isolation\n secrets * Secrets runtime reload controls\n security * Security tools and local config audits\n sessions * List stored conversation sessions\n setup Initialize local config and agent workspace\n skills * List and inspect available skills\n status Show channel health and recent session recipients\n system * System events, heartbeat, and presence\n tasks * Inspect durable background task state\n uninstall Uninstall the gateway service + local data (CLI remains)\n update * Update OpenClaw and inspect update channel status\n webhooks * Webhook helpers and integrations\n\nExamples:\n openclaw models --help\n Show detailed help for the models command.\n openclaw channels login --verbose\n Link personal WhatsApp Web and show QR + connection logs.\n openclaw message send --target +15555550123 --message \"Hi\" --json\n Send via your web session and print JSON result.\n openclaw gateway --port 18789\n Run the WebSocket Gateway locally.\n openclaw --dev gateway\n Run a dev Gateway (isolated state/config) on ws://127.0.0.1:19001.\n openclaw gateway --force\n Kill anything bound to the default gateway port, then start it.\n openclaw gateway ...\n Gateway control via WebSocket.\n openclaw agent --to +15555550123 --message \"Run summary\" --deliver\n Talk directly to the agent using the Gateway; optionally send the WhatsApp reply.\n openclaw message send --channel telegram --target @mychat --message \"Hi\"\n Send via your Telegram bot.\n\nDocs: https://docs.openclaw.ai/cli\n\n"
20
+ "rootHelpText": "\n🦞 SealClaw 2026.9.2-beta.14 (d0e6a1d) — The only crab in your contacts you actually want to hear from. 🦞\n\nUsage: openclaw [options] [command]\n\nOptions:\n --container <name> Run the CLI inside a running Podman/Docker container\n named <name> (default: env OPENCLAW_CONTAINER)\n --dev Dev profile: isolate state under ~/.openclaw-dev, default\n gateway port 19001, and shift derived ports\n (browser/canvas)\n -h, --help Display help for command\n --log-level <level> Global log level override for file + console\n (silent|fatal|error|warn|info|debug|trace)\n --no-color Disable ANSI colors\n --profile <name> Use a named profile (isolates\n OPENCLAW_STATE_DIR/OPENCLAW_CONFIG_PATH under\n ~/.openclaw-<name>)\n -V, --version output the version number\n\nCommands:\n Hint: commands suffixed with * have subcommands. Run <command> --help for details.\n acp * Agent Control Protocol tools\n agent Run one agent turn via the Gateway\n agents * Manage isolated agents (workspaces, auth, routing)\n approvals * Manage exec approvals (gateway or node host)\n backup * Create and verify local backup archives for OpenClaw\n state\n capability * Run provider-backed inference commands (fallback alias:\n infer)\n channels * Manage connected chat channels (Telegram, Discord, etc.)\n clawbot * Legacy clawbot command aliases\n commitments * List and manage inferred follow-up commitments\n completion Generate shell completion script\n config * Non-interactive config helpers\n (get/set/unset/file/validate). Default: starts guided\n setup.\n configure Interactive configuration for credentials, channels,\n gateway, and agent defaults\n crestodian Run a ring-zero setup or repair request\n cron * Manage cron jobs via the Gateway scheduler\n daemon * Gateway service (legacy alias)\n dashboard Open the Control UI with your current token\n devices * Device pairing + token management\n directory * Lookup contact and group IDs (self, peers, groups) for\n supported chat channels\n dns * DNS helpers for wide-area discovery (Tailscale + CoreDNS)\n docs Search the live OpenClaw docs\n doctor Health checks + quick fixes for the gateway and channels\n exec-policy * Show or synchronize requested exec policy with host\n approvals\n gateway * Run, inspect, and query the WebSocket Gateway\n health Fetch health from the running gateway\n help Display help for command\n hooks * Manage internal agent hooks\n infer * Run provider-backed inference commands\n logs Tail gateway file logs via RPC\n mcp * Manage OpenClaw MCP config and channel bridge\n memory Search, inspect, and reindex memory files\n message * Send, read, and manage messages\n migrate * Import state from another agent system\n models * Discover, scan, and configure models\n node * Run and manage the headless node host service\n nodes * Manage gateway-owned node pairing and node commands\n officecli-cli Install and run OfficeCLI\n onboard Interactive onboarding for gateway, workspace, and skills\n pairing * Secure DM pairing (approve inbound requests)\n plugins * Manage OpenClaw plugins\n proxy * Run the OpenClaw debug proxy and inspect captured traffic\n qr Generate mobile pairing QR/setup code\n reset Reset local config/state (keeps the CLI installed)\n sandbox * Manage sandbox containers for agent isolation\n secrets * Secrets runtime reload controls\n security * Security tools and local config audits\n sessions * List stored conversation sessions\n setup Initialize local config and agent workspace\n skills * List and inspect available skills\n status Show channel health and recent session recipients\n system * System events, heartbeat, and presence\n tasks * Inspect durable background task state\n uninstall Uninstall the gateway service + local data (CLI remains)\n update * Update OpenClaw and inspect update channel status\n webhooks * Webhook helpers and integrations\n\nExamples:\n openclaw models --help\n Show detailed help for the models command.\n openclaw channels login --verbose\n Link personal WhatsApp Web and show QR + connection logs.\n openclaw message send --target +15555550123 --message \"Hi\" --json\n Send via your web session and print JSON result.\n openclaw gateway --port 18789\n Run the WebSocket Gateway locally.\n openclaw --dev gateway\n Run a dev Gateway (isolated state/config) on ws://127.0.0.1:19001.\n openclaw gateway --force\n Kill anything bound to the default gateway port, then start it.\n openclaw gateway ...\n Gateway control via WebSocket.\n openclaw agent --to +15555550123 --message \"Run summary\" --deliver\n Talk directly to the agent using the Gateway; optionally send the WhatsApp reply.\n openclaw message send --channel telegram --target @mychat --message \"Hi\"\n Send via your Telegram bot.\n\nDocs: https://docs.openclaw.ai/cli\n\n"
21
21
  }
@@ -1 +1 @@
1
- 2026-09-10T04:30:42.843Z
1
+ 2026-09-10T06:56:29.390Z
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sealclaw",
3
- "version": "2026.9.2-beta.13",
3
+ "version": "2026.9.2-beta.14",
4
4
  "description": "Multi-channel AI gateway with extensible messaging integrations",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/openclaw/openclaw#readme",