groupchat 0.0.6 → 0.0.8
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/index.js +3356 -9
- package/package.json +4 -4
- package/dist/App-BVDV6OLB.js +0 -2756
- package/dist/chunk-EF672XXZ.js +0 -244
package/dist/chunk-EF672XXZ.js
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
// src/lib/config.ts
|
|
2
|
-
import { config as loadEnv } from "dotenv";
|
|
3
|
-
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") {
|
|
4
|
-
loadEnv();
|
|
5
|
-
}
|
|
6
|
-
var DEFAULT_CONFIG = {
|
|
7
|
-
consoleUrl: "https://app.groupchatty.com",
|
|
8
|
-
wsUrl: "wss://api.groupchatty.com/socket"
|
|
9
|
-
};
|
|
10
|
-
function getConfig() {
|
|
11
|
-
return {
|
|
12
|
-
consoleUrl: process.env.GROUPCHAT_CONSOLE_URL || DEFAULT_CONFIG.consoleUrl,
|
|
13
|
-
wsUrl: process.env.GROUPCHAT_WS_URL || DEFAULT_CONFIG.wsUrl
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// src/auth/auth-manager.ts
|
|
18
|
-
import crypto from "crypto";
|
|
19
|
-
import http from "http";
|
|
20
|
-
import open from "open";
|
|
21
|
-
|
|
22
|
-
// src/auth/token-storage.ts
|
|
23
|
-
import keytar from "keytar";
|
|
24
|
-
var SERVICE_NAME = "groupchat";
|
|
25
|
-
var TOKEN_ACCOUNT = "auth-token";
|
|
26
|
-
var EXPIRY_ACCOUNT = "auth-token-expiry";
|
|
27
|
-
async function storeToken(token, expiresAt) {
|
|
28
|
-
await keytar.setPassword(SERVICE_NAME, TOKEN_ACCOUNT, token);
|
|
29
|
-
await keytar.setPassword(SERVICE_NAME, EXPIRY_ACCOUNT, expiresAt.toISOString());
|
|
30
|
-
}
|
|
31
|
-
async function getToken() {
|
|
32
|
-
const token = await keytar.getPassword(SERVICE_NAME, TOKEN_ACCOUNT);
|
|
33
|
-
const expiryStr = await keytar.getPassword(SERVICE_NAME, EXPIRY_ACCOUNT);
|
|
34
|
-
if (!token || !expiryStr) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
const expiresAt = new Date(expiryStr);
|
|
38
|
-
if (expiresAt <= /* @__PURE__ */ new Date()) {
|
|
39
|
-
await clearToken();
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return { token, expiresAt };
|
|
43
|
-
}
|
|
44
|
-
async function clearToken() {
|
|
45
|
-
await keytar.deletePassword(SERVICE_NAME, TOKEN_ACCOUNT);
|
|
46
|
-
await keytar.deletePassword(SERVICE_NAME, EXPIRY_ACCOUNT);
|
|
47
|
-
}
|
|
48
|
-
async function hasValidToken() {
|
|
49
|
-
const stored = await getToken();
|
|
50
|
-
return stored !== null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// src/auth/auth-manager.ts
|
|
54
|
-
function generateState() {
|
|
55
|
-
return crypto.randomBytes(32).toString("hex");
|
|
56
|
-
}
|
|
57
|
-
async function findAvailablePort(startPort, endPort) {
|
|
58
|
-
for (let port = startPort; port <= endPort; port++) {
|
|
59
|
-
const available = await new Promise((resolve) => {
|
|
60
|
-
const server = http.createServer();
|
|
61
|
-
server.once("error", () => resolve(false));
|
|
62
|
-
server.once("listening", () => {
|
|
63
|
-
server.close();
|
|
64
|
-
resolve(true);
|
|
65
|
-
});
|
|
66
|
-
server.listen(port, "127.0.0.1");
|
|
67
|
-
});
|
|
68
|
-
if (available) {
|
|
69
|
-
return port;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
throw new Error(`No available port found in range ${startPort}-${endPort}`);
|
|
73
|
-
}
|
|
74
|
-
var SUCCESS_HTML = `
|
|
75
|
-
<!DOCTYPE html>
|
|
76
|
-
<html>
|
|
77
|
-
<head>
|
|
78
|
-
<meta charset="utf-8" />
|
|
79
|
-
<title>Terminal Chat - Authentication Successful</title>
|
|
80
|
-
<style>
|
|
81
|
-
body {
|
|
82
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;
|
|
83
|
-
background: #1F1F1F;
|
|
84
|
-
color: #CCCCCC;
|
|
85
|
-
display: flex;
|
|
86
|
-
align-items: center;
|
|
87
|
-
justify-content: center;
|
|
88
|
-
min-height: 100vh;
|
|
89
|
-
margin: 0;
|
|
90
|
-
}
|
|
91
|
-
.container { text-align: center; padding: 2rem; }
|
|
92
|
-
.success { color: #4EC9B0; font-size: 1.5rem; margin-bottom: 1rem; }
|
|
93
|
-
.message { color: #9D9D9D; }
|
|
94
|
-
</style>
|
|
95
|
-
</head>
|
|
96
|
-
<body>
|
|
97
|
-
<div class="container">
|
|
98
|
-
<div class="success">✓ Authentication successful!</div>
|
|
99
|
-
<div class="message">You can close this window and return to the terminal.</div>
|
|
100
|
-
</div>
|
|
101
|
-
</body>
|
|
102
|
-
</html>
|
|
103
|
-
`;
|
|
104
|
-
var ERROR_HTML = (message) => `
|
|
105
|
-
<!DOCTYPE html>
|
|
106
|
-
<html>
|
|
107
|
-
<head>
|
|
108
|
-
<meta charset="utf-8" />
|
|
109
|
-
<title>Terminal Chat - Authentication Failed</title>
|
|
110
|
-
<style>
|
|
111
|
-
body {
|
|
112
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace;
|
|
113
|
-
background: #1F1F1F;
|
|
114
|
-
color: #CCCCCC;
|
|
115
|
-
display: flex;
|
|
116
|
-
align-items: center;
|
|
117
|
-
justify-content: center;
|
|
118
|
-
min-height: 100vh;
|
|
119
|
-
margin: 0;
|
|
120
|
-
}
|
|
121
|
-
.container { text-align: center; padding: 2rem; }
|
|
122
|
-
.error { color: #F85149; font-size: 1.5rem; margin-bottom: 1rem; }
|
|
123
|
-
.message { color: #9D9D9D; }
|
|
124
|
-
</style>
|
|
125
|
-
</head>
|
|
126
|
-
<body>
|
|
127
|
-
<div class="container">
|
|
128
|
-
<div class="error">\u2717 Authentication failed</div>
|
|
129
|
-
<div class="message">${message}</div>
|
|
130
|
-
</div>
|
|
131
|
-
</body>
|
|
132
|
-
</html>
|
|
133
|
-
`;
|
|
134
|
-
function startAuthServer(port, expectedState, timeoutMs = 5 * 60 * 1e3) {
|
|
135
|
-
return new Promise((resolve, reject) => {
|
|
136
|
-
let settled = false;
|
|
137
|
-
const server = http.createServer((req, res) => {
|
|
138
|
-
if (settled) {
|
|
139
|
-
res.writeHead(400);
|
|
140
|
-
res.end();
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (!req.url?.startsWith("/callback")) {
|
|
144
|
-
res.writeHead(404);
|
|
145
|
-
res.end();
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
const url = new URL(req.url, `http://localhost:${port}`);
|
|
149
|
-
const token = url.searchParams.get("token");
|
|
150
|
-
const state = url.searchParams.get("state");
|
|
151
|
-
const expiresAt = url.searchParams.get("expiresAt");
|
|
152
|
-
if (state !== expectedState) {
|
|
153
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
154
|
-
res.end(ERROR_HTML("Invalid state parameter. Please try again."));
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
if (!token) {
|
|
158
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
159
|
-
res.end(ERROR_HTML("No token received. Please try again."));
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
settled = true;
|
|
163
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
164
|
-
res.end(SUCCESS_HTML);
|
|
165
|
-
server.close();
|
|
166
|
-
clearTimeout(timeout);
|
|
167
|
-
resolve({
|
|
168
|
-
token,
|
|
169
|
-
state,
|
|
170
|
-
expiresAt: expiresAt || new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3).toISOString()
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
const timeout = setTimeout(() => {
|
|
174
|
-
if (!settled) {
|
|
175
|
-
settled = true;
|
|
176
|
-
server.close();
|
|
177
|
-
reject(new Error("Authentication timed out. Please try again."));
|
|
178
|
-
}
|
|
179
|
-
}, timeoutMs);
|
|
180
|
-
server.on("error", (err) => {
|
|
181
|
-
if (!settled) {
|
|
182
|
-
settled = true;
|
|
183
|
-
clearTimeout(timeout);
|
|
184
|
-
reject(err);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
server.listen(port, "127.0.0.1");
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
async function login(onStatusChange) {
|
|
191
|
-
const config = getConfig();
|
|
192
|
-
const state = generateState();
|
|
193
|
-
onStatusChange?.("Starting authentication server...");
|
|
194
|
-
let port;
|
|
195
|
-
try {
|
|
196
|
-
port = await findAvailablePort(8080, 8099);
|
|
197
|
-
} catch (err) {
|
|
198
|
-
return {
|
|
199
|
-
success: false,
|
|
200
|
-
error: `Failed to find available port: ${err}`
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
const serverPromise = startAuthServer(port, state);
|
|
204
|
-
const callbackUrl = `http://localhost:${port}/callback`;
|
|
205
|
-
const authUrl = `${config.consoleUrl}/auth/cli?state=${state}&redirect_uri=${encodeURIComponent(callbackUrl)}`;
|
|
206
|
-
onStatusChange?.("Opening browser for authentication...");
|
|
207
|
-
try {
|
|
208
|
-
await open(authUrl);
|
|
209
|
-
} catch (err) {
|
|
210
|
-
return {
|
|
211
|
-
success: false,
|
|
212
|
-
error: `Failed to open browser: ${err}`
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
onStatusChange?.("Waiting for authentication...");
|
|
216
|
-
try {
|
|
217
|
-
const result = await serverPromise;
|
|
218
|
-
onStatusChange?.("Storing credentials...");
|
|
219
|
-
await storeToken(result.token, new Date(result.expiresAt));
|
|
220
|
-
return { success: true };
|
|
221
|
-
} catch (err) {
|
|
222
|
-
return {
|
|
223
|
-
success: false,
|
|
224
|
-
error: err instanceof Error ? err.message : String(err)
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
async function logout() {
|
|
229
|
-
await clearToken();
|
|
230
|
-
}
|
|
231
|
-
async function isAuthenticated() {
|
|
232
|
-
return hasValidToken();
|
|
233
|
-
}
|
|
234
|
-
async function getCurrentToken() {
|
|
235
|
-
return getToken();
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export {
|
|
239
|
-
getConfig,
|
|
240
|
-
login,
|
|
241
|
-
logout,
|
|
242
|
-
isAuthenticated,
|
|
243
|
-
getCurrentToken
|
|
244
|
-
};
|