antenna-fyi 1.3.11 β†’ 1.3.12

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/antenna.js CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  handlePass,
14
14
  handleWatch,
15
15
  handleSetup,
16
+ handleConfig,
16
17
  handleStatus,
17
18
  handleInstallSkill,
18
19
  handleInstallPlugin,
@@ -51,6 +52,8 @@ async function main() {
51
52
  }
52
53
  case "setup":
53
54
  return handleSetup(f);
55
+ case "config":
56
+ return handleConfig(f);
54
57
  case "status":
55
58
  return handleStatus(f);
56
59
  case "install-skill":
package/lib/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // antenna CLI command handlers
2
2
 
3
- import { scan, getProfile, setProfile, accept, checkMatches, checkin, createBindToken, discover, createEvent, endEvent, eventCheckin, joinEvent, eventScan, pass as passUser, uploadEventImage, updateEvent, approveParticipant, rejectParticipant, addCohost, sendEventMessage, getMyEventMessages, getClient } from "./core.js";
3
+ import { scan, getProfile, setProfile, accept, checkMatches, checkin, createBindToken, discover, createEvent, endEvent, eventCheckin, joinEvent, eventScan, pass as passUser, uploadEventImage, updateEvent, approveParticipant, rejectParticipant, addCohost, sendEventMessage, getMyEventMessages, getClient, verifyApiKey } from "./core.js";
4
4
  import { createInterface } from "readline";
5
5
  import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, renameSync } from "fs";
6
6
  import path from "path";
@@ -31,12 +31,13 @@ export function parseFlags(args) {
31
31
  }
32
32
 
