#!/usr/bin/env python3 """TempJar CLI — use TempJar temporary inboxes from the terminal. Requires: Python 3.8+, no extra packages (standard library only). Auth: an API key from https://tempjar.com/api - Set it once: python3 tempjar-cli.py set-key - Or per run: TEMPJAR_API_KEY=tj_live_... python3 tempjar-cli.py inboxes - Or per run: python3 tempjar-cli.py --api-key tj_live_... inboxes TempJar is receive-only: it never sends mail. Free plan: 1,000 emails received per account per calendar month, across all your API keys. """ import argparse import getpass import json import os import sys import urllib.request import urllib.error DEFAULT_BASE = "https://tempjar.com" CONFIG = os.path.expanduser("~/.config/tempjar/config.json") def load_key(args): if getattr(args, "api_key", None): return args.api_key env = os.environ.get("TEMPJAR_API_KEY", "") if env: return env try: with open(CONFIG) as f: return json.load(f).get("api_key", "") except Exception: return "" def save_key(key): os.makedirs(os.path.dirname(CONFIG), exist_ok=True) with open(CONFIG, "w") as f: json.dump({"api_key": key}, f) try: os.chmod(CONFIG, 0o600) except Exception: pass def call(base, key, path, method="GET", data=None): req = urllib.request.Request(base.rstrip("/") + path, method=method) if key: req.add_header("X-API-Key", key) body = None if data is not None: body = json.dumps(data).encode() req.add_header("Content-Type", "application/json") try: with urllib.request.urlopen(req, data=body, timeout=30) as r: return json.loads(r.read().decode() or "{}") except urllib.error.HTTPError as e: try: return json.loads(e.read().decode() or "{}") except Exception: return {"ok": False, "error": "HTTP %s" % e.code} except Exception as e: return {"ok": False, "error": str(e)} def show(result): print(json.dumps(result, indent=2, ensure_ascii=False)) if not result.get("ok"): sys.exit(1) def main(): p = argparse.ArgumentParser(prog="tempjar", description="TempJar CLI") p.add_argument("--base-url", default=DEFAULT_BASE, help="TempJar base URL") p.add_argument("--api-key", default=None, help="API key (or TEMPJAR_API_KEY)") sub = p.add_subparsers(dest="cmd", required=True) sub.add_parser("set-key", help="Save your API key locally (~/.config/tempjar/config.json)") sub.add_parser("domains", help="List mail domains") sub.add_parser("usage", help="Show free-plan quota: 1000 emails/month/account") sub.add_parser("inboxes", help="List your API inboxes") c = sub.add_parser("create", help="Create an inbox") c.add_argument("handle", help="Custom handle, at least 3 characters") c.add_argument("domain", help="Mail domain, e.g. jarinbox.com") c.add_argument("--password", default=None, help="Inbox password (generated if omitted)") c.add_argument("--lifespan", default="6mo", choices=["24h", "6mo"]) m = sub.add_parser("messages", help="List messages in an inbox") m.add_argument("inbox_id", type=int) m.add_argument("--limit", type=int, default=20) g = sub.add_parser("message", help="Get one message") g.add_argument("inbox_id", type=int) g.add_argument("message_id", type=int) d = sub.add_parser("delete", help="Delete an inbox") d.add_argument("inbox_id", type=int) r = sub.add_parser("register", help="Create a developer account (captcha required)") r.add_argument("--email", required=True) r.add_argument("--password", default=None, help="8+ characters (prompted if omitted)") sub.add_parser("mcp-config", help="Print an MCP client config snippet") args = p.parse_args() if args.cmd == "set-key": key = getpass.getpass("API key (tj_live_...): ").strip() if not key.startswith("tj_live_"): print("That does not look like a TempJar API key (tj_live_...).") sys.exit(1) save_key(key) print("Saved to %s" % CONFIG) return if args.cmd == "mcp-config": print(json.dumps({"mcpServers": {"tempjar": {"url": args.base_url.rstrip("/") + "/api/mcp", "headers": {"X-API-Key": "tj_live_YOUR_KEY_HERE"}}}}, indent=2)) return if args.cmd == "register": cap = call(args.base_url, "", "/api/captcha") if not cap.get("ok"): show(cap) print("Captcha: %s" % cap["question"]) answer = input("Answer: ").strip() password = args.password or getpass.getpass("Password (8+ characters): ") result = call(args.base_url, "", "/api/developer", "POST", { "action": "register", "email": args.email, "password": password, "captcha_token": cap["captcha_token"], "captcha_answer": answer}) show(result) if result.get("api_key"): print("\nSave this API key now — it is shown only once.") return key = load_key(args) if not key: print("No API key. Run: tempjar set-key (or set TEMPJAR_API_KEY)") sys.exit(1) if args.cmd == "domains": show(call(args.base_url, key, "/api/v1/domains")) elif args.cmd == "usage": show(call(args.base_url, key, "/api/v1/usage")) elif args.cmd == "inboxes": show(call(args.base_url, key, "/api/v1/inboxes")) elif args.cmd == "create": data = {"handle": args.handle, "domain": args.domain, "lifespan": args.lifespan} if args.password: data["password"] = args.password show(call(args.base_url, key, "/api/v1/inboxes", "POST", data)) elif args.cmd == "messages": show(call(args.base_url, key, "/api/v1/inboxes/%s/messages?limit=%s" % (args.inbox_id, args.limit))) elif args.cmd == "message": show(call(args.base_url, key, "/api/v1/inboxes/%s/messages/%s" % (args.inbox_id, args.message_id))) elif args.cmd == "delete": show(call(args.base_url, key, "/api/v1/inboxes/%s" % args.inbox_id, "DELETE")) if __name__ == "__main__": main()