@kamal0808/anytoany 0.1.0 → 0.3.0
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/cli.js +263 -51
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
import fs from 'node:fs';
|
|
21
21
|
import path from 'node:path';
|
|
22
22
|
import os from 'node:os';
|
|
23
|
+
import readline from 'node:readline';
|
|
24
|
+
import { execFileSync } from 'node:child_process';
|
|
25
|
+
import { fileURLToPath } from 'node:url';
|
|
23
26
|
|
|
24
27
|
const HOME = process.env.ANY_HOME || path.join(os.homedir(), '.any');
|
|
25
28
|
const CONFIG = path.join(HOME, 'config.json');
|
|
@@ -52,13 +55,17 @@ function resolve() {
|
|
|
52
55
|
const cfg = loadConfig();
|
|
53
56
|
const user = process.env.ANY_USER || cfg.user || hostnameUser();
|
|
54
57
|
const relay = process.env.ANY_RELAY_URL || cfg.relay || DEFAULT_RELAY;
|
|
55
|
-
const
|
|
56
|
-
return { user: norm(user), relay: relay.replace(/\/$/, ''),
|
|
58
|
+
const key = process.env.ANY_KEY || cfg.key || null;
|
|
59
|
+
return { user: norm(user), relay: relay.replace(/\/$/, ''), key };
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
function norm(user) {
|
|
60
63
|
if (!user) return null;
|
|
61
|
-
return user.startsWith('@') ? user : '@' + user;
|
|
64
|
+
return user.startsWith('@') ? user.toLowerCase() : '@' + user.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function validSlug(h) {
|
|
68
|
+
return /^@[a-z0-9_-]{2,30}$/.test(h);
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
function die(msg) {
|
|
@@ -66,11 +73,28 @@ function die(msg) {
|
|
|
66
73
|
process.exit(1);
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
// register (claim a handle, get your own key) — no auth needed
|
|
77
|
+
async function registerWithRelay(relay, handle, displayName) {
|
|
78
|
+
let res;
|
|
79
|
+
try {
|
|
80
|
+
res = await fetch(relay + '/register', {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: { 'content-type': 'application/json' },
|
|
83
|
+
body: JSON.stringify({ handle, displayName }),
|
|
84
|
+
});
|
|
85
|
+
} catch (e) {
|
|
86
|
+
die(`can't reach relay at ${relay} (${e.message})`);
|
|
87
|
+
}
|
|
88
|
+
const j = await res.json().catch(() => ({}));
|
|
89
|
+
if (!res.ok) die(j.error || `register failed (${res.status})`);
|
|
90
|
+
return j; // { handle, key, displayName }
|
|
91
|
+
}
|
|
92
|
+
|
|
69
93
|
async function api(relay, method, route, body) {
|
|
70
|
-
const {
|
|
94
|
+
const { key } = resolve();
|
|
71
95
|
const headers = {};
|
|
72
96
|
if (body) headers['content-type'] = 'application/json';
|
|
73
|
-
if (
|
|
97
|
+
if (key) headers['authorization'] = `Bearer ${key}`;
|
|
74
98
|
let res;
|
|
75
99
|
try {
|
|
76
100
|
res = await fetch(relay + route, {
|
|
@@ -119,12 +143,19 @@ function printMessage(m, { full = false } = {}) {
|
|
|
119
143
|
// ---- commands ------------------------------------------------------------
|
|
120
144
|
|
|
121
145
|
async function cmdRegister(args) {
|
|
122
|
-
|
|
123
|
-
if (!
|
|
146
|
+
let handle = norm(args[0]);
|
|
147
|
+
if (!handle) die('usage: any register @you [Display Name]');
|
|
148
|
+
if (!validSlug(handle)) die('handle must be 2-30 chars: a-z 0-9 _ - (e.g. @kamal)');
|
|
124
149
|
const cfg = loadConfig();
|
|
125
|
-
cfg.
|
|
150
|
+
const relay = (process.env.ANY_RELAY_URL || cfg.relay || DEFAULT_RELAY).replace(/\/$/, '');
|
|
151
|
+
const name = args.slice(1).join(' ') || null;
|
|
152
|
+
const acct = await registerWithRelay(relay, handle, name);
|
|
153
|
+
cfg.user = acct.handle;
|
|
154
|
+
cfg.key = acct.key;
|
|
155
|
+
cfg.relay = relay;
|
|
156
|
+
if (acct.displayName) cfg.displayName = acct.displayName;
|
|
126
157
|
saveConfig(cfg);
|
|
127
|
-
console.log(
|
|
158
|
+
console.log(`✓ registered as ${acct.handle}${acct.displayName ? ' (' + acct.displayName + ')' : ''}`);
|
|
128
159
|
}
|
|
129
160
|
|
|
130
161
|
async function cmdRelay(args) {
|
|
@@ -136,27 +167,20 @@ async function cmdRelay(args) {
|
|
|
136
167
|
console.log(`relay set to ${cfg.relay}`);
|
|
137
168
|
}
|
|
138
169
|
|
|
139
|
-
async function cmdToken(args) {
|
|
140
|
-
const t = args[0];
|
|
141
|
-
if (!t) die('usage: any token <shared-secret>');
|
|
142
|
-
const cfg = loadConfig();
|
|
143
|
-
cfg.token = t;
|
|
144
|
-
saveConfig(cfg);
|
|
145
|
-
console.log('token saved');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
170
|
async function cmdWhoami() {
|
|
149
|
-
const { user, relay,
|
|
150
|
-
console.log(`
|
|
151
|
-
console.log(`relay:
|
|
152
|
-
console.log(`
|
|
153
|
-
|
|
154
|
-
|
|
171
|
+
const { user, relay, key } = resolve();
|
|
172
|
+
console.log(`handle: ${user || '(unregistered — run: any setup)'}`);
|
|
173
|
+
console.log(`relay: ${relay}`);
|
|
174
|
+
console.log(`key: ${key ? 'set' : '(none — run: any setup)'}`);
|
|
175
|
+
if (key) {
|
|
176
|
+
const me = await api(relay, 'GET', '/whoami').catch(() => null);
|
|
177
|
+
if (me) console.log(`status: online as ${me.handle}${me.displayName ? ' (' + me.displayName + ')' : ''}`);
|
|
178
|
+
}
|
|
155
179
|
}
|
|
156
180
|
|
|
157
181
|
async function cmdSend(args) {
|
|
158
|
-
const {
|
|
159
|
-
if (!
|
|
182
|
+
const { key, relay } = resolve();
|
|
183
|
+
if (!key) die('not registered. Run: any setup');
|
|
160
184
|
const to = norm(args[0]);
|
|
161
185
|
if (!to) die('usage: any send @who <text | file>');
|
|
162
186
|
const rest = args.slice(1);
|
|
@@ -172,24 +196,20 @@ async function cmdSend(args) {
|
|
|
172
196
|
body = rest.join(' ');
|
|
173
197
|
}
|
|
174
198
|
|
|
175
|
-
const { id } = await api(relay, 'POST', '/send', {
|
|
199
|
+
const { id } = await api(relay, 'POST', '/send', { to, subject, body });
|
|
176
200
|
console.log(`sent → ${to} (#${id})`);
|
|
177
201
|
}
|
|
178
202
|
|
|
179
203
|
async function cmdInbox(args) {
|
|
180
|
-
const {
|
|
181
|
-
if (!
|
|
204
|
+
const { key, relay } = resolve();
|
|
205
|
+
if (!key) die('not registered. Run: any setup');
|
|
182
206
|
const all = args.includes('--all');
|
|
183
|
-
const { messages, count } = await api(
|
|
184
|
-
relay,
|
|
185
|
-
'GET',
|
|
186
|
-
`/inbox?user=${encodeURIComponent(user)}${all ? '&all=1' : ''}`,
|
|
187
|
-
);
|
|
207
|
+
const { messages, count } = await api(relay, 'GET', `/inbox${all ? '?all=1' : ''}`);
|
|
188
208
|
if (count === 0) {
|
|
189
209
|
console.log(all ? 'inbox empty' : 'no new messages');
|
|
190
210
|
return;
|
|
191
211
|
}
|
|
192
|
-
console.log(`${count} ${all ? 'message(s)' : 'new message(s)'}
|
|
212
|
+
console.log(`${count} ${all ? 'message(s)' : 'new message(s)'}:\n`);
|
|
193
213
|
for (const m of messages) {
|
|
194
214
|
printMessage(m);
|
|
195
215
|
console.log('');
|
|
@@ -208,34 +228,30 @@ async function cmdRead(args) {
|
|
|
208
228
|
}
|
|
209
229
|
|
|
210
230
|
async function cmdReply(args) {
|
|
211
|
-
const {
|
|
212
|
-
if (!
|
|
231
|
+
const { key, relay } = resolve();
|
|
232
|
+
if (!key) die('not registered. Run: any setup');
|
|
213
233
|
const id = Number(args[0]);
|
|
214
234
|
if (!id) die('usage: any reply <id> <text>');
|
|
215
235
|
const body = args.slice(1).join(' ');
|
|
216
236
|
if (!body) die('nothing to reply with');
|
|
217
|
-
const { id: newId } = await api(relay, 'POST', '/reply', { id,
|
|
237
|
+
const { id: newId } = await api(relay, 'POST', '/reply', { id, body });
|
|
218
238
|
console.log(`replied to #${id} (#${newId})`);
|
|
219
239
|
}
|
|
220
240
|
|
|
221
241
|
async function cmdWatch(args) {
|
|
222
|
-
const {
|
|
223
|
-
if (!
|
|
242
|
+
const { key, relay } = resolve();
|
|
243
|
+
if (!key) die('not registered. Run: any setup');
|
|
224
244
|
const i = args.indexOf('--interval');
|
|
225
245
|
const interval = (i >= 0 ? Number(args[i + 1]) : 5) * 1000;
|
|
226
246
|
const seen = new Set();
|
|
227
|
-
console.log(`watching inbox
|
|
247
|
+
console.log(`watching inbox (every ${interval / 1000}s). Ctrl-C to stop.\n`);
|
|
228
248
|
// Prime with existing unread so we only announce genuinely new arrivals.
|
|
229
|
-
const first = await api(relay, 'GET', `/inbox?
|
|
249
|
+
const first = await api(relay, 'GET', `/inbox?all=1`);
|
|
230
250
|
for (const m of first.messages) seen.add(m.id);
|
|
231
251
|
if (first.messages.length) console.log(`(${first.messages.length} existing message(s), waiting for new ones)\n`);
|
|
232
252
|
|
|
233
253
|
async function poll() {
|
|
234
|
-
const { messages } = await api(
|
|
235
|
-
relay,
|
|
236
|
-
'GET',
|
|
237
|
-
`/inbox?user=${encodeURIComponent(user)}&all=1`,
|
|
238
|
-
).catch(() => ({ messages: [] }));
|
|
254
|
+
const { messages } = await api(relay, 'GET', `/inbox?all=1`).catch(() => ({ messages: [] }));
|
|
239
255
|
for (const m of messages.sort((a, b) => a.ts - b.ts)) {
|
|
240
256
|
if (seen.has(m.id)) continue;
|
|
241
257
|
seen.add(m.id);
|
|
@@ -247,13 +263,208 @@ async function cmdWatch(args) {
|
|
|
247
263
|
setInterval(poll, interval);
|
|
248
264
|
}
|
|
249
265
|
|
|
266
|
+
// ---- watcher: poke (one tick) + setup (install the background watcher) ----
|
|
267
|
+
|
|
268
|
+
function deepLink(user, folder) {
|
|
269
|
+
const prompt =
|
|
270
|
+
`You are helping me (${user}) with my anytoany messages. Run \`any inbox\` to see new messages, ` +
|
|
271
|
+
`read the newest with \`any read <id>\`, and help me draft a reply. When I approve, send it with ` +
|
|
272
|
+
`\`any reply <id> "..."\`. Do not send anything until I confirm.`;
|
|
273
|
+
let url = `claude://code/new?q=${encodeURIComponent(prompt)}`;
|
|
274
|
+
if (folder) url += `&folder=${encodeURIComponent(folder)}`;
|
|
275
|
+
return url;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function fireBanner(m, link) {
|
|
279
|
+
const preview = (m.body || '').replace(/\s+/g, ' ').slice(0, 120);
|
|
280
|
+
const title = `📨 ${m.from} → you`;
|
|
281
|
+
try {
|
|
282
|
+
execFileSync(
|
|
283
|
+
'terminal-notifier',
|
|
284
|
+
['-title', title, '-subtitle', 'click to read & reply in Claude', '-message', preview, '-execute', `open '${link}'`],
|
|
285
|
+
{ stdio: 'ignore' },
|
|
286
|
+
);
|
|
287
|
+
return;
|
|
288
|
+
} catch {}
|
|
289
|
+
try {
|
|
290
|
+
execFileSync('osascript', ['-e', `display notification ${JSON.stringify(preview)} with title ${JSON.stringify(title)}`], { stdio: 'ignore' });
|
|
291
|
+
} catch {}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// one watch tick: alert on any new unread message. Run by launchd every ~15s.
|
|
295
|
+
async function cmdPoke() {
|
|
296
|
+
const { user, relay, key: authKey } = resolve();
|
|
297
|
+
if (!authKey) die('not registered (run: any setup)');
|
|
298
|
+
const slug = user.replace('@', '');
|
|
299
|
+
const seenPath = path.join(HOME, `poke-seen-${slug}.txt`);
|
|
300
|
+
const lockPath = path.join(HOME, `poke-${slug}.lock`);
|
|
301
|
+
fs.mkdirSync(HOME, { recursive: true });
|
|
302
|
+
try {
|
|
303
|
+
fs.mkdirSync(lockPath); // single-flight
|
|
304
|
+
} catch {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
const { messages } = await api(relay, 'GET', `/inbox`);
|
|
309
|
+
if (!messages || !messages.length) return;
|
|
310
|
+
let seen = new Set();
|
|
311
|
+
try {
|
|
312
|
+
seen = new Set(fs.readFileSync(seenPath, 'utf8').split('\n').filter(Boolean).map(Number));
|
|
313
|
+
} catch {}
|
|
314
|
+
const link = deepLink(user, process.env.ANY_FOLDER || loadConfig().folder || null);
|
|
315
|
+
for (const m of messages) {
|
|
316
|
+
if (seen.has(m.id)) continue;
|
|
317
|
+
fireBanner(m, link);
|
|
318
|
+
fs.appendFileSync(seenPath, m.id + '\n');
|
|
319
|
+
console.log(`alerted #${m.id} from ${m.from}`);
|
|
320
|
+
}
|
|
321
|
+
} finally {
|
|
322
|
+
try {
|
|
323
|
+
fs.rmdirSync(lockPath);
|
|
324
|
+
} catch {}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function promptLine(q) {
|
|
329
|
+
return new Promise((res) => {
|
|
330
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
331
|
+
rl.question(q, (a) => {
|
|
332
|
+
rl.close();
|
|
333
|
+
res(a.trim());
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function have(cmd) {
|
|
339
|
+
try {
|
|
340
|
+
execFileSync('/bin/bash', ['-lc', `command -v ${cmd}`], { stdio: 'ignore' });
|
|
341
|
+
return true;
|
|
342
|
+
} catch {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// one-command onboarding: claim a handle (get your key) + install the watcher
|
|
348
|
+
async function cmdSetup() {
|
|
349
|
+
const cfg = loadConfig();
|
|
350
|
+
const tty = process.stdin.isTTY;
|
|
351
|
+
const relay = (process.env.ANY_RELAY_URL || cfg.relay || DEFAULT_RELAY).replace(/\/$/, '');
|
|
352
|
+
|
|
353
|
+
if (!cfg.key) {
|
|
354
|
+
// claim a new account
|
|
355
|
+
let handle = norm(process.env.ANY_USER || cfg.user);
|
|
356
|
+
if (!handle) {
|
|
357
|
+
if (!tty) die('no handle — run interactively, or: any register @you first');
|
|
358
|
+
handle = norm(await promptLine('Pick a handle (e.g. @yash): '));
|
|
359
|
+
}
|
|
360
|
+
if (!validSlug(handle)) die('handle must be 2-30 chars: a-z 0-9 _ - (e.g. @kamal)');
|
|
361
|
+
let name = cfg.displayName || process.env.ANY_NAME || null;
|
|
362
|
+
if (tty && !name) name = (await promptLine('Display name (optional): ')) || null;
|
|
363
|
+
const acct = await registerWithRelay(relay, handle, name);
|
|
364
|
+
cfg.user = acct.handle;
|
|
365
|
+
cfg.key = acct.key;
|
|
366
|
+
cfg.relay = relay;
|
|
367
|
+
if (acct.displayName) cfg.displayName = acct.displayName;
|
|
368
|
+
saveConfig(cfg);
|
|
369
|
+
console.log(`✓ registered as ${acct.handle}${acct.displayName ? ' (' + acct.displayName + ')' : ''} → ${relay}`);
|
|
370
|
+
} else {
|
|
371
|
+
if (!cfg.relay) {
|
|
372
|
+
cfg.relay = relay;
|
|
373
|
+
saveConfig(cfg);
|
|
374
|
+
}
|
|
375
|
+
console.log(`✓ already registered as ${cfg.user} → ${cfg.relay}`);
|
|
376
|
+
}
|
|
377
|
+
const user = cfg.user;
|
|
378
|
+
|
|
379
|
+
const me = await api(cfg.relay, 'GET', '/whoami').catch(() => null);
|
|
380
|
+
console.log(me ? `✓ connected as ${me.handle}` : '! could not verify with relay (continuing)');
|
|
381
|
+
|
|
382
|
+
if (process.platform !== 'darwin') {
|
|
383
|
+
console.log('\nThe background banner watcher is macOS-only for now.');
|
|
384
|
+
console.log('You can still use: any inbox / any read <id> / any reply <id> "…"');
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ensure a stable global install (npx runs from a temp cache the watcher can't rely on)
|
|
389
|
+
if (!have('any')) {
|
|
390
|
+
console.log('Installing @kamal0808/anytoany globally…');
|
|
391
|
+
try {
|
|
392
|
+
execFileSync('npm', ['install', '-g', '@kamal0808/anytoany'], { stdio: 'inherit' });
|
|
393
|
+
} catch {}
|
|
394
|
+
}
|
|
395
|
+
if (!have('terminal-notifier')) {
|
|
396
|
+
console.log('Installing terminal-notifier (clickable banners)…');
|
|
397
|
+
try {
|
|
398
|
+
execFileSync('brew', ['install', 'terminal-notifier'], { stdio: 'inherit' });
|
|
399
|
+
} catch {
|
|
400
|
+
console.log('! could not auto-install — run: brew install terminal-notifier');
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// resolve a stable cli.js path for the LaunchAgent
|
|
405
|
+
let cliForAgent = fileURLToPath(import.meta.url);
|
|
406
|
+
try {
|
|
407
|
+
const anyBin = execFileSync('/bin/bash', ['-lc', 'command -v any'], { encoding: 'utf8' }).trim();
|
|
408
|
+
if (anyBin) cliForAgent = fs.realpathSync(anyBin);
|
|
409
|
+
} catch {}
|
|
410
|
+
|
|
411
|
+
const key = user.replace('@', '');
|
|
412
|
+
const label = `com.anytoany.poke.${key}`;
|
|
413
|
+
const laDir = path.join(os.homedir(), 'Library', 'LaunchAgents');
|
|
414
|
+
fs.mkdirSync(laDir, { recursive: true });
|
|
415
|
+
const plistPath = path.join(laDir, `${label}.plist`);
|
|
416
|
+
const nodeDir = path.dirname(process.execPath);
|
|
417
|
+
fs.writeFileSync(
|
|
418
|
+
plistPath,
|
|
419
|
+
`<?xml version="1.0" encoding="UTF-8"?>
|
|
420
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
421
|
+
<plist version="1.0">
|
|
422
|
+
<dict>
|
|
423
|
+
<key>Label</key><string>${label}</string>
|
|
424
|
+
<key>ProgramArguments</key>
|
|
425
|
+
<array><string>${process.execPath}</string><string>${cliForAgent}</string><string>poke</string></array>
|
|
426
|
+
<key>EnvironmentVariables</key>
|
|
427
|
+
<dict>
|
|
428
|
+
<key>ANY_USER</key><string>${user}</string>
|
|
429
|
+
<key>PATH</key><string>${nodeDir}:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
430
|
+
</dict>
|
|
431
|
+
<key>StartInterval</key><integer>15</integer>
|
|
432
|
+
<key>RunAtLoad</key><true/>
|
|
433
|
+
<key>StandardOutPath</key><string>/tmp/any-poke-${key}.log</string>
|
|
434
|
+
<key>StandardErrorPath</key><string>/tmp/any-poke-${key}.err</string>
|
|
435
|
+
</dict>
|
|
436
|
+
</plist>
|
|
437
|
+
`,
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
// prime seen-file so existing messages don't all alert at once
|
|
441
|
+
try {
|
|
442
|
+
const { messages } = await api(cfg.relay, 'GET', `/inbox?all=1`);
|
|
443
|
+
fs.writeFileSync(path.join(HOME, `poke-seen-${key}.txt`), (messages || []).map((m) => m.id).join('\n') + '\n');
|
|
444
|
+
} catch {}
|
|
445
|
+
|
|
446
|
+
const uid = process.getuid();
|
|
447
|
+
try {
|
|
448
|
+
execFileSync('launchctl', ['bootout', `gui/${uid}/${label}`], { stdio: 'ignore' });
|
|
449
|
+
} catch {}
|
|
450
|
+
try {
|
|
451
|
+
execFileSync('launchctl', ['bootstrap', `gui/${uid}`, plistPath], { stdio: 'ignore' });
|
|
452
|
+
console.log('✓ background watcher installed');
|
|
453
|
+
} catch {
|
|
454
|
+
console.log(`! load the watcher manually:\n launchctl bootstrap gui/${uid} ${plistPath}`);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
console.log(`\nDone — you're on the network as ${user}.`);
|
|
458
|
+
console.log('New message → a banner pops → click it → Claude Desktop opens to read & reply.');
|
|
459
|
+
}
|
|
460
|
+
|
|
250
461
|
function cmdHelp() {
|
|
251
462
|
console.log(`any — tiny message relay CLI
|
|
252
463
|
|
|
253
|
-
any
|
|
464
|
+
any setup one-command onboarding (claim @handle + banner watcher)
|
|
465
|
+
any register @you [Name] claim a handle (get your key)
|
|
254
466
|
any relay <url> point at the relay server
|
|
255
|
-
any
|
|
256
|
-
any whoami show identity + relay status
|
|
467
|
+
any whoami show your handle + relay status
|
|
257
468
|
|
|
258
469
|
any send @who <text|file> send a message (a file becomes subject+body)
|
|
259
470
|
any inbox [--all] list messages for you (default: unread only)
|
|
@@ -271,9 +482,10 @@ Env: ANY_USER, ANY_RELAY_URL, ANY_HOME
|
|
|
271
482
|
const [, , cmd, ...args] = process.argv;
|
|
272
483
|
|
|
273
484
|
const table = {
|
|
485
|
+
setup: cmdSetup,
|
|
486
|
+
poke: cmdPoke,
|
|
274
487
|
register: cmdRegister,
|
|
275
488
|
relay: cmdRelay,
|
|
276
|
-
token: cmdToken,
|
|
277
489
|
whoami: cmdWhoami,
|
|
278
490
|
send: cmdSend,
|
|
279
491
|
inbox: cmdInbox,
|