@punkcode/cli 0.1.15 → 0.1.16

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.
Files changed (2) hide show
  1. package/dist/cli.js +154 -15
  2. package/package.json +2 -1
package/dist/cli.js CHANGED
@@ -169,7 +169,7 @@ function runClaude(options, callbacks) {
169
169
  systemPrompt: opts.systemPrompt ?? { type: "preset", preset: "claude_code" },
170
170
  ...options.workingDirectory && { cwd: options.workingDirectory },
171
171
  ...options.sessionId && { resume: options.sessionId },
172
- maxThinkingTokens: 1e4,
172
+ thinking: { type: "adaptive" },
173
173
  includePartialMessages: true,
174
174
  canUseTool: async (toolName, input, toolOpts) => {
175
175
  if (!callbacks.onPermissionRequest) {
@@ -1604,6 +1604,135 @@ async function handleGetContext(socket, msg, defaultCwd) {
1604
1604
 
1605
1605
  // src/commands/login.ts
1606
1606
  import readline from "readline";
1607
+
1608
+ // src/lib/qr-login.ts
1609
+ import qrcode from "qrcode-terminal";
1610
+ var BACKEND_URL = process.env.BACKEND_URL || "https://api.punkcode.dev";
1611
+ var POLL_INTERVAL_MS = 3e3;
1612
+ var MAX_POLL_ATTEMPTS = 100;
1613
+ var QR_COLORS = [
1614
+ "\x1B[36m",
1615
+ // cyan
1616
+ "\x1B[35m",
1617
+ // magenta
1618
+ "\x1B[34m",
1619
+ // blue
1620
+ "\x1B[32m",
1621
+ // green
1622
+ "\x1B[33m",
1623
+ // yellow
1624
+ "\x1B[91m",
1625
+ // bright red
1626
+ "\x1B[96m",
1627
+ // bright cyan
1628
+ "\x1B[95m"
1629
+ // bright magenta
1630
+ ];
1631
+ var RESET = "\x1B[0m";
1632
+ async function loginWithQr() {
1633
+ const res = await fetch(`${BACKEND_URL}/auth/qr/init`, {
1634
+ method: "POST",
1635
+ headers: { "Content-Type": "application/json" }
1636
+ });
1637
+ if (!res.ok) {
1638
+ const body = await res.json().catch(() => ({}));
1639
+ throw new Error(body?.message ?? `Failed to init QR login: HTTP ${res.status}`);
1640
+ }
1641
+ const { pairingCode, secret, qrPayload } = await res.json();
1642
+ let qrLines = [];
1643
+ qrcode.generate(qrPayload, { small: true }, (code) => {
1644
+ qrLines = code.split("\n").filter(Boolean);
1645
+ });
1646
+ console.log();
1647
+ console.log(" Scan this QR code with the Punk app:");
1648
+ console.log();
1649
+ let colorIdx = 0;
1650
+ for (const line of qrLines) {
1651
+ console.log(` ${QR_COLORS[0]}${line}${RESET}`);
1652
+ }
1653
+ console.log();
1654
+ const spinner = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1655
+ let spinIdx = 0;
1656
+ let attempts = 0;
1657
+ let backoffMs = POLL_INTERVAL_MS;
1658
+ const recolorQr = () => {
1659
+ colorIdx = (colorIdx + 1) % QR_COLORS.length;
1660
+ const color = QR_COLORS[colorIdx];
1661
+ const moveUp = qrLines.length + 1;
1662
+ process.stdout.write(`\x1B[${moveUp}A`);
1663
+ for (const line of qrLines) {
1664
+ process.stdout.write(`\r ${color}${line}${RESET}
1665
+ `);
1666
+ }
1667
+ process.stdout.write("\n");
1668
+ };
1669
+ const colorTimer = setInterval(recolorQr, 800);
1670
+ const abort = new AbortController();
1671
+ const onSigint = () => {
1672
+ abort.abort();
1673
+ clearInterval(colorTimer);
1674
+ process.stdout.write("\r\x1B[K");
1675
+ console.log(" Login cancelled.");
1676
+ process.exit(0);
1677
+ };
1678
+ process.on("SIGINT", onSigint);
1679
+ try {
1680
+ while (attempts < MAX_POLL_ATTEMPTS) {
1681
+ process.stdout.write(`\r Waiting for confirmation... ${spinner[spinIdx++ % spinner.length]} `);
1682
+ await sleep(backoffMs);
1683
+ let poll;
1684
+ try {
1685
+ const pollRes = await fetch(
1686
+ `${BACKEND_URL}/auth/qr/poll?pairingCode=${pairingCode}&secret=${secret}`,
1687
+ { signal: abort.signal }
1688
+ );
1689
+ if (!pollRes.ok) {
1690
+ if (pollRes.status === 429) {
1691
+ backoffMs = Math.min(backoffMs * 2, 3e4);
1692
+ continue;
1693
+ }
1694
+ throw new Error(`Poll failed: HTTP ${pollRes.status}`);
1695
+ }
1696
+ poll = await pollRes.json();
1697
+ backoffMs = POLL_INTERVAL_MS;
1698
+ } catch {
1699
+ if (abort.signal.aborted) return;
1700
+ backoffMs = Math.min(backoffMs * 2, 3e4);
1701
+ continue;
1702
+ }
1703
+ attempts++;
1704
+ if (poll.status === "pending") {
1705
+ continue;
1706
+ }
1707
+ if (poll.status === "expired") {
1708
+ process.stdout.write("\r\x1B[K");
1709
+ throw new Error("QR code expired. Run `punk login` to try again.");
1710
+ }
1711
+ if (poll.status === "confirmed") {
1712
+ process.stdout.write("\r\x1B[K");
1713
+ saveAuth({
1714
+ idToken: poll.idToken,
1715
+ refreshToken: poll.refreshToken,
1716
+ expiresAt: Date.now() + parseInt(poll.expiresIn, 10) * 1e3,
1717
+ email: poll.email,
1718
+ uid: poll.uid
1719
+ });
1720
+ logger.info({ success: true, email: poll.email }, "Logged in");
1721
+ return;
1722
+ }
1723
+ }
1724
+ process.stdout.write("\r\x1B[K");
1725
+ throw new Error("QR code expired. Run `punk login` to try again.");
1726
+ } finally {
1727
+ clearInterval(colorTimer);
1728
+ process.removeListener("SIGINT", onSigint);
1729
+ }
1730
+ }
1731
+ function sleep(ms) {
1732
+ return new Promise((resolve) => setTimeout(resolve, ms));
1733
+ }
1734
+
1735
+ // src/commands/login.ts
1607
1736
  function prompt(question, hidden = false) {
1608
1737
  if (!hidden) {
1609
1738
  const rl = readline.createInterface({
@@ -1647,19 +1776,29 @@ function prompt(question, hidden = false) {
1647
1776
  stdin.on("data", onData);
1648
1777
  });
1649
1778
  }
1650
- async function login() {
1651
- const email = await prompt("Email: ");
1652
- const password = await prompt("Password: ", true);
1653
- if (!email || !password) {
1654
- logger.error("Email and password are required.");
1655
- process.exit(1);
1656
- }
1657
- try {
1658
- const auth = await signIn(email, password);
1659
- logger.info({ success: true, email: auth.email }, "Logged in");
1660
- } catch (err) {
1661
- logger.error({ err }, "Login failed");
1662
- process.exit(1);
1779
+ async function login(options) {
1780
+ if (options.email) {
1781
+ const email = await prompt("Email: ");
1782
+ const password = await prompt("Password: ", true);
1783
+ if (!email || !password) {
1784
+ logger.error("Email and password are required.");
1785
+ process.exit(1);
1786
+ }
1787
+ try {
1788
+ const auth = await signIn(email, password);
1789
+ logger.info({ success: true, email: auth.email }, "Logged in");
1790
+ } catch (err) {
1791
+ logger.error({ err }, "Login failed");
1792
+ process.exit(1);
1793
+ }
1794
+ } else {
1795
+ try {
1796
+ await loginWithQr();
1797
+ } catch (err) {
1798
+ logger.error({ err }, "QR login failed");
1799
+ console.log("\n Try email/password login: punk login --email");
1800
+ process.exit(1);
1801
+ }
1663
1802
  }
1664
1803
  }
1665
1804
  function logout() {
@@ -1670,7 +1809,7 @@ function logout() {
1670
1809
  // src/commands/index.ts
1671
1810
  function registerCommands(program2) {
1672
1811
  program2.command("connect").argument("[server]", "Backend server URL", "https://api.punkcode.dev").description("Connect to backend server").option("-t, --token <token>", "Authentication token").option("-d, --device-id <deviceId>", "Device identifier (defaults to hostname)").option("-n, --name <name>", "Custom device display name").option("--tag <tag>", "Device tag (repeatable, e.g. --tag home --tag mac --tag docker)", (val, acc) => [...acc, val], []).option("--cwd <directory>", "Working directory for sessions (default: ~/punk)").action(connect);
1673
- program2.command("login").description("Log in with your email and password").action(login);
1812
+ program2.command("login").description("Log in to your account").option("--email", "Use email/password login instead of QR code").action(login);
1674
1813
  program2.command("logout").description("Log out and clear stored credentials").action(logout);
1675
1814
  }
1676
1815
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@punkcode/cli",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Control Claude Code from your phone",
5
5
  "type": "module",
6
6
  "bin": {
@@ -59,6 +59,7 @@
59
59
  "execa": "^9.6.1",
60
60
  "pino": "^10.3.1",
61
61
  "pino-pretty": "^13.1.3",
62
+ "qrcode-terminal": "^0.12.0",
62
63
  "socket.io-client": "^4.8.3",
63
64
  "zod": "^4.3.6"
64
65
  }