@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.
- package/dist/cli.js +154 -15
- 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
|
-
|
|
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
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
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
|
|
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.
|
|
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
|
}
|