antenna-fyi 1.2.25 → 1.2.27

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/cli.js CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  import { scan, getProfile, setProfile, accept, checkMatches, checkin, createBindToken, discover, createEvent, endEvent, eventCheckin, joinEvent, eventScan, pass as passUser, uploadEventImage, updateEvent, approveParticipant, rejectParticipant, addCohost, getClient } from "./core.js";
4
4
  import { createInterface } from "readline";
5
- import { existsSync, mkdirSync, copyFileSync, readFileSync } from "fs";
5
+ import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, unlinkSync } from "fs";
6
+ import path from "path";
7
+ import os from "os";
6
8
  import { join, dirname, extname } from "path";
7
9
  import { fileURLToPath } from "url";
8
10
  import { homedir } from "os";
@@ -334,9 +336,21 @@ export async function handleSetup(f) {
334
336
 
335
337
  export async function handleStatus(f) {
336
338
  const supabaseUrl = process.env.ANTENNA_SUPABASE_URL || process.env.ANTENNA_URL || "https://bcudjloikmpcqwcptuyd.supabase.co";
337
- console.log("📡 Antenna Status\n");
339
+ console.log("\n📡 Antenna Status\n");
338
340
  console.log(` Supabase URL: ${supabaseUrl}`);
339
341
 
342
+ // Check watch process
343
+ const pidFile = path.join(os.homedir(), '.antenna', 'watch.pid');
344
+ try {
345
+ if (existsSync(pidFile)) {
346
+ const pid = parseInt(readFileSync(pidFile, 'utf8').trim());
347
+ try { process.kill(pid, 0); console.log(` Watch: ✅ running (PID ${pid})`); }
348
+ catch { console.log(' Watch: ❌ stale PID file (process dead)'); }
349
+ } else {
350
+ console.log(' Watch: ❌ not running');
351
+ }
352
+ } catch { console.log(' Watch: ❌ not running'); }
353
+
340
354
  if (f.id) {
341
355
  const profile = await getProfile({ device_id: f.id });
342
356
  if (profile) {
@@ -466,6 +480,17 @@ export async function handleWatch(f) {
466
480
  process.exit(1);
467
481
  }
468
482
 
483
+ // Write PID file for health check
484
+ const pidDir = path.join(os.homedir(), '.antenna');
485
+ const pidFile = path.join(pidDir, 'watch.pid');
486
+ try {
487
+ if (!existsSync(pidDir)) mkdirSync(pidDir, { recursive: true });
488
+ writeFileSync(pidFile, String(process.pid));
489
+ } catch {}
490
+ process.on('exit', () => { try { unlinkSync(pidFile); } catch {} });
491
+ process.on('SIGINT', () => { try { unlinkSync(pidFile); } catch {} process.exit(0); });
492
+ process.on('SIGTERM', () => { try { unlinkSync(pidFile); } catch {} process.exit(0); });
493
+
469
494
  const sb = getClient();
470
495
  const notified = new Set();
471
496
 
@@ -490,19 +515,36 @@ export async function handleWatch(f) {
490
515
  } catch { /* hermes not installed */ }
491
516
  }
492
517
 
493
- console.log(`📡 Watching for new matches for ${id}...`);
518
+ // Force stdout blocking mode for non-TTY environments (Hermes exec)
519
+ try { if (process.stdout._handle) process.stdout._handle.setBlocking(true); } catch {}
520
+
521
+ // Log to file as fallback
522
+ const logDir = path.join(os.homedir(), '.antenna');
523
+ const logFile = path.join(logDir, 'watch.log');
524
+ try { if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true }); } catch {}
525
+ const logStream = await import('fs').then(fs => fs.createWriteStream(logFile, { flags: 'a' }));
526
+ const _log = (msg) => {
527
+ const line = `[${new Date().toISOString()}] ${msg}`;
528
+ console.log(msg);
529
+ try { logStream.write(line + '\n'); } catch {}
530
+ };
531
+
532
+ _log(`📡 Watching for new matches for ${id}...`);
494
533
  if (pushMethod === "openclaw") {
495
- console.log(` 🔗 Detected OpenClaw — will push notifications to your channel.`);
534
+ _log(` 🔗 Detected OpenClaw — will push notifications to your channel.`);
496
535
  } else if (pushMethod === "hermes") {
497
- console.log(` 🔗 Detected Hermes — will push notifications to your channel.`);
536
+ _log(` 🔗 Detected Hermes — will push notifications to your channel.`);
498
537
  } else {
499
- console.log(` ℹ️ No agent framework detected — notifications will print here.`);
538
+ _log(` ℹ️ No agent framework detected — notifications will print here.`);
500
539
  }
501
- console.log(` Press Ctrl+C to stop.\n`);
540
+ _log(` Press Ctrl+C to stop.\n`);
541
+
542
+ // Keep process alive (prevent exit when backgrounded)
543
+ process.stdin.resume();
502
544
 
503
545
  // Push notification helper
504
546
  async function pushNotify(message) {
505
- console.log(message); // always print to terminal
547
+ _log(message); // always print to terminal
506
548
 
507
549
  if (pushMethod === "openclaw") {
508
550
  try {
@@ -536,22 +578,22 @@ export async function handleWatch(f) {
536
578
  // Initial check
537
579
  const initial = await checkMatches({ device_id: id });
538
580
  if (initial.mutual_matches?.length) {
539
- console.log(`🎉 You have ${initial.mutual_matches.length} mutual match(es)!`);
581
+ _log(`🎉 You have ${initial.mutual_matches.length} mutual match(es)!`);
540
582
  for (const m of initial.mutual_matches) {
541
583
  const key = `mutual:${m.device_id}`;
542
584
  notified.add(key);
543
- console.log(` ${m.emoji || "👤"} ${m.name}${m.their_contact ? " — contact: " + m.their_contact : ""}`);
585
+ _log(` ${m.emoji || "👤"} ${m.name}${m.their_contact ? " — contact: " + m.their_contact : ""}`);
544
586
  }
545
- console.log();
587
+ _log();
546
588
  }
547
589
  if (initial.incoming_accepts?.length) {
548
- console.log(`📩 ${initial.incoming_accepts.length} person(s) want to meet you!`);
590
+ _log(`📩 ${initial.incoming_accepts.length} person(s) want to meet you!`);
549
591
  for (const m of initial.incoming_accepts) {
550
592
  const key = `incoming:${m.device_id}`;
551
593
  notified.add(key);
552
- console.log(` ${m.emoji || "👤"} ${m.name} — ${m.line1 || ""}`);
594
+ _log(` ${m.emoji || "👤"} ${m.name} — ${m.line1 || ""}`);
553
595
  }
554
- console.log();
596
+ _log();
555
597
  }
556
598
 
557
599
  // Subscribe to realtime changes on matches table
@@ -607,9 +649,9 @@ export async function handleWatch(f) {
607
649
  )
608
650
  .subscribe((status) => {
609
651
  if (status === "SUBSCRIBED") {
610
- console.log("✅ Connected — listening for matches in real-time.");
652
+ _log("✅ Connected — listening for matches in real-time.");
611
653
  } else if (status === "CHANNEL_ERROR" || status === "TIMED_OUT") {
612
- console.log(`⚠️ Connection issue (${status}), retrying...`);
654
+ _log(`⚠️ Connection issue (${status}), retrying...`);
613
655
  }
614
656
  });
615
657
 
@@ -653,7 +695,7 @@ export async function handleWatch(f) {
653
695
  )
654
696
  .subscribe((status) => {
655
697
  if (status === "SUBSCRIBED") {
656
- console.log("✅ Connected — listening for event notifications.");
698
+ _log("✅ Connected — listening for event notifications.");
657
699
  }
658
700
  });
659
701
 
@@ -44,7 +44,7 @@ PROFILE_SCHEMA = {
44
44
  },
45
45
  "sender_id": {"type": "string"},
46
46
  "channel": {"type": "string"},
47
- "chat_id": {"type": "string", "description": "Chat/channel ID for notifications"},
47
+ "chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
48
48
  "display_name": {"type": "string", "description": "Display name"},
49
49
  "emoji": {"type": "string", "description": "Profile emoji"},
50
50
  "line1": {"type": "string", "description": "Who you are / what you do"},
@@ -67,7 +67,7 @@ ACCEPT_SCHEMA = {
67
67
  "properties": {
68
68
  "sender_id": {"type": "string"},
69
69
  "channel": {"type": "string"},
70
- "chat_id": {"type": "string", "description": "Chat/channel ID for notifications"},
70
+ "chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
71
71
  "ref": {
72
72
  "type": "string",
73
73
  "description": "Ref number from scan results (e.g. '1')",
@@ -97,7 +97,7 @@ CHECKIN_SCHEMA = {
97
97
  "lng": {"type": "number", "description": "Longitude"},
98
98
  "sender_id": {"type": "string"},
99
99
  "channel": {"type": "string"},
100
- "chat_id": {"type": "string", "description": "Chat/channel ID for notifications"},
100
+ "chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
101
101
  "place_name": {
102
102
  "type": "string",
103
103
  "description": "Name of the place (optional)",
@@ -117,7 +117,7 @@ CHECK_MATCHES_SCHEMA = {
117
117
  "properties": {
118
118
  "sender_id": {"type": "string"},
119
119
  "channel": {"type": "string"},
120
- "chat_id": {"type": "string", "description": "Chat/channel ID for notifications"},
120
+ "chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
121
121
  },
122
122
  "required": ["sender_id", "channel"],
123
123
  },
@@ -133,7 +133,7 @@ BIND_SCHEMA = {
133
133
  "properties": {
134
134
  "sender_id": {"type": "string"},
135
135
  "channel": {"type": "string"},
136
- "chat_id": {"type": "string", "description": "Chat/channel ID for notifications"},
136
+ "chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
137
137
  "purpose": {"type": "string", "description": "'profile' (default) or 'event'"},
138
138
  "event_code": {"type": "string", "description": "Event code (when purpose=event)"},
139
139
  },
@@ -287,7 +287,7 @@ EVENT_UPDATE_SCHEMA = {
287
287
  "code": {"type": "string"},
288
288
  "sender_id": {"type": "string"},
289
289
  "channel": {"type": "string"},
290
- "chat_id": {"type": "string", "description": "Chat/channel ID for notifications"},
290
+ "chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
291
291
  "name": {"type": "string"},
292
292
  "description": {"type": "string"},
293
293
  "og_image": {"type": "string"},
@@ -309,7 +309,7 @@ EVENT_APPROVE_SCHEMA = {
309
309
  "code": {"type": "string"},
310
310
  "sender_id": {"type": "string"},
311
311
  "channel": {"type": "string"},
312
- "chat_id": {"type": "string", "description": "Chat/channel ID for notifications"},
312
+ "chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
313
313
  "ref": {"type": "string"},
314
314
  },
315
315
  "required": ["code", "sender_id", "channel", "ref"],
@@ -325,7 +325,7 @@ EVENT_REJECT_SCHEMA = {
325
325
  "code": {"type": "string"},
326
326
  "sender_id": {"type": "string"},
327
327
  "channel": {"type": "string"},
328
- "chat_id": {"type": "string", "description": "Chat/channel ID for notifications"},
328
+ "chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
329
329
  "ref": {"type": "string"},
330
330
  },
331
331
  "required": ["code", "sender_id", "channel", "ref"],
@@ -341,7 +341,7 @@ EVENT_ADD_HOST_SCHEMA = {
341
341
  "code": {"type": "string"},
342
342
  "sender_id": {"type": "string"},
343
343
  "channel": {"type": "string"},
344
- "chat_id": {"type": "string", "description": "Chat/channel ID for notifications"},
344
+ "chat_id": {"type": "string", "description": "REQUIRED for notifications. Pass chat/channel ID from message context."},
345
345
  "ref": {"type": "string"},
346
346
  },
347
347
  "required": ["code", "sender_id", "channel", "ref"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-fyi",
3
- "version": "1.2.25",
3
+ "version": "1.2.27",
4
4
  "description": "Antenna — nearby people discovery. CLI + MCP server + OpenClaw skill & plugin, all in one package.",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -68,6 +68,8 @@ antenna watch --id <device_id> &
68
68
  ```
69
69
  这会保持一个 WebSocket 连接,有新匹配或活动审批变更时立即通知。比 cron 更实时。
70
70
 
71
+ **Health check:** 每次对话开始时,跑 `antenna status --id <device_id>` 检查 watch 是否在跑。如果显示 "Watch: ❌ not running",重新启动 `antenna watch --id <device_id> &`。
72
+
71
73
  如果 exec 不支持后台进程,cron 是 fallback。
72
74
 
73
75
  ## When to use