@foxden-app/foxclaw 0.5.71 → 0.5.72

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/.env.example CHANGED
@@ -68,6 +68,7 @@ CODEX_CLI_BIN=/absolute/path/to/codex
68
68
  # put the node's contact token first and list peer contact @usernames below.
69
69
  # AUTH_SYNC_ENABLED=true
70
70
  # AUTH_SYNC_KEY=<shared-key-with-at-least-32-bytes>
71
+ # Imported remote auth is not relayed; list every contact bot that must receive this node's auth directly.
71
72
  # AUTH_SYNC_PEERS=@other_node_bot,@third_node_bot
72
73
  # AUTH_SYNC_CLUSTER_ID=my-codex-auth-pool
73
74
  # AUTH_SYNC_NODE_ID=
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable FoxClaw changes are listed here. Each release note is bilingual so GitHub Releases and the npm package are useful to both Chinese and English readers.
4
4
 
5
+ ## 0.5.72 - 2026-07-11
6
+
7
+ ### 中文
8
+ - 完整适配 pnpm 11 隔离全局包布局:升级后从 pnpm 生成的 `foxclaw` shim 读取 `global/v11/<instance>/node_modules/.../dist/main.js` 真实入口,不再把 `pnpm root -g` 误当成包目录。
9
+ - pnpm 管理的 FoxClaw 自更新现在按现有安装布局固定 pnpm major,避免 pnpm 10 安装误选 `PNPM_HOME/bin` 中的 pnpm 11;本次更新显式设置 `minimumReleaseAge=0`,确保刚发布的 `@latest` 不会被 pnpm 11 默认 24 小时发布年龄策略回退到旧版。
10
+
11
+ ### English
12
+ - Fully supports pnpm 11's isolated global package layout. After installation, FoxClaw reads the real `global/v11/<instance>/node_modules/.../dist/main.js` target from pnpm's generated CLI shim instead of treating `pnpm root -g` as the package directory.
13
+ - Self-update now pins the pnpm major to the existing installation layout, preventing a pnpm 10 installation from selecting pnpm 11 under `PNPM_HOME/bin`. The update also sets `minimumReleaseAge=0` so pnpm 11's default 24-hour release-age policy does not resolve `@latest` to an older FoxClaw release.
14
+
5
15
  ## 0.5.71 - 2026-07-11
6
16
 
7
17
  ### 中文
package/dist/update.d.ts CHANGED
@@ -87,5 +87,6 @@ export declare function readPendingClusterUpdateBroadcast(filePath: string): Pen
87
87
  export declare function writePendingClusterUpdateBroadcast(filePath: string, value: PendingClusterUpdateBroadcast): void;
88
88
  export declare function clearPendingClusterUpdateBroadcast(filePath: string): void;
89
89
  export declare function resolveFoxclawEntryPointFromGlobalRoot(globalRoot: string, exists?: (target: string) => boolean): string | null;
90
+ export declare function resolveFoxclawEntryPointFromPnpmHome(pnpmHome: string, exists?: (target: string) => boolean, readText?: (target: string) => string): string | null;
90
91
  export declare function extractReleaseNotes(changelog: string, version: string, locale: AppLocale): string[] | null;
91
92
  export {};