33
33
  export async function handleScan(f) {
34
- if (!f.lat && !f.lng && !f.id) return console.error("Usage: antenna scan --lat 39.99 --lng 116.48 [--radius 500] (max 1000) [--id <platform>:<user_id>]\n Or just: antenna scan --id <platform>:<user_id> (uses saved location from GPS bind)");
34
+ const _scanId = resolveId(f);
35
+ if (!f.lat && !f.lng && !_scanId) return console.error("Usage: antenna scan --lat 39.99 --lng 116.48 [--radius 500] (max 1000) [--id <platform>:<user_id>]\n Or just: antenna scan --id <platform>:<user_id> (uses saved location from GPS bind)");
35
36
  const result = await scan({
36
37
  lat: f.lat ? +f.lat : undefined,
37
38
  lng: f.lng ? +f.lng : undefined,
38
39
  radius_m: +(f.radius || 500),
39
- device_id: f.id || null,
40
+ device_id: _scanId || null,
40
41
  });
41
42
  if (result.count === 0) return console.log(result.message || "πŸ“‘ No one nearby");
42
43
  if (result.global) {
@@ -64,10 +65,11 @@ export async function handleScan(f) {
64
65
  }
65
66
 
66
67
  export async function handleProfile(f) {
67
- if (!f.id) return console.error("Usage: antenna profile --id <platform>:<user_id> [--name Yi --emoji 🦦 --line1 '...' --line2 '...' --line3 '...']");
68
+ const id = resolveId(f);
69
+ if (!id) return console.error("Usage: antenna profile --id <platform>:<user_id> [--name Yi --emoji 🦦 --line1 '...' --line2 '...' --line3 '...']");
68
70
  if (f.name || f.line1 || f.line2 || f.line3 || f.visible !== undefined || f.hide !== undefined) {
69
71
  const visible = f.hide ? false : (f.visible !== undefined ? f.visible === 'true' || f.visible === true : undefined);
70
- const payload = { device_id: f.id };
72
+ const payload = { device_id: id };
71
73
  if (f.name) payload.display_name = f.name;
72
74
  if (f.emoji) payload.emoji = f.emoji;
73
75
  if (f.line1 !== undefined) payload.line1 = f.line1;
@@ -78,7 +80,7 @@ export async function handleProfile(f) {
78
80
  console.log("βœ… Profile saved");
79
81
  console.log(JSON.stringify(data, null, 2));
80
82
  } else {
81
- const data = await getProfile({ device_id: f.id });
83
+ const data = await getProfile({ device_id: id });
82
84
  if (!data) return console.log("No profile yet. Create one with --name and --line1/2/3");
83
85
  console.log(`${data.emoji || "πŸ‘€"} ${data.display_name || "Anonymous"}`);
84
86
  if (data.line1) console.log(` ${data.line1}`);
@@ -88,9 +90,10 @@ export async function handleProfile(f) {
88
90
  }
89
91
 
90
92
  export async function handleAccept(f) {
91
- if (!f.id || (!f.target && !f.ref)) return console.error("Usage: antenna accept --id <platform>:<user_id> --ref 1 [--contact 'WeChat: yi']\n antenna accept --id <platform>:<user_id> --target <ref_or_device_id> [--contact 'WeChat: yi']");
93
+ const id = resolveId(f);
94
+ if (!id || (!f.target && !f.ref)) return console.error("Usage: antenna accept --id <platform>:<user_id> --ref 1 [--contact 'WeChat: yi']\n antenna accept --id <platform>:<user_id> --target <ref_or_device_id> [--contact 'WeChat: yi']");
92
95
  const result = await accept({
93
- device_id: f.id,
96
+ device_id: id,
94
97
  target_device_id: f.target || null,
95
98
  ref: f.ref || null,
96
99
  contact_info: f.contact,
@@ -100,18 +103,20 @@ export async function handleAccept(f) {
100
103
  }
101
104
 
102
105
  export async function handleCheckin(f) {
103
- if (!f.id || !f.lat || !f.lng) return console.error("Usage: antenna checkin --id <platform>:<user_id> --lat 39.99 --lng 116.48 [--place 'δΈ‰ι‡Œε±―']");
106
+ const id = resolveId(f);
107
+ if (!id || !f.lat || !f.lng) return console.error("Usage: antenna checkin --id <platform>:<user_id> --lat 39.99 --lng 116.48 [--place 'δΈ‰ι‡Œε±―']");
104
108
  const result = await checkin({
105
109
  lat: +f.lat,
106
110
  lng: +f.lng,
107
- device_id: f.id,
111
+ device_id: id,
108
112
  });
109
113
  console.log(result.checked_in ? "βœ… " + result.message : "❌ " + result.message);
110
114
  }
111
115
 
112
116
  export async function handleMatches(f) {
113
- if (!f.id) return console.error("Usage: antenna matches --id <platform>:<user_id>");
114
- const result = await checkMatches({ device_id: f.id });
117
+ const id = resolveId(f);
118
+ if (!id) return console.error("Usage: antenna matches --id <platform>:<user_id>");
119
+ const result = await checkMatches({ device_id: id });
115
120
  if (!result.mutual_matches.length && !result.incoming_accepts.length) {
116
121
  return console.log(result.message);
117
122
  }
@@ -130,8 +135,9 @@ export async function handleMatches(f) {
130
135
  }
131
136
 
132
137
  export async function handleDiscover(f) {
133
- if (!f.id) return console.error("Usage: antenna discover --id <platform>:<user_id>");
134
- const result = await discover({ device_id: f.id });
138
+ const id = resolveId(f);
139
+ if (!id) return console.error("Usage: antenna discover --id <platform>:<user_id>");
140
+ const result = await discover({ device_id: id });
135
141
  if (result.count === 0) return console.log(result.message || "🌍 No global recommendation available right now.");
136
142
  console.log(`🌍 Global discover:\n`);
137
143
  result.profiles.forEach((p) => {
@@ -145,6 +151,7 @@ export async function handleDiscover(f) {
145
151
  }
146
152
 
147
153
  export async function handleEvent(f) {
154
+ f.id = f.id || resolveId(f);
148
155
  const sub = f._?.[0] || Object.keys(f).find(k => ["create", "join", "scan", "end", "checkin", "upload-image"].includes(k));
149
156
 
150
157
  if (f['upload-image']) {
@@ -180,7 +187,8 @@ export async function handleEvent(f) {
180
187
 
181
188
  if (f.create || (!f.join && !f.scan && !f.end && !f.update && !f.approve && !f.reject && !f['add-host'] && f.name)) {
182
189
  if (!f.name) return console.error("Usage: antenna event --create --name 'AI Meetup' --id <platform>:<user_id> --starts-at '2026-04-19T14:00' --ends-at '2026-04-19T18:00' [--lat 34.05 --lng -118.25] [--desc 'description'] [--og-image 'url'] [--requires-approval] [--screening-questions 'Q1|Q2']");
183
- if (!f.id) return console.error("❌ --id is required (e.g. --id <platform>:<user_id>). Creator identity needed to manage the event.");
190
+ const id = resolveId(f);
191
+ if (!id) return console.error("❌ --id is required (e.g. --id <platform>:<user_id>). Creator identity needed to manage the event.");
184
192
  if (!f['starts-at'] || !f['ends-at']) return console.error("❌ --starts-at and --ends-at are required. Example: --starts-at '2026-04-19T14:00' --ends-at '2026-04-19T18:00'");
185
193
  const result = await createEvent({ name: f.name, device_id: f.id, lat: f.lat ? +f.lat : undefined, lng: f.lng ? +f.lng : undefined, starts_at: f['starts-at'], ends_at: f['ends-at'], description: f.desc || undefined, og_image: f['og-image'] || undefined, requires_approval: f['requires-approval'] === true || f['requires-approval'] === 'true' || undefined, screening_questions: f['screening-questions'] ? f['screening-questions'].split('|') : undefined });
186
194
  console.log(`\nπŸŽ‰ Event created!\n`);
@@ -294,8 +302,9 @@ export async function handleEvent(f) {
294
302
  }
295
303
 
296
304
  export async function handleBind(f) {
297
- if (!f.id) return console.error("Usage: antenna bind --id <platform>:<user_id>");
298
- const result = await createBindToken({ device_id: f.id });
305
+ const id = resolveId(f);
306
+ if (!id) return console.error("Usage: antenna bind --id <platform>:<user_id>");
307
+ const result = await createBindToken({ device_id: id });
299
308
  console.log("\nπŸ”— GPS Binding Link:\n");
300
309
  console.log(` ${result.url}\n`);
301
310
  console.log("Send this to the user. Opening it on their phone will share GPS with their agent.");
@@ -303,10 +312,11 @@ export async function handleBind(f) {
303
312
  }
304
313
 
305
314
  export async function handlePass(f) {
306
- if (!f.id) return console.error("Usage: antenna pass --id <platform>:<user_id> --target <ref_or_device_id>");
315
+ const id = resolveId(f);
316
+ if (!id) return console.error("Usage: antenna pass --id <platform>:<user_id> --target <ref_or_device_id>");
307
317
  if (!f.target && !f.ref) return console.error("Usage: antenna pass --id <platform>:<user_id> --target <ref_or_device_id> (or --ref 1)");
308
318
  const result = await passUser({
309
- device_id: f.id,
319
+ device_id: id,
310
320
  target_device_id: f.target,
311
321
  ref: f.ref,
312
322
  });
@@ -347,6 +357,70 @@ export async function handleSetup(f) {
347
357
  console.log();
348
358
  }
349
359
 
360
+ // ─── Config file helpers ─────────────────────────────────────────────
361
+
362
+ const CONFIG_DIR = path.join(os.homedir(), '.antenna');
363
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
364
+
365
+ export function loadConfig() {
366
+ try {
367
+ if (existsSync(CONFIG_FILE)) {
368
+ return JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
369
+ }
370
+ } catch {}
371
+ return {};
372
+ }
373
+
374
+ function saveConfig(config) {
375
+ if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
376
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n');
377
+ }
378
+
379
+ /** Resolve device_id: --id flag > config file > null */
380
+ function resolveId(f) {
381
+ if (f.id) return f.id;
382
+ const config = loadConfig();
383
+ if (config.device_id) return config.device_id;
384
+ return null;
385
+ }
386
+
387
+ export async function handleConfig(f) {
388
+ if (f.key) {
389
+ // Verify the key against Supabase
390
+ console.log('\nπŸ“‘ Verifying API key...');
391
+ try {
392
+ const result = await verifyApiKey({ key: f.key });
393
+ if (!result.valid) {
394
+ console.error(`\n❌ ${result.error || 'Invalid API key'}`);
395
+ process.exit(1);
396
+ }
397
+ const config = loadConfig();
398
+ config.key = f.key;
399
+ config.device_id = result.device_id;
400
+ config.display_name = result.display_name;
401
+ saveConfig(config);
402
+ console.log(`\nβœ… Authenticated as ${result.display_name || 'Antenna user'}`);
403
+ console.log(` Device ID: ${result.device_id}`);
404
+ console.log(` Config saved to ~/.antenna/config.json\n`);
405
+ } catch (e) {
406
+ console.error(`\n❌ Failed to verify key: ${e.message}`);
407
+ process.exit(1);
408
+ }
409
+ } else {
410
+ // Show current config
411
+ const config = loadConfig();
412
+ if (config.key) {
413
+ console.log('\nπŸ“‘ Antenna Config\n');
414
+ console.log(` Key: ${config.key.slice(0, 10)}...${config.key.slice(-4)}`);
415
+ console.log(` Device ID: ${config.device_id || '(unknown)'}`);
416
+ console.log(` Name: ${config.display_name || '(unknown)'}\n`);
417
+ } else {
418
+ console.log('\nπŸ“‘ No API key configured.');
419
+ console.log(' Run: antenna config --key <your-api-key>\n');
420
+ }
421
+ }
422
+ }
423
+
350
424
  export async function handleStatus(f) {
351
425
  const supabaseUrl = process.env.ANTENNA_SUPABASE_URL || process.env.ANTENNA_URL || "https://bcudjloikmpcqwcptuyd.supabase.co";
352
426
  console.log("\nπŸ“‘ Antenna Status\n");
@@ -907,6 +981,7 @@ Usage:
907
981
  antenna bind --id <platform>:<user_id>
908
982
  antenna serve Start MCP server (stdio transport)
909
983
  antenna setup Interactive profile setup [--id <platform>:<user_id>]
984
+ antenna config Configure API key [--key <ant_...>]
910
985
  antenna status Show config & status [--id <platform>:<user_id>]
911
986
  antenna install-skill Install SKILL.md (detects OpenClaw + Hermes)
912
987
  antenna install-plugin Copy OpenClaw plugin template to cwd
package/lib/core.js CHANGED
@@ -735,3 +735,12 @@ export async function getMyEventMessages({ device_id, supabaseUrl, supabaseKey }
735
735
  count: (data || []).length,
736
736
  };
737
737
  }
738
+
739
+ // ─── verifyApiKey ────────────────────────────────────────────────────
740
+
741
+ export async function verifyApiKey({ key, supabaseUrl, supabaseKey }) {
742
+ const sb = getClient(supabaseUrl, supabaseKey);
743
+ const { data, error } = await sb.rpc("verify_api_key", { p_key: key });
744
+ if (error) throw new Error(error.message);
745
+ return data;
746
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-fyi",
3
- "version": "1.3.11",
3
+ "version": "1.3.12",
4
4
  "description": "Antenna \u2014 nearby people discovery. CLI + MCP server + OpenClaw skill & plugin, all in one package.",
5
5
  "type": "module",
6
6
  "bin": {