airgen-cli 0.1.0 → 0.1.1
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/client.d.ts +4 -0
- package/dist/client.js +51 -5
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* AIRGen HTTP API client with JWT authentication.
|
|
3
3
|
*
|
|
4
4
|
* Handles login, token caching, and automatic refresh.
|
|
5
|
+
* Persists session tokens to ~/.airgen-session for reuse across CLI invocations.
|
|
5
6
|
*/
|
|
6
7
|
export interface ClientConfig {
|
|
7
8
|
apiUrl: string;
|
|
@@ -13,6 +14,9 @@ export declare class AirgenClient {
|
|
|
13
14
|
private auth;
|
|
14
15
|
private loginPromise;
|
|
15
16
|
constructor(config: ClientConfig);
|
|
17
|
+
private loadSession;
|
|
18
|
+
private saveSession;
|
|
19
|
+
private clearSession;
|
|
16
20
|
/** The base API URL this client is configured for. */
|
|
17
21
|
get apiUrl(): string;
|
|
18
22
|
get<T = unknown>(path: string, query?: Record<string, string | number | undefined>): Promise<T>;
|
package/dist/client.js
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
* AIRGen HTTP API client with JWT authentication.
|
|
3
3
|
*
|
|
4
4
|
* Handles login, token caching, and automatic refresh.
|
|
5
|
+
* Persists session tokens to ~/.airgen-session for reuse across CLI invocations.
|
|
5
6
|
*/
|
|
7
|
+
import { readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
const SESSION_FILE = join(homedir(), ".airgen-session");
|
|
6
11
|
export class AirgenClient {
|
|
7
12
|
config;
|
|
8
13
|
auth = {
|
|
@@ -13,6 +18,44 @@ export class AirgenClient {
|
|
|
13
18
|
loginPromise = null;
|
|
14
19
|
constructor(config) {
|
|
15
20
|
this.config = config;
|
|
21
|
+
this.loadSession();
|
|
22
|
+
}
|
|
23
|
+
loadSession() {
|
|
24
|
+
try {
|
|
25
|
+
const raw = readFileSync(SESSION_FILE, "utf-8");
|
|
26
|
+
const session = JSON.parse(raw);
|
|
27
|
+
// Only reuse session if it matches current config
|
|
28
|
+
if (session.apiUrl === this.config.apiUrl && session.email === this.config.email) {
|
|
29
|
+
this.auth = {
|
|
30
|
+
accessToken: session.accessToken,
|
|
31
|
+
refreshToken: session.refreshToken,
|
|
32
|
+
tokenExpiresAt: session.tokenExpiresAt,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// No session file or invalid — start fresh
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
saveSession() {
|
|
41
|
+
try {
|
|
42
|
+
writeFileSync(SESSION_FILE, JSON.stringify({
|
|
43
|
+
apiUrl: this.config.apiUrl,
|
|
44
|
+
email: this.config.email,
|
|
45
|
+
accessToken: this.auth.accessToken,
|
|
46
|
+
refreshToken: this.auth.refreshToken,
|
|
47
|
+
tokenExpiresAt: this.auth.tokenExpiresAt,
|
|
48
|
+
}), { mode: 0o600 });
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Non-fatal — session just won't persist
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
clearSession() {
|
|
55
|
+
try {
|
|
56
|
+
unlinkSync(SESSION_FILE);
|
|
57
|
+
}
|
|
58
|
+
catch { /* ignore */ }
|
|
16
59
|
}
|
|
17
60
|
/** The base API URL this client is configured for. */
|
|
18
61
|
get apiUrl() {
|
|
@@ -53,7 +96,7 @@ export class AirgenClient {
|
|
|
53
96
|
headers["Authorization"] = `Bearer ${this.auth.accessToken}`;
|
|
54
97
|
}
|
|
55
98
|
if (this.auth.refreshToken) {
|
|
56
|
-
headers["Cookie"] = `
|
|
99
|
+
headers["Cookie"] = `refreshToken=${this.auth.refreshToken}`;
|
|
57
100
|
}
|
|
58
101
|
const url = `${this.config.apiUrl}${path}`;
|
|
59
102
|
const res = await globalThis.fetch(url, { method: "POST", headers, body });
|
|
@@ -105,7 +148,7 @@ export class AirgenClient {
|
|
|
105
148
|
headers["Content-Type"] = "application/json";
|
|
106
149
|
}
|
|
107
150
|
if (this.auth.refreshToken) {
|
|
108
|
-
headers["Cookie"] = `
|
|
151
|
+
headers["Cookie"] = `refreshToken=${this.auth.refreshToken}`;
|
|
109
152
|
}
|
|
110
153
|
const url = `${this.config.apiUrl}${path}`;
|
|
111
154
|
try {
|
|
@@ -194,11 +237,12 @@ export class AirgenClient {
|
|
|
194
237
|
this.auth.accessToken = data.token;
|
|
195
238
|
this.auth.tokenExpiresAt = decodeTokenExpiry(data.token);
|
|
196
239
|
this.auth.refreshToken = extractRefreshToken(res);
|
|
240
|
+
this.saveSession();
|
|
197
241
|
}
|
|
198
242
|
async refresh() {
|
|
199
243
|
const headers = {};
|
|
200
244
|
if (this.auth.refreshToken) {
|
|
201
|
-
headers["Cookie"] = `
|
|
245
|
+
headers["Cookie"] = `refreshToken=${this.auth.refreshToken}`;
|
|
202
246
|
}
|
|
203
247
|
const res = await globalThis.fetch(`${this.config.apiUrl}/auth/refresh`, {
|
|
204
248
|
method: "POST",
|
|
@@ -207,6 +251,7 @@ export class AirgenClient {
|
|
|
207
251
|
if (!res.ok) {
|
|
208
252
|
// Refresh failed — clear state and re-login
|
|
209
253
|
this.auth = { accessToken: null, refreshToken: null, tokenExpiresAt: 0 };
|
|
254
|
+
this.clearSession();
|
|
210
255
|
await this.login();
|
|
211
256
|
return;
|
|
212
257
|
}
|
|
@@ -217,6 +262,7 @@ export class AirgenClient {
|
|
|
217
262
|
if (newRefresh) {
|
|
218
263
|
this.auth.refreshToken = newRefresh;
|
|
219
264
|
}
|
|
265
|
+
this.saveSession();
|
|
220
266
|
}
|
|
221
267
|
}
|
|
222
268
|
// ── Error class ───────────────────────────────────────────────
|
|
@@ -248,8 +294,8 @@ function extractRefreshToken(res) {
|
|
|
248
294
|
const setCookie = res.headers.get("set-cookie");
|
|
249
295
|
if (!setCookie)
|
|
250
296
|
return null;
|
|
251
|
-
// Parse
|
|
252
|
-
const match = setCookie.match(/
|
|
297
|
+
// Parse refreshToken cookie value
|
|
298
|
+
const match = setCookie.match(/refreshToken=([^;]+)/);
|
|
253
299
|
return match ? match[1] : null;
|
|
254
300
|
}
|
|
255
301
|
function buildQueryString(params) {
|