@lawrenceliang-btc/atel-sdk 1.1.2 → 1.1.4

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/bin/atel.mjs CHANGED
@@ -86,6 +86,7 @@ const NETWORK_FILE = resolve(ATEL_DIR, 'network.json');
86
86
  const TRACES_DIR = resolve(ATEL_DIR, 'traces');
87
87
  const PENDING_FILE = resolve(ATEL_DIR, 'pending-tasks.json');
88
88
  const RESULT_PUSH_QUEUE_FILE = resolve(ATEL_DIR, 'pending-result-pushes.json');
89
+ const NOTIFY_TARGETS_FILE = resolve(ATEL_DIR, 'notify-targets.json');
89
90
  const KEYS_DIR = resolve(ATEL_DIR, 'keys');
90
91
  const ANCHOR_FILE = resolve(KEYS_DIR, 'anchor.json');
91
92
 
@@ -95,6 +96,144 @@ const DEFAULT_POLICY = { rateLimit: 60, maxPayloadBytes: 1048576, maxConcurrent:
95
96
 
96
97
  function ensureDir() { if (!existsSync(ATEL_DIR)) mkdirSync(ATEL_DIR, { recursive: true }); }
97
98
 
99
+ // ═══════════════════════════════════════════════════════════════════
100
+ // Notification Target System — auto-discover gateway, manage targets
101
+ // ═══════════════════════════════════════════════════════════════════
102
+
103
+ function loadNotifyTargets() {
104
+ try { return JSON.parse(readFileSync(NOTIFY_TARGETS_FILE, 'utf-8')); }
105
+ catch { return { version: 1, defaultChannel: 'telegram', targets: [] }; }
106
+ }
107
+
108
+ function saveNotifyTargets(data) {
109
+ ensureDir();
110
+ writeFileSync(NOTIFY_TARGETS_FILE, JSON.stringify(data, null, 2));
111
+ }
112
+
113
+ // Auto-detect current TG chat from OpenClaw session state
114
+ function discoverTelegramChat() {
115
+ const sessionPaths = [
116
+ join(process.env.HOME || '', '.openclaw/agents/main/sessions/sessions.json'),
117
+ join(process.env.HOME || '', '.openclaw/sessions/sessions.json'),
118
+ ];
119
+ for (const p of sessionPaths) {
120
+ try {
121
+ const data = JSON.parse(readFileSync(p, 'utf-8'));
122
+ // sessions.json can be { "agent:main:main": { lastChannel, lastTo, ... } } or flat
123
+ const entries = typeof data === 'object' ? Object.values(data) : [];
124
+ for (const session of entries) {
125
+ if (session?.lastChannel === 'telegram' && typeof session?.lastTo === 'string' && session.lastTo.startsWith('telegram:')) {
126
+ return session.lastTo.split(':', 2)[1]; // "telegram:123456" → "123456"
127
+ }
128
+ }
129
+ // Also check top-level
130
+ if (data?.lastChannel === 'telegram' && typeof data?.lastTo === 'string' && data.lastTo.startsWith('telegram:')) {
131
+ return data.lastTo.split(':', 2)[1];
132
+ }
133
+ } catch {}
134
+ }
135
+ return null;
136
+ }
137
+
138
+ // Auto-bind TG notification target if not already bound
139
+ function autoBindNotifications() {
140
+ const targets = loadNotifyTargets();
141
+ if (targets.targets.length > 0) return; // already has targets
142
+
143
+ const chatId = discoverTelegramChat();
144
+ if (!chatId) return;
145
+
146
+ const botToken = discoverTelegramBot();
147
+ const id = `tg_${chatId}`;
148
+ targets.targets.push({
149
+ id, channel: 'telegram', target: chatId,
150
+ botToken: botToken || undefined,
151
+ label: 'owner', enabled: true,
152
+ createdAt: new Date().toISOString(), lastUsedAt: null,
153
+ });
154
+ saveNotifyTargets(targets);
155
+ console.log(`🔔 Auto-bound TG notifications to chat ${chatId}`);
156
+ }
157
+
158
+ // Auto-discover OpenClaw gateway: read token + find port
159
+ function discoverGateway() {
160
+ // 1. Env override
161
+ if (process.env.ATEL_NOTIFY_GATEWAY || process.env.OPENCLAW_GATEWAY_URL) {
162
+ const url = process.env.ATEL_NOTIFY_GATEWAY || process.env.OPENCLAW_GATEWAY_URL;
163
+ let token = '';
164
+ try { token = JSON.parse(readFileSync(join(process.env.HOME || '', '.openclaw/openclaw.json'), 'utf-8')).gateway?.auth?.token || ''; } catch {}
165
+ return { url, token };
166
+ }
167
+ // 2. Read from ~/.openclaw/openclaw.json
168
+ try {
169
+ const cfg = JSON.parse(readFileSync(join(process.env.HOME || '', '.openclaw/openclaw.json'), 'utf-8'));
170
+ const token = cfg.gateway?.auth?.token || '';
171
+ const port = cfg.gateway?.port || 18789;
172
+ const bind = cfg.gateway?.bind || '127.0.0.1';
173
+ return { url: `http://${bind}:${port}`, token };
174
+ } catch { return null; }
175
+ }
176
+
177
+ // Read TG bot token from openclaw config
178
+ function discoverTelegramBot() {
179
+ try {
180
+ const cfg = JSON.parse(readFileSync(join(process.env.HOME || '', '.openclaw/openclaw.json'), 'utf-8'));
181
+ const botToken = cfg.channels?.telegram?.botToken;
182
+ if (!botToken || botToken.includes('${')) return null; // template var, not resolved
183
+ return botToken;
184
+ } catch { return null; }
185
+ // Also check env
186
+ }
187
+
188
+ // Send notification to all enabled targets (fire-and-forget, never blocks)
189
+ async function pushTradeNotification(eventType, payload, body) {
190
+ const targets = loadNotifyTargets();
191
+ const enabled = (targets.targets || []).filter(t => t.enabled !== false);
192
+ if (enabled.length === 0) return;
193
+
194
+ const templates = {
195
+ 'order_accepted': (p) => `📋 订单已被接单\n订单: ${p.orderId || body?.orderId || '?'}\n执行方已开始处理,进入里程碑阶段`,
196
+ 'milestone_submitted': (p) => `📝 里程碑 M${p.milestoneIndex ?? '?'} 已提交\n订单: ${p.orderId || body?.orderId || '?'}\n等待审核`,
197
+ 'milestone_verified': (p) => `✅ 里程碑 M${p.milestoneIndex ?? '?'} 审核通过\n订单: ${p.orderId || body?.orderId || '?'}`,
198
+ 'milestone_rejected': (p) => `❌ 里程碑 M${p.milestoneIndex ?? '?'} 被拒绝\n订单: ${p.orderId || body?.orderId || '?'}\n原因: ${p.rejectReason || '未说明'}`,
199
+ 'order_settled': (p) => `💰 订单已结算完成\n订单: ${p.orderId || body?.orderId || '?'}\nUSDC 已支付`,
200
+ };
201
+ const tmpl = templates[eventType];
202
+ if (!tmpl) return;
203
+ const message = tmpl(payload);
204
+
205
+ for (const target of enabled) {
206
+ try {
207
+ if (target.channel === 'telegram' && target.botToken) {
208
+ // Direct TG Bot API — no gateway needed
209
+ await fetch(`https://api.telegram.org/bot${target.botToken}/sendMessage`, {
210
+ method: 'POST',
211
+ headers: { 'Content-Type': 'application/json' },
212
+ body: JSON.stringify({ chat_id: target.target, text: message, parse_mode: 'HTML' }),
213
+ signal: AbortSignal.timeout(5000),
214
+ }).catch(() => {});
215
+ } else if (target.channel === 'gateway') {
216
+ // OpenClaw gateway
217
+ const gw = discoverGateway();
218
+ if (gw?.url && gw?.token) {
219
+ await fetch(`${gw.url}/tools/invoke`, {
220
+ method: 'POST',
221
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${gw.token}` },
222
+ body: JSON.stringify({ tool: 'message', args: { action: 'send', message, target: target.target } }),
223
+ signal: AbortSignal.timeout(5000),
224
+ }).catch(() => {});
225
+ }
226
+ }
227
+ // Update lastUsedAt
228
+ target.lastUsedAt = new Date().toISOString();
229
+ } catch (e) {
230
+ // Never block main flow
231
+ }
232
+ }
233
+ // Best-effort save lastUsedAt
234
+ try { saveNotifyTargets(targets); } catch {}
235
+ }
236
+
98
237
  // ═══════════════════════════════════════════════════════════════════
99
238
  // CLI UX Improvements - Helper Functions
100
239
  // ═══════════════════════════════════════════════════════════════════
@@ -2241,29 +2380,10 @@ async function cmdStart(port) {
2241
2380
  }
2242
2381
  }
2243
2382
 
2244
- // 3.5. Trade event → push status summary to user via NOTIFY_GATEWAY (TG etc.)
2245
- // This runs BEFORE the hook so user sees status even if hook is slow/fails.
2246
- const tradeNotifyEvents = {
2247
- 'order_accepted': (p) => `📋 订单已被接单\n订单: ${p.orderId || body.orderId}\nExecutor 开始工作,进入里程碑流程`,
2248
- 'milestone_submitted': (p) => `📝 里程碑 M${p.milestoneIndex ?? '?'} 已提交\n订单: ${p.orderId || body.orderId}\n等待审核`,
2249
- 'milestone_verified': (p) => `✅ 里程碑 M${p.milestoneIndex ?? '?'} 审核通过\n订单: ${p.orderId || body.orderId}`,
2250
- 'milestone_rejected': (p) => `❌ 里程碑 M${p.milestoneIndex ?? '?'} 被拒绝\n订单: ${p.orderId || body.orderId}\n原因: ${p.rejectReason || '未说明'}`,
2251
- 'order_settled': (p) => `💰 订单已结算完成\n订单: ${p.orderId || body.orderId}\nUSDC 已支付`,
2252
- };
2253
- if (ATEL_NOTIFY_GATEWAY && ATEL_NOTIFY_TARGET && tradeNotifyEvents[event]) {
2254
- try {
2255
- const summaryMsg = tradeNotifyEvents[event](payload);
2256
- const token = (() => { try { return JSON.parse(readFileSync(join(process.env.HOME || '', '.openclaw/openclaw.json'), 'utf-8')).gateway?.auth?.token || ''; } catch { return ''; } })();
2257
- if (token) {
2258
- fetch(`${ATEL_NOTIFY_GATEWAY}/tools/invoke`, {
2259
- method: 'POST',
2260
- headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
2261
- body: JSON.stringify({ tool: 'message', args: { action: 'send', message: summaryMsg, target: ATEL_NOTIFY_TARGET } }),
2262
- signal: AbortSignal.timeout(5000),
2263
- }).then(() => log({ event: 'trade_notify_user_sent', eventType: event })).catch(e => log({ event: 'trade_notify_user_failed', eventType: event, error: e.message }));
2264
- }
2265
- } catch (e) { log({ event: 'trade_notify_user_error', eventType: event, error: e.message }); }
2266
- }
2383
+ // 3.5. Trade event → push status summary to user via notify targets (TG etc.)
2384
+ // Runs BEFORE the hook so user sees status even if hook is slow/fails.
2385
+ // Uses .atel/notify-targets.json — auto-discovered, no manual config needed.
2386
+ pushTradeNotification(event, payload, body).catch(e => log({ event: 'trade_notify_error', error: e.message }));
2267
2387
 
2268
2388
  // 4. Agent command hook: forward notification to agent's AI
2269
2389
  // Skip order_created — accepting orders requires human confirmation
@@ -3131,6 +3251,9 @@ async function cmdStart(port) {
3131
3251
 
3132
3252
  await endpoint.start();
3133
3253
 
3254
+ // Auto-bind TG notifications on first start
3255
+ try { autoBindNotifications(); } catch (e) { /* never block startup */ }
3256
+
3134
3257
  // Background retry for failed result pushes (durable queue)
3135
3258
  const flushResultPushQueue = async () => {
3136
3259
  if (!resultPushQueue.length) return;
@@ -6467,6 +6590,151 @@ const commands = {
6467
6590
  }
6468
6591
  return cmdSend({ _: args, json: rawArgs.includes('--json') });
6469
6592
  },
6593
+ // Notification Management
6594
+ notify: async () => {
6595
+ const subCmd = args[0];
6596
+ if (!subCmd || subCmd === 'help' || subCmd === '--help') {
6597
+ console.log('Usage: atel notify <status|bind|add|remove|enable|disable|test>');
6598
+ console.log(' status Show current notification targets');
6599
+ console.log(' bind <chatId> [--bot-token T] Bind TG chat as notification target');
6600
+ console.log(' add telegram <chatId> [--label name] [--bot-token T] Add a target');
6601
+ console.log(' remove <id> Remove a target');
6602
+ console.log(' enable <id> Enable a target');
6603
+ console.log(' disable <id> Disable a target (mute)');
6604
+ console.log(' test Send test notification to all targets');
6605
+ process.exit(0);
6606
+ }
6607
+ const targets = loadNotifyTargets();
6608
+
6609
+ if (subCmd === 'status') {
6610
+ const gw = discoverGateway();
6611
+ console.log('Gateway:', gw ? `${gw.url} (token: ${gw.token ? '✅' : '❌'})` : '❌ not found');
6612
+ const botToken = discoverTelegramBot();
6613
+ console.log('TG Bot:', botToken ? `✅ (${botToken.split(':')[0]})` : '❌ not configured');
6614
+ console.log(`\nTargets (${targets.targets.length}):`);
6615
+ if (targets.targets.length === 0) {
6616
+ console.log(' (none) — use "atel notify bind <chatId>" to add');
6617
+ }
6618
+ for (const t of targets.targets) {
6619
+ const status = t.enabled !== false ? '✅' : '🔇';
6620
+ console.log(` ${status} [${t.id}] ${t.channel}:${t.target} label=${t.label || '-'} last=${t.lastUsedAt || 'never'}`);
6621
+ }
6622
+ return;
6623
+ }
6624
+
6625
+ if (subCmd === 'bind') {
6626
+ let chatId = args[1];
6627
+ // Auto-detect from OpenClaw session if not provided
6628
+ if (!chatId) {
6629
+ chatId = discoverTelegramChat();
6630
+ if (chatId) {
6631
+ console.log(`🔍 Auto-detected TG chat: ${chatId}`);
6632
+ } else {
6633
+ console.error('Could not auto-detect TG chat. Usage: atel notify bind <chatId> [--bot-token TOKEN]');
6634
+ process.exit(1);
6635
+ }
6636
+ }
6637
+ // Get bot token: --bot-token flag > env > openclaw config
6638
+ let botToken = rawArgs.includes('--bot-token') ? rawArgs[rawArgs.indexOf('--bot-token') + 1] : '';
6639
+ if (!botToken) botToken = process.env.TELEGRAM_BOT_TOKEN || '';
6640
+ if (!botToken) botToken = discoverTelegramBot() || '';
6641
+ const id = `tg_${chatId}`;
6642
+ // Remove existing with same id
6643
+ targets.targets = targets.targets.filter(t => t.id !== id);
6644
+ targets.targets.push({
6645
+ id, channel: 'telegram', target: String(chatId),
6646
+ botToken: botToken || undefined,
6647
+ label: 'owner', enabled: true,
6648
+ createdAt: new Date().toISOString(), lastUsedAt: null,
6649
+ });
6650
+ saveNotifyTargets(targets);
6651
+ console.log(`✅ Bound TG chat ${chatId} as notification target (id: ${id})`);
6652
+ if (!botToken) console.log('⚠️ No bot token found. Set TELEGRAM_BOT_TOKEN or use --bot-token');
6653
+ return;
6654
+ }
6655
+
6656
+ if (subCmd === 'add') {
6657
+ const channel = args[1]; const target = args[2];
6658
+ if (!channel || !target) { console.error('Usage: atel notify add telegram <chatId> [--label name] [--bot-token T]'); process.exit(1); }
6659
+ let label = '';
6660
+ if (rawArgs.includes('--label')) label = rawArgs[rawArgs.indexOf('--label') + 1] || '';
6661
+ let botToken = rawArgs.includes('--bot-token') ? rawArgs[rawArgs.indexOf('--bot-token') + 1] : '';
6662
+ if (!botToken && channel === 'telegram') botToken = process.env.TELEGRAM_BOT_TOKEN || discoverTelegramBot() || '';
6663
+ const id = `${channel.substring(0,2)}_${target}`;
6664
+ targets.targets = targets.targets.filter(t => t.id !== id);
6665
+ targets.targets.push({
6666
+ id, channel, target: String(target),
6667
+ botToken: (channel === 'telegram' && botToken) ? botToken : undefined,
6668
+ label: label || '', enabled: true,
6669
+ createdAt: new Date().toISOString(), lastUsedAt: null,
6670
+ });
6671
+ saveNotifyTargets(targets);
6672
+ console.log(`✅ Added ${channel}:${target} (id: ${id})`);
6673
+ return;
6674
+ }
6675
+
6676
+ if (subCmd === 'remove') {
6677
+ const id = args[1];
6678
+ if (!id) { console.error('Usage: atel notify remove <id>'); process.exit(1); }
6679
+ const before = targets.targets.length;
6680
+ targets.targets = targets.targets.filter(t => t.id !== id);
6681
+ saveNotifyTargets(targets);
6682
+ console.log(targets.targets.length < before ? `✅ Removed ${id}` : `⚠️ Target ${id} not found`);
6683
+ return;
6684
+ }
6685
+
6686
+ if (subCmd === 'enable' || subCmd === 'disable') {
6687
+ const id = args[1];
6688
+ if (!id) { console.error(`Usage: atel notify ${subCmd} <id>`); process.exit(1); }
6689
+ const t = targets.targets.find(t => t.id === id);
6690
+ if (!t) { console.error(`Target ${id} not found`); process.exit(1); }
6691
+ t.enabled = subCmd === 'enable';
6692
+ saveNotifyTargets(targets);
6693
+ console.log(`✅ ${id} ${subCmd}d`);
6694
+ return;
6695
+ }
6696
+
6697
+ if (subCmd === 'test') {
6698
+ console.log('Sending test notification to all enabled targets...');
6699
+ const enabled = targets.targets.filter(t => t.enabled !== false);
6700
+ if (enabled.length === 0) { console.log('No enabled targets. Use "atel notify bind <chatId>" first.'); return; }
6701
+ for (const target of enabled) {
6702
+ try {
6703
+ if (target.channel === 'telegram' && target.botToken) {
6704
+ const resp = await fetch(`https://api.telegram.org/bot${target.botToken}/sendMessage`, {
6705
+ method: 'POST',
6706
+ headers: { 'Content-Type': 'application/json' },
6707
+ body: JSON.stringify({ chat_id: target.target, text: '🔔 ATEL 通知测试\n如果你看到这条消息,说明通知已正确配置!' }),
6708
+ signal: AbortSignal.timeout(10000),
6709
+ });
6710
+ const data = await resp.json();
6711
+ console.log(` ${target.id}: ${data.ok ? '✅ sent' : '❌ ' + (data.description || 'failed')}`);
6712
+ } else if (target.channel === 'gateway') {
6713
+ const gw = discoverGateway();
6714
+ if (gw?.url && gw?.token) {
6715
+ const resp = await fetch(`${gw.url}/tools/invoke`, {
6716
+ method: 'POST',
6717
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${gw.token}` },
6718
+ body: JSON.stringify({ tool: 'message', args: { action: 'send', message: '🔔 ATEL notification test', target: target.target } }),
6719
+ signal: AbortSignal.timeout(10000),
6720
+ });
6721
+ console.log(` ${target.id}: ${resp.ok ? '✅ sent' : '❌ status ' + resp.status}`);
6722
+ } else {
6723
+ console.log(` ${target.id}: ❌ gateway not found`);
6724
+ }
6725
+ } else {
6726
+ console.log(` ${target.id}: ❌ unsupported channel ${target.channel}`);
6727
+ }
6728
+ } catch (e) {
6729
+ console.log(` ${target.id}: ❌ ${e.message}`);
6730
+ }
6731
+ }
6732
+ return;
6733
+ }
6734
+
6735
+ console.error('Unknown subcommand. Use: atel notify help');
6736
+ process.exit(1);
6737
+ },
6470
6738
  // Alias System
6471
6739
  alias: () => {
6472
6740
  const subCmd = args[0];
@@ -6596,6 +6864,15 @@ Alias Commands:
6596
6864
  alias list [--json] List all aliases
6597
6865
  alias remove <alias> Remove an alias
6598
6866
 
6867
+ Notification Commands:
6868
+ notify status Show notification targets and gateway status
6869
+ notify bind <chatId> [--bot-token T] Bind TG chat as default notification target
6870
+ notify add telegram <chatId> Add a notification target [--label name] [--bot-token T]
6871
+ notify remove <id> Remove a notification target
6872
+ notify enable <id> Enable a muted target
6873
+ notify disable <id> Mute a target (keep but don't send)
6874
+ notify test Send test notification to all enabled targets
6875
+
6599
6876
  Environment:
6600
6877
  ATEL_DIR Identity directory (default: .atel)
6601
6878
  ATEL_REGISTRY Registry URL (default: https://api.atelai.org)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawrenceliang-btc/atel-sdk",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "ATEL Protocol SDK - Agent Trust & Exchange Layer",
5
5
  "repository": {
6
6
  "type": "git",
@@ -64,6 +64,36 @@ pm2 save 2>/dev/null || true
64
64
 
65
65
  echo "⏳ Waiting for wallet (15s)..."
66
66
  sleep 15
67
+
68
+ # 自动绑定当前 TG 会话为通知目标
69
+ SESSION_FILE="$HOME/.openclaw/agents/main/sessions/sessions.json"
70
+ CHAT_ID=""
71
+ if [ -f "$SESSION_FILE" ]; then
72
+ CHAT_ID=$(python3 - <<'PY'
73
+ import json, os
74
+ p=os.path.expanduser("~/.openclaw/agents/main/sessions/sessions.json")
75
+ try:
76
+ data=json.load(open(p))
77
+ for v in (data.values() if isinstance(data,dict) else [data]):
78
+ if isinstance(v,dict) and v.get("lastChannel")=="telegram":
79
+ lt=v.get("lastTo","")
80
+ if lt.startswith("telegram:"):
81
+ print(lt.split(":",1)[1])
82
+ break
83
+ except:
84
+ pass
85
+ PY
86
+ )
87
+ fi
88
+
89
+ if [ -n "$CHAT_ID" ]; then
90
+ echo "🔔 Binding notifications to current Telegram chat: $CHAT_ID"
91
+ cd "$WORKSPACE" && atel notify bind "$CHAT_ID" 2>/dev/null || true
92
+ cd "$WORKSPACE" && atel notify test 2>/dev/null || true
93
+ else
94
+ echo "⚠️ Could not auto-detect Telegram chat. Run: atel notify bind <chat_id>"
95
+ fi
96
+
67
97
  echo "========================================="
68
98
  echo "🤝 ATEL Agent Ready!"
69
99
  cd "$WORKSPACE" && ATEL_PLATFORM=https://api.atelai.org atel info 2>&1 | head -6 || true
@@ -341,6 +371,20 @@ atel approve <taskId> # 批准任务
341
371
  - `order status must be created` → 订单已被接,不用重复操作
342
372
  - `session file locked` → 等 30 秒再试
343
373
 
374
+ ## 通知管理
375
+
376
+ 订单状态推送由 SDK 自动完成,你不需要手动给用户发重复的状态摘要。
377
+
378
+ ```bash
379
+ atel notify status # 查看当前通知配置
380
+ atel notify bind <chatId> # 绑定 TG 聊天
381
+ atel notify add telegram <chatId> # 添加通知目标
382
+ atel notify remove <id> # 删除目标
383
+ atel notify disable <id> # 临时静默
384
+ atel notify enable <id> # 恢复通知
385
+ atel notify test # 发送测试通知
386
+ ```
387
+
344
388
  ## 重要规则
345
389
 
346
390
  1. **所有 atel 命令必须在 ~/atel-workspace 目录执行**
@@ -350,3 +394,5 @@ atel approve <taskId> # 批准任务
350
394
  5. 里程碑按顺序完成:0 → 1 → 2 → 3 → 4
351
395
  6. 命令失败等几秒重试
352
396
  7. 环境变量用 `ATEL_PLATFORM`(不是 `ATEL_API`)
397
+ 8. **订单状态推送由 SDK 自动做,你不需要重复给用户发状态摘要**。只在需要解释、追问、异常处理时主动回复用户
398
+ 9. **setup 成功后,必须尝试自动绑定当前 TG 会话**(`atel notify bind`)。绑定成功告诉用户"通知已启用";失败了再提示用户手动补绑
@@ -84,6 +84,35 @@ echo "========================================="
84
84
  echo "🤝 ATEL Agent Ready!"
85
85
  echo "========================================="
86
86
  cd "$WORKSPACE" && ATEL_PLATFORM=https://api.atelai.org atel info 2>&1 | head -6 || true
87
+ # 自动绑定当前 TG 会话为通知目标
88
+ SESSION_FILE="$HOME/.openclaw/agents/main/sessions/sessions.json"
89
+ CHAT_ID=""
90
+ if [ -f "$SESSION_FILE" ]; then
91
+ CHAT_ID=$(python3 - <<'PY'
92
+ import json, os
93
+ p=os.path.expanduser("~/.openclaw/agents/main/sessions/sessions.json")
94
+ try:
95
+ data=json.load(open(p))
96
+ for v in (data.values() if isinstance(data,dict) else [data]):
97
+ if isinstance(v,dict) and v.get("lastChannel")=="telegram":
98
+ lt=v.get("lastTo","")
99
+ if lt.startswith("telegram:"):
100
+ print(lt.split(":",1)[1])
101
+ break
102
+ except:
103
+ pass
104
+ PY
105
+ )
106
+ fi
107
+
108
+ if [ -n "$CHAT_ID" ]; then
109
+ echo "🔔 Binding notifications to current Telegram chat: $CHAT_ID"
110
+ cd "$WORKSPACE" && atel notify bind "$CHAT_ID" 2>/dev/null || true
111
+ cd "$WORKSPACE" && atel notify test 2>/dev/null || true
112
+ else
113
+ echo "⚠️ Could not auto-detect Telegram chat. Run: atel notify bind <chat_id>"
114
+ fi
115
+
87
116
  echo "DID: $DID"
88
117
  echo "Port: $PORT"
89
118
  echo "pm2: $(pm2 jlist 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(d[0]['pm2_env']['status'] if d else 'unknown')" 2>/dev/null || echo 'check: pm2 status')"