@foxden-app/foxclaw 0.6.4 → 0.6.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
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.6.6 - 2026-08-10
6
+
7
+ ### 中文
8
+ - Telegram 远端初始化从断网中恢复后会立即刷新桥接状态,使 `/status` 和运行时状态继续显示 bot 用户名,而不是停留在离线启动时的空值。
9
+
10
+ ### English
11
+ - Refreshes bridge status as soon as Telegram remote initialization recovers, so `/status` and runtime state show the bot username instead of retaining the empty offline-start value.
12
+
13
+ ## 0.6.5 - 2026-08-10
14
+
15
+ ### 中文
16
+ - Telegram bot 身份现在直接从标准 token 的数字前缀获得,FoxClaw 启动不再依赖远端 `getMe` 成功;断网重启时服务会保持运行,也不会因 systemd 连续重试而进入 `Start request repeated too quickly`。
17
+ - `getMe`、命令菜单注册和消息轮询统一在后台恢复循环中执行。Telegram 恢复后会自动完成初始化并继续收发消息,无需人工重启;Telegram 可用而 Codex 上游不可用时,既有桥接错误仍会正常发回聊天。
18
+
19
+ ### English
20
+ - Telegram bot identity is now derived from the numeric prefix of a standard token, so FoxClaw startup no longer depends on a successful remote `getMe` call. The service stays alive across offline restarts instead of exhausting systemd retries with `Start request repeated too quickly`.
21
+ - `getMe`, command registration, and update polling now recover through the same background loop. Telegram automatically resumes without a manual restart, while existing bridge errors remain chat-visible when Telegram is reachable but the Codex upstream is unavailable.
22
+
5
23
  ## 0.6.4 - 2026-08-09
6
24
 
7
25
  ### 中文
@@ -174,6 +174,10 @@ export class BridgeSessionCore {
174
174
  }
175
175
  /** Wire Telegram inbound events. Call before {@link startCodexApp}. */
176
176
  registerTelegramInboundHandlers() {
177
+ this.bot.on('remoteReady', () => {
178
+ this.botUsername = this.bot.username;
179
+ this.updateStatus();
180
+ });
177
181
  this.bot.on('text', (event) => {
178
182
  void this.withLock(event.scopeId, async () => this.handleText(event)).catch((error) => {
179
183
  void this.handleAsyncError('telegram.text', error, event.scopeId);
@@ -103,3 +103,4 @@ export declare class TelegramGateway extends EventEmitter {
103
103
  private handleUpdate;
104
104
  private isAllowedChat;
105
105
  }
106
+ export declare function parseTelegramBotId(botToken: string): number | null;
@@ -27,7 +27,10 @@ export class TelegramGateway extends EventEmitter {
27
27
  this.logger = logger;
28
28
  this.namespacedScopes = namespacedScopes;
29
29
  this.commandProvider = commandProvider;
30
- this.botKey = `telegram:${crypto.createHash('sha256').update(this.botToken).digest('hex').slice(0, 8)}`;
30
+ const tokenHash = crypto.createHash('sha256').update(this.botToken).digest('hex').slice(0, 8);
31
+ const tokenBotId = parseTelegramBotId(this.botToken);
32
+ this.botUserId = tokenBotId;
33
+ this.botKey = tokenBotId === null ? `telegram:${tokenHash}` : `telegram:bot${tokenBotId}`;
31
34
  }
32
35
  get username() {
33
36
  return this.botUsername;
@@ -36,6 +39,8 @@ export class TelegramGateway extends EventEmitter {
36
39
  return this.botUserId === null ? null : `bot${this.botUserId}`;
37
40
  }
38
41
  async initializeIdentity() {
42
+ if (this.identity)
43
+ return this.identity;
39
44
  await this.resolveBotIdentity(true);
40
45
  return this.identity;
41
46
  }
@@ -43,10 +48,6 @@ export class TelegramGateway extends EventEmitter {
43
48
  if (this.running)
44
49
  return;
45
50
  this.running = true;
46
- if (this.botUserId === null) {
47
- await this.resolveBotIdentity(this.namespacedScopes);
48
- }
49
- await this.registerCommands();
50
51
  void this.pollLoop();
51
52
  }
52
53
  stop() {
@@ -238,8 +239,17 @@ export class TelegramGateway extends EventEmitter {
238
239
  });
239
240
  }
240
241
  async pollLoop() {
242
+ let remoteInitialized = false;
241
243
  while (this.running) {
242
244
  try {
245
+ if (!remoteInitialized) {
246
+ await this.resolveBotIdentity(true);
247
+ await this.registerCommands();
248
+ remoteInitialized = true;
249
+ this.emit('remoteReady');
250
+ if (!this.running)
251
+ return;
252
+ }
243
253
  const offset = this.store.getTelegramOffset(this.botKey) + 1;
244
254
  const result = await callTelegramApi(this.botToken, 'getUpdates', {
245
255
  timeout: Math.max(1, Math.floor(this.pollIntervalMs / 1000)),
@@ -340,6 +350,13 @@ export class TelegramGateway extends EventEmitter {
340
350
  function sleep(ms) {
341
351
  return new Promise(resolve => setTimeout(resolve, ms));
342
352
  }
353
+ export function parseTelegramBotId(botToken) {
354
+ const match = /^(\d+):/.exec(botToken.trim());
355
+ if (!match)
356
+ return null;
357
+ const botId = Number(match[1]);
358
+ return Number.isSafeInteger(botId) && botId > 0 ? botId : null;
359
+ }
343
360
  function extractAttachments(message) {
344
361
  const attachments = [];
345
362
  const largestPhoto = pickLargestPhoto(message.photo ?? []);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "Foxden local execution claw for controlling Codex and OpenCode from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",