@clawpow/cli 0.1.10 → 0.1.11
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/commands/auth/login.js +16 -100
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/lib/auth.d.ts +15 -0
- package/dist/lib/auth.js +56 -0
- package/dist/lib/auth.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { createServer } from "node:http";
|
|
3
2
|
import chalk from "chalk";
|
|
4
3
|
import ora from "ora";
|
|
5
4
|
import open from "open";
|
|
6
5
|
import { saveCredentials, getCredentials, isTokenExpired } from "../../lib/config.js";
|
|
7
|
-
import {
|
|
6
|
+
import { requestDeviceCode, pollForToken, getTokenExpiry } from "../../lib/auth.js";
|
|
8
7
|
import { handleError } from "../../lib/errors.js";
|
|
9
8
|
export const loginCommand = new Command("login")
|
|
10
9
|
.description("Authenticate with ClawPow")
|
|
@@ -15,120 +14,37 @@ export const loginCommand = new Command("login")
|
|
|
15
14
|
console.log(chalk.green("✓") + ` Already logged in as ${chalk.bold(existing.email)}`);
|
|
16
15
|
return;
|
|
17
16
|
}
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
});
|
|
27
|
-
console.log("Opening browser for authentication...");
|
|
28
|
-
console.log(chalk.dim(`If the browser doesn't open, visit: ${authorizationUrl}`));
|
|
17
|
+
const device = await requestDeviceCode();
|
|
18
|
+
console.log();
|
|
19
|
+
console.log(chalk.bold(" Enter this code in your browser:"));
|
|
20
|
+
console.log();
|
|
21
|
+
console.log(` ${chalk.cyan.bold(device.user_code)}`);
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(chalk.dim(` ${device.verification_uri_complete}`));
|
|
24
|
+
console.log();
|
|
29
25
|
try {
|
|
30
|
-
await open(
|
|
26
|
+
await open(device.verification_uri_complete);
|
|
31
27
|
}
|
|
32
|
-
catch { /* URL
|
|
33
|
-
const spinner = ora("Waiting for
|
|
28
|
+
catch { /* URL printed above */ }
|
|
29
|
+
const spinner = ora("Waiting for authorization...").start();
|
|
34
30
|
try {
|
|
35
|
-
const
|
|
36
|
-
spinner.text = "Exchanging authorization code...";
|
|
37
|
-
const result = await workos.userManagement.authenticateWithCode({
|
|
38
|
-
code,
|
|
39
|
-
clientId,
|
|
40
|
-
codeVerifier,
|
|
41
|
-
});
|
|
31
|
+
const result = await pollForToken(device.device_code, device.interval, device.expires_in);
|
|
42
32
|
spinner.stop();
|
|
43
|
-
const email = result.user?.email ?? "unknown";
|
|
44
33
|
saveCredentials({
|
|
45
34
|
accessToken: result.accessToken,
|
|
46
|
-
refreshToken: result.refreshToken
|
|
35
|
+
refreshToken: result.refreshToken,
|
|
47
36
|
expiresAt: getTokenExpiry(result.accessToken),
|
|
48
|
-
email,
|
|
37
|
+
email: result.email,
|
|
49
38
|
});
|
|
50
|
-
console.log(chalk.green("✓") + ` Logged in as ${chalk.bold(email)}`);
|
|
39
|
+
console.log(chalk.green("✓") + ` Logged in as ${chalk.bold(result.email)}`);
|
|
51
40
|
}
|
|
52
41
|
catch (error) {
|
|
53
42
|
spinner.stop();
|
|
54
43
|
throw error;
|
|
55
44
|
}
|
|
56
|
-
finally {
|
|
57
|
-
close();
|
|
58
|
-
}
|
|
59
45
|
}
|
|
60
46
|
catch (error) {
|
|
61
47
|
handleError(error);
|
|
62
48
|
}
|
|
63
49
|
});
|
|
64
|
-
function startCallbackServer() {
|
|
65
|
-
return new Promise((resolve, reject) => {
|
|
66
|
-
let codeResolve;
|
|
67
|
-
let codeReject;
|
|
68
|
-
const codePromise = new Promise((res, rej) => {
|
|
69
|
-
codeResolve = res;
|
|
70
|
-
codeReject = rej;
|
|
71
|
-
});
|
|
72
|
-
const server = createServer(async (req, res) => {
|
|
73
|
-
const url = new URL(req.url, `http://localhost`);
|
|
74
|
-
if (url.pathname === "/callback") {
|
|
75
|
-
const code = url.searchParams.get("code");
|
|
76
|
-
const error = url.searchParams.get("error");
|
|
77
|
-
if (error) {
|
|
78
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
79
|
-
res.end("<html><body><h2>Authentication failed</h2><p>You can close this tab.</p></body></html>");
|
|
80
|
-
codeReject(new Error(`Authentication failed: ${error}`));
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
if (!code) {
|
|
84
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
85
|
-
res.end("<html><body><h2>Missing authorization code</h2></body></html>");
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
89
|
-
res.end("<html><body><h2>Authenticated!</h2><p>You can close this tab and return to your terminal.</p></body></html>");
|
|
90
|
-
codeResolve(code);
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
res.writeHead(404);
|
|
94
|
-
res.end();
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
server.on("error", (err) => {
|
|
98
|
-
if (err.code === "EADDRINUSE") {
|
|
99
|
-
reject(new Error("Port 19780 is in use. Please free it and try again."));
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
reject(err);
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
server.listen(19780, "127.0.0.1", () => {
|
|
106
|
-
const addr = server.address();
|
|
107
|
-
if (!addr || typeof addr === "string") {
|
|
108
|
-
reject(new Error("Failed to start callback server"));
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
resolve({
|
|
112
|
-
port: addr.port,
|
|
113
|
-
waitForCode: async () => {
|
|
114
|
-
const timeout = setTimeout(() => {
|
|
115
|
-
codeReject(new Error("Authentication timed out. Please try again."));
|
|
116
|
-
}, 5 * 60 * 1000);
|
|
117
|
-
try {
|
|
118
|
-
const code = await codePromise;
|
|
119
|
-
clearTimeout(timeout);
|
|
120
|
-
return code;
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
clearTimeout(timeout);
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
close: () => {
|
|
128
|
-
server.close();
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
50
|
//# sourceMappingURL=login.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,2BAA2B,CAAC;KACxC,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;QAClC,IAAI,QAAQ,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,yBAAyB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACtF,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAEzC,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACxD,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,yBAAyB,EAAE,CAAC,CAAC,CAAC;QAChE,OAAO,CAAC,GAAG,EAAE,CAAC;QAEd,IAAI,CAAC;YAAC,MAAM,IAAI,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,uBAAuB,CAAC,CAAC;QAEvF,MAAM,OAAO,GAAG,GAAG,CAAC,8BAA8B,CAAC,CAAC,KAAK,EAAE,CAAC;QAE5D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;YAC1F,OAAO,CAAC,IAAI,EAAE,CAAC;YAEf,eAAe,CAAC;gBACd,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,SAAS,EAAE,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC;gBAC7C,KAAK,EAAE,MAAM,CAAC,KAAK;aACpB,CAAC,CAAC;YAEH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,iBAAiB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC9E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
package/dist/lib/auth.d.ts
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { WorkOS } from "@workos-inc/node";
|
|
2
|
+
export interface DeviceAuthorizationResponse {
|
|
3
|
+
device_code: string;
|
|
4
|
+
user_code: string;
|
|
5
|
+
verification_uri: string;
|
|
6
|
+
verification_uri_complete: string;
|
|
7
|
+
expires_in: number;
|
|
8
|
+
interval: number;
|
|
9
|
+
}
|
|
10
|
+
export interface DeviceTokenResult {
|
|
11
|
+
accessToken: string;
|
|
12
|
+
refreshToken: string | null;
|
|
13
|
+
email: string;
|
|
14
|
+
}
|
|
2
15
|
export declare function getWorkOS(): WorkOS;
|
|
3
16
|
export declare function getWorkOSClientId(): string;
|
|
4
17
|
export declare function getTokenExpiry(jwt: string): number;
|
|
18
|
+
export declare function requestDeviceCode(): Promise<DeviceAuthorizationResponse>;
|
|
19
|
+
export declare function pollForToken(deviceCode: string, interval: number, expiresIn: number): Promise<DeviceTokenResult>;
|
|
5
20
|
export declare function refreshAccessToken(refreshToken: string): Promise<{
|
|
6
21
|
accessToken: string;
|
|
7
22
|
refreshToken: string | null;
|
package/dist/lib/auth.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { WorkOS } from "@workos-inc/node";
|
|
2
2
|
const WORKOS_CLIENT_ID = process.env.CLAWPOW_WORKOS_CLIENT_ID ?? "client_01KA6B3HHV243WCC23AJE79T04";
|
|
3
|
+
const WORKOS_DEVICE_AUTH_URL = "https://api.workos.com/user_management/authorize/device";
|
|
4
|
+
const WORKOS_DEVICE_TOKEN_URL = "https://api.workos.com/user_management/authenticate";
|
|
3
5
|
export function getWorkOS() {
|
|
4
6
|
return new WorkOS("", { clientId: WORKOS_CLIENT_ID });
|
|
5
7
|
}
|
|
@@ -24,6 +26,60 @@ export function getTokenExpiry(jwt) {
|
|
|
24
26
|
}
|
|
25
27
|
return Date.now() + 3600 * 1000;
|
|
26
28
|
}
|
|
29
|
+
export async function requestDeviceCode() {
|
|
30
|
+
const res = await fetch(WORKOS_DEVICE_AUTH_URL, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: { "Content-Type": "application/json" },
|
|
33
|
+
body: JSON.stringify({ client_id: WORKOS_CLIENT_ID }),
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
const text = await res.text();
|
|
37
|
+
throw new Error(`Device authorization request failed (${res.status}): ${text}`);
|
|
38
|
+
}
|
|
39
|
+
return res.json();
|
|
40
|
+
}
|
|
41
|
+
export async function pollForToken(deviceCode, interval, expiresIn) {
|
|
42
|
+
const deadline = Date.now() + expiresIn * 1000;
|
|
43
|
+
let pollInterval = interval * 1000;
|
|
44
|
+
while (Date.now() < deadline) {
|
|
45
|
+
await new Promise((r) => setTimeout(r, pollInterval));
|
|
46
|
+
const res = await fetch(WORKOS_DEVICE_TOKEN_URL, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: { "Content-Type": "application/json" },
|
|
49
|
+
body: JSON.stringify({
|
|
50
|
+
client_id: WORKOS_CLIENT_ID,
|
|
51
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
52
|
+
device_code: deviceCode,
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
if (res.ok) {
|
|
56
|
+
const data = await res.json();
|
|
57
|
+
return {
|
|
58
|
+
accessToken: data.access_token,
|
|
59
|
+
refreshToken: data.refresh_token ?? null,
|
|
60
|
+
email: data.user?.email ?? "unknown",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const error = await res.json().catch(() => ({ error: "unknown" }));
|
|
64
|
+
const errorCode = error.error ?? error.code;
|
|
65
|
+
if (errorCode === "authorization_pending") {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
else if (errorCode === "slow_down") {
|
|
69
|
+
pollInterval += 5000;
|
|
70
|
+
}
|
|
71
|
+
else if (errorCode === "access_denied") {
|
|
72
|
+
throw new Error("Authorization was denied by the user.");
|
|
73
|
+
}
|
|
74
|
+
else if (errorCode === "expired_token") {
|
|
75
|
+
throw new Error("Device code expired. Please try again.");
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
throw new Error(`Authentication failed: ${errorCode}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
throw new Error("Device code expired. Please try again.");
|
|
82
|
+
}
|
|
27
83
|
export async function refreshAccessToken(refreshToken) {
|
|
28
84
|
try {
|
|
29
85
|
const workos = getWorkOS();
|
package/dist/lib/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/lib/auth.ts"],"names":[],"mappings":"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"}
|