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 +3 -0
- package/lib/cli.js +94 -19
- package/lib/core.js +9 -0
- package/package.json +1 -1
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
114
|
-
|
|
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
|
-
|
|
134
|
-
|
|
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
|
-
|
|
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
|
-
|
|
298
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
+
}
|