polygram 0.13.0 → 0.13.2
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/lib/config-scope.js +19 -1
- package/lib/handlers/download.js +12 -3
- package/package.json +1 -1
- package/polygram.js +7 -5
package/lib/config-scope.js
CHANGED
|
@@ -46,4 +46,22 @@ function filterConfigToBot(config, botName) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the active bot's effective config: the per-bot block
|
|
51
|
+
* `config.bots[botName]` layered OVER the shared top-level `bot` block, so
|
|
52
|
+
* shared fields (e.g. `apiRoot`) set once at the top level survive, and any
|
|
53
|
+
* per-bot field of the same name wins.
|
|
54
|
+
*
|
|
55
|
+
* Without this merge, a plain `config.bot = config.bots[botName]` silently
|
|
56
|
+
* DROPS every top-level shared field. That orphaned `apiRoot` and ran both VPS
|
|
57
|
+
* bots on cloud Telegram (20/50) instead of the 2 GB local Bot API server for
|
|
58
|
+
* weeks (discovered 2026-06-16) — createBot reads `config.bot.apiRoot` after
|
|
59
|
+
* the alias, so the top-level value never reached it.
|
|
60
|
+
*/
|
|
61
|
+
function activeBotConfig(config, botName) {
|
|
62
|
+
const top = (config && config.bot) || {};
|
|
63
|
+
const perBot = (config && config.bots && config.bots[botName]) || {};
|
|
64
|
+
return { ...top, ...perBot };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { parseBotArg, parseDbArg, filterConfigToBot, activeBotConfig };
|
package/lib/handlers/download.js
CHANGED
|
@@ -106,10 +106,19 @@ function createDownloadAttachments({
|
|
|
106
106
|
} catch (e) {
|
|
107
107
|
if (e.code === 'EEXIST') {
|
|
108
108
|
logger.log?.(`[attach] ${chatId} ← ${att.kind} ${safeName} (race: already on disk)`);
|
|
109
|
-
} else if (e.code === 'EXDEV') {
|
|
110
|
-
fs.copyFileSync(fileInfo.file_path, localPath); // cross-device fallback
|
|
111
109
|
} else {
|
|
112
|
-
|
|
110
|
+
// A hard link can fail even when a byte copy succeeds: EXDEV (the
|
|
111
|
+
// bot-api data dir is a different device/overlay than the inbox) or
|
|
112
|
+
// EPERM (the volume's filesystem refuses a cross-fs / cross-owner
|
|
113
|
+
// hard link — the local Bot API container writes files as a
|
|
114
|
+
// different user, e.g. `messagebus`, and polygram reads them via
|
|
115
|
+
// group membership). Copy works whenever we can READ the source
|
|
116
|
+
// (group perm) + WRITE the inbox; if the source read is genuinely
|
|
117
|
+
// denied, copyFileSync throws EACCES and the outer handler marks
|
|
118
|
+
// the attachment failed. Falling back on EXDEV only (the old
|
|
119
|
+
// behaviour) left EPERM uncaught → every inbound file failed once
|
|
120
|
+
// the local Bot API server was a Docker volume (2026-06-16).
|
|
121
|
+
fs.copyFileSync(fileInfo.file_path, localPath);
|
|
113
122
|
}
|
|
114
123
|
}
|
|
115
124
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polygram",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "Telegram daemon for Claude Code that preserves the OpenClaw per-chat session model. Migration path for OpenClaw users moving to Claude Code.",
|
|
5
5
|
"main": "lib/ipc/client.js",
|
|
6
6
|
"bin": {
|
package/polygram.js
CHANGED
|
@@ -88,7 +88,7 @@ const agentLoader = require('./lib/agents/loader');
|
|
|
88
88
|
const { createSender } = require('./lib/telegram/api');
|
|
89
89
|
const { createAsyncLock } = require('./lib/async-lock');
|
|
90
90
|
const { sweepInbox } = require('./lib/db/inbox');
|
|
91
|
-
const { parseBotArg, parseDbArg, filterConfigToBot } = require('./lib/config-scope');
|
|
91
|
+
const { parseBotArg, parseDbArg, filterConfigToBot, activeBotConfig } = require('./lib/config-scope');
|
|
92
92
|
const { createStore: createPairingsStore, parseTtl: parsePairingTtl } = require('./lib/db/pairings');
|
|
93
93
|
const { transcribe: transcribeVoice, isVoiceAttachment } = require('./lib/telegram/voice');
|
|
94
94
|
const { createStreamer } = require('./lib/telegram/streamer');
|
|
@@ -2136,10 +2136,12 @@ async function main() {
|
|
|
2136
2136
|
}
|
|
2137
2137
|
try {
|
|
2138
2138
|
config = filterConfigToBot(config, BOT_NAME);
|
|
2139
|
-
// Convenience: config.bot is the current bot's config
|
|
2140
|
-
//
|
|
2141
|
-
//
|
|
2142
|
-
|
|
2139
|
+
// Convenience: config.bot is the current bot's EFFECTIVE config — the
|
|
2140
|
+
// per-bot block layered over the shared top-level `bot` block (so shared
|
|
2141
|
+
// fields like apiRoot survive; per-bot fields win). A plain
|
|
2142
|
+
// `= config.bots[BOT_NAME]` silently dropped top-level shared fields and
|
|
2143
|
+
// orphaned apiRoot (both bots ran on cloud, not the 2GB local server).
|
|
2144
|
+
config.bot = activeBotConfig(config, BOT_NAME);
|
|
2143
2145
|
} catch (err) {
|
|
2144
2146
|
console.error(`[fatal] ${err.message}`);
|
|
2145
2147
|
process.exit(2);
|