package/dist/update.js CHANGED
@@ -13,7 +13,9 @@ export function inferPnpmHomeFromEntryPoint(entryPoint) {
13
13
  const normalizedEntryPoint = entryPoint.replace(/\\/g, '/');
14
14
  const globalMarker = '/global/';
15
15
  const globalIndex = normalizedEntryPoint.indexOf(globalMarker);
16
- if (globalIndex <= 0 || !normalizedEntryPoint.includes('/.pnpm/')) {
16
+ const pnpm10Layout = normalizedEntryPoint.includes('/.pnpm/');
17
+ const pnpm11Layout = /\/global\/v\d+\/[^/]+\/node_modules\/@foxden-app\/foxclaw\//.test(normalizedEntryPoint);
18
+ if (globalIndex <= 0 || (!pnpm10Layout && !pnpm11Layout)) {
17
19
  return null;
18
20
  }
19
21
  return normalizedEntryPoint.slice(0, globalIndex);
@@ -32,23 +34,6 @@ export function inferPnpmFallbackPackageFromEntryPoint(entryPoint) {
32
34
  export function resolveSelfUpdateInstaller(entryPoint, nodePath = process.execPath, exists = fs.existsSync, env = process.env) {
33
35
  const pnpmHome = inferPnpmHomeFromEntryPoint(entryPoint);
34
36
  if (pnpmHome) {
35
- const commandName = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
36
- const candidates = executableCandidates(commandName, nodePath, env, [
37
- path.join(pnpmHome, commandName),
38
- path.join(pnpmHome, 'bin', commandName),
39
- env.PNPM_HOME?.trim() ? path.join(env.PNPM_HOME.trim(), commandName) : '',
40
- env.PNPM_HOME?.trim() ? path.join(env.PNPM_HOME.trim(), 'bin', commandName) : '',
41
- ]);
42
- const pnpmCommand = candidates.find((candidate) => exists(candidate));
43
- if (pnpmCommand) {
44
- return {
45
- manager: 'pnpm',
46
- command: pnpmCommand,
47
- installArgs: ['add', '--global', PACKAGE_SPEC],
48
- rootArgs: ['root', '--global'],
49
- pnpmHome,
50
- };
51
- }
52
37
  const npmCommandName = process.platform === 'win32' ? 'npm.cmd' : 'npm';
53
38
  const npmCommand = executableCandidates(npmCommandName, nodePath, env)
54
39
  .find((candidate) => exists(candidate)) ?? npmCommandName;
@@ -56,8 +41,8 @@ export function resolveSelfUpdateInstaller(entryPoint, nodePath = process.execPa
56
41
  return {
57
42
  manager: 'pnpm',
58
43
  command: npmCommand,
59
- installArgs: ['exec', '--yes', `--package=${fallbackPackage}`, '--', 'pnpm', 'add', '--global', PACKAGE_SPEC],
60
- rootArgs: ['exec', '--yes', `--package=${fallbackPackage}`, '--', 'pnpm', 'root', '--global'],
44
+ installArgs: ['exec', '--yes', `--package=${fallbackPackage}`, '--', 'pnpm', '--config.minimum-release-age=0', 'add', '--global', PACKAGE_SPEC],
45
+ rootArgs: ['exec', '--yes', `--package=${fallbackPackage}`, '--', 'pnpm', '--config.minimum-release-age=0', 'root', '--global'],
61
46
  pnpmHome,
62
47
  };
63
48
  }
@@ -493,6 +478,29 @@ export function resolveFoxclawEntryPointFromGlobalRoot(globalRoot, exists = fs.e
493
478
  ];
494
479
  return candidates.find(candidate => exists(candidate)) ?? null;
495
480
  }
481
+ export function resolveFoxclawEntryPointFromPnpmHome(pnpmHome, exists = fs.existsSync, readText = (target) => fs.readFileSync(target, 'utf8')) {
482
+ const shimCandidates = [
483
+ path.join(pnpmHome, process.platform === 'win32' ? 'foxclaw.cmd' : 'foxclaw'),
484
+ path.join(pnpmHome, 'bin', process.platform === 'win32' ? 'foxclaw.cmd' : 'foxclaw'),
485
+ ];
486
+ for (const shim of shimCandidates) {
487
+ if (!exists(shim))
488
+ continue;
489
+ let contents = '';
490
+ try {
491
+ contents = readText(shim);
492
+ }
493
+ catch {
494
+ continue;
495
+ }
496
+ const matches = contents.match(/(?:[A-Za-z]:[\\/]|\/)[^\r\n"']*?[\\/]node_modules[\\/]@foxden-app[\\/]foxclaw[\\/]dist[\\/]main\.js/g);
497
+ const entryPoint = matches?.at(-1) ?? null;
498
+ if (entryPoint && exists(entryPoint)) {
499
+ return entryPoint;
500
+ }
501
+ }
502
+ return null;
503
+ }
496
504
  function resolveUpdatedEntryPoint(installer, env) {
497
505
  const result = spawnSync(installer.command, installer.rootArgs, { encoding: 'utf8', env });
498
506
  if (result.error) {
@@ -505,7 +513,10 @@ function resolveUpdatedEntryPoint(installer, env) {
505
513
  if (!globalRoot) {
506
514
  throw new Error(`Could not locate the updated global package root using ${installer.manager}.`);
507
515
  }
508
- const updatedEntryPoint = resolveFoxclawEntryPointFromGlobalRoot(globalRoot);
516
+ const updatedEntryPoint = (installer.pnpmHome
517
+ ? resolveFoxclawEntryPointFromPnpmHome(installer.pnpmHome)
518
+ : null)
519
+ ?? resolveFoxclawEntryPointFromGlobalRoot(globalRoot);
509
520
  if (!updatedEntryPoint) {
510
521
  throw new Error(`Updated FoxClaw entry point was not found below ${globalRoot} (checked direct and node_modules layouts).`);
511
522
  }
@@ -113,6 +113,8 @@ For more peers, separate bot usernames with commas:
113
113
  AUTH_SYNC_PEERS=@foxclaw_node_a_bot,@foxclaw_node_b_bot,@foxclaw_node_c_bot
114
114
  ```
115
115
 
116
+ Auth imported from a remote peer is not automatically forwarded to other peers, which prevents synchronization loops. A star topology therefore guarantees only direct leaf-to-hub synchronization. If auth newly produced on any leaf must reach every node, every contact bot must list all other contact bots in `AUTH_SYNC_PEERS`, with matching reciprocal allowlists on those peers.
117
+
116
118
  Generate a shared key with a password manager or `openssl`:
117
119
 
118
120
  ```bash
@@ -113,6 +113,8 @@ AUTH_AUTO_DELETE_NEEDS_REPAIR=false
113
113
  AUTH_SYNC_PEERS=@foxclaw_node_a_bot,@foxclaw_node_b_bot,@foxclaw_node_c_bot
114
114
  ```
115
115
 
116
+ 远端导入的 auth 不会再次自动转发给本节点的其他 peer,以避免同步环路。因此星形拓扑只保证每个叶子与中心节点直接互通;如果任意叶子节点产生的新 auth 都要到达所有节点,每个联系人 bot 都必须把其他联系人 bot 列入 `AUTH_SYNC_PEERS`,并在对端做对称 allowlist 配置。
117
+
116
118
  建议用密码管理器或 `openssl` 生成共享密钥:
117
119
 
118
120
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.5.71",
3
+ "version": "0.5.72",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",