@alfe.ai/openclaw-telegram 0.0.1

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/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # `@alfe.ai/openclaw-telegram`
2
+
3
+ Local Telegram user-session access for an Alfe/OpenClaw agent. It uses
4
+ Telethon/MTProto, so it can see the same dialog list as the signed-in Telegram
5
+ account instead of the restricted Bot API view.
6
+
7
+ The Telegram device session never leaves the agent machine. After enabling the
8
+ Telegram Connection in Alfe, run:
9
+
10
+ ```bash
11
+ npx -y @alfe.ai/openclaw-telegram@0.0.1 login --agent-id <agent-id>
12
+ ```
13
+
14
+ If the package binary is already on `PATH`, `alfe-telegram login` is the
15
+ equivalent shorthand. The dashboard supplies the agent-specific command; when
16
+ `--agent-id` is omitted, the CLI resolves it from the local agent token.
17
+
18
+ The command creates an isolated local Python runtime, asks for the API ID/hash
19
+ from `my.telegram.org`, then completes phone-code and optional 2FA login. It
20
+ does not print the resulting session.
21
+
22
+ Agent tools:
23
+
24
+ - `telegram_status`
25
+ - `telegram_list_chats`
26
+ - `telegram_list_subscriptions`
27
+ - `telegram_subscribe`
28
+ - `telegram_unsubscribe`
29
+
30
+ Subscriptions are explicit and read-only toward Telegram. Incoming text and
31
+ captions can trigger agent turns; generated responses are not posted back.
32
+ Only subscribe where you have permission to process the messages, and follow
33
+ Telegram's API terms.
34
+
35
+ Removing the Alfe Connection stops and uninstalls the listener but deliberately
36
+ does not erase a logged-in device during an ordinary reinstall. Before retiring
37
+ the machine, revoke the device session and remove its local state with:
38
+
39
+ ```bash
40
+ npx -y @alfe.ai/openclaw-telegram@0.0.1 logout --agent-id <agent-id>
41
+ ```
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
3
+ import { homedir } from 'node:os'
4
+ import { dirname, join } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { spawnSync } from 'node:child_process'
7
+ import { AgentApiClient } from '@alfe.ai/agent-api-client'
8
+ import { resolveConfig } from '@alfe.ai/config'
9
+
10
+ const TELETHON_VERSION = '1.44.0'
11
+ const MAX_AGENT_ID_CHARS = 256
12
+ const SAFE_AGENT_ID = /^[A-Za-z0-9_-]+$/u
13
+ const packageDir = dirname(dirname(fileURLToPath(import.meta.url)))
14
+ const bridgeScript = join(packageDir, 'python', 'bridge.py')
15
+
16
+ function privateEnv() {
17
+ const result = {}
18
+ for (const key of ['HOME', 'PATH', 'LANG', 'LC_ALL', 'TMPDIR', 'SYSTEMROOT', 'WINDIR']) {
19
+ if (process.env[key] !== undefined) result[key] = process.env[key]
20
+ }
21
+ result.PYTHONUNBUFFERED = '1'
22
+ return result
23
+ }
24
+
25
+ function requireAgentId(value) {
26
+ if (
27
+ typeof value !== 'string'
28
+ || value.length === 0
29
+ || value.length > MAX_AGENT_ID_CHARS
30
+ || !SAFE_AGENT_ID.test(value)
31
+ ) throw new Error('Invalid agent ID')
32
+ return value
33
+ }
34
+
35
+ async function resolveAgentId(explicitAgentId) {
36
+ if (explicitAgentId) return requireAgentId(explicitAgentId)
37
+ try {
38
+ const config = resolveConfig()
39
+ const identity = await new AgentApiClient({ apiKey: config.apiKey, apiUrl: config.apiUrl }).whoami()
40
+ return requireAgentId(identity.agentId)
41
+ } catch {
42
+ throw new Error('Could not resolve this agent identity. Pass --agent-id from the dashboard setup command.')
43
+ }
44
+ }
45
+
46
+ function localPaths(agentId) {
47
+ const stateDir = join(homedir(), '.alfe', 'agents', requireAgentId(agentId), 'telegram')
48
+ const runtimeDir = join(stateDir, 'runtime')
49
+ return {
50
+ stateDir,
51
+ runtimeDir,
52
+ markerFile: join(runtimeDir, '.telethon-version'),
53
+ }
54
+ }
55
+
56
+ function runtimePython(runtimeDir) {
57
+ const unix = join(runtimeDir, 'bin', 'python3')
58
+ if (existsSync(unix)) return unix
59
+ return join(runtimeDir, 'Scripts', 'python.exe')
60
+ }
61
+
62
+ function run(command, args) {
63
+ const result = spawnSync(command, args, {
64
+ stdio: 'inherit',
65
+ env: privateEnv(),
66
+ })
67
+ if (result.error) return 1
68
+ return result.status ?? 1
69
+ }
70
+
71
+ function markerMatches(markerFile) {
72
+ try {
73
+ return readFileSync(markerFile, 'utf8').trim() === TELETHON_VERSION
74
+ } catch {
75
+ return false
76
+ }
77
+ }
78
+
79
+ function installRuntime(paths) {
80
+ mkdirSync(paths.stateDir, { recursive: true, mode: 0o700 })
81
+ if (!existsSync(runtimePython(paths.runtimeDir))) {
82
+ console.log('Creating the private Telegram Python runtime…')
83
+ const created = run('python3', ['-m', 'venv', paths.runtimeDir])
84
+ if (created !== 0) {
85
+ console.error('Could not create the Python runtime. Install Python 3 with venv support and retry.')
86
+ return created
87
+ }
88
+ }
89
+ if (markerMatches(paths.markerFile)) return 0
90
+ console.log(`Installing Telethon ${TELETHON_VERSION} into the private runtime…`)
91
+ const installed = run(runtimePython(paths.runtimeDir), [
92
+ '-m', 'pip', 'install',
93
+ '--disable-pip-version-check',
94
+ '--no-input',
95
+ `Telethon==${TELETHON_VERSION}`,
96
+ ])
97
+ if (installed !== 0) {
98
+ console.error('Could not install Telethon. Check this machine\'s network access and retry.')
99
+ return installed
100
+ }
101
+ writeFileSync(paths.markerFile, `${TELETHON_VERSION}\n`, { mode: 0o600 })
102
+ return 0
103
+ }
104
+
105
+ function printStatus(agentId, paths) {
106
+ const runtimeInstalled = existsSync(runtimePython(paths.runtimeDir)) && markerMatches(paths.markerFile)
107
+ const configured = existsSync(join(paths.stateDir, 'config.json'))
108
+ const sessionPresent = existsSync(join(paths.stateDir, 'user.session'))
109
+ console.log(`Agent: ${agentId}`)
110
+ console.log(`Runtime installed: ${runtimeInstalled ? 'yes' : 'no'}`)
111
+ console.log(`Login configured: ${configured && sessionPresent ? 'yes' : 'no'}`)
112
+ console.log(`Local state directory: ${paths.stateDir}`)
113
+ return configured && sessionPresent ? 0 : 1
114
+ }
115
+
116
+ function parseAgentId(args) {
117
+ let agentId
118
+ for (let index = 0; index < args.length; index += 1) {
119
+ const value = args[index]
120
+ if (value === '--agent-id') {
121
+ agentId = args[index + 1]
122
+ if (!agentId) throw new Error('--agent-id requires a value')
123
+ index += 1
124
+ continue
125
+ }
126
+ if (value?.startsWith('--agent-id=')) {
127
+ agentId = value.slice('--agent-id='.length)
128
+ if (!agentId) throw new Error('--agent-id requires a value')
129
+ continue
130
+ }
131
+ throw new Error(`Unknown option: ${value ?? ''}`)
132
+ }
133
+ return agentId
134
+ }
135
+
136
+ function usage() {
137
+ console.log(`Usage:
138
+ alfe-telegram login [--agent-id ID] Install the local runtime and sign in privately
139
+ alfe-telegram logout [--agent-id ID] Revoke this agent's Telegram device session and remove local state
140
+ alfe-telegram install-runtime [--agent-id ID] Install/update this agent's pinned Telethon runtime
141
+ alfe-telegram status [--agent-id ID] Check this agent's local setup without revealing credentials`)
142
+ }
143
+
144
+ async function main() {
145
+ const command = process.argv[2] ?? 'help'
146
+ if (command === 'help' || command === '--help' || command === '-h') {
147
+ usage()
148
+ return 0
149
+ }
150
+ if (!['install-runtime', 'login', 'logout', 'status'].includes(command)) {
151
+ usage()
152
+ return 2
153
+ }
154
+
155
+ let agentId
156
+ try {
157
+ agentId = await resolveAgentId(parseAgentId(process.argv.slice(3)))
158
+ } catch (error) {
159
+ console.error(error instanceof Error ? error.message : 'Could not resolve agent identity.')
160
+ return 2
161
+ }
162
+ const paths = localPaths(agentId)
163
+
164
+ if (command === 'install-runtime') return installRuntime(paths)
165
+ if (command === 'login') {
166
+ const installed = installRuntime(paths)
167
+ if (installed !== 0) return installed
168
+ return run(runtimePython(paths.runtimeDir), [bridgeScript, 'login', '--state-dir', paths.stateDir])
169
+ }
170
+ if (command === 'logout') {
171
+ if (!existsSync(runtimePython(paths.runtimeDir))) {
172
+ console.error('The local Telegram runtime is not installed for this agent.')
173
+ return 1
174
+ }
175
+ return run(runtimePython(paths.runtimeDir), [bridgeScript, 'logout', '--state-dir', paths.stateDir])
176
+ }
177
+ return printStatus(agentId, paths)
178
+ }
179
+
180
+ process.exitCode = await main()
package/dist/index.cjs ADDED
@@ -0,0 +1,4 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_telegram_bridge = require("./telegram-bridge.cjs");
3
+ exports.TelegramBridge = require_telegram_bridge.TelegramBridge;
4
+ exports.TelegramBridgeUnavailable = require_telegram_bridge.TelegramBridgeUnavailable;
@@ -0,0 +1,2 @@
1
+ import { a as TelegramBridgeUnavailable, i as TelegramBridgeStatus, n as TelegramBridgeEvent, r as TelegramBridgeLogger, t as TelegramBridge } from "./telegram-bridge.cjs";
2
+ export { TelegramBridge, type TelegramBridgeEvent, type TelegramBridgeLogger, type TelegramBridgeStatus, TelegramBridgeUnavailable };
@@ -0,0 +1,2 @@
1
+ import { a as TelegramBridgeUnavailable, i as TelegramBridgeStatus, n as TelegramBridgeEvent, r as TelegramBridgeLogger, t as TelegramBridge } from "./telegram-bridge.js";
2
+ export { TelegramBridge, type TelegramBridgeEvent, type TelegramBridgeLogger, type TelegramBridgeStatus, TelegramBridgeUnavailable };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { n as TelegramBridgeUnavailable, t as TelegramBridge } from "./telegram-bridge.js";
2
+ export { TelegramBridge, TelegramBridgeUnavailable };