@palmyr/cli 1.5.6 → 1.7.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/README.md +39 -4
- package/dist/app.d.ts +3 -0
- package/dist/app.js +1 -0
- package/dist/app.js.map +1 -1
- package/dist/cli.js +760 -45
- package/dist/cli.js.map +1 -1
- package/dist/credential-store.d.ts +4 -0
- package/dist/credential-store.js +56 -1
- package/dist/credential-store.js.map +1 -1
- package/dist/pay.js +36 -6
- package/dist/pay.js.map +1 -1
- package/dist/sdk.d.ts +34 -1
- package/dist/sdk.js +60 -2
- package/dist/sdk.js.map +1 -1
- package/dist/vault.d.ts +49 -2
- package/dist/vault.js +241 -68
- package/dist/vault.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -213,13 +213,35 @@ function subcommandHelp(command, subcommand, options) {
|
|
|
213
213
|
const WALLET_HELP = {
|
|
214
214
|
create: [
|
|
215
215
|
{ flag: '--name <name>', desc: 'Wallet name', hint: 'default: "My Wallet"' },
|
|
216
|
-
{ flag: '--managed', desc: 'Create managed wallet with human oversight via passkey' },
|
|
217
|
-
{ flag: '--
|
|
216
|
+
{ flag: '--managed', desc: 'Create managed wallet with human oversight via passkey (single-create only)' },
|
|
217
|
+
{ flag: '--solana', desc: 'Materialize the Solana account only', hint: 'default: both chains' },
|
|
218
|
+
{ flag: '--base', desc: 'Materialize the Base/EVM account only', hint: 'pair with --solana for both (default)' },
|
|
219
|
+
{ flag: '--tag <name>', desc: 'Folder-like grouping tag', hint: 'e.g. palmyr-demo — required with --count' },
|
|
220
|
+
{ flag: '--count <N>', desc: 'Bulk-create N wallets in one call (1-500)', hint: 'unmanaged only; requires --tag' },
|
|
221
|
+
{ flag: '--name-prefix <p>', desc: 'Bulk name prefix; suffixed `-001..-N`', hint: 'default: same as --tag' },
|
|
218
222
|
],
|
|
219
223
|
import: [
|
|
220
224
|
{ flag: '--mnemonic <words>', desc: 'BIP-39 mnemonic phrase (required)' },
|
|
221
225
|
{ flag: '--name <name>', desc: 'Wallet name', hint: 'default: "Imported Wallet"' },
|
|
222
226
|
{ flag: '--managed', desc: 'Import as managed wallet' },
|
|
227
|
+
{ flag: '--solana', desc: 'Materialize the Solana account only' },
|
|
228
|
+
{ flag: '--base', desc: 'Materialize the Base/EVM account only' },
|
|
229
|
+
{ flag: '--tag <name>', desc: 'Assign a tag at import time' },
|
|
230
|
+
],
|
|
231
|
+
tags: [
|
|
232
|
+
{ flag: '(no args)', desc: 'List all tags with wallet count, chains, and date range' },
|
|
233
|
+
],
|
|
234
|
+
tag: [
|
|
235
|
+
{ flag: '<WALLET_ID>', desc: 'Wallet id or name (positional or --id)' },
|
|
236
|
+
{ flag: '<TAG>', desc: 'Tag to assign (positional)' },
|
|
237
|
+
{ flag: '--clear', desc: 'Remove the tag from this wallet instead of assigning one' },
|
|
238
|
+
],
|
|
239
|
+
'tag-delete': [
|
|
240
|
+
{ flag: '<TAG>', desc: 'Tag to wipe (positional)' },
|
|
241
|
+
{ flag: '--confirm', desc: 'Required — deletes every wallet sharing this tag (irreversible)' },
|
|
242
|
+
],
|
|
243
|
+
list: [
|
|
244
|
+
{ flag: '--tag <name>', desc: 'Filter to wallets in this tag' },
|
|
223
245
|
],
|
|
224
246
|
'sign-message': [
|
|
225
247
|
{ flag: '<WALLET_ID>', desc: 'Wallet ID (positional or --id)' },
|
|
@@ -355,6 +377,327 @@ const WALLET_HELP = {
|
|
|
355
377
|
{ flag: '--dst-decimals <n>', desc: 'Dest token decimals', hint: 'default 6 (USDC-like)' },
|
|
356
378
|
],
|
|
357
379
|
};
|
|
380
|
+
const PHONE_HELP = {
|
|
381
|
+
search: [
|
|
382
|
+
{ flag: '--country <ISO>', desc: 'Country code', hint: 'default US (e.g. US, GB, AE)' },
|
|
383
|
+
{ flag: '--limit <N>', desc: 'Max results to return' },
|
|
384
|
+
{ flag: '(price)', desc: 'Free — no payment required' },
|
|
385
|
+
{ flag: '(example)', desc: 'palmyr phone search --country US --json' },
|
|
386
|
+
],
|
|
387
|
+
buy: [
|
|
388
|
+
{ flag: '--country <ISO>', desc: 'Country code (required)', hint: 'e.g. US, GB' },
|
|
389
|
+
{ flag: '--area <code>', desc: 'Preferred area code (optional, US only)' },
|
|
390
|
+
{ flag: '(price)', desc: '$3.00 per number provisioned' },
|
|
391
|
+
{ flag: '(example)', desc: 'palmyr phone buy --country US' },
|
|
392
|
+
],
|
|
393
|
+
sms: [
|
|
394
|
+
{ flag: '--id <PHONE_ID>', desc: 'Source phone number id (required)' },
|
|
395
|
+
{ flag: '--to <+E.164>', desc: 'Destination phone number (required)', hint: 'e.g. +15551234567' },
|
|
396
|
+
{ flag: '--body <text>', desc: 'Message body (required)' },
|
|
397
|
+
{ flag: '(price)', desc: '$0.05 per SMS sent' },
|
|
398
|
+
{ flag: '(example)', desc: 'palmyr phone sms --id PN_abc --to +15551234567 --body "hi"' },
|
|
399
|
+
],
|
|
400
|
+
call: [
|
|
401
|
+
{ flag: '--id <PHONE_ID>', desc: 'Source phone number id (required)' },
|
|
402
|
+
{ flag: '--to <+E.164>', desc: 'Destination phone number (required)' },
|
|
403
|
+
{ flag: '--tts <text>', desc: 'Text-to-speech to play on answer (optional)' },
|
|
404
|
+
{ flag: '(price)', desc: '$0.10 per call placed' },
|
|
405
|
+
{ flag: '(example)', desc: 'palmyr phone call --id PN_abc --to +15551234567 --tts "hello"' },
|
|
406
|
+
],
|
|
407
|
+
list: [
|
|
408
|
+
{ flag: '(no args)', desc: 'List phone numbers owned by your wallet' },
|
|
409
|
+
{ flag: '(price)', desc: '$0.01 per call' },
|
|
410
|
+
{ flag: '(example)', desc: 'palmyr phone list --json' },
|
|
411
|
+
],
|
|
412
|
+
messages: [
|
|
413
|
+
{ flag: '--id <PHONE_ID>', desc: 'Phone number id to read SMS for (required; positional also accepted)' },
|
|
414
|
+
{ flag: '(price)', desc: '$0.02 per call' },
|
|
415
|
+
{ flag: '(example)', desc: 'palmyr phone messages --id PN_abc' },
|
|
416
|
+
],
|
|
417
|
+
calls: [
|
|
418
|
+
{ flag: '--id <PHONE_ID>', desc: 'Phone number id to list calls for (required; positional also accepted)' },
|
|
419
|
+
{ flag: '(price)', desc: '$0.02 per call' },
|
|
420
|
+
{ flag: '(example)', desc: 'palmyr phone calls --id PN_abc' },
|
|
421
|
+
],
|
|
422
|
+
release: [
|
|
423
|
+
{ flag: '--id <PHONE_ID>', desc: 'Phone number id to release (required; positional also accepted)' },
|
|
424
|
+
{ flag: '(price)', desc: '$0.01 per release (stops monthly Telnyx billing)' },
|
|
425
|
+
{ flag: '(example)', desc: 'palmyr phone release --id PN_abc' },
|
|
426
|
+
],
|
|
427
|
+
'call-info': [
|
|
428
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id (required; --id and positional also accepted)' },
|
|
429
|
+
{ flag: '(price)', desc: '$0.02 per lookup' },
|
|
430
|
+
{ flag: '(example)', desc: 'palmyr phone call-info --call CC_abc' },
|
|
431
|
+
],
|
|
432
|
+
speak: [
|
|
433
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id of a live call (required)' },
|
|
434
|
+
{ flag: '--text <text>', desc: 'TTS text to speak (required; --tts alias accepted)' },
|
|
435
|
+
{ flag: '--voice <name>', desc: 'TTS voice (optional, provider default otherwise)' },
|
|
436
|
+
{ flag: '--language <code>', desc: 'TTS language code (optional, e.g. en-US)' },
|
|
437
|
+
{ flag: '(price)', desc: '$0.08 per speak action' },
|
|
438
|
+
{ flag: '(example)', desc: 'palmyr phone speak --call CC_abc --text "please hold"' },
|
|
439
|
+
],
|
|
440
|
+
play: [
|
|
441
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id of a live call (required)' },
|
|
442
|
+
{ flag: '--url <audio_url>', desc: 'Public audio URL to play (required; --audio-url alias accepted)' },
|
|
443
|
+
{ flag: '(price)', desc: '$0.08 per playback' },
|
|
444
|
+
{ flag: '(example)', desc: 'palmyr phone play --call CC_abc --url https://example.com/hold.mp3' },
|
|
445
|
+
],
|
|
446
|
+
dtmf: [
|
|
447
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id of a live call (required)' },
|
|
448
|
+
{ flag: '--digits <seq>', desc: 'DTMF digit sequence (required; positional also accepted)', hint: 'e.g. "1234#"' },
|
|
449
|
+
{ flag: '(price)', desc: '$0.02 per DTMF send' },
|
|
450
|
+
{ flag: '(example)', desc: 'palmyr phone dtmf --call CC_abc --digits "1234#"' },
|
|
451
|
+
],
|
|
452
|
+
gather: [
|
|
453
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id of a live call (required)' },
|
|
454
|
+
{ flag: '--min-digits <N>', desc: 'Minimum digits to collect (optional)' },
|
|
455
|
+
{ flag: '--max-digits <N>', desc: 'Maximum digits to collect (optional)' },
|
|
456
|
+
{ flag: '--timeout <ms>', desc: 'Per-input timeout in milliseconds (optional)' },
|
|
457
|
+
{ flag: '--terminating-digit <d>', desc: 'Digit that ends collection (optional, e.g. "#")' },
|
|
458
|
+
{ flag: '--prompt <text>', desc: 'Optional TTS prompt to play before gathering' },
|
|
459
|
+
{ flag: '--prompt-voice <name>', desc: 'TTS voice for the prompt (optional)' },
|
|
460
|
+
{ flag: '(price)', desc: '$0.08 per gather action' },
|
|
461
|
+
{ flag: '(example)', desc: 'palmyr phone gather --call CC_abc --max-digits 4 --terminating-digit "#" --prompt "Enter PIN"' },
|
|
462
|
+
],
|
|
463
|
+
record: [
|
|
464
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id of a live call (required)' },
|
|
465
|
+
{ flag: '--format <fmt>', desc: 'Recording format (optional, provider default otherwise)' },
|
|
466
|
+
{ flag: '(price)', desc: '$0.10 per record start' },
|
|
467
|
+
{ flag: '(example)', desc: 'palmyr phone record --call CC_abc --format mp3' },
|
|
468
|
+
],
|
|
469
|
+
'record-stop': [
|
|
470
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id of a live call (required)' },
|
|
471
|
+
{ flag: '(price)', desc: '$0.02 per stop' },
|
|
472
|
+
{ flag: '(example)', desc: 'palmyr phone record-stop --call CC_abc' },
|
|
473
|
+
],
|
|
474
|
+
hangup: [
|
|
475
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id of a live call (required)' },
|
|
476
|
+
{ flag: '(price)', desc: '$0.02 per hangup' },
|
|
477
|
+
{ flag: '(example)', desc: 'palmyr phone hangup --call CC_abc' },
|
|
478
|
+
],
|
|
479
|
+
answer: [
|
|
480
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id of an inbound call (required)' },
|
|
481
|
+
{ flag: '(price)', desc: '$0.02 per answer' },
|
|
482
|
+
{ flag: '(example)', desc: 'palmyr phone answer --call CC_abc' },
|
|
483
|
+
],
|
|
484
|
+
transfer: [
|
|
485
|
+
{ flag: '--call <CALL_ID>', desc: 'Call control id of a live call (required)' },
|
|
486
|
+
{ flag: '--to <+E.164>', desc: 'Destination phone number to bridge into (required)' },
|
|
487
|
+
{ flag: '(price)', desc: '$0.10 per transfer' },
|
|
488
|
+
{ flag: '(example)', desc: 'palmyr phone transfer --call CC_abc --to +15557654321' },
|
|
489
|
+
],
|
|
490
|
+
};
|
|
491
|
+
const EMAIL_HELP = {
|
|
492
|
+
create: [
|
|
493
|
+
{ flag: '--name <name>', desc: 'Inbox name (required)' },
|
|
494
|
+
{ flag: '--domain <domain>', desc: 'Wallet-owned domain to host the inbox on (optional)', hint: 'default: Palmyr-hosted domain' },
|
|
495
|
+
{ flag: '--wallet <id|name>', desc: 'Wallet to own the inbox (optional)' },
|
|
496
|
+
{ flag: '(price)', desc: '$2.00 per inbox provisioned' },
|
|
497
|
+
{ flag: '(example)', desc: 'palmyr email create --name agent --domain example.com' },
|
|
498
|
+
],
|
|
499
|
+
list: [
|
|
500
|
+
{ flag: '(no args)', desc: 'List inboxes owned by your wallet' },
|
|
501
|
+
{ flag: '(price)', desc: '$0.01 per call' },
|
|
502
|
+
{ flag: '(example)', desc: 'palmyr email list --json' },
|
|
503
|
+
],
|
|
504
|
+
status: [
|
|
505
|
+
{ flag: '<domain>', desc: 'Domain to check (positional or --domain)', hint: 'e.g. example.com' },
|
|
506
|
+
{ flag: '(price)', desc: '$0.01 per call' },
|
|
507
|
+
{ flag: '(example)', desc: 'palmyr email status example.com' },
|
|
508
|
+
],
|
|
509
|
+
register: [
|
|
510
|
+
{ flag: '<domain>', desc: 'Wallet-owned domain to (re-)register with Mailgun (positional or --domain)' },
|
|
511
|
+
{ flag: '(price)', desc: '$0.05 per registration' },
|
|
512
|
+
{ flag: '(example)', desc: 'palmyr email register example.com' },
|
|
513
|
+
],
|
|
514
|
+
read: [
|
|
515
|
+
{ flag: '--id <INBOX_ID>', desc: 'Inbox id (required; positional also accepted)' },
|
|
516
|
+
{ flag: '(price)', desc: '$0.02 per call' },
|
|
517
|
+
{ flag: '(example)', desc: 'palmyr email read --id INB_abc123' },
|
|
518
|
+
],
|
|
519
|
+
send: [
|
|
520
|
+
{ flag: '--id <INBOX_ID>', desc: 'Source inbox id (required)' },
|
|
521
|
+
{ flag: '--to <addr>', desc: 'Destination email (required)' },
|
|
522
|
+
{ flag: '--subject <text>', desc: 'Subject line (required)' },
|
|
523
|
+
{ flag: '--body <text>', desc: 'Message body (required)' },
|
|
524
|
+
{ flag: '(price)', desc: '$0.08 per email sent' },
|
|
525
|
+
{ flag: '(example)', desc: 'palmyr email send --id INB_abc --to user@x.com --subject Hi --body "..."' },
|
|
526
|
+
],
|
|
527
|
+
threads: [
|
|
528
|
+
{ flag: '--id <INBOX_ID>', desc: 'Inbox id (required; positional also accepted)' },
|
|
529
|
+
{ flag: '(price)', desc: '$0.02 per call' },
|
|
530
|
+
{ flag: '(example)', desc: 'palmyr email threads --id INB_abc123' },
|
|
531
|
+
],
|
|
532
|
+
};
|
|
533
|
+
const COMPUTE_HELP = {
|
|
534
|
+
plans: [
|
|
535
|
+
{ flag: '--location <loc>', desc: 'Filter to types deployable in this datacenter (optional)', hint: 'e.g. fsn1, nbg1, hel1, ash, hil' },
|
|
536
|
+
{ flag: '(price)', desc: 'Free — live discovery from Hetzner' },
|
|
537
|
+
{ flag: '(example)', desc: 'palmyr compute plans --location fsn1 --json' },
|
|
538
|
+
],
|
|
539
|
+
locations: [
|
|
540
|
+
{ flag: '(no args)', desc: 'List Hetzner datacenters + per-location server-type availability' },
|
|
541
|
+
{ flag: '(price)', desc: 'Free' },
|
|
542
|
+
],
|
|
543
|
+
'install-recipes': [
|
|
544
|
+
{ flag: '(no args)', desc: 'List available agent install recipes (hermes, openclaw, …)' },
|
|
545
|
+
{ flag: '(price)', desc: 'Free' },
|
|
546
|
+
],
|
|
547
|
+
'ssh-key': [
|
|
548
|
+
{ flag: 'add <pubkey-file>', desc: 'Upload a key to Hetzner', hint: '[--name "label"]' },
|
|
549
|
+
{ flag: 'list', desc: 'List uploaded Hetzner SSH keys' },
|
|
550
|
+
{ flag: 'delete <id>', desc: 'Remove a Hetzner SSH key' },
|
|
551
|
+
{ flag: '(price)', desc: 'add $0.10 · list $0.01 · delete $0.01' },
|
|
552
|
+
],
|
|
553
|
+
deploy: [
|
|
554
|
+
{ flag: '--type <name>', desc: 'Hetzner server type', hint: 'default cx23' },
|
|
555
|
+
{ flag: '--name <name>', desc: 'Server name', hint: 'default agent-<timestamp>' },
|
|
556
|
+
{ flag: '--location <loc>', desc: 'Hetzner datacenter (optional)', hint: 'e.g. fsn1, nbg1' },
|
|
557
|
+
{ flag: '--install <recipes>', desc: 'Comma-separated install recipes', hint: 'e.g. hermes,openclaw' },
|
|
558
|
+
{ flag: '--no-install', desc: 'Skip cloud-init entirely (vanilla Ubuntu)' },
|
|
559
|
+
{ flag: '--generate-ssh-key', desc: 'GOLDEN PATH (default): generate a fresh keypair locally' },
|
|
560
|
+
{ flag: '--pubkey-file <path>', desc: 'Use an existing public key from disk' },
|
|
561
|
+
{ flag: '--pubkey "ssh-..."', desc: 'Use an inline public key string' },
|
|
562
|
+
{ flag: '--ssh-key <id>', desc: 'Numeric Hetzner key id (already uploaded)' },
|
|
563
|
+
{ flag: '--no-wait', desc: 'Return immediately instead of waiting for SSH-ready' },
|
|
564
|
+
{ flag: '--wait-timeout <s>', desc: 'Override readiness timeout (30–900s)' },
|
|
565
|
+
{ flag: '(price)', desc: '$6.00 per deploy (Hetzner billing flows through)' },
|
|
566
|
+
{ flag: '(example)', desc: 'palmyr compute deploy --type cx23 --install hermes' },
|
|
567
|
+
],
|
|
568
|
+
wait: [
|
|
569
|
+
{ flag: '<name|id>', desc: 'Server (positional, name or numeric id)' },
|
|
570
|
+
{ flag: '--install <recipes>', desc: 'Also gate on the install marker file (e.g. hermes)' },
|
|
571
|
+
{ flag: '--key <path>', desc: 'Path to the private key for SSH verification' },
|
|
572
|
+
{ flag: '--wait-timeout <s>', desc: 'Override readiness timeout (30–900s)' },
|
|
573
|
+
{ flag: '(price)', desc: '$0.01 per readiness poll (server status check)' },
|
|
574
|
+
{ flag: '(example)', desc: 'palmyr compute wait my-vps --install hermes' },
|
|
575
|
+
],
|
|
576
|
+
ssh: [
|
|
577
|
+
{ flag: '<name|id>', desc: 'Server (positional)' },
|
|
578
|
+
{ flag: '(price)', desc: 'Free — local cache lookup only' },
|
|
579
|
+
{ flag: '(example)', desc: 'palmyr compute ssh my-vps' },
|
|
580
|
+
],
|
|
581
|
+
exec: [
|
|
582
|
+
{ flag: '<name|id> -- <cmd> [args...]', desc: 'Run a one-shot command via Palmyr SSH bridge' },
|
|
583
|
+
{ flag: '--timeout <s>', desc: 'Command timeout (1–120s)' },
|
|
584
|
+
{ flag: '(price)', desc: '$0.05 per command' },
|
|
585
|
+
{ flag: '(example)', desc: 'palmyr compute exec my-vps -- systemctl status openclaw' },
|
|
586
|
+
],
|
|
587
|
+
rename: [
|
|
588
|
+
{ flag: '<name|id> <new-name>', desc: 'Rename server (metadata-only, no reboot)' },
|
|
589
|
+
{ flag: '(price)', desc: '$0.01 per rename' },
|
|
590
|
+
],
|
|
591
|
+
'reset-password': [
|
|
592
|
+
{ flag: '<name|id>', desc: 'Rotate the root password (Hetzner-side)' },
|
|
593
|
+
{ flag: '(price)', desc: '$0.10 per action' },
|
|
594
|
+
],
|
|
595
|
+
console: [
|
|
596
|
+
{ flag: '<name|id>', desc: 'Get a noVNC console URL (break-glass)' },
|
|
597
|
+
{ flag: '(price)', desc: '$0.10 per action' },
|
|
598
|
+
],
|
|
599
|
+
reboot: [
|
|
600
|
+
{ flag: '<name|id>', desc: 'Reboot the server' },
|
|
601
|
+
{ flag: '(price)', desc: '$0.10 per action' },
|
|
602
|
+
],
|
|
603
|
+
'setup-ssh': [
|
|
604
|
+
{ flag: '--id <SERVER_ID>', desc: 'Server id (required; positional also accepted)' },
|
|
605
|
+
{ flag: '--pubkey-file <path>', desc: 'Public key file to inject' },
|
|
606
|
+
{ flag: '--pubkey "ssh-..."', desc: 'Inline public key string' },
|
|
607
|
+
{ flag: '(price)', desc: '$0.01 per call' },
|
|
608
|
+
{ flag: '(example)', desc: 'palmyr compute setup-ssh --id 12345 --pubkey-file ~/.ssh/id_ed25519.pub' },
|
|
609
|
+
],
|
|
610
|
+
list: [
|
|
611
|
+
{ flag: '(no args)', desc: 'List your deployed servers' },
|
|
612
|
+
{ flag: '(price)', desc: '$0.01 per call' },
|
|
613
|
+
],
|
|
614
|
+
delete: [
|
|
615
|
+
{ flag: '--id <SERVER_ID>', desc: 'Server id (required; positional also accepted)' },
|
|
616
|
+
{ flag: '(price)', desc: '$0.10 per deletion (Hetzner billing stops on confirm)' },
|
|
617
|
+
],
|
|
618
|
+
};
|
|
619
|
+
const DOMAIN_HELP = {
|
|
620
|
+
check: [
|
|
621
|
+
{ flag: '--name <domain>', desc: 'Domain or root name (positional also accepted)' },
|
|
622
|
+
{ flag: '(price)', desc: 'Free — availability lookup only' },
|
|
623
|
+
{ flag: '(example)', desc: 'palmyr domain check example.dev' },
|
|
624
|
+
],
|
|
625
|
+
pricing: [
|
|
626
|
+
{ flag: '--name <root>', desc: 'Root name to price across TLDs (positional also accepted)' },
|
|
627
|
+
{ flag: '(price)', desc: 'Free' },
|
|
628
|
+
{ flag: '(example)', desc: 'palmyr domain pricing example' },
|
|
629
|
+
],
|
|
630
|
+
buy: [
|
|
631
|
+
{ flag: '--name <domain>', desc: 'Fully-qualified domain (required, e.g. example.dev)' },
|
|
632
|
+
{ flag: '(price)', desc: 'Dynamic — registrar cost × 1.25 markup (charged via x402)' },
|
|
633
|
+
{ flag: '(example)', desc: 'palmyr domain buy --name example.dev' },
|
|
634
|
+
],
|
|
635
|
+
list: [
|
|
636
|
+
{ flag: '(no args)', desc: 'List domains owned or shared with your wallet' },
|
|
637
|
+
{ flag: '(price)', desc: '$0.0001 ownership-proof micro-payment' },
|
|
638
|
+
],
|
|
639
|
+
dns: [
|
|
640
|
+
{ flag: '--name <domain>', desc: 'Domain to read DNS for (positional also accepted)' },
|
|
641
|
+
{ flag: '(price)', desc: '$0.0001 ownership-proof micro-payment' },
|
|
642
|
+
],
|
|
643
|
+
'transfer-ownership': [
|
|
644
|
+
{ flag: '--name <domain>', desc: 'Domain to transfer (required)' },
|
|
645
|
+
{ flag: '--to <wallet>', desc: 'Recipient wallet address (required)' },
|
|
646
|
+
{ flag: '(price)', desc: '$0.0001 ownership-proof micro-payment' },
|
|
647
|
+
],
|
|
648
|
+
share: [
|
|
649
|
+
{ flag: '--name <domain>', desc: 'Domain to share (required)' },
|
|
650
|
+
{ flag: '--with <wallet>', desc: 'Wallet to grant shared access (required)' },
|
|
651
|
+
{ flag: '(price)', desc: '$0.0001 ownership-proof micro-payment' },
|
|
652
|
+
],
|
|
653
|
+
unshare: [
|
|
654
|
+
{ flag: '--name <domain>', desc: 'Domain to revoke from (required)' },
|
|
655
|
+
{ flag: '--from <wallet>', desc: 'Wallet to revoke (required)' },
|
|
656
|
+
{ flag: '(price)', desc: '$0.0001 ownership-proof micro-payment' },
|
|
657
|
+
],
|
|
658
|
+
};
|
|
659
|
+
const CHAT_HELP = {
|
|
660
|
+
run: [
|
|
661
|
+
{ flag: '"<intent>"', desc: 'Plain-string intent (positional or --intent)' },
|
|
662
|
+
{ flag: '--budget <USDC>', desc: 'Max spend cap (required, positive USDC)' },
|
|
663
|
+
{ flag: '--quality <q>', desc: 'Quality tier', hint: 'fast | cheap | best (default best)' },
|
|
664
|
+
{ flag: '--execute', desc: 'Auto-execute the plan once generated' },
|
|
665
|
+
{ flag: '--auto-approve-under <USDC>', desc: 'Skip approval prompts for steps cheaper than this' },
|
|
666
|
+
{ flag: '(price)', desc: '$0.10 orchestration fee + sum of per-step costs (capped by --budget)' },
|
|
667
|
+
{ flag: '(example)', desc: 'palmyr chat run "launch a sneaker brand" --budget 50' },
|
|
668
|
+
],
|
|
669
|
+
resume: [
|
|
670
|
+
{ flag: '<session_id>', desc: 'Existing session id (positional)' },
|
|
671
|
+
{ flag: '"<follow-up>"', desc: 'Follow-up intent (positional remainder or --intent)' },
|
|
672
|
+
{ flag: '--approve', desc: 'Approve a previously-generated plan' },
|
|
673
|
+
{ flag: '--plan-id <id>', desc: 'Plan id to approve (pair with --approve)' },
|
|
674
|
+
{ flag: '--budget <USDC>', desc: 'Override session budget (default $20)' },
|
|
675
|
+
{ flag: '--execute', desc: 'Auto-execute the new plan' },
|
|
676
|
+
{ flag: '(price)', desc: '$0.10 orchestration fee per new plan + per-step costs' },
|
|
677
|
+
{ flag: '(example)', desc: 'palmyr chat resume sess_abc "now post 3 videos"' },
|
|
678
|
+
],
|
|
679
|
+
status: [
|
|
680
|
+
{ flag: '<session_id>', desc: 'Session id (positional)' },
|
|
681
|
+
{ flag: '(price)', desc: 'Free — session inspection' },
|
|
682
|
+
],
|
|
683
|
+
cancel: [
|
|
684
|
+
{ flag: '<session_id>', desc: 'Session id (positional)' },
|
|
685
|
+
{ flag: '(price)', desc: 'Free — halts execution and refunds remaining escrow' },
|
|
686
|
+
],
|
|
687
|
+
sessions: [
|
|
688
|
+
{ flag: '(no args)', desc: 'List your active i402 sessions' },
|
|
689
|
+
{ flag: '(price)', desc: 'Free' },
|
|
690
|
+
],
|
|
691
|
+
capabilities: [
|
|
692
|
+
{ flag: '(no args)', desc: 'List canonical capability classes (e.g. web_search, mint_nft)' },
|
|
693
|
+
{ flag: '(price)', desc: 'Free' },
|
|
694
|
+
],
|
|
695
|
+
providers: [
|
|
696
|
+
{ flag: '--capability <name>', desc: 'Filter providers by capability (optional)' },
|
|
697
|
+
{ flag: '(price)', desc: 'Free' },
|
|
698
|
+
{ flag: '(example)', desc: 'palmyr chat providers --capability web_search' },
|
|
699
|
+
],
|
|
700
|
+
};
|
|
358
701
|
/**
|
|
359
702
|
* Render a per-command menu (no subcommand given). On a TTY → Ink MenuScreen
|
|
360
703
|
* with the Palmyr aesthetic. In agent mode → flat JSON listing the available
|
|
@@ -651,7 +994,7 @@ async function main() {
|
|
|
651
994
|
break;
|
|
652
995
|
}
|
|
653
996
|
case 'phone': {
|
|
654
|
-
if (!subcommand || flags.help) {
|
|
997
|
+
if (!subcommand || (flags.help && !PHONE_HELP[subcommand])) {
|
|
655
998
|
showMenu({
|
|
656
999
|
command: 'phone',
|
|
657
1000
|
title: 'phone',
|
|
@@ -660,13 +1003,31 @@ async function main() {
|
|
|
660
1003
|
commands: [
|
|
661
1004
|
{ name: 'search', description: 'Search available numbers', hint: '--country US' },
|
|
662
1005
|
{ name: 'buy', description: 'Buy a phone number', hint: '--country US' },
|
|
1006
|
+
{ name: 'list', description: 'List numbers owned by your wallet' },
|
|
1007
|
+
{ name: 'release', description: 'Release a phone number', hint: '--id PHONE_ID' },
|
|
663
1008
|
{ name: 'sms', description: 'Send an SMS', hint: '--id ID --to +1... --body "hi"' },
|
|
1009
|
+
{ name: 'messages', description: 'Read SMS messages received on a number', hint: '--id PHONE_ID' },
|
|
664
1010
|
{ name: 'call', description: 'Place a voice call', hint: '--id ID --to +1... --tts "hello"' },
|
|
1011
|
+
{ name: 'calls', description: 'List calls placed/received on a number', hint: '--id PHONE_ID' },
|
|
1012
|
+
{ name: 'call-info', description: 'Get details on a single call', hint: '--call CALL_CONTROL_ID' },
|
|
1013
|
+
{ name: 'speak', description: 'TTS into a live call', hint: '--call ID --text "..." [--voice V]' },
|
|
1014
|
+
{ name: 'play', description: 'Play an audio URL into a live call', hint: '--call ID --url https://...' },
|
|
1015
|
+
{ name: 'dtmf', description: 'Send DTMF tones to a live call', hint: '--call ID --digits "1234#"' },
|
|
1016
|
+
{ name: 'gather', description: 'Collect DTMF input from caller', hint: '--call ID [--min-digits N --max-digits N --timeout MS --prompt "..."]' },
|
|
1017
|
+
{ name: 'record', description: 'Start recording a live call', hint: '--call ID [--format mp3]' },
|
|
1018
|
+
{ name: 'record-stop', description: 'Stop recording a live call', hint: '--call ID' },
|
|
1019
|
+
{ name: 'hangup', description: 'End a live call', hint: '--call ID' },
|
|
1020
|
+
{ name: 'answer', description: 'Answer an inbound call', hint: '--call ID' },
|
|
1021
|
+
{ name: 'transfer', description: 'Transfer a live call to another number', hint: '--call ID --to +1...' },
|
|
665
1022
|
],
|
|
666
1023
|
fromHome,
|
|
667
1024
|
});
|
|
668
1025
|
break;
|
|
669
1026
|
}
|
|
1027
|
+
if (flags.help && subcommand && PHONE_HELP[subcommand]) {
|
|
1028
|
+
subcommandHelp('phone', subcommand, PHONE_HELP[subcommand]);
|
|
1029
|
+
break;
|
|
1030
|
+
}
|
|
670
1031
|
switch (subcommand) {
|
|
671
1032
|
case 'search': {
|
|
672
1033
|
const country = flags.country || 'US';
|
|
@@ -735,12 +1096,130 @@ async function main() {
|
|
|
735
1096
|
render(React.createElement(SuccessScreen, { version: VERSION, title: 'calling', subtitle: to, details: [{ label: 'To', value: to }, { label: 'Call ID', value: data.callControlId || data.id || '' }], footerLeft: 'Call initiated' }));
|
|
736
1097
|
break;
|
|
737
1098
|
}
|
|
738
|
-
|
|
1099
|
+
case 'list': {
|
|
1100
|
+
const data = await ao.phoneListNumbers();
|
|
1101
|
+
return print(data);
|
|
1102
|
+
}
|
|
1103
|
+
case 'messages': {
|
|
1104
|
+
const id = flags.id || positional[0];
|
|
1105
|
+
if (!id)
|
|
1106
|
+
err('--id PHONE_ID required');
|
|
1107
|
+
const data = await ao.phoneMessages(id);
|
|
1108
|
+
return print(data);
|
|
1109
|
+
}
|
|
1110
|
+
case 'calls': {
|
|
1111
|
+
const id = flags.id || positional[0];
|
|
1112
|
+
if (!id)
|
|
1113
|
+
err('--id PHONE_ID required');
|
|
1114
|
+
const data = await ao.phoneCalls(id);
|
|
1115
|
+
return print(data);
|
|
1116
|
+
}
|
|
1117
|
+
case 'release': {
|
|
1118
|
+
const id = flags.id || positional[0];
|
|
1119
|
+
if (!id)
|
|
1120
|
+
err('--id PHONE_ID required');
|
|
1121
|
+
const data = await ao.phoneRelease(id);
|
|
1122
|
+
return print(data);
|
|
1123
|
+
}
|
|
1124
|
+
case 'call-info': {
|
|
1125
|
+
const callId = flags.call || flags.id || positional[0];
|
|
1126
|
+
if (!callId)
|
|
1127
|
+
err('--call CALL_CONTROL_ID required');
|
|
1128
|
+
const data = await ao.phoneCallInfo(callId);
|
|
1129
|
+
return print(data);
|
|
1130
|
+
}
|
|
1131
|
+
case 'speak': {
|
|
1132
|
+
const callId = flags.call || flags.id;
|
|
1133
|
+
const text = flags.text || flags.tts;
|
|
1134
|
+
if (!callId || !text)
|
|
1135
|
+
err('--call <id>, --text <text> required');
|
|
1136
|
+
const voice = flags.voice;
|
|
1137
|
+
const language = flags.language;
|
|
1138
|
+
const data = await ao.phoneSpeak(callId, text, { ...(voice ? { voice } : {}), ...(language ? { language } : {}) });
|
|
1139
|
+
return print(data);
|
|
1140
|
+
}
|
|
1141
|
+
case 'play': {
|
|
1142
|
+
const callId = flags.call || flags.id;
|
|
1143
|
+
const audioUrl = flags.url || flags['audio-url'];
|
|
1144
|
+
if (!callId || !audioUrl)
|
|
1145
|
+
err('--call <id>, --url <https://...> required');
|
|
1146
|
+
const data = await ao.phonePlay(callId, audioUrl);
|
|
1147
|
+
return print(data);
|
|
1148
|
+
}
|
|
1149
|
+
case 'dtmf': {
|
|
1150
|
+
const callId = flags.call || flags.id;
|
|
1151
|
+
const digits = flags.digits || positional[0];
|
|
1152
|
+
if (!callId || !digits)
|
|
1153
|
+
err('--call <id>, --digits "1234#" required');
|
|
1154
|
+
const data = await ao.phoneDtmf(callId, digits);
|
|
1155
|
+
return print(data);
|
|
1156
|
+
}
|
|
1157
|
+
case 'gather': {
|
|
1158
|
+
const callId = flags.call || flags.id;
|
|
1159
|
+
if (!callId)
|
|
1160
|
+
err('--call <id> required');
|
|
1161
|
+
const parseInt0 = (v) => (typeof v === 'string' ? parseInt(v, 10) : undefined);
|
|
1162
|
+
const opts = {};
|
|
1163
|
+
const minDigits = parseInt0(flags['min-digits']);
|
|
1164
|
+
if (minDigits !== undefined && Number.isFinite(minDigits))
|
|
1165
|
+
opts.minDigits = minDigits;
|
|
1166
|
+
const maxDigits = parseInt0(flags['max-digits']);
|
|
1167
|
+
if (maxDigits !== undefined && Number.isFinite(maxDigits))
|
|
1168
|
+
opts.maxDigits = maxDigits;
|
|
1169
|
+
const timeoutMillis = parseInt0(flags.timeout);
|
|
1170
|
+
if (timeoutMillis !== undefined && Number.isFinite(timeoutMillis))
|
|
1171
|
+
opts.timeoutMillis = timeoutMillis;
|
|
1172
|
+
if (flags['terminating-digit'])
|
|
1173
|
+
opts.terminatingDigit = flags['terminating-digit'];
|
|
1174
|
+
if (flags.prompt)
|
|
1175
|
+
opts.prompt = flags.prompt;
|
|
1176
|
+
if (flags['prompt-voice'])
|
|
1177
|
+
opts.promptVoice = flags['prompt-voice'];
|
|
1178
|
+
const data = await ao.phoneGather(callId, opts);
|
|
1179
|
+
return print(data);
|
|
1180
|
+
}
|
|
1181
|
+
case 'record': {
|
|
1182
|
+
const callId = flags.call || flags.id;
|
|
1183
|
+
if (!callId)
|
|
1184
|
+
err('--call <id> required');
|
|
1185
|
+
const data = await ao.phoneRecord(callId, flags.format);
|
|
1186
|
+
return print(data);
|
|
1187
|
+
}
|
|
1188
|
+
case 'record-stop': {
|
|
1189
|
+
const callId = flags.call || flags.id;
|
|
1190
|
+
if (!callId)
|
|
1191
|
+
err('--call <id> required');
|
|
1192
|
+
const data = await ao.phoneRecordStop(callId);
|
|
1193
|
+
return print(data);
|
|
1194
|
+
}
|
|
1195
|
+
case 'hangup': {
|
|
1196
|
+
const callId = flags.call || flags.id;
|
|
1197
|
+
if (!callId)
|
|
1198
|
+
err('--call <id> required');
|
|
1199
|
+
const data = await ao.phoneHangup(callId);
|
|
1200
|
+
return print(data);
|
|
1201
|
+
}
|
|
1202
|
+
case 'answer': {
|
|
1203
|
+
const callId = flags.call || flags.id;
|
|
1204
|
+
if (!callId)
|
|
1205
|
+
err('--call <id> required');
|
|
1206
|
+
const data = await ao.phoneAnswer(callId);
|
|
1207
|
+
return print(data);
|
|
1208
|
+
}
|
|
1209
|
+
case 'transfer': {
|
|
1210
|
+
const callId = flags.call || flags.id;
|
|
1211
|
+
const to = flags.to;
|
|
1212
|
+
if (!callId || !to)
|
|
1213
|
+
err('--call <id>, --to <+E.164> required');
|
|
1214
|
+
const data = await ao.phoneTransfer(callId, to);
|
|
1215
|
+
return print(data);
|
|
1216
|
+
}
|
|
1217
|
+
default: err(`Unknown phone command: ${subcommand}. Try: search, buy, list, release, sms, messages, call, calls, call-info, speak, play, dtmf, gather, record, record-stop, hangup, answer, transfer`);
|
|
739
1218
|
}
|
|
740
1219
|
break;
|
|
741
1220
|
}
|
|
742
1221
|
case 'email': {
|
|
743
|
-
if (!subcommand || flags.help) {
|
|
1222
|
+
if (!subcommand || (flags.help && !EMAIL_HELP[subcommand])) {
|
|
744
1223
|
showMenu({
|
|
745
1224
|
command: 'email',
|
|
746
1225
|
title: 'email',
|
|
@@ -759,6 +1238,10 @@ async function main() {
|
|
|
759
1238
|
});
|
|
760
1239
|
break;
|
|
761
1240
|
}
|
|
1241
|
+
if (flags.help && subcommand && EMAIL_HELP[subcommand]) {
|
|
1242
|
+
subcommandHelp('email', subcommand, EMAIL_HELP[subcommand]);
|
|
1243
|
+
break;
|
|
1244
|
+
}
|
|
762
1245
|
switch (subcommand) {
|
|
763
1246
|
case 'create': {
|
|
764
1247
|
const name = flags.name || positional[0];
|
|
@@ -850,7 +1333,7 @@ async function main() {
|
|
|
850
1333
|
break;
|
|
851
1334
|
}
|
|
852
1335
|
case 'compute': {
|
|
853
|
-
if (!subcommand || flags.help) {
|
|
1336
|
+
if (!subcommand || (flags.help && !COMPUTE_HELP[subcommand])) {
|
|
854
1337
|
showMenu({
|
|
855
1338
|
command: 'compute',
|
|
856
1339
|
title: 'compute',
|
|
@@ -877,6 +1360,10 @@ async function main() {
|
|
|
877
1360
|
});
|
|
878
1361
|
break;
|
|
879
1362
|
}
|
|
1363
|
+
if (flags.help && subcommand && COMPUTE_HELP[subcommand]) {
|
|
1364
|
+
subcommandHelp('compute', subcommand, COMPUTE_HELP[subcommand]);
|
|
1365
|
+
break;
|
|
1366
|
+
}
|
|
880
1367
|
switch (subcommand) {
|
|
881
1368
|
case 'plans': {
|
|
882
1369
|
// --location filters to types deployable in that location. Each
|
|
@@ -1452,7 +1939,7 @@ async function main() {
|
|
|
1452
1939
|
break;
|
|
1453
1940
|
}
|
|
1454
1941
|
case 'domain': {
|
|
1455
|
-
if (!subcommand || flags.help) {
|
|
1942
|
+
if (!subcommand || (flags.help && !DOMAIN_HELP[subcommand])) {
|
|
1456
1943
|
showMenu({
|
|
1457
1944
|
command: 'domain',
|
|
1458
1945
|
title: 'domain',
|
|
@@ -1472,6 +1959,10 @@ async function main() {
|
|
|
1472
1959
|
});
|
|
1473
1960
|
break;
|
|
1474
1961
|
}
|
|
1962
|
+
if (flags.help && subcommand && DOMAIN_HELP[subcommand]) {
|
|
1963
|
+
subcommandHelp('domain', subcommand, DOMAIN_HELP[subcommand]);
|
|
1964
|
+
break;
|
|
1965
|
+
}
|
|
1475
1966
|
switch (subcommand) {
|
|
1476
1967
|
case 'check': {
|
|
1477
1968
|
const name = flags.name || positional[0];
|
|
@@ -1636,11 +2127,14 @@ async function main() {
|
|
|
1636
2127
|
subtitle: 'Non-custodial HD wallet',
|
|
1637
2128
|
footerLeft: 'Solana + Base wallet operations',
|
|
1638
2129
|
commands: [
|
|
1639
|
-
{ name: 'create', description: 'Create
|
|
1640
|
-
{ name: 'import', description: 'Import from mnemonic', hint: '--mnemonic "..."' },
|
|
1641
|
-
{ name: 'list', description: 'List all wallets' },
|
|
2130
|
+
{ name: 'create', description: 'Create one or many wallets', hint: '[--tag X --count 100] [--solana|--base] [--managed]' },
|
|
2131
|
+
{ name: 'import', description: 'Import from mnemonic', hint: '--mnemonic "..." [--tag X]' },
|
|
2132
|
+
{ name: 'list', description: 'List all wallets', hint: '[--tag <name>]' },
|
|
1642
2133
|
{ name: 'info', description: 'Wallet details', hint: 'WALLET_ID' },
|
|
1643
2134
|
{ name: 'addresses', description: 'Show all chain addresses', hint: 'WALLET_ID' },
|
|
2135
|
+
{ name: 'tags', description: 'List wallet tags with counts' },
|
|
2136
|
+
{ name: 'tag', description: 'Assign / change / clear a wallet tag', hint: 'WALLET_ID TAG | WALLET_ID --clear' },
|
|
2137
|
+
{ name: 'tag-delete', description: 'Cascade-delete every wallet under a tag', hint: 'TAG --confirm' },
|
|
1644
2138
|
{ name: 'sign-message', description: 'Sign a message', hint: 'WALLET_ID --chain evm --msg "hello"' },
|
|
1645
2139
|
{ name: 'export', description: 'Export mnemonic for backup', hint: 'WALLET_ID --confirm' },
|
|
1646
2140
|
{ name: 'api-key', description: 'Create agent API key', hint: 'WALLET_ID --name my-agent' },
|
|
@@ -1679,16 +2173,72 @@ async function main() {
|
|
|
1679
2173
|
switch (subcommand) {
|
|
1680
2174
|
case 'create': {
|
|
1681
2175
|
const isManaged = !!flags.managed;
|
|
2176
|
+
const tagRaw = flags.tag || undefined;
|
|
2177
|
+
const countRaw = flags.count;
|
|
2178
|
+
const count = countRaw !== undefined ? parseInt(String(countRaw), 10) : 1;
|
|
2179
|
+
if (!Number.isFinite(count) || count < 1)
|
|
2180
|
+
err('--count must be a positive integer', EXIT.BAD_INPUT);
|
|
2181
|
+
// Chain filter: --solana / --base. Default (neither) → both chains.
|
|
2182
|
+
const wantSol = !!flags.solana;
|
|
2183
|
+
const wantBase = !!flags.base;
|
|
2184
|
+
const chains = (wantSol && !wantBase) ? ['solana']
|
|
2185
|
+
: (wantBase && !wantSol) ? ['base']
|
|
2186
|
+
: ['solana', 'base'];
|
|
2187
|
+
// ─── Bulk path ───
|
|
2188
|
+
if (count > 1) {
|
|
2189
|
+
if (isManaged)
|
|
2190
|
+
err('Bulk wallet creation only supports unmanaged wallets — managed wallets need per-wallet passkey setup.', EXIT.BAD_INPUT);
|
|
2191
|
+
if (!tagRaw)
|
|
2192
|
+
err('--tag is required when --count > 1', EXIT.BAD_INPUT);
|
|
2193
|
+
if (count > 500)
|
|
2194
|
+
err('--count must be ≤ 500 per call', EXIT.BAD_INPUT);
|
|
2195
|
+
const prefix = flags['name-prefix'] || tagRaw;
|
|
2196
|
+
const { createLocalWalletsBatch } = await import('./vault.js');
|
|
2197
|
+
const { storeSecretsBatch } = await import('./credential-store.js');
|
|
2198
|
+
// Progress to stderr so JSON on stdout stays clean
|
|
2199
|
+
if (!AGENT_MODE)
|
|
2200
|
+
process.stderr.write(`creating ${count} wallets under tag "${tagRaw}"...\n`);
|
|
2201
|
+
const results = createLocalWalletsBatch(prefix, count, 'unmanaged', { tag: tagRaw, chains });
|
|
2202
|
+
if (!AGENT_MODE)
|
|
2203
|
+
process.stderr.write(`sealing ${count} session secrets in OS credential store...\n`);
|
|
2204
|
+
storeSecretsBatch(results.map(r => ({ account: r.id, secret: r.sessionSecret })));
|
|
2205
|
+
log(`wallet create: ${count} wallets under tag "${tagRaw}" (chains=${chains.join(',')})`);
|
|
2206
|
+
if (AGENT_MODE) {
|
|
2207
|
+
print({
|
|
2208
|
+
count: results.length,
|
|
2209
|
+
tag: tagRaw,
|
|
2210
|
+
chains,
|
|
2211
|
+
wallets: results.map(r => ({
|
|
2212
|
+
id: r.id,
|
|
2213
|
+
name: r.name,
|
|
2214
|
+
mode: r.mode,
|
|
2215
|
+
tag: r.tag,
|
|
2216
|
+
chains: r.chains,
|
|
2217
|
+
solana: r.solanaAddress,
|
|
2218
|
+
base: r.evmAddress,
|
|
2219
|
+
})),
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
else {
|
|
2223
|
+
console.log(`\n ${t.success}✔${t.reset} Created ${count} wallets under tag ${t.accent}${tagRaw}${t.reset}`);
|
|
2224
|
+
console.log(` ${t.muted}chains:${t.reset} ${chains.join(', ')}`);
|
|
2225
|
+
console.log(` ${t.muted}names: ${t.reset}${results[0].name} … ${results[results.length - 1].name}`);
|
|
2226
|
+
console.log(`\n ${t.muted}List them: ${t.reset}palmyr wallet list --tag ${tagRaw}`);
|
|
2227
|
+
console.log(` ${t.muted}Delete all: ${t.reset}palmyr wallet tag-delete ${tagRaw} --confirm\n`);
|
|
2228
|
+
}
|
|
2229
|
+
break;
|
|
2230
|
+
}
|
|
2231
|
+
// ─── Single-create path ───
|
|
1682
2232
|
// Accept --name (primary) or --label (alias)
|
|
1683
2233
|
const name = flags.name || flags.label || 'My Wallet';
|
|
1684
2234
|
const mode = isManaged ? 'managed' : 'unmanaged';
|
|
1685
2235
|
// Create locally — no server needed for the key material
|
|
1686
2236
|
const { createLocalWallet } = await import('./vault.js');
|
|
1687
|
-
const w = createLocalWallet(name, mode);
|
|
2237
|
+
const w = createLocalWallet(name, mode, { tag: tagRaw, chains });
|
|
1688
2238
|
// Store session secret in OS credential store
|
|
1689
2239
|
const { storeSecret } = await import('./credential-store.js');
|
|
1690
2240
|
storeSecret(w.id, w.sessionSecret);
|
|
1691
|
-
log(`wallet create: ${w.id} (${mode})`);
|
|
2241
|
+
log(`wallet create: ${w.id} (${mode}${tagRaw ? `, tag=${tagRaw}` : ''}, chains=${chains.join(',')})`);
|
|
1692
2242
|
// For managed wallets, register metadata with the server to get a setup link
|
|
1693
2243
|
let setupLink;
|
|
1694
2244
|
if (isManaged) {
|
|
@@ -1723,6 +2273,7 @@ async function main() {
|
|
|
1723
2273
|
mode: w.mode,
|
|
1724
2274
|
solana: w.solanaAddress,
|
|
1725
2275
|
base: w.evmAddress,
|
|
2276
|
+
tag: w.tag,
|
|
1726
2277
|
}));
|
|
1727
2278
|
if (setupLink) {
|
|
1728
2279
|
console.log(`\n ${t.accent}Setup link${t.reset} — send to the human who will manage this wallet:`);
|
|
@@ -1741,8 +2292,14 @@ async function main() {
|
|
|
1741
2292
|
err('--mnemonic "your twelve words..." required');
|
|
1742
2293
|
const name = flags.name || flags.label || 'Imported Wallet';
|
|
1743
2294
|
const mode = flags.managed ? 'managed' : 'unmanaged';
|
|
2295
|
+
const tagRaw = flags.tag || undefined;
|
|
2296
|
+
const wantSol = !!flags.solana;
|
|
2297
|
+
const wantBase = !!flags.base;
|
|
2298
|
+
const chains = (wantSol && !wantBase) ? ['solana']
|
|
2299
|
+
: (wantBase && !wantSol) ? ['base']
|
|
2300
|
+
: ['solana', 'base'];
|
|
1744
2301
|
const { importLocalWallet } = await import('./vault.js');
|
|
1745
|
-
const w = importLocalWallet(name, mnemonic, mode);
|
|
2302
|
+
const w = importLocalWallet(name, mnemonic, mode, { tag: tagRaw, chains });
|
|
1746
2303
|
// Store session secret
|
|
1747
2304
|
const { storeSecret } = await import('./credential-store.js');
|
|
1748
2305
|
storeSecret(w.id, w.sessionSecret);
|
|
@@ -1755,6 +2312,7 @@ async function main() {
|
|
|
1755
2312
|
mode: w.mode,
|
|
1756
2313
|
solana: w.solanaAddress,
|
|
1757
2314
|
base: w.evmAddress,
|
|
2315
|
+
tag: w.tag,
|
|
1758
2316
|
}));
|
|
1759
2317
|
}
|
|
1760
2318
|
else {
|
|
@@ -1765,7 +2323,10 @@ async function main() {
|
|
|
1765
2323
|
case 'list': {
|
|
1766
2324
|
// List from local vault — no server needed
|
|
1767
2325
|
const { listVaultWallets } = await import('./vault.js');
|
|
1768
|
-
const
|
|
2326
|
+
const tagFilter = flags.tag;
|
|
2327
|
+
let wallets = listVaultWallets();
|
|
2328
|
+
if (tagFilter)
|
|
2329
|
+
wallets = wallets.filter(w => w.tag === tagFilter);
|
|
1769
2330
|
if (!AGENT_MODE) {
|
|
1770
2331
|
render(React.createElement(WalletListScreen, {
|
|
1771
2332
|
version: VERSION,
|
|
@@ -1775,14 +2336,93 @@ async function main() {
|
|
|
1775
2336
|
mode: w.mode,
|
|
1776
2337
|
solana: w.solanaAddress,
|
|
1777
2338
|
base: w.evmAddress,
|
|
2339
|
+
tag: w.tag,
|
|
1778
2340
|
})),
|
|
1779
2341
|
}));
|
|
1780
2342
|
}
|
|
1781
2343
|
else {
|
|
1782
|
-
print({ wallets });
|
|
2344
|
+
print({ wallets, ...(tagFilter ? { tag: tagFilter } : {}) });
|
|
2345
|
+
}
|
|
2346
|
+
break;
|
|
2347
|
+
}
|
|
2348
|
+
case 'tags': {
|
|
2349
|
+
const { listTags } = await import('./vault.js');
|
|
2350
|
+
const tags = listTags();
|
|
2351
|
+
if (AGENT_MODE) {
|
|
2352
|
+
print({ tags });
|
|
2353
|
+
}
|
|
2354
|
+
else {
|
|
2355
|
+
if (tags.length === 0) {
|
|
2356
|
+
console.log(`\n ${t.muted}No tagged wallets yet.${t.reset}`);
|
|
2357
|
+
console.log(` ${t.muted}Create some: ${t.reset}palmyr wallet create --tag demo --count 5\n`);
|
|
2358
|
+
}
|
|
2359
|
+
else {
|
|
2360
|
+
console.log(`\n ${t.accent}wallet tags${t.reset}`);
|
|
2361
|
+
for (const tg of tags) {
|
|
2362
|
+
console.log(` ${t.bold}${tg.name}${t.reset} ${t.muted}·${t.reset} ${tg.count} wallet(s) ${t.muted}·${t.reset} ${tg.chains.join(',')}`);
|
|
2363
|
+
}
|
|
2364
|
+
console.log('');
|
|
2365
|
+
}
|
|
1783
2366
|
}
|
|
1784
2367
|
break;
|
|
1785
2368
|
}
|
|
2369
|
+
case 'tag': {
|
|
2370
|
+
const walletId = positional[0] || flags.id;
|
|
2371
|
+
if (!walletId)
|
|
2372
|
+
err('Wallet ID required: palmyr wallet tag <WALLET_ID> <TAG> | --clear', EXIT.BAD_INPUT);
|
|
2373
|
+
const wantClear = !!flags.clear;
|
|
2374
|
+
const newTag = positional[1] || flags.tag;
|
|
2375
|
+
if (!wantClear && !newTag)
|
|
2376
|
+
err('Pass a TAG or --clear', EXIT.BAD_INPUT);
|
|
2377
|
+
if (wantClear && newTag)
|
|
2378
|
+
err('Cannot pass both a TAG and --clear', EXIT.BAD_INPUT);
|
|
2379
|
+
const { tagWallet } = await import('./vault.js');
|
|
2380
|
+
const out = tagWallet(walletId, wantClear ? null : newTag);
|
|
2381
|
+
log(`wallet tag: ${out.id} → ${out.tag ?? '(cleared)'}`);
|
|
2382
|
+
print({ success: true, ...out });
|
|
2383
|
+
break;
|
|
2384
|
+
}
|
|
2385
|
+
case 'tag-delete': {
|
|
2386
|
+
const tagArg = positional[0] || flags.tag;
|
|
2387
|
+
if (!tagArg)
|
|
2388
|
+
err('Tag required: palmyr wallet tag-delete <TAG> --confirm', EXIT.BAD_INPUT);
|
|
2389
|
+
if (!flags.confirm) {
|
|
2390
|
+
err(`This will permanently delete every wallet tagged "${tagArg}" and their session secrets.\n\n` +
|
|
2391
|
+
' Re-run with --confirm to proceed:\n' +
|
|
2392
|
+
` palmyr wallet tag-delete ${tagArg} --confirm`, EXIT.BAD_INPUT);
|
|
2393
|
+
}
|
|
2394
|
+
const { walletsByTag, deleteLocalWallet } = await import('./vault.js');
|
|
2395
|
+
const { deleteSecret } = await import('./credential-store.js');
|
|
2396
|
+
const targets = walletsByTag(tagArg);
|
|
2397
|
+
if (targets.length === 0)
|
|
2398
|
+
err(`No wallets found with tag "${tagArg}"`, EXIT.NOT_FOUND);
|
|
2399
|
+
const deleted = [];
|
|
2400
|
+
const failed = [];
|
|
2401
|
+
for (const w of targets) {
|
|
2402
|
+
try {
|
|
2403
|
+
const out = deleteLocalWallet(w.id);
|
|
2404
|
+
try {
|
|
2405
|
+
deleteSecret(w.id);
|
|
2406
|
+
}
|
|
2407
|
+
catch { }
|
|
2408
|
+
deleted.push(out);
|
|
2409
|
+
if (!AGENT_MODE)
|
|
2410
|
+
process.stderr.write(`deleted ${out.name} (${out.id.slice(0, 8)}...)\n`);
|
|
2411
|
+
}
|
|
2412
|
+
catch (e) {
|
|
2413
|
+
failed.push({ id: w.id, name: w.name, error: e?.message || String(e) });
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
log(`wallet tag-delete: ${deleted.length} deleted, ${failed.length} failed (tag=${tagArg})`);
|
|
2417
|
+
print({
|
|
2418
|
+
success: failed.length === 0,
|
|
2419
|
+
tag: tagArg,
|
|
2420
|
+
deleted: deleted.length,
|
|
2421
|
+
wallets: deleted,
|
|
2422
|
+
...(failed.length > 0 ? { failed } : {}),
|
|
2423
|
+
});
|
|
2424
|
+
break;
|
|
2425
|
+
}
|
|
1786
2426
|
case 'info': {
|
|
1787
2427
|
const walletId = positional[0] || flags.id;
|
|
1788
2428
|
if (!walletId)
|
|
@@ -3741,12 +4381,12 @@ async function main() {
|
|
|
3741
4381
|
}
|
|
3742
4382
|
err(`Unknown trading-keystore subcommand: ${sub}. Try: init, unlock, lock, list, status, derive, export`, EXIT.BAD_INPUT);
|
|
3743
4383
|
}
|
|
3744
|
-
default: err(`Unknown wallet command: ${subcommand}. Try: create, import, list, info, export, addresses, sign-message, api-key, config, use, request-approval, buy, cohort, template, positions, position, sell, sync, pnl, journal, watch, brief, daemon, triggers, trading-keystore, evm-quote`);
|
|
4384
|
+
default: err(`Unknown wallet command: ${subcommand}. Try: create, import, list, info, tags, tag, tag-delete, export, addresses, sign-message, api-key, config, use, request-approval, buy, cohort, template, positions, position, sell, sync, pnl, journal, watch, brief, daemon, triggers, trading-keystore, evm-quote`);
|
|
3745
4385
|
}
|
|
3746
4386
|
break;
|
|
3747
4387
|
}
|
|
3748
4388
|
case 'chat': {
|
|
3749
|
-
if (!subcommand || flags.help) {
|
|
4389
|
+
if (!subcommand || (flags.help && !CHAT_HELP[subcommand])) {
|
|
3750
4390
|
showMenu({
|
|
3751
4391
|
command: 'chat',
|
|
3752
4392
|
title: 'chat',
|
|
@@ -3765,6 +4405,10 @@ async function main() {
|
|
|
3765
4405
|
});
|
|
3766
4406
|
break;
|
|
3767
4407
|
}
|
|
4408
|
+
if (flags.help && subcommand && CHAT_HELP[subcommand]) {
|
|
4409
|
+
subcommandHelp('chat', subcommand, CHAT_HELP[subcommand]);
|
|
4410
|
+
break;
|
|
4411
|
+
}
|
|
3768
4412
|
switch (subcommand) {
|
|
3769
4413
|
case 'run': {
|
|
3770
4414
|
const intent = (positional.join(' ') || flags.intent || '').trim();
|
|
@@ -4001,14 +4645,18 @@ async function main() {
|
|
|
4001
4645
|
const sv = await import('./social-vault.js');
|
|
4002
4646
|
const platform = 'twitter';
|
|
4003
4647
|
// Resolve a username to its server-side account_id and which table
|
|
4004
|
-
// holds it. X accounts can live in
|
|
4005
|
-
//
|
|
4006
|
-
//
|
|
4007
|
-
//
|
|
4008
|
-
//
|
|
4648
|
+
// holds it. X accounts can live in THREE places:
|
|
4649
|
+
// - x_accounts (legacy pool, rarely used now)
|
|
4650
|
+
// - social_account_pool (where `palmyr twitter buy` writes)
|
|
4651
|
+
// - social_registered_accounts (BYO-registered)
|
|
4652
|
+
// The local vault doesn't track which, so we query all three in
|
|
4653
|
+
// parallel and find a match. Used by transfer / share / unshare
|
|
4654
|
+
// to dispatch to the correct endpoint family. Returns null if the
|
|
4655
|
+
// username isn't on any of them.
|
|
4009
4656
|
const resolveServerAccount = async (username) => {
|
|
4010
|
-
const [xMine, reg] = await Promise.allSettled([
|
|
4657
|
+
const [xMine, poolMine, reg] = await Promise.allSettled([
|
|
4011
4658
|
ao.xAccountsMine(),
|
|
4659
|
+
ao.socialTwitterPoolMine(),
|
|
4012
4660
|
ao.socialTwitterListRegistered(),
|
|
4013
4661
|
]);
|
|
4014
4662
|
if (xMine.status === 'fulfilled') {
|
|
@@ -4016,6 +4664,11 @@ async function main() {
|
|
|
4016
4664
|
if (m)
|
|
4017
4665
|
return { kind: 'x_accounts', id: m.id };
|
|
4018
4666
|
}
|
|
4667
|
+
if (poolMine.status === 'fulfilled') {
|
|
4668
|
+
const m = (poolMine.value?.accounts || []).find((a) => a.username === username);
|
|
4669
|
+
if (m)
|
|
4670
|
+
return { kind: 'pool', id: m.id };
|
|
4671
|
+
}
|
|
4019
4672
|
if (reg.status === 'fulfilled') {
|
|
4020
4673
|
const m = (reg.value?.accounts || []).find((a) => a.username === username);
|
|
4021
4674
|
if (m)
|
|
@@ -4035,26 +4688,32 @@ async function main() {
|
|
|
4035
4688
|
const existing = sv.getAccount(platform, username);
|
|
4036
4689
|
if (existing)
|
|
4037
4690
|
return existing;
|
|
4038
|
-
const [xRes, regRes] = await Promise.allSettled([
|
|
4691
|
+
const [xRes, poolRes, regRes] = await Promise.allSettled([
|
|
4039
4692
|
ao.xAccountsMine(),
|
|
4693
|
+
ao.socialTwitterPoolMine(),
|
|
4040
4694
|
ao.socialTwitterRegisteredMine(),
|
|
4041
4695
|
]);
|
|
4042
4696
|
const xMatch = xRes.status === 'fulfilled'
|
|
4043
4697
|
? (xRes.value?.accounts || []).find((a) => a.username === username)
|
|
4044
4698
|
: null;
|
|
4699
|
+
const poolMatch = poolRes.status === 'fulfilled'
|
|
4700
|
+
? (poolRes.value?.accounts || []).find((a) => a.username === username)
|
|
4701
|
+
: null;
|
|
4045
4702
|
const regMatch = regRes.status === 'fulfilled'
|
|
4046
4703
|
? (regRes.value?.accounts || []).find((a) => a.username === username)
|
|
4047
4704
|
: null;
|
|
4048
|
-
const match = xMatch || regMatch;
|
|
4705
|
+
const match = xMatch || poolMatch || regMatch;
|
|
4049
4706
|
if (!match) {
|
|
4050
4707
|
err(`twitter account "${username}" not found locally or on the server (this wallet has no access)`, EXIT.NOT_FOUND);
|
|
4051
4708
|
}
|
|
4709
|
+
// Pool-mine and registered-mine return creds nested under
|
|
4710
|
+
// `credentials`; legacy x_accounts/mine returns them flat alongside
|
|
4711
|
+
// cookies. Normalize so the import works the same in either case.
|
|
4052
4712
|
const cookies = (match.cookies || []);
|
|
4053
4713
|
const ct0 = cookies.find((c) => c.name === 'ct0')?.value;
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
? regMatch.credentials
|
|
4714
|
+
const hasNestedCreds = poolMatch || regMatch;
|
|
4715
|
+
const creds = hasNestedCreds
|
|
4716
|
+
? match.credentials
|
|
4058
4717
|
: {
|
|
4059
4718
|
login: match.email || match.username,
|
|
4060
4719
|
password: match.password,
|
|
@@ -4062,11 +4721,16 @@ async function main() {
|
|
|
4062
4721
|
auth_token: match.auth_token || undefined,
|
|
4063
4722
|
ct0,
|
|
4064
4723
|
};
|
|
4065
|
-
const summary = sv.importAccount(platform, username, creds, {
|
|
4724
|
+
const summary = sv.importAccount(platform, username, creds, {
|
|
4725
|
+
source: 'auto-claim',
|
|
4726
|
+
proxy_session_id: poolMatch?.proxy_session_id,
|
|
4727
|
+
country: poolMatch?.country || undefined,
|
|
4728
|
+
});
|
|
4066
4729
|
if (cookies.length > 0) {
|
|
4067
4730
|
sv.saveSession(summary.id, platform, cookies);
|
|
4068
4731
|
}
|
|
4069
|
-
|
|
4732
|
+
const sourceLabel = poolMatch ? 'pool' : regMatch ? 'registered' : 'x_accounts';
|
|
4733
|
+
log(`auto-imported @${username} from server (${sourceLabel} → local vault)`);
|
|
4070
4734
|
return summary;
|
|
4071
4735
|
};
|
|
4072
4736
|
if (!subcommand) {
|
|
@@ -4171,11 +4835,13 @@ async function main() {
|
|
|
4171
4835
|
if (skipRemote) {
|
|
4172
4836
|
return print({ accounts: localAccounts, count: localAccounts.length, source: 'local' });
|
|
4173
4837
|
}
|
|
4174
|
-
const [xRes, regRes] = await Promise.allSettled([
|
|
4838
|
+
const [xRes, poolRes, regRes] = await Promise.allSettled([
|
|
4175
4839
|
ao.xAccountsMine(),
|
|
4840
|
+
ao.socialTwitterPoolMine(),
|
|
4176
4841
|
ao.socialTwitterRegisteredMine(),
|
|
4177
4842
|
]);
|
|
4178
4843
|
const xAccounts = xRes.status === 'fulfilled' ? (xRes.value?.accounts || []) : [];
|
|
4844
|
+
const poolAccounts = poolRes.status === 'fulfilled' ? (poolRes.value?.accounts || []) : [];
|
|
4179
4845
|
const regAccounts = regRes.status === 'fulfilled' ? (regRes.value?.accounts || []) : [];
|
|
4180
4846
|
const localUsernames = new Set(localAccounts.map(a => a.username));
|
|
4181
4847
|
const serverOnly = [];
|
|
@@ -4189,6 +4855,16 @@ async function main() {
|
|
|
4189
4855
|
});
|
|
4190
4856
|
}
|
|
4191
4857
|
}
|
|
4858
|
+
for (const a of poolAccounts) {
|
|
4859
|
+
if (a.username && !localUsernames.has(a.username)) {
|
|
4860
|
+
serverOnly.push({
|
|
4861
|
+
username: a.username,
|
|
4862
|
+
access: a.access || 'owner',
|
|
4863
|
+
source_table: 'pool',
|
|
4864
|
+
status: 'server-only — run `palmyr twitter claim` to import',
|
|
4865
|
+
});
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4192
4868
|
for (const a of regAccounts) {
|
|
4193
4869
|
if (a.username && !localUsernames.has(a.username)) {
|
|
4194
4870
|
serverOnly.push({
|
|
@@ -5088,6 +5764,13 @@ async function main() {
|
|
|
5088
5764
|
spinReg.stop('Registered', true);
|
|
5089
5765
|
resolved = { kind: 'registered', id: regData.id };
|
|
5090
5766
|
}
|
|
5767
|
+
// Transfer-on-pool isn't wired yet (pool table has no transfer
|
|
5768
|
+
// endpoint with the async rotation machinery). Surface a clear
|
|
5769
|
+
// workaround instead of a silent 404.
|
|
5770
|
+
if (resolved.kind === 'pool') {
|
|
5771
|
+
err(`@${username} is a pool-bought account and transfer isn't supported on the pool table yet — share/unshare work.\n\n` +
|
|
5772
|
+
`Workaround: \`palmyr twitter register ${username}\` first (uploads to the registered table), then re-run the transfer.`, EXIT.GENERAL);
|
|
5773
|
+
}
|
|
5091
5774
|
const spin = new Spinner();
|
|
5092
5775
|
spin.start(`Rotating @${username} password and transferring…`);
|
|
5093
5776
|
// Kick off the transfer. Server responds 202 with a transfer_id;
|
|
@@ -5169,7 +5852,9 @@ async function main() {
|
|
|
5169
5852
|
}
|
|
5170
5853
|
const data = resolved.kind === 'x_accounts'
|
|
5171
5854
|
? await ao.xAccountShare(resolved.id, withWallet)
|
|
5172
|
-
:
|
|
5855
|
+
: resolved.kind === 'pool'
|
|
5856
|
+
? await ao.socialTwitterPoolShare(resolved.id, withWallet)
|
|
5857
|
+
: await ao.socialTwitterRegisteredShare(resolved.id, withWallet);
|
|
5173
5858
|
log(`twitter share: @${username} → ${withWallet}`);
|
|
5174
5859
|
return print({ ...data, source_table: resolved.kind });
|
|
5175
5860
|
}
|
|
@@ -5193,9 +5878,20 @@ async function main() {
|
|
|
5193
5878
|
spin.start(`Unsharing @${username} and rotating password…`);
|
|
5194
5879
|
let data;
|
|
5195
5880
|
try {
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5881
|
+
if (resolved.kind === 'x_accounts') {
|
|
5882
|
+
data = await ao.xAccountUnshare(resolved.id, targetWallet, { rotate });
|
|
5883
|
+
}
|
|
5884
|
+
else if (resolved.kind === 'pool') {
|
|
5885
|
+
// The pool unshare endpoint doesn't support rotate today
|
|
5886
|
+
// (rotation infra lives on the transfers async pipeline and
|
|
5887
|
+
// hasn't been wired to pool yet). Warn and call without rotate.
|
|
5888
|
+
if (rotate)
|
|
5889
|
+
warn(`--rotate not supported on pool-bought accounts yet — performing unshare only`);
|
|
5890
|
+
data = await ao.socialTwitterPoolUnshare(resolved.id, targetWallet);
|
|
5891
|
+
}
|
|
5892
|
+
else {
|
|
5893
|
+
data = await ao.socialTwitterRegisteredUnshare(resolved.id, targetWallet, { rotate });
|
|
5894
|
+
}
|
|
5199
5895
|
}
|
|
5200
5896
|
catch (e) {
|
|
5201
5897
|
if (spin)
|
|
@@ -5284,20 +5980,23 @@ async function main() {
|
|
|
5284
5980
|
}
|
|
5285
5981
|
case 'claim': {
|
|
5286
5982
|
// Fetch server-side accounts the calling wallet owns or has shared
|
|
5287
|
-
// access to — from
|
|
5983
|
+
// access to — from all THREE tables. x_accounts (legacy pool),
|
|
5984
|
+
// social_account_pool (where `palmyr twitter buy` writes), and
|
|
5288
5985
|
// social_registered_accounts (BYO-registered) are queried in
|
|
5289
|
-
// parallel;
|
|
5290
|
-
// already in the local vault so the receiver of a transfer
|
|
5291
|
-
// pick up the account in one command.
|
|
5292
|
-
const [xRes, regRes] = await Promise.allSettled([
|
|
5986
|
+
// parallel; all three contribute to the claim list. Import any
|
|
5987
|
+
// not already in the local vault so the receiver of a transfer
|
|
5988
|
+
// or share can pick up the account in one command.
|
|
5989
|
+
const [xRes, poolRes, regRes] = await Promise.allSettled([
|
|
5293
5990
|
ao.xAccountsMine(),
|
|
5991
|
+
ao.socialTwitterPoolMine(),
|
|
5294
5992
|
ao.socialTwitterRegisteredMine(),
|
|
5295
5993
|
]);
|
|
5296
5994
|
const xAccounts = xRes.status === 'fulfilled' ? (xRes.value?.accounts || []) : [];
|
|
5995
|
+
const poolAccountsRaw = poolRes.status === 'fulfilled' ? (poolRes.value?.accounts || []) : [];
|
|
5297
5996
|
const regAccountsRaw = regRes.status === 'fulfilled' ? (regRes.value?.accounts || []) : [];
|
|
5298
|
-
// Normalize the registered shape (creds + cookies live in
|
|
5299
|
-
// fields) to the same flat shape as x_accounts so the
|
|
5300
|
-
// doesn't have to branch on source.
|
|
5997
|
+
// Normalize the registered + pool shape (creds + cookies live in
|
|
5998
|
+
// nested fields) to the same flat shape as x_accounts so the
|
|
5999
|
+
// loop below doesn't have to branch on source.
|
|
5301
6000
|
const regAccounts = regAccountsRaw.map(a => ({
|
|
5302
6001
|
username: a.username,
|
|
5303
6002
|
email: a.credentials?.email,
|
|
@@ -5307,8 +6006,20 @@ async function main() {
|
|
|
5307
6006
|
access: a.access,
|
|
5308
6007
|
source_table: 'registered',
|
|
5309
6008
|
}));
|
|
6009
|
+
const poolAccounts = poolAccountsRaw.map(a => ({
|
|
6010
|
+
username: a.username,
|
|
6011
|
+
email: a.credentials?.email,
|
|
6012
|
+
password: a.credentials?.password,
|
|
6013
|
+
auth_token: a.credentials?.auth_token,
|
|
6014
|
+
cookies: a.cookies || [],
|
|
6015
|
+
access: a.access,
|
|
6016
|
+
proxy_session_id: a.proxy_session_id,
|
|
6017
|
+
country: a.country,
|
|
6018
|
+
source_table: 'pool',
|
|
6019
|
+
}));
|
|
5310
6020
|
const accounts = [
|
|
5311
6021
|
...xAccounts.map(a => ({ ...a, source_table: 'x_accounts' })),
|
|
6022
|
+
...poolAccounts,
|
|
5312
6023
|
...regAccounts,
|
|
5313
6024
|
];
|
|
5314
6025
|
if (accounts.length === 0) {
|
|
@@ -5332,7 +6043,11 @@ async function main() {
|
|
|
5332
6043
|
auth_token: a.auth_token || undefined,
|
|
5333
6044
|
ct0,
|
|
5334
6045
|
};
|
|
5335
|
-
const summary = sv.importAccount(platform, a.username, creds, {
|
|
6046
|
+
const summary = sv.importAccount(platform, a.username, creds, {
|
|
6047
|
+
source: 'claim',
|
|
6048
|
+
proxy_session_id: a.proxy_session_id,
|
|
6049
|
+
country: a.country || undefined,
|
|
6050
|
+
});
|
|
5336
6051
|
// Save the cookies so `palmyr twitter login` can use the
|
|
5337
6052
|
// cookie-fast-path instead of re-driving the login form.
|
|
5338
6053
|
if (Array.isArray(a.cookies) && a.cookies.length > 0) {
|